These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / fc.c
1 /*
2  * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stddef.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <byteswap.h>
33 #include <ipxe/refcnt.h>
34 #include <ipxe/list.h>
35 #include <ipxe/tables.h>
36 #include <ipxe/timer.h>
37 #include <ipxe/retry.h>
38 #include <ipxe/interface.h>
39 #include <ipxe/xfer.h>
40 #include <ipxe/iobuf.h>
41 #include <ipxe/fc.h>
42 #include <ipxe/fcels.h>
43 #include <ipxe/fcns.h>
44
45 /** @file
46  *
47  * Fibre Channel
48  *
49  */
50
51 /** List of Fibre Channel ports */
52 LIST_HEAD ( fc_ports );
53
54 /** List of Fibre Channel peers */
55 LIST_HEAD ( fc_peers );
56
57 /******************************************************************************
58  *
59  * Well-known addresses
60  *
61  ******************************************************************************
62  */
63
64 /** Unassigned port ID */
65 struct fc_port_id fc_empty_port_id = { .bytes = { 0x00, 0x00, 0x00 } };
66
67 /** F_Port contoller port ID */
68 struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } };
69
70 /** Generic services port ID */
71 struct fc_port_id fc_gs_port_id = { .bytes = { 0xff, 0xff, 0xfc } };
72
73 /** Point-to-point low port ID */
74 struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } };
75
76 /** Point-to-point high port ID */
77 struct fc_port_id fc_ptp_high_port_id = { .bytes = { 0x01, 0x01, 0x02 } };
78
79 /******************************************************************************
80  *
81  * Utility functions
82  *
83  ******************************************************************************
84  */
85
86 /**
87  * Format Fibre Channel port ID
88  *
89  * @v id                Fibre Channel port ID
90  * @ret id_text         Port ID text
91  */
92 const char * fc_id_ntoa ( const struct fc_port_id *id ) {
93         static char id_text[ FC_PORT_ID_STRLEN + 1 /* NUL */ ];
94
95         snprintf ( id_text, sizeof ( id_text ), "%02x.%02x.%02x",
96                    id->bytes[0], id->bytes[1], id->bytes[2] );
97         return id_text;
98 }
99
100 /**
101  * Parse Fibre Channel port ID
102  *
103  * @v id_text           Port ID text
104  * @ret id              Fibre Channel port ID
105  * @ret rc              Return status code
106  */
107 int fc_id_aton ( const char *id_text, struct fc_port_id *id ) {
108         char *ptr = ( ( char * ) id_text );
109         unsigned int i = 0;
110
111         while ( 1 ) {
112                 id->bytes[i++] = strtoul ( ptr, &ptr, 16 );
113                 if ( i == sizeof ( id->bytes ) )
114                         return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
115                 if ( *ptr != '.' )
116                         return -EINVAL;
117                 ptr++;
118         }
119 }
120
121 /**
122  * Format Fibre Channel WWN
123  *
124  * @v wwn               Fibre Channel WWN
125  * @ret wwn_text        WWN text
126  */
127 const char * fc_ntoa ( const struct fc_name *wwn ) {
128         static char wwn_text[ FC_NAME_STRLEN + 1 /* NUL */ ];
129
130         snprintf ( wwn_text, sizeof ( wwn_text ),
131                    "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
132                    wwn->bytes[0], wwn->bytes[1], wwn->bytes[2], wwn->bytes[3],
133                    wwn->bytes[4], wwn->bytes[5], wwn->bytes[6], wwn->bytes[7] );
134         return wwn_text;
135 }
136
137 /**
138  * Parse Fibre Channel WWN
139  *
140  * @v wwn_text          WWN text
141  * @ret wwn             Fibre Channel WWN
142  * @ret rc              Return status code
143  */
144 int fc_aton ( const char *wwn_text, struct fc_name *wwn ) {
145         char *ptr = ( ( char * ) wwn_text );
146         unsigned int i = 0;
147
148         while ( 1 ) {
149                 wwn->bytes[i++] = strtoul ( ptr, &ptr, 16 );
150                 if ( i == sizeof ( wwn->bytes ) )
151                         return ( ( *ptr == '\0' ) ? 0 : -EINVAL );
152                 if ( *ptr != ':' )
153                         return -EINVAL;
154                 ptr++;
155         }
156 }
157
158 /**
159  * Fill Fibre Channel socket address
160  *
161  * @v sa_fc             Fibre Channel socket address to fill in
162  * @v id                Fibre Channel port ID
163  * @ret sa              Socket address
164  */
165 struct sockaddr * fc_fill_sockaddr ( struct sockaddr_fc *sa_fc,
166                                      struct fc_port_id *id ) {
167         union {
168                 struct sockaddr sa;
169                 struct sockaddr_fc fc;
170         } *u = container_of ( sa_fc, typeof ( *u ), fc );
171
172         memset ( sa_fc, 0, sizeof ( *sa_fc ) );
173         sa_fc->sfc_family = AF_FC;
174         memcpy ( &sa_fc->sfc_port_id, id, sizeof ( sa_fc->sfc_port_id ) );
175         return &u->sa;
176 }
177
178 /******************************************************************************
179  *
180  * Fibre Channel link state
181  *
182  ******************************************************************************
183  */
184
185 /** Default link status code */
186 #define EUNKNOWN_LINK_STATUS __einfo_error ( EINFO_EUNKNOWN_LINK_STATUS )
187 #define EINFO_EUNKNOWN_LINK_STATUS \
188         __einfo_uniqify ( EINFO_EINPROGRESS, 0x01, "Unknown" )
189
190 /**
191  * Mark Fibre Channel link as up
192  *
193  * @v link              Fibre Channel link state monitor
194  */
195 static void fc_link_up ( struct fc_link_state *link ) {
196
197         /* Stop retry timer */
198         stop_timer ( &link->timer );
199
200         /* Record link state */
201         link->rc = 0;
202 }
203
204 /**
205  * Mark Fibre Channel link as down
206  *
207  * @v link              Fibre Channel link state monitor
208  * @v rc                Link state
209  */
210 static void fc_link_err ( struct fc_link_state *link, int rc ) {
211
212         /* Record link state */
213         if ( rc == 0 )
214                 rc = -EUNKNOWN_LINK_STATUS;
215         link->rc = rc;
216
217         /* Schedule another link examination */
218         start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
219 }
220
221 /**
222  * Examine Fibre Channel link state
223  *
224  * @v link              Fibre Channel link state monitor
225  */
226 static void fc_link_examine ( struct fc_link_state *link ) {
227
228         link->examine ( link );
229 }
230
231 /**
232  * Handle Fibre Channel link retry timer expiry
233  */
234 static void fc_link_expired ( struct retry_timer *timer, int over __unused ) {
235         struct fc_link_state *link =
236                 container_of ( timer, struct fc_link_state, timer );
237
238         /* Schedule another link examination */
239         start_timer_fixed ( &link->timer, FC_LINK_RETRY_DELAY );
240
241         /* Examine link */
242         fc_link_examine ( link );
243 }
244
245 /**
246  * Initialise Fibre Channel link state monitor
247  *
248  * @v link              Fibre Channel link state monitor
249  * @v examine           Examine link state method
250  * @v refcnt            Reference counter
251  */
252 static void fc_link_init ( struct fc_link_state *link,
253                            void ( * examine ) ( struct fc_link_state *link ),
254                            struct refcnt *refcnt ) {
255
256         link->rc = -EUNKNOWN_LINK_STATUS;
257         timer_init ( &link->timer, fc_link_expired, refcnt );
258         link->examine = examine;
259 }
260
261 /**
262  * Start monitoring Fibre Channel link state
263  *
264  * @v link              Fibre Channel link state monitor
265  */
266 static void fc_link_start ( struct fc_link_state *link ) {
267         start_timer_nodelay ( &link->timer );
268 }
269
270 /**
271  * Stop monitoring Fibre Channel link state
272  *
273  * @v link              Fibre Channel link state monitor
274  */
275 static void fc_link_stop ( struct fc_link_state *link ) {
276         stop_timer ( &link->timer );
277 }
278
279 /******************************************************************************
280  *
281  * Fibre Channel exchanges
282  *
283  ******************************************************************************
284  */
285
286 /** A Fibre Channel exchange */
287 struct fc_exchange {
288         /** Reference count */
289         struct refcnt refcnt;
290         /** Fibre Channel port */
291         struct fc_port *port;
292         /** List of active exchanges within this port */
293         struct list_head list;
294
295         /** Peer port ID */
296         struct fc_port_id peer_port_id;
297         /** Data structure type */
298         unsigned int type;
299         /** Flags */
300         unsigned int flags;
301         /** Local exchange ID */
302         uint16_t xchg_id;
303         /** Peer exchange ID */
304         uint16_t peer_xchg_id;
305         /** Active sequence ID */
306         uint8_t seq_id;
307         /** Active sequence count */
308         uint16_t seq_cnt;
309
310         /** Timeout timer */
311         struct retry_timer timer;
312
313         /** Upper-layer protocol interface */
314         struct interface ulp;
315 };
316
317 /** Fibre Channel exchange flags */
318 enum fc_exchange_flags {
319         /** We are the exchange originator */
320         FC_XCHG_ORIGINATOR = 0x0001,
321         /** We have the sequence initiative */
322         FC_XCHG_SEQ_INITIATIVE = 0x0002,
323         /** This is the first sequence of the exchange */
324         FC_XCHG_SEQ_FIRST = 0x0004,
325 };
326
327 /** Fibre Channel timeout */
328 #define FC_TIMEOUT ( 1 * TICKS_PER_SEC )
329
330 /**
331  * Create local Fibre Channel exchange identifier
332  *
333  * @ret xchg_id         Local exchange ID
334  */
335 static unsigned int fc_new_xchg_id ( void ) {
336         static uint16_t next_id = 0x0000;
337
338         /* We must avoid using FC_RX_ID_UNKNOWN (0xffff) */
339         next_id += 2;
340         return next_id;
341 }
342
343 /**
344  * Create local Fibre Channel sequence identifier
345  *
346  * @ret seq_id          Local sequence identifier
347  */
348 static unsigned int fc_new_seq_id ( void ) {
349         static uint8_t seq_id = 0x00;
350
351         return (++seq_id);
352 }
353
354 /**
355  * Free Fibre Channel exchange
356  *
357  * @v refcnt            Reference count
358  */
359 static void fc_xchg_free ( struct refcnt *refcnt ) {
360         struct fc_exchange *xchg =
361                 container_of ( refcnt, struct fc_exchange, refcnt );
362
363         assert ( ! timer_running ( &xchg->timer ) );
364         assert ( list_empty ( &xchg->list ) );
365
366         fc_port_put ( xchg->port );
367         free ( xchg );
368 }
369
370 /**
371  * Close Fibre Channel exchange
372  *
373  * @v xchg              Fibre Channel exchange
374  * @v rc                Reason for close
375  */
376 static void fc_xchg_close ( struct fc_exchange *xchg, int rc ) {
377         struct fc_port *port = xchg->port;
378
379         if ( rc != 0 ) {
380                 DBGC2 ( port, "FCXCHG %s/%04x closed: %s\n",
381                         port->name, xchg->xchg_id, strerror ( rc ) );
382         }
383
384         /* Stop timer */
385         stop_timer ( &xchg->timer );
386
387         /* If list still holds a reference, remove from list of open
388          * exchanges and drop list's reference.
389          */
390         if ( ! list_empty ( &xchg->list ) ) {
391                 list_del ( &xchg->list );
392                 INIT_LIST_HEAD ( &xchg->list );
393                 ref_put ( &xchg->refcnt );
394         }
395
396         /* Shutdown interfaces */
397         intf_shutdown ( &xchg->ulp, rc );
398 }
399
400 /**
401  * Handle exchange timeout
402  *
403  * @v timer             Timeout timer
404  * @v over              Failure indicator
405  */
406 static void fc_xchg_expired ( struct retry_timer *timer, int over __unused ) {
407         struct fc_exchange *xchg =
408                 container_of ( timer, struct fc_exchange, timer );
409         struct fc_port *port = xchg->port;
410
411         DBGC ( port, "FCXCHG %s/%04x timed out\n", port->name, xchg->xchg_id );
412
413         /* Terminate the exchange */
414         fc_xchg_close ( xchg, -ETIMEDOUT );
415 }
416
417 /**
418  * Check Fibre Channel exchange window
419  *
420  * @v xchg              Fibre Channel exchange
421  * @ret len             Length opf window
422  */
423 static size_t fc_xchg_window ( struct fc_exchange *xchg __unused ) {
424
425         /* We don't currently store the path MTU */
426         return FC_LOGIN_DEFAULT_MTU;
427 }
428
429 /**
430  * Allocate Fibre Channel I/O buffer
431  *
432  * @v xchg              Fibre Channel exchange
433  * @v len               Payload length
434  * @ret iobuf           I/O buffer, or NULL
435  */
436 static struct io_buffer * fc_xchg_alloc_iob ( struct fc_exchange *xchg,
437                                               size_t len ) {
438         struct fc_port *port = xchg->port;
439         struct io_buffer *iobuf;
440
441         iobuf = xfer_alloc_iob ( &port->transport,
442                                  ( sizeof ( struct fc_frame_header ) + len ) );
443         if ( iobuf ) {
444                 iob_reserve ( iobuf, sizeof ( struct fc_frame_header ) );
445         }
446         return iobuf;
447 }
448
449 /**
450  * Transmit data as part of a Fibre Channel exchange
451  *
452  * @v xchg              Fibre Channel exchange
453  * @v iobuf             I/O buffer
454  * @v meta              Data transfer metadata
455  * @ret rc              Return status code
456  */
457 static int fc_xchg_tx ( struct fc_exchange *xchg, struct io_buffer *iobuf,
458                         struct xfer_metadata *meta ) {
459         struct fc_port *port = xchg->port;
460         struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest );
461         struct fc_frame_header *fchdr;
462         unsigned int r_ctl;
463         unsigned int f_ctl_es;
464         int rc;
465
466         /* Sanity checks */
467         if ( ! ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) ) {
468                 DBGC ( port, "FCXCHG %s/%04x cannot transmit while not "
469                        "holding sequence initiative\n",
470                        port->name, xchg->xchg_id );
471                 rc = -EBUSY;
472                 goto done;
473         }
474
475         /* Calculate routing control */
476         switch ( xchg->type ) {
477         case FC_TYPE_ELS:
478                 r_ctl = FC_R_CTL_ELS;
479                 if ( meta->flags & XFER_FL_RESPONSE ) {
480                         r_ctl |= FC_R_CTL_SOL_CTRL;
481                 } else {
482                         r_ctl |= FC_R_CTL_UNSOL_CTRL;
483                 }
484                 break;
485         case FC_TYPE_CT:
486                 r_ctl = FC_R_CTL_DATA;
487                 if ( meta->flags & XFER_FL_RESPONSE ) {
488                         r_ctl |= FC_R_CTL_SOL_CTRL;
489                 } else {
490                         r_ctl |= FC_R_CTL_UNSOL_CTRL;
491                 }
492                 break;
493         default:
494                 r_ctl = FC_R_CTL_DATA;
495                 switch ( meta->flags &
496                          ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) {
497                 case ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ):
498                         r_ctl |= FC_R_CTL_CMD_STAT;
499                         break;
500                 case ( XFER_FL_CMD_STAT ):
501                         r_ctl |= FC_R_CTL_UNSOL_CMD;
502                         break;
503                 case ( XFER_FL_RESPONSE ):
504                         r_ctl |= FC_R_CTL_SOL_DATA;
505                         break;
506                 default:
507                         r_ctl |= FC_R_CTL_UNSOL_DATA;
508                         break;
509                 }
510                 break;
511         }
512
513         /* Calculate exchange and sequence control */
514         f_ctl_es = 0;
515         if ( ! ( xchg->flags & FC_XCHG_ORIGINATOR ) )
516                 f_ctl_es |= FC_F_CTL_ES_RESPONDER;
517         if ( xchg->flags & FC_XCHG_SEQ_FIRST )
518                 f_ctl_es |= FC_F_CTL_ES_FIRST;
519         if ( meta->flags & XFER_FL_OUT )
520                 f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_LAST );
521         if ( meta->flags & XFER_FL_OVER )
522                 f_ctl_es |= ( FC_F_CTL_ES_END | FC_F_CTL_ES_TRANSFER );
523
524         /* Create frame header */
525         fchdr = iob_push ( iobuf, sizeof ( *fchdr ) );
526         memset ( fchdr, 0, sizeof ( *fchdr ) );
527         fchdr->r_ctl = r_ctl;
528         memcpy ( &fchdr->d_id,
529                  ( dest ? &dest->sfc_port_id : &xchg->peer_port_id ),
530                  sizeof ( fchdr->d_id ) );
531         memcpy ( &fchdr->s_id, &port->port_id, sizeof ( fchdr->s_id ) );
532         fchdr->type = xchg->type;
533         fchdr->f_ctl_es = f_ctl_es;
534         fchdr->seq_id = xchg->seq_id;
535         fchdr->seq_cnt = htons ( xchg->seq_cnt++ );
536         fchdr->ox_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ?
537                                xchg->xchg_id : xchg->peer_xchg_id );
538         fchdr->rx_id = htons ( ( xchg->flags & FC_XCHG_ORIGINATOR ) ?
539                                xchg->peer_xchg_id : xchg->xchg_id );
540         if ( meta->flags & XFER_FL_ABS_OFFSET ) {
541                 fchdr->f_ctl_misc |= FC_F_CTL_MISC_REL_OFF;
542                 fchdr->parameter = htonl ( meta->offset );
543         }
544
545         /* Relinquish sequence initiative if applicable */
546         if ( meta->flags & XFER_FL_OVER ) {
547                 xchg->flags &= ~( FC_XCHG_SEQ_INITIATIVE | FC_XCHG_SEQ_FIRST );
548                 xchg->seq_cnt = 0;
549         }
550
551         /* Reset timeout */
552         start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
553
554         /* Deliver frame */
555         if ( ( rc = xfer_deliver_iob ( &port->transport,
556                                        iob_disown ( iobuf ) ) ) != 0 ) {
557                 DBGC ( port, "FCXCHG %s/%04x cannot transmit: %s\n",
558                        port->name, xchg->xchg_id, strerror ( rc ) );
559                 goto done;
560         }
561
562  done:
563         free_iob ( iobuf );
564         return rc;
565 }
566
567 /** Mapping from Fibre Channel routing control information to xfer metadata */
568 static const uint8_t fc_r_ctl_info_meta_flags[ FC_R_CTL_INFO_MASK + 1 ] = {
569         [FC_R_CTL_UNCAT]        = ( 0 ),
570         [FC_R_CTL_SOL_DATA]     = ( XFER_FL_RESPONSE ),
571         [FC_R_CTL_UNSOL_CTRL]   = ( XFER_FL_CMD_STAT ),
572         [FC_R_CTL_SOL_CTRL]     = ( XFER_FL_CMD_STAT ),
573         [FC_R_CTL_UNSOL_DATA]   = ( 0 ),
574         [FC_R_CTL_DATA_DESC]    = ( XFER_FL_CMD_STAT ),
575         [FC_R_CTL_UNSOL_CMD]    = ( XFER_FL_CMD_STAT ),
576         [FC_R_CTL_CMD_STAT]     = ( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ),
577 };
578
579 /**
580  * Receive data as part of a Fibre Channel exchange
581  *
582  * @v xchg              Fibre Channel exchange
583  * @v iobuf             I/O buffer
584  * @v meta              Data transfer metadata
585  * @ret rc              Return status code
586  */
587 static int fc_xchg_rx ( struct fc_exchange *xchg, struct io_buffer *iobuf,
588                         struct xfer_metadata *meta __unused ) {
589         struct fc_port *port = xchg->port;
590         struct fc_frame_header *fchdr = iobuf->data;
591         struct xfer_metadata fc_meta;
592         struct sockaddr_fc src;
593         struct sockaddr_fc dest;
594         int rc;
595
596         /* Record peer exchange ID */
597         xchg->peer_xchg_id =
598                 ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ?
599                         fchdr->rx_id : fchdr->ox_id );
600
601         /* Sequence checks */
602         if ( xchg->flags & FC_XCHG_SEQ_INITIATIVE ) {
603                 DBGC ( port, "FCXCHG %s/%04x received frame while holding "
604                        "sequence initiative\n", port->name, xchg->xchg_id );
605                 rc = -EBUSY;
606                 goto done;
607         }
608         if ( ntohs ( fchdr->seq_cnt ) != xchg->seq_cnt ) {
609                 DBGC ( port, "FCXCHG %s/%04x received out-of-order frame %d "
610                        "(expected %d)\n", port->name, xchg->xchg_id,
611                        ntohs ( fchdr->seq_cnt ), xchg->seq_cnt );
612                 rc = -EPIPE;
613                 goto done;
614         }
615         if ( xchg->seq_cnt == 0 )
616                 xchg->seq_id = fchdr->seq_id;
617         xchg->seq_cnt++;
618         if ( fchdr->seq_id != xchg->seq_id ) {
619                 DBGC ( port, "FCXCHG %s/%04x received frame for incorrect "
620                        "sequence %02x (expected %02x)\n", port->name,
621                        xchg->xchg_id, fchdr->seq_id, xchg->seq_id );
622                 rc = -EPIPE;
623                 goto done;
624         }
625
626         /* Check for end of sequence and transfer of sequence initiative */
627         if ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) {
628                 xchg->seq_cnt = 0;
629                 if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) {
630                         xchg->flags |= FC_XCHG_SEQ_INITIATIVE;
631                         xchg->seq_id = fc_new_seq_id();
632                 }
633         }
634
635         /* Construct metadata */
636         memset ( &fc_meta, 0, sizeof ( fc_meta ) );
637         fc_meta.flags =
638                 fc_r_ctl_info_meta_flags[ fchdr->r_ctl & FC_R_CTL_INFO_MASK ];
639         if ( fchdr->f_ctl_es & FC_F_CTL_ES_TRANSFER ) {
640                 fc_meta.flags |= XFER_FL_OVER;
641         }
642         if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) &&
643              ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) {
644                 fc_meta.flags |= XFER_FL_OUT;
645         }
646         if ( fchdr->f_ctl_misc & FC_F_CTL_MISC_REL_OFF ) {
647                 fc_meta.flags |= XFER_FL_ABS_OFFSET;
648                 fc_meta.offset = ntohl ( fchdr->parameter );
649         }
650         fc_meta.src = fc_fill_sockaddr ( &src, &fchdr->s_id );
651         fc_meta.dest = fc_fill_sockaddr ( &dest, &fchdr->d_id );
652
653         /* Reset timeout */
654         start_timer_fixed ( &xchg->timer, FC_TIMEOUT );
655
656         /* Deliver via exchange's ULP interface */
657         iob_pull ( iobuf, sizeof ( *fchdr ) );
658         if ( ( rc = xfer_deliver ( &xchg->ulp, iob_disown ( iobuf ),
659                                    &fc_meta ) ) != 0 ) {
660                 DBGC ( port, "FCXCHG %s/%04x cannot deliver frame: %s\n",
661                        port->name, xchg->xchg_id, strerror ( rc ) );
662                 goto done;
663         }
664
665         /* Close exchange if applicable */
666         if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_LAST ) &&
667              ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ) {
668                 fc_xchg_close ( xchg, 0 );
669         }
670
671  done:
672         free_iob ( iobuf );
673         return rc;
674 }
675
676 /** Fibre Channel exchange ULP interface operations */
677 static struct interface_operation fc_xchg_ulp_op[] = {
678         INTF_OP ( xfer_deliver, struct fc_exchange *, fc_xchg_tx ),
679         INTF_OP ( xfer_alloc_iob, struct fc_exchange *, fc_xchg_alloc_iob ),
680         INTF_OP ( xfer_window, struct fc_exchange *, fc_xchg_window ),
681         INTF_OP ( intf_close, struct fc_exchange *, fc_xchg_close ),
682 };
683
684 /** Fibre Channel exchange ULP interface descriptor */
685 static struct interface_descriptor fc_xchg_ulp_desc =
686         INTF_DESC ( struct fc_exchange, ulp, fc_xchg_ulp_op );
687
688 /**
689  * Create new Fibre Channel exchange
690  *
691  * @v port              Fibre Channel port
692  * @v peer_port_id      Peer port ID
693  * @ret xchg            Exchange, or NULL
694  */
695 static struct fc_exchange * fc_xchg_create ( struct fc_port *port,
696                                              struct fc_port_id *peer_port_id,
697                                              unsigned int type ) {
698         struct fc_exchange *xchg;
699
700         /* Allocate and initialise structure */
701         xchg = zalloc ( sizeof ( *xchg ) );
702         if ( ! xchg )
703                 return NULL;
704         ref_init ( &xchg->refcnt, fc_xchg_free );
705         intf_init ( &xchg->ulp, &fc_xchg_ulp_desc, &xchg->refcnt );
706         timer_init ( &xchg->timer, fc_xchg_expired, &xchg->refcnt );
707         xchg->port = fc_port_get ( port );
708         memcpy ( &xchg->peer_port_id, peer_port_id,
709                  sizeof ( xchg->peer_port_id ) );
710         xchg->type = type;
711         xchg->xchg_id = fc_new_xchg_id();
712         xchg->peer_xchg_id = FC_RX_ID_UNKNOWN;
713         xchg->seq_id = fc_new_seq_id();
714
715         /* Transfer reference to list of exchanges and return */
716         list_add ( &xchg->list, &port->xchgs );
717         return xchg;
718 }
719
720 /**
721  * Originate a new Fibre Channel exchange
722  *
723  * @v parent            Interface to which to attach
724  * @v port              Fibre Channel port
725  * @v peer_port_id      Peer port ID
726  * @ret xchg_id         Exchange ID, or negative error
727  */
728 int fc_xchg_originate ( struct interface *parent, struct fc_port *port,
729                         struct fc_port_id *peer_port_id, unsigned int type ) {
730         struct fc_exchange *xchg;
731
732         /* Allocate and initialise structure */
733         xchg = fc_xchg_create ( port, peer_port_id, type );
734         if ( ! xchg )
735                 return -ENOMEM;
736         xchg->flags = ( FC_XCHG_ORIGINATOR | FC_XCHG_SEQ_INITIATIVE |
737                         FC_XCHG_SEQ_FIRST );
738
739         DBGC2 ( port, "FCXCHG %s/%04x originating to %s (type %02x)\n",
740                 port->name, xchg->xchg_id, fc_id_ntoa ( &xchg->peer_port_id ),
741                 xchg->type );
742
743         /* Attach to parent interface and return */
744         intf_plug_plug ( &xchg->ulp, parent );
745         return xchg->xchg_id;
746 }
747
748 /**
749  * Open a new responder Fibre Channel exchange
750  *
751  * @v port              Fibre Channel port
752  * @v fchdr             Fibre Channel frame header
753  * @ret xchg            Fibre Channel exchange, or NULL
754  */
755 static struct fc_exchange * fc_xchg_respond ( struct fc_port *port,
756                                               struct fc_frame_header *fchdr ) {
757         struct fc_exchange *xchg;
758         struct fc_responder *responder;
759         unsigned int type = fchdr->type;
760         int rc;
761
762         /* Allocate and initialise structure */
763         xchg = fc_xchg_create ( port, &fchdr->s_id, type );
764         if ( ! xchg )
765                 return NULL;
766         xchg->seq_id = fchdr->seq_id;
767
768         DBGC2 ( port, "FCXCHG %s/%04x responding to %s xchg %04x (type "
769                 "%02x)\n", port->name, xchg->xchg_id,
770                 fc_id_ntoa ( &xchg->peer_port_id ),
771                 ntohs ( fchdr->ox_id ), xchg->type );
772
773         /* Find a responder, if any */
774         for_each_table_entry ( responder, FC_RESPONDERS ) {
775                 if ( responder->type == type ) {
776                         if ( ( rc = responder->respond ( &xchg->ulp, port,
777                                                          &fchdr->d_id,
778                                                          &fchdr->s_id ) ) !=0 ){
779                                 DBGC ( port, "FCXCHG %s/%04x could not "
780                                        "respond: %s\n", port->name,
781                                        xchg->xchg_id, strerror ( rc ) );
782                         }
783                 }
784                 break;
785         }
786
787         /* We may or may not have a ULP attached at this point, but
788          * the exchange does exist.
789          */
790         return xchg;
791 }
792
793 /******************************************************************************
794  *
795  * Fibre Channel ports
796  *
797  ******************************************************************************
798  */
799
800 /**
801  * Close Fibre Channel port
802  *
803  * @v port              Fibre Channel port
804  * @v rc                Reason for close
805  */
806 static void fc_port_close ( struct fc_port *port, int rc ) {
807         struct fc_exchange *xchg;
808         struct fc_exchange *tmp;
809
810         DBGC ( port, "FCPORT %s closed\n", port->name );
811
812         /* Log out port, if necessary */
813         if ( fc_link_ok ( &port->link ) )
814                 fc_port_logout ( port, rc );
815
816         /* Stop link monitor */
817         fc_link_stop ( &port->link );
818
819         /* Shut down interfaces */
820         intf_shutdown ( &port->transport, rc );
821         intf_shutdown ( &port->flogi, rc );
822         intf_shutdown ( &port->ns_plogi, rc );
823
824         /* Shut down any remaining exchanges */
825         list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list )
826                 fc_xchg_close ( xchg, rc );
827
828         /* Remove from list of ports */
829         list_del ( &port->list );
830         INIT_LIST_HEAD ( &port->list );
831 }
832
833 /**
834  * Identify Fibre Channel exchange by local exchange ID
835  *
836  * @v port              Fibre Channel port
837  * @v xchg_id           Local exchange ID
838  * @ret xchg            Fibre Channel exchange, or NULL
839  */
840 static struct fc_exchange * fc_port_demux ( struct fc_port *port,
841                                             unsigned int xchg_id ) {
842         struct fc_exchange *xchg;
843
844         list_for_each_entry ( xchg, &port->xchgs, list ) {
845                 if ( xchg->xchg_id == xchg_id )
846                         return xchg;
847         }
848         return NULL;
849 }
850
851 /**
852  * Handle received frame from Fibre Channel port
853  *
854  * @v port              Fibre Channel port
855  * @v iobuf             I/O buffer
856  * @v meta              Data transfer metadata
857  * @ret rc              Return status code
858  */
859 static int fc_port_deliver ( struct fc_port *port, struct io_buffer *iobuf,
860                              struct xfer_metadata *meta ) {
861         struct fc_frame_header *fchdr = iobuf->data;
862         unsigned int xchg_id;
863         struct fc_exchange *xchg;
864         int rc;
865
866         /* Sanity check */
867         if ( iob_len ( iobuf ) < sizeof ( *fchdr ) ) {
868                 DBGC ( port, "FCPORT %s received underlength frame (%zd "
869                        "bytes)\n", port->name, iob_len ( iobuf ) );
870                 rc = -EINVAL;
871                 goto err_sanity;
872         }
873
874         /* Verify local port ID */
875         if ( ( memcmp ( &fchdr->d_id, &port->port_id,
876                         sizeof ( fchdr->d_id ) ) != 0 ) &&
877              ( memcmp ( &fchdr->d_id, &fc_f_port_id,
878                         sizeof ( fchdr->d_id ) ) != 0 ) &&
879              ( memcmp ( &port->port_id, &fc_empty_port_id,
880                         sizeof ( port->port_id ) ) != 0 ) ) {
881                 DBGC ( port, "FCPORT %s received frame for incorrect port ID "
882                        "%s\n", port->name, fc_id_ntoa ( &fchdr->d_id ) );
883                 rc = -ENOTCONN;
884                 goto err_port_id;
885         }
886
887         /* Demultiplex amongst active exchanges */
888         xchg_id = ntohs ( ( fchdr->f_ctl_es & FC_F_CTL_ES_RESPONDER ) ?
889                           fchdr->ox_id : fchdr->rx_id );
890         xchg = fc_port_demux ( port, xchg_id );
891
892         /* If we have no active exchange and this frame starts a new
893          * exchange, try to create a new responder exchange
894          */
895         if ( ( fchdr->f_ctl_es & FC_F_CTL_ES_FIRST ) &&
896              ( fchdr->seq_cnt == 0 ) ) {
897
898                 /* Create new exchange */
899                 xchg = fc_xchg_respond ( port, fchdr );
900                 if ( ! xchg ) {
901                         DBGC ( port, "FCPORT %s cannot create new exchange\n",
902                                port->name );
903                         rc = -ENOMEM;
904                         goto err_respond;
905                 }
906         }
907
908         /* Fail if no exchange exists */
909         if ( ! xchg ) {
910                 DBGC ( port, "FCPORT %s xchg %04x unknown\n",
911                        port->name, xchg_id );
912                 rc = -ENOTCONN;
913                 goto err_no_xchg;
914         }
915
916         /* Pass received frame to exchange */
917         ref_get ( &xchg->refcnt );
918         if ( ( rc = fc_xchg_rx ( xchg, iob_disown ( iobuf ), meta ) ) != 0 )
919                 goto err_xchg_rx;
920
921  err_xchg_rx:
922         ref_put ( &xchg->refcnt );
923  err_no_xchg:
924  err_respond:
925  err_port_id:
926  err_sanity:
927         free_iob ( iobuf );
928         return rc;
929 }
930
931 /**
932  * Log in Fibre Channel port
933  *
934  * @v port              Fibre Channel port
935  * @v port_id           Local port ID
936  * @v link_node_wwn     Link node name
937  * @v link_port_wwn     Link port name
938  * @v has_fabric        Link is to a fabric
939  * @ret rc              Return status code
940  */
941 int fc_port_login ( struct fc_port *port, struct fc_port_id *port_id,
942                     const struct fc_name *link_node_wwn,
943                     const struct fc_name *link_port_wwn, int has_fabric ) {
944         struct fc_peer *peer;
945         struct fc_peer *tmp;
946         int rc;
947
948         /* Perform implicit logout if logged in and details differ */
949         if ( fc_link_ok ( &port->link ) &&
950              ( ( ( !! ( port->flags & FC_PORT_HAS_FABRIC ) ) !=
951                  ( !! has_fabric ) ) ||
952                ( memcmp ( &port->link_node_wwn, link_node_wwn,
953                           sizeof ( port->link_node_wwn ) ) != 0 ) ||
954                ( memcmp ( &port->link_port_wwn, link_port_wwn,
955                           sizeof ( port->link_port_wwn ) ) != 0 ) ||
956                ( has_fabric &&
957                  ( memcmp ( &port->port_id, port_id,
958                             sizeof ( port->port_id ) ) != 0 ) ) ) ) {
959                 fc_port_logout ( port, 0 );
960         }
961
962         /* Log in, if applicable */
963         if ( ! fc_link_ok ( &port->link ) ) {
964
965                 /* Record link port name */
966                 memcpy ( &port->link_node_wwn, link_node_wwn,
967                          sizeof ( port->link_node_wwn ) );
968                 memcpy ( &port->link_port_wwn, link_port_wwn,
969                          sizeof ( port->link_port_wwn ) );
970                 DBGC ( port, "FCPORT %s logged in to %s",
971                        port->name, fc_ntoa ( &port->link_node_wwn ) );
972                 DBGC ( port, " port %s\n", fc_ntoa ( &port->link_port_wwn ) );
973
974                 /* Calculate local (and possibly remote) port IDs */
975                 if ( has_fabric ) {
976                         port->flags |= FC_PORT_HAS_FABRIC;
977                         memcpy ( &port->port_id, port_id,
978                                  sizeof ( port->port_id ) );
979                 } else {
980                         port->flags &= ~FC_PORT_HAS_FABRIC;
981                         if ( memcmp ( &port->port_wwn, link_port_wwn,
982                                       sizeof ( port->port_wwn ) ) > 0 ) {
983                                 memcpy ( &port->port_id, &fc_ptp_high_port_id,
984                                          sizeof ( port->port_id ) );
985                                 memcpy ( &port->ptp_link_port_id,
986                                          &fc_ptp_low_port_id,
987                                          sizeof ( port->ptp_link_port_id ) );
988                         } else {
989                                 memcpy ( &port->port_id, &fc_ptp_low_port_id,
990                                          sizeof ( port->port_id ) );
991                                 memcpy ( &port->ptp_link_port_id,
992                                          &fc_ptp_high_port_id,
993                                          sizeof ( port->ptp_link_port_id ) );
994                         }
995                 }
996                 DBGC ( port, "FCPORT %s logged in via a %s, with local ID "
997                        "%s\n", port->name,
998                        ( ( port->flags & FC_PORT_HAS_FABRIC ) ?
999                          "fabric" : "point-to-point link" ),
1000                        fc_id_ntoa ( &port->port_id ) );
1001         }
1002
1003         /* Log in to name server, if attached to a fabric */
1004         if ( has_fabric && ! ( port->flags & FC_PORT_HAS_NS ) ) {
1005
1006                 DBGC ( port, "FCPORT %s attempting login to name server\n",
1007                        port->name );
1008
1009                 intf_restart ( &port->ns_plogi, -ECANCELED );
1010                 if ( ( rc = fc_els_plogi ( &port->ns_plogi, port,
1011                                            &fc_gs_port_id ) ) != 0 ) {
1012                         DBGC ( port, "FCPORT %s could not initiate name "
1013                                "server PLOGI: %s\n",
1014                                port->name, strerror ( rc ) );
1015                         fc_port_logout ( port, rc );
1016                         return rc;
1017                 }
1018         }
1019
1020         /* Record login */
1021         fc_link_up ( &port->link );
1022
1023         /* Notify peers of link state change */
1024         list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) {
1025                 fc_peer_get ( peer );
1026                 fc_link_examine ( &peer->link );
1027                 fc_peer_put ( peer );
1028         }
1029
1030         return 0;
1031 }
1032
1033 /**
1034  * Log out Fibre Channel port
1035  *
1036  * @v port              Fibre Channel port
1037  * @v rc                Reason for logout
1038  */
1039 void fc_port_logout ( struct fc_port *port, int rc ) {
1040         struct fc_peer *peer;
1041         struct fc_peer *tmp;
1042
1043         DBGC ( port, "FCPORT %s logged out: %s\n",
1044                port->name, strerror ( rc ) );
1045
1046         /* Erase port details */
1047         memset ( &port->port_id, 0, sizeof ( port->port_id ) );
1048         port->flags = 0;
1049
1050         /* Record logout */
1051         fc_link_err ( &port->link, rc );
1052
1053         /* Notify peers of link state change */
1054         list_for_each_entry_safe ( peer, tmp, &fc_peers, list ) {
1055                 fc_peer_get ( peer );
1056                 fc_link_examine ( &peer->link );
1057                 fc_peer_put ( peer );
1058         }
1059 }
1060
1061 /**
1062  * Handle FLOGI completion
1063  *
1064  * @v port              Fibre Channel port
1065  * @v rc                Reason for completion
1066  */
1067 static void fc_port_flogi_done ( struct fc_port *port, int rc ) {
1068
1069         intf_restart ( &port->flogi, rc );
1070
1071         if ( rc != 0 )
1072                 fc_port_logout ( port, rc );
1073 }
1074
1075 /**
1076  * Handle name server PLOGI completion
1077  *
1078  * @v port              Fibre Channel port
1079  * @v rc                Reason for completion
1080  */
1081 static void fc_port_ns_plogi_done ( struct fc_port *port, int rc ) {
1082
1083         intf_restart ( &port->ns_plogi, rc );
1084
1085         if ( rc == 0 ) {
1086                 port->flags |= FC_PORT_HAS_NS;
1087                 DBGC ( port, "FCPORT %s logged in to name server\n",
1088                        port->name );
1089         } else {
1090                 DBGC ( port, "FCPORT %s could not log in to name server: %s\n",
1091                        port->name, strerror ( rc ) );
1092                 /* Absence of a name server is not a fatal error */
1093         }
1094 }
1095
1096 /**
1097  * Examine Fibre Channel port link state
1098  *
1099  * @ link               Fibre Channel link state monitor
1100  */
1101 static void fc_port_examine ( struct fc_link_state *link ) {
1102         struct fc_port *port = container_of ( link, struct fc_port, link );
1103         int rc;
1104
1105         /* Do nothing if already logged in */
1106         if ( fc_link_ok ( &port->link ) )
1107                 return;
1108
1109         DBGC ( port, "FCPORT %s attempting login\n", port->name );
1110
1111         /* Try to create FLOGI ELS */
1112         intf_restart ( &port->flogi, -ECANCELED );
1113         if ( ( rc = fc_els_flogi ( &port->flogi, port ) ) != 0 ) {
1114                 DBGC ( port, "FCPORT %s could not initiate FLOGI: %s\n",
1115                        port->name, strerror ( rc ) );
1116                 fc_port_logout ( port, rc );
1117                 return;
1118         }
1119 }
1120
1121 /**
1122  * Handle change of flow control window
1123  *
1124  * @v port              Fibre Channel port
1125  */
1126 static void fc_port_window_changed ( struct fc_port *port ) {
1127         size_t window;
1128
1129         /* Check if transport layer is ready */
1130         window = xfer_window ( &port->transport );
1131         if ( window > 0 ) {
1132
1133                 /* Transport layer is ready.  Start login if the link
1134                  * is not already up.
1135                  */
1136                 if ( ! fc_link_ok ( &port->link ) )
1137                         fc_link_start ( &port->link );
1138
1139         } else {
1140
1141                 /* Transport layer is not ready.  Log out port and
1142                  * wait for transport layer before attempting log in
1143                  * again.
1144                  */
1145                 fc_port_logout ( port, -ENOTCONN );
1146                 fc_link_stop ( &port->link );
1147         }
1148 }
1149
1150 /** Fibre Channel port transport interface operations */
1151 static struct interface_operation fc_port_transport_op[] = {
1152         INTF_OP ( xfer_deliver, struct fc_port *, fc_port_deliver ),
1153         INTF_OP ( xfer_window_changed, struct fc_port *,
1154                   fc_port_window_changed ),
1155         INTF_OP ( intf_close, struct fc_port *, fc_port_close ),
1156 };
1157
1158 /** Fibre Channel port transport interface descriptor */
1159 static struct interface_descriptor fc_port_transport_desc =
1160         INTF_DESC ( struct fc_port, transport, fc_port_transport_op );
1161
1162 /** Fibre Channel port FLOGI interface operations */
1163 static struct interface_operation fc_port_flogi_op[] = {
1164         INTF_OP ( intf_close, struct fc_port *, fc_port_flogi_done ),
1165 };
1166
1167 /** Fibre Channel port FLOGI interface descriptor */
1168 static struct interface_descriptor fc_port_flogi_desc =
1169         INTF_DESC ( struct fc_port, flogi, fc_port_flogi_op );
1170
1171 /** Fibre Channel port name server PLOGI interface operations */
1172 static struct interface_operation fc_port_ns_plogi_op[] = {
1173         INTF_OP ( intf_close, struct fc_port *, fc_port_ns_plogi_done ),
1174 };
1175
1176 /** Fibre Channel port name server PLOGI interface descriptor */
1177 static struct interface_descriptor fc_port_ns_plogi_desc =
1178         INTF_DESC ( struct fc_port, ns_plogi, fc_port_ns_plogi_op );
1179
1180 /**
1181  * Create Fibre Channel port
1182  *
1183  * @v transport         Transport interface
1184  * @v node              Fibre Channel node name
1185  * @v port              Fibre Channel port name
1186  * @v name              Symbolic port name
1187  * @ret rc              Return status code
1188  */
1189 int fc_port_open ( struct interface *transport, const struct fc_name *node_wwn,
1190                    const struct fc_name *port_wwn, const char *name ) {
1191         struct fc_port *port;
1192
1193         /* Allocate and initialise structure */
1194         port = zalloc ( sizeof ( *port ) );
1195         if ( ! port )
1196                 return -ENOMEM;
1197         ref_init ( &port->refcnt, NULL );
1198         intf_init ( &port->transport, &fc_port_transport_desc, &port->refcnt );
1199         fc_link_init ( &port->link, fc_port_examine, &port->refcnt );
1200         intf_init ( &port->flogi, &fc_port_flogi_desc, &port->refcnt );
1201         intf_init ( &port->ns_plogi, &fc_port_ns_plogi_desc, &port->refcnt );
1202         list_add_tail ( &port->list, &fc_ports );
1203         INIT_LIST_HEAD ( &port->xchgs );
1204         memcpy ( &port->node_wwn, node_wwn, sizeof ( port->node_wwn ) );
1205         memcpy ( &port->port_wwn, port_wwn, sizeof ( port->port_wwn ) );
1206         snprintf ( port->name, sizeof ( port->name ), "%s", name );
1207
1208         DBGC ( port, "FCPORT %s opened as %s",
1209                port->name, fc_ntoa ( &port->node_wwn ) );
1210         DBGC ( port, " port %s\n", fc_ntoa ( &port->port_wwn ) );
1211
1212         /* Attach to transport layer, mortalise self, and return */
1213         intf_plug_plug ( &port->transport, transport );
1214         ref_put ( &port->refcnt );
1215         return 0;
1216 }
1217
1218 /**
1219  * Find Fibre Channel port by name
1220  *
1221  * @v name              Fibre Channel port name
1222  * @ret port            Fibre Channel port, or NULL
1223  */
1224 struct fc_port * fc_port_find ( const char *name ) {
1225         struct fc_port *port;
1226
1227         list_for_each_entry ( port, &fc_ports, list ) {
1228                 if ( strcmp ( name, port->name ) == 0 )
1229                         return port;
1230         }
1231         return NULL;
1232 }
1233
1234 /******************************************************************************
1235  *
1236  * Fibre Channel peers
1237  *
1238  ******************************************************************************
1239  */
1240
1241 /**
1242  * Close Fibre Channel peer
1243  *
1244  * @v peer              Fibre Channel peer
1245  * @v rc                Reason for close
1246  */
1247 static void fc_peer_close ( struct fc_peer *peer, int rc ) {
1248
1249         DBGC ( peer, "FCPEER %s closed: %s\n",
1250                fc_ntoa ( &peer->port_wwn ) , strerror ( rc ) );
1251
1252         /* Sanity check */
1253         assert ( list_empty ( &peer->ulps ) );
1254
1255         /* Stop link timer */
1256         fc_link_stop ( &peer->link );
1257
1258         /* Shut down interfaces */
1259         intf_shutdown ( &peer->plogi, rc );
1260
1261         /* Remove from list of peers */
1262         list_del ( &peer->list );
1263         INIT_LIST_HEAD ( &peer->list );
1264 }
1265
1266 /**
1267  * Increment Fibre Channel peer active usage count
1268  *
1269  * @v peer              Fibre Channel peer
1270  */
1271 static void fc_peer_increment ( struct fc_peer *peer ) {
1272
1273         /* Increment our usage count */
1274         peer->usage++;
1275 }
1276
1277 /**
1278  * Decrement Fibre Channel peer active usage count
1279  *
1280  * @v peer              Fibre Channel peer
1281  */
1282 static void fc_peer_decrement ( struct fc_peer *peer ) {
1283
1284         /* Sanity check */
1285         assert ( peer->usage > 0 );
1286
1287         /* Decrement our usage count and log out if we reach zero */
1288         if ( --(peer->usage) == 0 )
1289                 fc_peer_logout ( peer, 0 );
1290 }
1291
1292 /**
1293  * Log in Fibre Channel peer
1294  *
1295  * @v peer              Fibre Channel peer
1296  * @v port              Fibre Channel port
1297  * @v port_id           Port ID
1298  * @ret rc              Return status code
1299  */
1300 int fc_peer_login ( struct fc_peer *peer, struct fc_port *port,
1301                     struct fc_port_id *port_id ) {
1302         struct fc_ulp *ulp;
1303         struct fc_ulp *tmp;
1304
1305         /* Perform implicit logout if logged in and details differ */
1306         if ( fc_link_ok ( &peer->link ) &&
1307              ( ( peer->port != port ) ||
1308                ( memcmp ( &peer->port_id, port_id,
1309                           sizeof ( peer->port_id ) ) !=0 ) ) ) {
1310                 fc_peer_logout ( peer, 0 );
1311         }
1312
1313         /* Log in, if applicable */
1314         if ( ! fc_link_ok ( &peer->link ) ) {
1315
1316                 /* Record peer details */
1317                 assert ( peer->port == NULL );
1318                 peer->port = fc_port_get ( port );
1319                 memcpy ( &peer->port_id, port_id, sizeof ( peer->port_id ) );
1320                 DBGC ( peer, "FCPEER %s logged in via %s as %s\n",
1321                        fc_ntoa ( &peer->port_wwn ), peer->port->name,
1322                        fc_id_ntoa ( &peer->port_id ) );
1323
1324                 /* Add login reference */
1325                 fc_peer_get ( peer );
1326         }
1327
1328         /* Record login */
1329         fc_link_up ( &peer->link );
1330
1331         /* Notify ULPs of link state change */
1332         list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1333                 fc_ulp_get ( ulp );
1334                 fc_link_examine ( &ulp->link );
1335                 fc_ulp_put ( ulp );
1336         }
1337
1338         return 0;
1339 }
1340
1341 /**
1342  * Log out Fibre Channel peer
1343  *
1344  * @v peer              Fibre Channel peer
1345  * @v rc                Reason for logout
1346  */
1347 void fc_peer_logout ( struct fc_peer *peer, int rc ) {
1348         struct fc_ulp *ulp;
1349         struct fc_ulp *tmp;
1350
1351         DBGC ( peer, "FCPEER %s logged out: %s\n",
1352                fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
1353
1354         /* Drop login reference, if applicable */
1355         if ( fc_link_ok ( &peer->link ) )
1356                 fc_peer_put ( peer );
1357
1358         /* Erase peer details */
1359         fc_port_put ( peer->port );
1360         peer->port = NULL;
1361
1362         /* Record logout */
1363         fc_link_err ( &peer->link, rc );
1364
1365         /* Notify ULPs of link state change */
1366         list_for_each_entry_safe ( ulp, tmp, &peer->ulps, list ) {
1367                 fc_ulp_get ( ulp );
1368                 fc_link_examine ( &ulp->link );
1369                 fc_ulp_put ( ulp );
1370         }
1371
1372         /* Close peer if there are no active users */
1373         if ( peer->usage == 0 )
1374                 fc_peer_close ( peer, rc );
1375 }
1376
1377 /**
1378  * Handle PLOGI completion
1379  *
1380  * @v peer              Fibre Channel peer
1381  * @v rc                Reason for completion
1382  */
1383 static void fc_peer_plogi_done ( struct fc_peer *peer, int rc ) {
1384
1385         intf_restart ( &peer->plogi, rc );
1386
1387         if ( rc != 0 )
1388                 fc_peer_logout ( peer, rc );
1389 }
1390
1391 /**
1392  * Initiate PLOGI
1393  *
1394  * @v peer              Fibre Channel peer
1395  * @v port              Fibre Channel port
1396  * @v peer_port_id      Peer port ID
1397  * @ret rc              Return status code
1398  */
1399 static int fc_peer_plogi ( struct fc_peer *peer, struct fc_port *port,
1400                            struct fc_port_id *peer_port_id ) {
1401         int rc;
1402
1403         /* Try to create PLOGI ELS */
1404         intf_restart ( &peer->plogi, -ECANCELED );
1405         if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) {
1406                 DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n",
1407                        fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
1408                 fc_peer_logout ( peer, rc );
1409                 return rc;
1410         }
1411
1412         return 0;
1413 }
1414
1415 /**
1416  * Examine Fibre Channel peer link state
1417  *
1418  * @ link               Fibre Channel link state monitor
1419  */
1420 static void fc_peer_examine ( struct fc_link_state *link ) {
1421         struct fc_peer *peer = container_of ( link, struct fc_peer, link );
1422         struct fc_port *port;
1423         int rc;
1424
1425         /* Check to see if underlying port link has gone down */
1426         if ( peer->port && ( ! fc_link_ok ( &peer->port->link ) ) ) {
1427                 fc_peer_logout ( peer, -ENOTCONN );
1428                 return;
1429         }
1430
1431         /* Do nothing if already logged in */
1432         if ( fc_link_ok ( &peer->link ) )
1433                 return;
1434
1435         DBGC ( peer, "FCPEER %s attempting login\n",
1436                fc_ntoa ( &peer->port_wwn ) );
1437
1438         /* Sanity check */
1439         assert ( peer->port == NULL );
1440
1441         /* First, look for a port with the peer attached via a
1442          * point-to-point link.
1443          */
1444         list_for_each_entry ( port, &fc_ports, list ) {
1445                 if ( fc_link_ok ( &port->link ) &&
1446                      ( ! ( port->flags & FC_PORT_HAS_FABRIC ) ) &&
1447                      ( memcmp ( &peer->port_wwn, &port->link_port_wwn,
1448                                 sizeof ( peer->port_wwn ) ) == 0 ) ) {
1449                         /* Use this peer port ID, and stop looking */
1450                         fc_peer_plogi ( peer, port, &port->ptp_link_port_id );
1451                         return;
1452                 }
1453         }
1454
1455         /* If the peer is not directly attached, try initiating a name
1456          * server lookup on any suitable ports.
1457          */
1458         list_for_each_entry ( port, &fc_ports, list ) {
1459                 if ( fc_link_ok ( &port->link ) &&
1460                      ( port->flags & FC_PORT_HAS_FABRIC ) &&
1461                      ( port->flags & FC_PORT_HAS_NS ) ) {
1462                         if ( ( rc = fc_ns_query ( peer, port,
1463                                                   fc_peer_plogi ) ) != 0 ) {
1464                                 DBGC ( peer, "FCPEER %s could not attempt "
1465                                        "name server lookup on %s: %s\n",
1466                                        fc_ntoa ( &peer->port_wwn ), port->name,
1467                                        strerror ( rc ) );
1468                                 /* Non-fatal */
1469                         }
1470                 }
1471         }
1472 }
1473
1474 /** Fibre Channel peer PLOGI interface operations */
1475 static struct interface_operation fc_peer_plogi_op[] = {
1476         INTF_OP ( intf_close, struct fc_peer *, fc_peer_plogi_done ),
1477 };
1478
1479 /** Fibre Channel peer PLOGI interface descriptor */
1480 static struct interface_descriptor fc_peer_plogi_desc =
1481         INTF_DESC ( struct fc_peer, plogi, fc_peer_plogi_op );
1482
1483 /**
1484  * Create Fibre Channel peer
1485  *
1486  * @v port_wwn          Node name
1487  * @ret peer            Fibre Channel peer, or NULL
1488  */
1489 static struct fc_peer * fc_peer_create ( const struct fc_name *port_wwn ) {
1490         struct fc_peer *peer;
1491
1492         /* Allocate and initialise structure */
1493         peer = zalloc ( sizeof ( *peer ) );
1494         if ( ! peer )
1495                 return NULL;
1496         ref_init ( &peer->refcnt, NULL );
1497         fc_link_init ( &peer->link, fc_peer_examine, &peer->refcnt );
1498         intf_init ( &peer->plogi, &fc_peer_plogi_desc, &peer->refcnt );
1499         list_add_tail ( &peer->list, &fc_peers );
1500         memcpy ( &peer->port_wwn, port_wwn, sizeof ( peer->port_wwn ) );
1501         INIT_LIST_HEAD ( &peer->ulps );
1502
1503         /* Start link monitor */
1504         fc_link_start ( &peer->link );
1505
1506         DBGC ( peer, "FCPEER %s created\n", fc_ntoa ( &peer->port_wwn ) );
1507         return peer;
1508 }
1509
1510 /**
1511  * Get Fibre Channel peer by node name
1512  *
1513  * @v port_wwn          Node name
1514  * @ret peer            Fibre Channel peer, or NULL
1515  */
1516 struct fc_peer * fc_peer_get_wwn ( const struct fc_name *port_wwn ) {
1517         struct fc_peer *peer;
1518
1519         /* Look for an existing peer */
1520         list_for_each_entry ( peer, &fc_peers, list ) {
1521                 if ( memcmp ( &peer->port_wwn, port_wwn,
1522                               sizeof ( peer->port_wwn ) ) == 0 )
1523                         return fc_peer_get ( peer );
1524         }
1525
1526         /* Create a new peer */
1527         peer = fc_peer_create ( port_wwn );
1528         if ( ! peer )
1529                 return NULL;
1530
1531         return peer;
1532 }
1533
1534 /**
1535  * Get Fibre Channel peer by port ID
1536  *
1537  * @v port              Fibre Channel port
1538  * @v peer_port_id      Peer port ID
1539  * @ret peer            Fibre Channel peer, or NULL
1540  */
1541 struct fc_peer * fc_peer_get_port_id ( struct fc_port *port,
1542                                        const struct fc_port_id *peer_port_id ){
1543         struct fc_peer *peer;
1544
1545         /* Look for an existing peer */
1546         list_for_each_entry ( peer, &fc_peers, list ) {
1547                 if ( ( peer->port == port ) &&
1548                      ( memcmp ( &peer->port_id, peer_port_id,
1549                                 sizeof ( peer->port_id ) ) == 0 ) )
1550                         return fc_peer_get ( peer );
1551         }
1552
1553         /* Cannot create a new peer, since we have no port name to use */
1554         return NULL;
1555 }
1556
1557 /******************************************************************************
1558  *
1559  * Fibre Channel upper-layer protocols
1560  *
1561  ******************************************************************************
1562  */
1563
1564 /**
1565  * Free Fibre Channel upper-layer protocol
1566  *
1567  * @v refcnt            Reference count
1568  */
1569 static void fc_ulp_free ( struct refcnt *refcnt ) {
1570         struct fc_ulp *ulp = container_of ( refcnt, struct fc_ulp, refcnt );
1571
1572         fc_peer_put ( ulp->peer );
1573         free ( ulp );
1574 }
1575
1576 /**
1577  * Close Fibre Channel upper-layer protocol
1578  *
1579  * @v ulp               Fibre Channel upper-layer protocol
1580  * @v rc                Reason for close
1581  */
1582 static void fc_ulp_close ( struct fc_ulp *ulp, int rc ) {
1583
1584         DBGC ( ulp, "FCULP %s/%02x closed: %s\n",
1585                fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1586
1587         /* Sanity check */
1588         assert ( list_empty ( &ulp->users ) );
1589
1590         /* Stop link monitor */
1591         fc_link_stop ( &ulp->link );
1592
1593         /* Shut down interfaces */
1594         intf_shutdown ( &ulp->prli, rc );
1595
1596         /* Remove from list of ULPs */
1597         list_del ( &ulp->list );
1598         INIT_LIST_HEAD ( &ulp->list );
1599 }
1600
1601 /**
1602  * Attach Fibre Channel upper-layer protocol user
1603  *
1604  * @v ulp               Fibre Channel upper-layer protocol
1605  * @v user              Fibre Channel upper-layer protocol user
1606  */
1607 void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ) {
1608
1609         /* Sanity check */
1610         assert ( user->ulp == NULL );
1611
1612         /* Increment peer's usage count */
1613         fc_peer_increment ( ulp->peer );
1614
1615         /* Attach user */
1616         user->ulp = fc_ulp_get ( ulp );
1617         list_add ( &user->list, &ulp->users );
1618 }
1619
1620 /**
1621  * Detach Fibre Channel upper-layer protocol user
1622  *
1623  * @v user              Fibre Channel upper-layer protocol user
1624  */
1625 void fc_ulp_detach ( struct fc_ulp_user *user ) {
1626         struct fc_ulp *ulp = user->ulp;
1627
1628         /* Do nothing if not attached */
1629         if ( ! ulp )
1630                 return;
1631
1632         /* Sanity checks */
1633         list_check_contains_entry ( user, &ulp->users, list );
1634
1635         /* Detach user and log out if no users remain */
1636         list_del ( &user->list );
1637         if ( list_empty ( &ulp->users ) )
1638                 fc_ulp_logout ( ulp, 0 );
1639
1640         /* Decrement our peer's usage count */
1641         fc_peer_decrement ( ulp->peer );
1642
1643         /* Drop reference */
1644         user->ulp = NULL;
1645         fc_ulp_put ( ulp );
1646 }
1647
1648 /**
1649  * Log in Fibre Channel upper-layer protocol
1650  *
1651  * @v ulp               Fibre Channel upper-layer protocol
1652  * @v param             Service parameters
1653  * @v param_len         Length of service parameters
1654  * @v originated        Login was originated by us
1655  * @ret rc              Return status code
1656  */
1657 int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len,
1658                    int originated ) {
1659         struct fc_ulp_user *user;
1660         struct fc_ulp_user *tmp;
1661
1662         /* Perform implicit logout if logged in and service parameters differ */
1663         if ( fc_link_ok ( &ulp->link ) &&
1664              ( ( ulp->param_len != param_len ) ||
1665                ( memcmp ( ulp->param, param, ulp->param_len ) != 0 ) ) ) {
1666                 fc_ulp_logout ( ulp, 0 );
1667         }
1668
1669         /* Work around a bug in some versions of the Linux Fibre
1670          * Channel stack, which fail to fully initialise image pairs
1671          * established via a PRLI originated by the Linux stack
1672          * itself.
1673          */
1674         if ( originated )
1675                 ulp->flags |= FC_ULP_ORIGINATED_LOGIN_OK;
1676         if ( ! ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) ) {
1677                 DBGC ( ulp, "FCULP %s/%02x sending extra PRLI to work around "
1678                        "Linux bug\n",
1679                        fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1680                 fc_link_stop ( &ulp->link );
1681                 fc_link_start ( &ulp->link );
1682                 return 0;
1683         }
1684
1685         /* Log in, if applicable */
1686         if ( ! fc_link_ok ( &ulp->link ) ) {
1687
1688                 /* Record service parameters */
1689                 assert ( ulp->param == NULL );
1690                 assert ( ulp->param_len == 0 );
1691                 ulp->param = malloc ( param_len );
1692                 if ( ! ulp->param ) {
1693                         DBGC ( ulp, "FCULP %s/%02x could not record "
1694                                "parameters\n",
1695                                fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1696                         return -ENOMEM;
1697                 }
1698                 memcpy ( ulp->param, param, param_len );
1699                 ulp->param_len = param_len;
1700                 DBGC ( ulp, "FCULP %s/%02x logged in with parameters:\n",
1701                        fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1702                 DBGC_HDA ( ulp, 0, ulp->param, ulp->param_len );
1703
1704                 /* Add login reference */
1705                 fc_ulp_get ( ulp );
1706         }
1707
1708         /* Record login */
1709         fc_link_up ( &ulp->link );
1710
1711         /* Notify users of link state change */
1712         list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
1713                 fc_ulp_user_get ( user );
1714                 user->examine ( user );
1715                 fc_ulp_user_put ( user );
1716         }
1717
1718         return 0;
1719 }
1720
1721 /**
1722  * Log out Fibre Channel upper-layer protocol
1723  *
1724  * @v ulp               Fibre Channel upper-layer protocol
1725  * @v rc                Reason for logout
1726  */
1727 void fc_ulp_logout ( struct fc_ulp *ulp, int rc ) {
1728         struct fc_ulp_user *user;
1729         struct fc_ulp_user *tmp;
1730
1731         DBGC ( ulp, "FCULP %s/%02x logged out: %s\n",
1732                fc_ntoa ( &ulp->peer->port_wwn ), ulp->type, strerror ( rc ) );
1733
1734         /* Drop login reference, if applicable */
1735         if ( fc_link_ok ( &ulp->link ) )
1736                 fc_ulp_put ( ulp );
1737
1738         /* Discard service parameters */
1739         free ( ulp->param );
1740         ulp->param = NULL;
1741         ulp->param_len = 0;
1742         ulp->flags = 0;
1743
1744         /* Record logout */
1745         fc_link_err ( &ulp->link, rc );
1746
1747         /* Notify users of link state change */
1748         list_for_each_entry_safe ( user, tmp, &ulp->users, list ) {
1749                 fc_ulp_user_get ( user );
1750                 user->examine ( user );
1751                 fc_ulp_user_put ( user );
1752         }
1753
1754         /* Close ULP if there are no clients attached */
1755         if ( list_empty ( &ulp->users ) )
1756                 fc_ulp_close ( ulp, rc );
1757 }
1758
1759 /**
1760  * Handle PRLI completion
1761  *
1762  * @v ulp               Fibre Channel upper-layer protocol
1763  * @v rc                Reason for completion
1764  */
1765 static void fc_ulp_prli_done ( struct fc_ulp *ulp, int rc ) {
1766
1767         intf_restart ( &ulp->prli, rc );
1768
1769         if ( rc != 0 )
1770                 fc_ulp_logout ( ulp, rc );
1771 }
1772
1773 /**
1774  * Examine Fibre Channel upper-layer protocol link state
1775  *
1776  * @ link               Fibre Channel link state monitor
1777  */
1778 static void fc_ulp_examine ( struct fc_link_state *link ) {
1779         struct fc_ulp *ulp = container_of ( link, struct fc_ulp, link );
1780         int rc;
1781
1782         /* Check to see if underlying peer link has gone down */
1783         if ( ! fc_link_ok ( &ulp->peer->link ) ) {
1784                 fc_ulp_logout ( ulp, -ENOTCONN );
1785                 return;
1786         }
1787
1788         /* Do nothing if already logged in */
1789         if ( fc_link_ok ( &ulp->link ) &&
1790              ( ulp->flags & FC_ULP_ORIGINATED_LOGIN_OK ) )
1791                 return;
1792
1793         DBGC ( ulp, "FCULP %s/%02x attempting login\n",
1794                fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1795
1796         /* Try to create PRLI ELS */
1797         intf_restart ( &ulp->prli, -ECANCELED );
1798         if ( ( rc = fc_els_prli ( &ulp->prli, ulp->peer->port,
1799                                   &ulp->peer->port_id, ulp->type ) ) != 0 ) {
1800                 DBGC ( ulp, "FCULP %s/%02x could not initiate PRLI: %s\n",
1801                        fc_ntoa ( &ulp->peer->port_wwn ), ulp->type,
1802                        strerror ( rc ) );
1803                 fc_ulp_logout ( ulp, rc );
1804                 return;
1805         }
1806 }
1807
1808 /** Fibre Channel upper-layer protocol PRLI interface operations */
1809 static struct interface_operation fc_ulp_prli_op[] = {
1810         INTF_OP ( intf_close, struct fc_ulp *, fc_ulp_prli_done ),
1811 };
1812
1813 /** Fibre Channel upper-layer protocol PRLI interface descriptor */
1814 static struct interface_descriptor fc_ulp_prli_desc =
1815         INTF_DESC ( struct fc_ulp, prli, fc_ulp_prli_op );
1816
1817 /**
1818  * Create Fibre Channel upper-layer protocl
1819  *
1820  * @v peer              Fibre Channel peer
1821  * @v type              Type
1822  * @ret ulp             Fibre Channel upper-layer protocol, or NULL
1823  */
1824 static struct fc_ulp * fc_ulp_create ( struct fc_peer *peer,
1825                                        unsigned int type ) {
1826         struct fc_ulp *ulp;
1827
1828         /* Allocate and initialise structure */
1829         ulp = zalloc ( sizeof ( *ulp ) );
1830         if ( ! ulp )
1831                 return NULL;
1832         ref_init ( &ulp->refcnt, fc_ulp_free );
1833         fc_link_init ( &ulp->link, fc_ulp_examine, &ulp->refcnt );
1834         intf_init ( &ulp->prli, &fc_ulp_prli_desc, &ulp->refcnt );
1835         ulp->peer = fc_peer_get ( peer );
1836         list_add_tail ( &ulp->list, &peer->ulps );
1837         ulp->type = type;
1838         INIT_LIST_HEAD ( &ulp->users );
1839
1840         /* Start link state monitor */
1841         fc_link_start ( &ulp->link );
1842
1843         DBGC ( ulp, "FCULP %s/%02x created\n",
1844                fc_ntoa ( &ulp->peer->port_wwn ), ulp->type );
1845         return ulp;
1846 }
1847
1848 /**
1849  * Get Fibre Channel upper-layer protocol by peer and type
1850  *
1851  * @v peer              Fibre Channel peer
1852  * @v type              Type
1853  * @ret ulp             Fibre Channel upper-layer protocol, or NULL
1854  */
1855 static struct fc_ulp * fc_ulp_get_type ( struct fc_peer *peer,
1856                                          unsigned int type ) {
1857         struct fc_ulp *ulp;
1858
1859         /* Look for an existing ULP */
1860         list_for_each_entry ( ulp, &peer->ulps, list ) {
1861                 if ( ulp->type == type )
1862                         return fc_ulp_get ( ulp );
1863         }
1864
1865         /* Create a new ULP */
1866         ulp = fc_ulp_create ( peer, type );
1867         if ( ! ulp )
1868                 return NULL;
1869
1870         return ulp;
1871 }
1872
1873 /**
1874  * Get Fibre Channel upper-layer protocol by port name and type
1875  *
1876  * @v port_wwn          Port name
1877  * @v type              Type
1878  * @ret ulp             Fibre Channel upper-layer protocol, or NULL
1879  */
1880 struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn,
1881                                       unsigned int type ) {
1882         struct fc_ulp *ulp;
1883         struct fc_peer *peer;
1884
1885         /* Get peer */
1886         peer = fc_peer_get_wwn ( port_wwn );
1887         if ( ! peer )
1888                 goto err_peer_get_wwn;
1889
1890         /* Get ULP */
1891         ulp = fc_ulp_get_type ( peer, type );
1892         if ( ! ulp )
1893                 goto err_ulp_get_type;
1894
1895         /* Drop temporary reference to peer */
1896         fc_peer_put ( peer );
1897
1898         return ulp;
1899
1900         fc_ulp_put ( ulp );
1901  err_ulp_get_type:
1902         fc_peer_put ( peer );
1903  err_peer_get_wwn:
1904         return NULL;
1905 }
1906
1907 /**
1908  * Get Fibre Channel upper-layer protocol by port ID and type
1909  *
1910  * @v port              Fibre Channel port
1911  * @v peer_port_id      Peer port ID
1912  * @v type              Type
1913  * @ret ulp             Fibre Channel upper-layer protocol, or NULL
1914  */
1915 struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port,
1916                                           const struct fc_port_id *peer_port_id,
1917                                           unsigned int type ) {
1918         struct fc_ulp *ulp;
1919         struct fc_peer *peer;
1920
1921         /* Get peer */
1922         peer = fc_peer_get_port_id ( port, peer_port_id );
1923         if ( ! peer )
1924                 goto err_peer_get_wwn;
1925
1926         /* Get ULP */
1927         ulp = fc_ulp_get_type ( peer, type );
1928         if ( ! ulp )
1929                 goto err_ulp_get_type;
1930
1931         /* Drop temporary reference to peer */
1932         fc_peer_put ( peer );
1933
1934         return ulp;
1935
1936         fc_ulp_put ( ulp );
1937  err_ulp_get_type:
1938         fc_peer_put ( peer );
1939  err_peer_get_wwn:
1940         return NULL;
1941 }
1942
1943 /* Drag in objects via fc_ports */
1944 REQUIRING_SYMBOL ( fc_ports );
1945
1946 /* Drag in Fibre Channel configuration */
1947 REQUIRE_OBJECT ( config_fc );