These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / net / fcels.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 <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <assert.h>
31 #include <byteswap.h>
32 #include <ipxe/interface.h>
33 #include <ipxe/xfer.h>
34 #include <ipxe/iobuf.h>
35 #include <ipxe/process.h>
36 #include <ipxe/fc.h>
37 #include <ipxe/fcels.h>
38
39 /** @file
40  *
41  * Fibre Channel Extended Link Services
42  *
43  */
44
45 /** Fibre Channel ELS transaction debug message format */
46 #define FCELS_FMT "FCELS %s %s %s %s"
47
48 /** Fibre Channel ELS transaction debug message arguments */
49 #define FCELS_ARGS( els )                                               \
50         (els)->port->name,                                              \
51         ( (els)->handler ? (els)->handler->name : "unknown ELS" ),      \
52         ( fc_els_is_request ( els ) ? "to" : "from" ),                  \
53         fc_id_ntoa ( &(els)->peer_port_id )
54
55 struct fc_els_handler fc_els_unknown_handler __fc_els_handler;
56
57 /**
58  * Free Fibre Channel ELS transaction
59  *
60  * @v refcnt            Reference count
61  */
62 static void fc_els_free ( struct refcnt *refcnt ) {
63         struct fc_els *els = container_of ( refcnt, struct fc_els, refcnt );
64
65         assert ( ! process_running ( &els->process ) );
66         fc_port_put ( els->port );
67         free ( els );
68 }
69
70 /**
71  * Close Fibre Channel ELS transaction
72  *
73  * @v els               Fibre Channel ELS transaction
74  * @v rc                Reason for close
75  */
76 static void fc_els_close ( struct fc_els *els, int rc ) {
77
78         if ( rc != 0 ) {
79                 DBGC ( els, FCELS_FMT " complete (%s)\n",
80                        FCELS_ARGS ( els ), strerror ( rc ) );
81         }
82
83         /* Stop process */
84         process_del ( &els->process );
85
86         /* Shut down interfaces */
87         intf_shutdown ( &els->xchg, rc );
88         intf_shutdown ( &els->job, rc );
89 }
90
91 /**
92  * Detect Fibre Channel ELS frame handler
93  *
94  * @v els               Fibre Channel ELS transaction
95  * @v command           ELS command code
96  * @ret handler         ELS handler, or NULL
97  */
98 static struct fc_els_handler * fc_els_detect ( struct fc_els *els,
99                                                const void *data,
100                                                size_t len ) {
101         const struct fc_els_frame_common *frame = data;
102         struct fc_els_handler *handler;
103         int rc;
104
105         /* Sanity check */
106         if ( len < sizeof ( *frame ) )
107                 return NULL;
108
109         /* Try each handler in turn */
110         for_each_table_entry ( handler, FC_ELS_HANDLERS ) {
111                 if ( ( rc = handler->detect ( els, data, len ) ) == 0 )
112                         return handler;
113         }
114
115         return NULL;
116 }
117
118 /**
119  * Transmit Fibre Channel ELS frame
120  *
121  * @v els               Fibre Channel ELS transaction
122  * @v data              Data to transmit
123  * @v len               Length of data
124  * @ret rc              Return status code
125  */
126 int fc_els_tx ( struct fc_els *els, const void *data, size_t len ) {
127         struct xfer_metadata meta;
128         struct sockaddr_fc dest;
129         int rc;
130
131         DBGC2 ( els, FCELS_FMT " transmitting:\n", FCELS_ARGS ( els ) );
132         DBGC2_HDA ( els, 0, data, len );
133
134         /* Construct metadata */
135         memset ( &meta, 0, sizeof ( meta ) );
136         meta.flags = ( fc_els_is_request ( els ) ?
137                        XFER_FL_OVER : ( XFER_FL_RESPONSE | XFER_FL_OUT ) );
138         meta.dest = fc_fill_sockaddr ( &dest, &els->peer_port_id );
139
140         /* Transmit frame */
141         if ( ( rc = xfer_deliver_raw_meta ( &els->xchg, data, len,
142                                             &meta ) ) != 0 ) {
143                 DBGC ( els, FCELS_FMT " could not deliver frame: %s\n",
144                        FCELS_ARGS ( els ), strerror ( rc ) );
145                 return rc;
146         }
147
148         return 0;
149 }
150
151 /**
152  * Receive Fibre Channel ELS frame
153  *
154  * @v els               Fibre Channel ELS transaction
155  * @v iobuf             I/O buffer
156  * @v meta              Data transfer metadata
157  * @ret rc              Return status code
158  */
159 static int fc_els_rx ( struct fc_els *els,
160                        struct io_buffer *iobuf,
161                        struct xfer_metadata *meta ) {
162         struct fc_els_frame_common *frame = iobuf->data;
163         struct sockaddr_fc *src = ( ( struct sockaddr_fc * ) meta->src );
164         struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest );
165         size_t len = iob_len ( iobuf );
166         int rc;
167
168         /* Sanity check */
169         if ( len < sizeof ( *frame ) ) {
170                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
171                        FCELS_ARGS ( els ) );
172                 DBGC_HDA ( els, 0, frame, len );
173                 rc = -EINVAL;
174                 goto done;
175         }
176         if ( ! src ) {
177                 DBGC ( els, FCELS_FMT " received frame missing source "
178                        "address:\n", FCELS_ARGS ( els ) );
179                 rc = -EINVAL;
180                 goto done;
181         }
182         if ( ! dest ) {
183                 DBGC ( els, FCELS_FMT " received frame missing destination "
184                        "address:\n", FCELS_ARGS ( els ) );
185                 rc = -EINVAL;
186                 goto done;
187         }
188
189         /* Check for rejection responses */
190         if ( fc_els_is_request ( els ) &&
191              ( frame->command != FC_ELS_LS_ACC ) ) {
192                 DBGC ( els, FCELS_FMT " rejected:\n", FCELS_ARGS ( els ) );
193                 DBGC_HDA ( els, 0, frame, len );
194                 rc = -EACCES;
195                 goto done;
196         }
197
198         /* Update port IDs */
199         memcpy ( &els->port_id, &dest->sfc_port_id, sizeof ( els->port_id ) );
200         memcpy ( &els->peer_port_id, &src->sfc_port_id,
201                  sizeof ( els->peer_port_id ) );
202
203         /* Determine handler, if necessary */
204         if ( ! els->handler )
205                 els->handler = fc_els_detect ( els, frame, len );
206         if ( ! els->handler )
207                 els->handler = &fc_els_unknown_handler;
208
209         DBGC2 ( els, FCELS_FMT " received:\n", FCELS_ARGS ( els ) );
210         DBGC2_HDA ( els, 0, frame, len );
211
212         /* Handle received frame */
213         if ( ( rc = els->handler->rx ( els, frame, len ) ) != 0 ) {
214                 DBGC ( els, FCELS_FMT " could not handle received frame: "
215                        "%s\n", FCELS_ARGS ( els ), strerror ( rc ) );
216                 DBGC_HDA ( els, 0, frame, len );
217                 goto done;
218         }
219
220  done:
221         /* Free I/O buffer */
222         free_iob ( iobuf );
223
224         /* Close transaction */
225         fc_els_close ( els, rc );
226
227         return rc;
228 }
229
230 /** Fibre Channel ELS exchange interface operations */
231 static struct interface_operation fc_els_xchg_op[] = {
232         INTF_OP ( xfer_deliver, struct fc_els *, fc_els_rx ),
233         INTF_OP ( intf_close, struct fc_els *, fc_els_close ),
234 };
235
236 /** Fibre Channel ELS exchange interface descriptor */
237 static struct interface_descriptor fc_els_xchg_desc =
238         INTF_DESC ( struct fc_els, xchg, fc_els_xchg_op );
239
240 /** Fibre Channel ELS job control interface operations */
241 static struct interface_operation fc_els_job_op[] = {
242         INTF_OP ( intf_close, struct fc_els *, fc_els_close ),
243 };
244
245 /** Fibre Channel ELS job control interface descriptor */
246 static struct interface_descriptor fc_els_job_desc =
247         INTF_DESC ( struct fc_els, job, fc_els_job_op );
248
249 /**
250  * Fibre Channel ELS process
251  *
252  * @v els               Fibre Channel ELS transaction
253  */
254 static void fc_els_step ( struct fc_els *els ) {
255         int xchg_id;
256         int rc;
257
258         /* Sanity check */
259         assert ( fc_els_is_request ( els ) );
260
261         /* Create exchange */
262         if ( ( xchg_id = fc_xchg_originate ( &els->xchg, els->port,
263                                              &els->peer_port_id,
264                                              FC_TYPE_ELS ) ) < 0 ) {
265                 rc = xchg_id;
266                 DBGC ( els, FCELS_FMT " could not create exchange: %s\n",
267                        FCELS_ARGS ( els ), strerror ( rc ) );
268                 fc_els_close ( els, rc );
269                 return;
270         }
271
272         /* Transmit request */
273         if ( ( rc = els->handler->tx ( els ) ) != 0 ) {
274                 DBGC ( els, FCELS_FMT " could not transmit request: %s\n",
275                        FCELS_ARGS ( els ), strerror ( rc ) );
276                 fc_els_close ( els, rc );
277                 return;
278         }
279 }
280
281 /** Fibre Channel ELS process descriptor */
282 static struct process_descriptor fc_els_process_desc =
283         PROC_DESC_ONCE ( struct fc_els, process, fc_els_step );
284
285 /**
286  * Create ELS transaction
287  *
288  * @v port              Fibre Channel port
289  * @v port_id           Local port ID
290  * @v peer_port_id      Peer port ID
291  * @ret els             Fibre Channel ELS transaction, or NULL
292  */
293 static struct fc_els * fc_els_create ( struct fc_port *port,
294                                        struct fc_port_id *port_id,
295                                        struct fc_port_id *peer_port_id ) {
296         struct fc_els *els;
297
298         /* Allocate and initialise structure */
299         els = zalloc ( sizeof ( *els ) );
300         if ( ! els )
301                 return NULL;
302         ref_init ( &els->refcnt, fc_els_free );
303         intf_init ( &els->job, &fc_els_job_desc, &els->refcnt );
304         intf_init ( &els->xchg, &fc_els_xchg_desc, &els->refcnt );
305         process_init_stopped ( &els->process, &fc_els_process_desc,
306                                &els->refcnt );
307         els->port = fc_port_get ( port );
308         memcpy ( &els->port_id, port_id, sizeof ( els->port_id ) );
309         memcpy ( &els->peer_port_id, peer_port_id,
310                  sizeof ( els->peer_port_id ) );
311         return els;
312 }
313
314 /**
315  * Create ELS request
316  *
317  * @v job               Parent job-control interface
318  * @v port              Fibre Channel port
319  * @v peer_port_id      Peer port ID
320  * @v handler           ELS handler
321  * @ret rc              Return status code
322  */
323 int fc_els_request ( struct interface *job, struct fc_port *port,
324                      struct fc_port_id *peer_port_id,
325                      struct fc_els_handler *handler ) {
326         struct fc_els *els;
327
328         /* Allocate and initialise structure */
329         els = fc_els_create ( port, &port->port_id, peer_port_id );
330         if ( ! els )
331                 return -ENOMEM;
332         els->handler = handler;
333         els->flags = FC_ELS_REQUEST;
334         process_add ( &els->process );
335
336         /* Attach to parent job interface, mortalise self, and return */
337         intf_plug_plug ( &els->job, job );
338         ref_put ( &els->refcnt );
339         return 0;
340 }
341
342 /**
343  * Create ELS response
344  *
345  * @v xchg              Exchange interface
346  * @v port              Fibre Channel port
347  * @v port_id           Local port ID
348  * @v peer_port_id      Peer port ID
349  * @ret rc              Return status code
350  */
351 static int fc_els_respond ( struct interface *xchg, struct fc_port *port,
352                             struct fc_port_id *port_id,
353                             struct fc_port_id *peer_port_id ) {
354         struct fc_els *els;
355
356         /* Allocate and initialise structure */
357         els = fc_els_create ( port, port_id, peer_port_id );
358         if ( ! els )
359                 return -ENOMEM;
360
361         /* Attach to exchange interface, mortalise self, and return */
362         intf_plug_plug ( &els->xchg, xchg );
363         ref_put ( &els->refcnt );
364         return 0;
365 }
366
367 /** Fibre Channel ELS responder */
368 struct fc_responder fc_els_responder __fc_responder = {
369         .type = FC_TYPE_ELS,
370         .respond = fc_els_respond,
371 };
372
373 /******************************************************************************
374  *
375  * Unknown ELS handler
376  *
377  ******************************************************************************
378  */
379
380 /**
381  * Transmit unknown ELS request
382  *
383  * @v els               Fibre Channel ELS transaction
384  * @ret rc              Return status code
385  */
386 static int fc_els_unknown_tx ( struct fc_els *els __unused ) {
387         return -ENOTSUP;
388 }
389
390 /**
391  * Transmit unknown ELS response
392  *
393  * @v els               Fibre Channel ELS transaction
394  * @ret rc              Return status code
395  */
396 static int fc_els_unknown_tx_response ( struct fc_els *els ) {
397         struct fc_ls_rjt_frame ls_rjt;
398
399         /* Construct LS_RJT */
400         memset ( &ls_rjt, 0, sizeof ( ls_rjt ) );
401         ls_rjt.command = FC_ELS_LS_RJT;
402         ls_rjt.reason = FC_ELS_RJT_UNSUPPORTED;
403
404         /* Transmit LS_RJT */
405         return fc_els_tx ( els, &ls_rjt, sizeof ( ls_rjt ) );
406 }
407
408 /**
409  * Receive unknown ELS
410  *
411  * @v els               Fibre Channel ELS transaction
412  * @v data              ELS frame
413  * @v len               Length of ELS frame
414  * @ret rc              Return status code
415  */
416 static int fc_els_unknown_rx ( struct fc_els *els, void *data, size_t len ) {
417         int rc;
418
419         DBGC ( els, FCELS_FMT ":\n", FCELS_ARGS ( els ) );
420         DBGC_HDA ( els, 0, data, len );
421
422         /* Transmit response, if applicable */
423         if ( ! fc_els_is_request ( els ) ) {
424                 if ( ( rc = fc_els_unknown_tx_response ( els ) ) != 0 )
425                         return rc;
426         }
427
428         return 0;
429 }
430
431 /**
432  * Detect unknown ELS
433  *
434  * @v els               Fibre Channel ELS transaction
435  * @v data              ELS frame
436  * @v len               Length of ELS frame
437  * @ret rc              Return status code
438  */
439 static int fc_els_unknown_detect ( struct fc_els *els __unused,
440                                    const void *data __unused,
441                                    size_t len __unused ) {
442         return -ENOTSUP;
443 }
444
445 /** Unknown ELS handler */
446 struct fc_els_handler fc_els_unknown_handler __fc_els_handler = {
447         .name           = "UNKNOWN",
448         .tx             = fc_els_unknown_tx,
449         .rx             = fc_els_unknown_rx,
450         .detect         = fc_els_unknown_detect,
451 };
452
453 /******************************************************************************
454  *
455  * FLOGI
456  *
457  ******************************************************************************
458  */
459
460 /**
461  * Transmit FLOGI
462  *
463  * @v els               Fibre Channel ELS transaction
464  * @ret rc              Return status code
465  */
466 static int fc_els_flogi_tx ( struct fc_els *els ) {
467         struct fc_login_frame flogi;
468
469         /* Construct FLOGI */
470         memset ( &flogi, 0, sizeof ( flogi ) );
471         flogi.command = fc_els_tx_command ( els, FC_ELS_FLOGI );
472         flogi.common.version = htons ( FC_LOGIN_VERSION );
473         flogi.common.credit = htons ( FC_LOGIN_DEFAULT_B2B );
474         flogi.common.flags = htons ( FC_LOGIN_CONTINUOUS_OFFSET );
475         flogi.common.mtu = htons ( FC_LOGIN_DEFAULT_MTU );
476         memcpy ( &flogi.port_wwn, &els->port->port_wwn,
477                  sizeof ( flogi.port_wwn ) );
478         memcpy ( &flogi.node_wwn, &els->port->node_wwn,
479                  sizeof ( flogi.node_wwn ) );
480         flogi.class3.flags = htons ( FC_LOGIN_CLASS_VALID |
481                                      FC_LOGIN_CLASS_SEQUENTIAL );
482
483         /* Transmit FLOGI */
484         return fc_els_tx ( els, &flogi, sizeof ( flogi ) );
485 }
486
487 /**
488  * Receive FLOGI
489  *
490  * @v els               Fibre Channel ELS transaction
491  * @v data              ELS frame
492  * @v len               Length of ELS frame
493  * @ret rc              Return status code
494  */
495 static int fc_els_flogi_rx ( struct fc_els *els, void *data, size_t len ) {
496         struct fc_login_frame *flogi = data;
497         int has_fabric;
498         int rc;
499
500         /* Sanity check */
501         if ( len < sizeof ( *flogi ) ) {
502                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
503                        FCELS_ARGS ( els ) );
504                 DBGC_HDA ( els, 0, data, len );
505                 return -EINVAL;
506         }
507
508         /* Extract parameters */
509         has_fabric = ( flogi->common.flags & htons ( FC_LOGIN_F_PORT ) );
510         DBGC ( els, FCELS_FMT " has node %s\n", FCELS_ARGS ( els ),
511                fc_ntoa ( &flogi->node_wwn ) );
512         DBGC ( els, FCELS_FMT " has port %s\n", FCELS_ARGS ( els ),
513                fc_ntoa ( &flogi->port_wwn ) );
514         if ( has_fabric ) {
515                 DBGC ( els, FCELS_FMT " has fabric with", FCELS_ARGS ( els ) );
516                 DBGC ( els, " local ID %s\n", fc_id_ntoa ( &els->port_id ) );
517         } else {
518                 DBGC ( els, FCELS_FMT " has point-to-point link\n",
519                        FCELS_ARGS ( els ) );
520         }
521
522         /* Log in port */
523         if ( ( rc = fc_port_login ( els->port, &els->port_id, &flogi->node_wwn,
524                                     &flogi->port_wwn, has_fabric ) ) != 0 ) {
525                 DBGC ( els, FCELS_FMT " could not log in port: %s\n",
526                        FCELS_ARGS ( els ), strerror ( rc ) );
527                 return rc;
528         }
529
530         /* Send any responses to the newly-assigned peer port ID, if
531          * applicable.
532          */
533         if ( ! has_fabric ) {
534                 memcpy ( &els->peer_port_id, &els->port->ptp_link_port_id,
535                          sizeof ( els->peer_port_id ) );
536         }
537
538         /* Transmit response, if applicable */
539         if ( ! fc_els_is_request ( els ) ) {
540                 if ( ( rc = fc_els_flogi_tx ( els ) ) != 0 )
541                         return rc;
542         }
543
544         return 0;
545 }
546
547 /**
548  * Detect FLOGI
549  *
550  * @v els               Fibre Channel ELS transaction
551  * @v data              ELS frame
552  * @v len               Length of ELS frame
553  * @ret rc              Return status code
554  */
555 static int fc_els_flogi_detect ( struct fc_els *els __unused, const void *data,
556                                  size_t len __unused ) {
557         const struct fc_login_frame *flogi = data;
558
559         /* Check for FLOGI */
560         if ( flogi->command != FC_ELS_FLOGI )
561                 return -EINVAL;
562
563         return 0;
564 }
565
566 /** FLOGI ELS handler */
567 struct fc_els_handler fc_els_flogi_handler __fc_els_handler = {
568         .name           = "FLOGI",
569         .tx             = fc_els_flogi_tx,
570         .rx             = fc_els_flogi_rx,
571         .detect         = fc_els_flogi_detect,
572 };
573
574 /**
575  * Create FLOGI request
576  *
577  * @v parent            Parent interface
578  * @v port              Fibre Channel port
579  * @ret rc              Return status code
580  */
581 int fc_els_flogi ( struct interface *parent, struct fc_port *port ) {
582
583         return fc_els_request ( parent, port, &fc_f_port_id,
584                                 &fc_els_flogi_handler );
585 }
586
587 /******************************************************************************
588  *
589  * PLOGI
590  *
591  ******************************************************************************
592  */
593
594 /**
595  * Transmit PLOGI
596  *
597  * @v els               Fibre Channel ELS transaction
598  * @ret rc              Return status code
599  */
600 static int fc_els_plogi_tx ( struct fc_els *els ) {
601         struct fc_login_frame plogi;
602
603         /* Construct PLOGI */
604         memset ( &plogi, 0, sizeof ( plogi ) );
605         plogi.command = fc_els_tx_command ( els, FC_ELS_PLOGI );
606         plogi.common.version = htons ( FC_LOGIN_VERSION );
607         plogi.common.credit = htons ( FC_LOGIN_DEFAULT_B2B );
608         plogi.common.flags = htons ( FC_LOGIN_CONTINUOUS_OFFSET );
609         plogi.common.mtu = htons ( FC_LOGIN_DEFAULT_MTU );
610         plogi.common.u.plogi.max_seq = htons ( FC_LOGIN_DEFAULT_MAX_SEQ );
611         plogi.common.u.plogi.rel_offs = htons ( FC_LOGIN_DEFAULT_REL_OFFS );
612         plogi.common.e_d_tov = htonl ( FC_LOGIN_DEFAULT_E_D_TOV );
613         memcpy ( &plogi.port_wwn, &els->port->port_wwn,
614                  sizeof ( plogi.port_wwn ) );
615         memcpy ( &plogi.node_wwn, &els->port->node_wwn,
616                  sizeof ( plogi.node_wwn ) );
617         plogi.class3.flags = htons ( FC_LOGIN_CLASS_VALID |
618                                      FC_LOGIN_CLASS_SEQUENTIAL );
619         plogi.class3.mtu = htons ( FC_LOGIN_DEFAULT_MTU );
620         plogi.class3.max_seq = htons ( FC_LOGIN_DEFAULT_MAX_SEQ );
621         plogi.class3.max_seq_per_xchg = 1;
622
623         /* Transmit PLOGI */
624         return fc_els_tx ( els, &plogi, sizeof ( plogi ) );
625 }
626
627 /**
628  * Receive PLOGI
629  *
630  * @v els               Fibre Channel ELS transaction
631  * @v data              ELS frame
632  * @v len               Length of ELS frame
633  * @ret rc              Return status code
634  */
635 static int fc_els_plogi_rx ( struct fc_els *els, void *data, size_t len ) {
636         struct fc_login_frame *plogi = data;
637         struct fc_peer *peer;
638         int rc;
639
640         /* Sanity checks */
641         if ( len < sizeof ( *plogi ) ) {
642                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
643                        FCELS_ARGS ( els ) );
644                 DBGC_HDA ( els, 0, data, len );
645                 rc = -EINVAL;
646                 goto err_sanity;
647         }
648         if ( ! fc_link_ok ( &els->port->link ) ) {
649                 DBGC ( els, FCELS_FMT " received while port link is down\n",
650                        FCELS_ARGS ( els ) );
651                 rc = -EINVAL;
652                 goto err_sanity;
653         }
654
655         /* Extract parameters */
656         DBGC ( els, FCELS_FMT " has node %s\n", FCELS_ARGS ( els ),
657                fc_ntoa ( &plogi->node_wwn ) );
658         DBGC ( els, FCELS_FMT " has port %s as %s\n",
659                FCELS_ARGS ( els ), fc_ntoa ( &plogi->port_wwn ),
660                fc_id_ntoa ( &els->peer_port_id ) );
661
662         /* Get peer */
663         peer = fc_peer_get_wwn ( &plogi->port_wwn );
664         if ( ! peer ) {
665                 DBGC ( els, FCELS_FMT " could not create peer\n",
666                        FCELS_ARGS ( els ) );
667                 rc = -ENOMEM;
668                 goto err_peer_get_wwn;
669         }
670
671         /* Record login */
672         if ( ( rc = fc_peer_login ( peer, els->port,
673                                     &els->peer_port_id ) ) != 0 ) {
674                 DBGC ( els, FCELS_FMT " could not log in peer: %s\n",
675                        FCELS_ARGS ( els ), strerror ( rc ) );
676                 goto err_login;
677         }
678
679         /* Transmit response, if applicable */
680         if ( ! fc_els_is_request ( els ) ) {
681                 if ( ( rc = fc_els_plogi_tx ( els ) ) != 0 )
682                         goto err_plogi_tx;
683         }
684
685         /* Drop temporary reference to peer */
686         fc_peer_put ( peer );
687
688         return 0;
689
690  err_plogi_tx:
691  err_login:
692         fc_peer_put ( peer );
693  err_peer_get_wwn:
694  err_sanity:
695         return rc;
696 }
697
698 /**
699  * Detect PLOGI
700  *
701  * @v els               Fibre Channel ELS transaction
702  * @v data              ELS frame
703  * @v len               Length of ELS frame
704  * @ret rc              Return status code
705  */
706 static int fc_els_plogi_detect ( struct fc_els *els __unused, const void *data,
707                                  size_t len __unused ) {
708         const struct fc_login_frame *plogi = data;
709
710         /* Check for PLOGI */
711         if ( plogi->command != FC_ELS_PLOGI )
712                 return -EINVAL;
713
714         return 0;
715 }
716
717 /** PLOGI ELS handler */
718 struct fc_els_handler fc_els_plogi_handler __fc_els_handler = {
719         .name           = "PLOGI",
720         .tx             = fc_els_plogi_tx,
721         .rx             = fc_els_plogi_rx,
722         .detect         = fc_els_plogi_detect,
723 };
724
725 /**
726  * Create PLOGI request
727  *
728  * @v parent            Parent interface
729  * @v port              Fibre Channel port
730  * @v peer_port_id      Peer port ID
731  * @ret rc              Return status code
732  */
733 int fc_els_plogi ( struct interface *parent, struct fc_port *port,
734                    struct fc_port_id *peer_port_id ) {
735
736         return fc_els_request ( parent, port, peer_port_id,
737                                 &fc_els_plogi_handler );
738 }
739
740 /******************************************************************************
741  *
742  * LOGO
743  *
744  ******************************************************************************
745  */
746
747 /**
748  * Transmit LOGO request
749  *
750  * @v els               Fibre Channel ELS transaction
751  * @ret rc              Return status code
752  */
753 static int fc_els_logo_tx ( struct fc_els *els ) {
754         struct fc_logout_request_frame logo;
755
756         /* Construct LOGO */
757         memset ( &logo, 0, sizeof ( logo ) );
758         logo.command = FC_ELS_LOGO;
759         memcpy ( &logo.port_id, &els->port->port_id, sizeof ( logo.port_id ) );
760         memcpy ( &logo.port_wwn, &els->port->port_wwn,
761                  sizeof ( logo.port_wwn ) );
762
763         /* Transmit LOGO */
764         return fc_els_tx ( els, &logo, sizeof ( logo ) );
765 }
766
767 /**
768  * Transmit LOGO response
769  *
770  * @v els               Fibre Channel ELS transaction
771  * @ret rc              Return status code
772  */
773 static int fc_els_logo_tx_response ( struct fc_els *els ) {
774         struct fc_logout_response_frame logo;
775
776         /* Construct LOGO */
777         memset ( &logo, 0, sizeof ( logo ) );
778         logo.command = FC_ELS_LS_ACC;
779
780         /* Transmit LOGO */
781         return fc_els_tx ( els, &logo, sizeof ( logo ) );
782 }
783
784 /**
785  * Log out individual peer or whole port as applicable
786  *
787  * @v els               Fibre Channel ELS transaction
788  * @v port_id           Peer port ID
789  */
790 static void fc_els_logo_logout ( struct fc_els *els,
791                                  struct fc_port_id *peer_port_id ) {
792         struct fc_peer *peer;
793
794         if ( ( memcmp ( peer_port_id, &fc_f_port_id,
795                         sizeof ( *peer_port_id ) ) == 0 ) ||
796              ( memcmp ( peer_port_id, &els->port->port_id,
797                         sizeof ( *peer_port_id ) ) == 0 ) ) {
798                 fc_port_logout ( els->port, 0 );
799         } else {
800                 peer = fc_peer_get_port_id ( els->port, peer_port_id );
801                 if ( peer ) {
802                         fc_peer_logout ( peer, 0 );
803                         fc_peer_put ( peer );
804                 }
805         }
806 }
807
808 /**
809  * Receive LOGO request
810  *
811  * @v els               Fibre Channel ELS transaction
812  * @v data              ELS frame
813  * @v len               Length of ELS frame
814  * @ret rc              Return status code
815  */
816 static int fc_els_logo_rx_request ( struct fc_els *els, void *data,
817                                     size_t len ) {
818         struct fc_logout_request_frame *logo = data;
819         int rc;
820
821         /* Sanity check */
822         if ( len < sizeof ( *logo ) ) {
823                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
824                        FCELS_ARGS ( els ) );
825                 DBGC_HDA ( els, 0, data, len );
826                 return -EINVAL;
827         }
828
829         DBGC ( els, FCELS_FMT " has port %s as %s\n", FCELS_ARGS ( els ),
830                fc_ntoa ( &logo->port_wwn ), fc_id_ntoa ( &logo->port_id ) );
831
832         /* Log out individual peer or whole port as applicable */
833         fc_els_logo_logout ( els, &logo->port_id );
834
835         /* Transmit repsonse */
836         if ( ( rc = fc_els_logo_tx_response ( els ) ) != 0 )
837                 return rc;
838
839         return 0;
840 }
841
842 /**
843  * Receive LOGO response
844  *
845  * @v els               Fibre Channel ELS transaction
846  * @v data              ELS frame
847  * @v len               Length of ELS frame
848  * @ret rc              Return status code
849  */
850 static int fc_els_logo_rx_response ( struct fc_els *els, void *data __unused,
851                                      size_t len __unused ) {
852
853         /* Log out individual peer or whole port as applicable */
854         fc_els_logo_logout ( els, &els->peer_port_id );
855
856         return 0;
857 }
858
859 /**
860  * Receive LOGO
861  *
862  * @v els               Fibre Channel ELS transaction
863  * @v data              ELS frame
864  * @v len               Length of ELS frame
865  * @ret rc              Return status code
866  */
867 static int fc_els_logo_rx ( struct fc_els *els, void *data, size_t len ) {
868
869         if ( fc_els_is_request ( els ) ) {
870                 return fc_els_logo_rx_response ( els, data, len );
871         } else {
872                 return fc_els_logo_rx_request ( els, data, len );
873         }
874 }
875
876 /**
877  * Detect LOGO
878  *
879  * @v els               Fibre Channel ELS transaction
880  * @v data              ELS frame
881  * @v len               Length of ELS frame
882  * @ret rc              Return status code
883  */
884 static int fc_els_logo_detect ( struct fc_els *els __unused, const void *data,
885                                 size_t len __unused ) {
886         const struct fc_logout_request_frame *logo = data;
887
888         /* Check for LOGO */
889         if ( logo->command != FC_ELS_LOGO )
890                 return -EINVAL;
891
892         return 0;
893 }
894
895 /** LOGO ELS handler */
896 struct fc_els_handler fc_els_logo_handler __fc_els_handler = {
897         .name           = "LOGO",
898         .tx             = fc_els_logo_tx,
899         .rx             = fc_els_logo_rx,
900         .detect         = fc_els_logo_detect,
901 };
902
903 /**
904  * Create LOGO request
905  *
906  * @v parent            Parent interface
907  * @v port              Fibre Channel port
908  * @v peer_port_id      Peer port ID
909  * @ret rc              Return status code
910  */
911 int fc_els_logo ( struct interface *parent, struct fc_port *port,
912                   struct fc_port_id *peer_port_id ) {
913
914         return fc_els_request ( parent, port, peer_port_id,
915                                 &fc_els_logo_handler );
916 }
917
918 /******************************************************************************
919  *
920  * PRLI
921  *
922  ******************************************************************************
923  */
924
925 /**
926  * Find PRLI descriptor
927  *
928  * @v type              Upper-layer protocol type
929  * @ret descriptor      PRLI descriptor, or NULL
930  */
931 static struct fc_els_prli_descriptor *
932 fc_els_prli_descriptor ( unsigned int type ) {
933         struct fc_els_prli_descriptor *descriptor;
934
935         for_each_table_entry ( descriptor, FC_ELS_PRLI_DESCRIPTORS ) {
936                 if ( descriptor->type == type )
937                         return descriptor;
938         }
939         return NULL;
940 }
941
942 /**
943  * Transmit PRLI
944  *
945  * @v els               Fibre Channel ELS transaction
946  * @v descriptor        ELS PRLI descriptor
947  * @v param             Service parameters
948  * @ret rc              Return status code
949  */
950 int fc_els_prli_tx ( struct fc_els *els,
951                      struct fc_els_prli_descriptor *descriptor, void *param ) {
952         struct {
953                 struct fc_prli_frame frame;
954                 uint8_t param[descriptor->param_len];
955         } __attribute__ (( packed )) prli;
956         struct fc_ulp *ulp;
957         int rc;
958
959         /* Get ULP */
960         ulp = fc_ulp_get_port_id_type ( els->port, &els->peer_port_id,
961                                         descriptor->type );
962         if ( ! ulp ) {
963                 rc = -ENOMEM;
964                 goto err_get_port_id_type;
965         }
966
967         /* Build frame for transmission */
968         memset ( &prli, 0, sizeof ( prli ) );
969         prli.frame.command = fc_els_tx_command ( els, FC_ELS_PRLI );
970         prli.frame.page_len =
971                 ( sizeof ( prli.frame.page ) + sizeof ( prli.param ) );
972         prli.frame.len = htons ( sizeof ( prli ) );
973         prli.frame.page.type = descriptor->type;
974         if ( fc_els_is_request ( els ) ) {
975                 prli.frame.page.flags |= htons ( FC_PRLI_ESTABLISH );
976         } else if ( fc_link_ok ( &ulp->link ) ) {
977                 prli.frame.page.flags |= htons ( FC_PRLI_ESTABLISH |
978                                                     FC_PRLI_RESPONSE_SUCCESS );
979         }
980         memcpy ( &prli.param, param, sizeof ( prli.param ) );
981
982         /* Transmit frame */
983         if ( ( rc = fc_els_tx ( els, &prli, sizeof ( prli ) ) ) != 0 )
984                 goto err_tx;
985
986         /* Drop temporary reference to ULP */
987         fc_ulp_put ( ulp );
988
989         return 0;
990
991  err_tx:
992         fc_ulp_put ( ulp );
993  err_get_port_id_type:
994         return rc;
995 }
996
997 /**
998  * Receive PRLI
999  *
1000  * @v els               Fibre Channel ELS transaction
1001  * @v descriptor        ELS PRLI descriptor
1002  * @v frame             ELS frame
1003  * @v len               Length of ELS frame
1004  * @ret rc              Return status code
1005  */
1006 int fc_els_prli_rx ( struct fc_els *els,
1007                      struct fc_els_prli_descriptor *descriptor,
1008                      void *data, size_t len ) {
1009         struct {
1010                 struct fc_prli_frame frame;
1011                 uint8_t param[descriptor->param_len];
1012         } __attribute__ (( packed )) *prli = data;
1013         struct fc_ulp *ulp;
1014         int rc;
1015
1016         /* Sanity check */
1017         if ( len < sizeof ( *prli ) ) {
1018                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
1019                        FCELS_ARGS ( els ) );
1020                 DBGC_HDA ( els, 0, data, len );
1021                 rc = -EINVAL;
1022                 goto err_sanity;
1023         }
1024
1025         DBGC ( els, FCELS_FMT " has parameters:\n", FCELS_ARGS ( els ) );
1026         DBGC_HDA ( els, 0, prli->param, sizeof ( prli->param ) );
1027
1028         /* Get ULP */
1029         ulp = fc_ulp_get_port_id_type ( els->port, &els->peer_port_id,
1030                                         descriptor->type );
1031         if ( ! ulp ) {
1032                 rc = -ENOMEM;
1033                 goto err_get_port_id_type;
1034         }
1035
1036         /* Sanity check */
1037         if ( ! fc_link_ok ( &ulp->peer->link ) ) {
1038                 DBGC ( els, FCELS_FMT " received while peer link is down\n",
1039                        FCELS_ARGS ( els ) );
1040                 rc = -EINVAL;
1041                 goto err_link;
1042         }
1043
1044         /* Log in ULP, if applicable */
1045         if ( prli->frame.page.flags & htons ( FC_PRLI_ESTABLISH ) ) {
1046                 if ( ( rc = fc_ulp_login ( ulp, prli->param,
1047                                            sizeof ( prli->param ),
1048                                            fc_els_is_request ( els ) ) ) != 0 ){
1049                         DBGC ( els, FCELS_FMT " could not log in ULP: %s\n",
1050                                FCELS_ARGS ( els ), strerror ( rc ) );
1051                         goto err_login;
1052                 }
1053         } else {
1054                 if ( fc_els_is_request ( els ) ) {
1055                         fc_ulp_logout ( ulp, -EACCES );
1056                 } else {
1057                         /* This is just an information-gathering PRLI; do not
1058                          * log in or out
1059                          */
1060                 }
1061         }
1062
1063         /* Transmit response, if applicable */
1064         if ( ! fc_els_is_request ( els ) ) {
1065                 if ( ( rc = els->handler->tx ( els ) ) != 0 )
1066                         goto err_tx;
1067         }
1068
1069         /* Drop temporary reference to ULP */
1070         fc_ulp_put ( ulp );
1071
1072         return 0;
1073
1074  err_tx:
1075  err_login:
1076  err_link:
1077         fc_ulp_put ( ulp );
1078  err_get_port_id_type:
1079  err_sanity:
1080         return rc;
1081 }
1082
1083 /**
1084  * Detect PRLI
1085  *
1086  * @v els               Fibre Channel ELS transaction
1087  * @v descriptor        ELS PRLI descriptor
1088  * @v data              ELS frame
1089  * @v len               Length of ELS frame
1090  * @ret rc              Return status code
1091  */
1092 int fc_els_prli_detect ( struct fc_els *els __unused,
1093                          struct fc_els_prli_descriptor *descriptor,
1094                          const void *data, size_t len ) {
1095         const struct {
1096                 struct fc_prli_frame frame;
1097                 uint8_t param[descriptor->param_len];
1098         } __attribute__ (( packed )) *prli = data;
1099
1100         /* Check for PRLI */
1101         if ( prli->frame.command != FC_ELS_PRLI )
1102                 return -EINVAL;
1103
1104         /* Check for sufficient length to contain service parameter page */
1105         if ( len < sizeof ( *prli ) )
1106                 return -EINVAL;
1107
1108         /* Check for upper-layer protocol type */
1109         if ( prli->frame.page.type != descriptor->type )
1110                 return -EINVAL;
1111
1112         return 0;
1113 }
1114
1115 /**
1116  * Create PRLI request
1117  *
1118  * @v parent            Parent interface
1119  * @v port              Fibre Channel port
1120  * @v peer_port_id      Peer port ID
1121  * @v type              Upper-layer protocol type
1122  * @ret rc              Return status code
1123  */
1124 int fc_els_prli ( struct interface *parent, struct fc_port *port,
1125                   struct fc_port_id *peer_port_id, unsigned int type ) {
1126         struct fc_els_prli_descriptor *descriptor;
1127
1128         /* Find a PRLI descriptor */
1129         descriptor = fc_els_prli_descriptor ( type );
1130         if ( ! descriptor )
1131                 return -ENOTSUP;
1132
1133         return fc_els_request ( parent, port, peer_port_id,
1134                                 descriptor->handler );
1135 }
1136
1137 /******************************************************************************
1138  *
1139  * RTV
1140  *
1141  ******************************************************************************
1142  */
1143
1144 /**
1145  * Transmit RTV response
1146  *
1147  * @v els               Fibre Channel ELS transaction
1148  * @ret rc              Return status code
1149  */
1150 static int fc_els_rtv_tx_response ( struct fc_els *els ) {
1151         struct fc_rtv_response_frame rtv;
1152
1153         /* Construct RTV */
1154         memset ( &rtv, 0, sizeof ( rtv ) );
1155         rtv.command = FC_ELS_LS_ACC;
1156         rtv.e_d_tov = htonl ( FC_LOGIN_DEFAULT_E_D_TOV );
1157
1158         /* Transmit RTV */
1159         return fc_els_tx ( els, &rtv, sizeof ( rtv ) );
1160 }
1161
1162 /**
1163  * Receive RTV
1164  *
1165  * @v els               Fibre Channel ELS transaction
1166  * @v data              ELS frame
1167  * @v len               Length of ELS frame
1168  * @ret rc              Return status code
1169  */
1170 static int fc_els_rtv_rx ( struct fc_els *els, void *data __unused,
1171                            size_t len __unused ) {
1172         int rc;
1173
1174         DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
1175
1176         /* Transmit response */
1177         if ( ! fc_els_is_request ( els ) ) {
1178                 if ( ( rc = fc_els_rtv_tx_response ( els ) ) != 0 )
1179                         return rc;
1180         }
1181
1182         return 0;
1183 }
1184
1185 /**
1186  * Detect RTV
1187  *
1188  * @v els               Fibre Channel ELS transaction
1189  * @v data              ELS frame
1190  * @v len               Length of ELS frame
1191  * @ret rc              Return status code
1192  */
1193 static int fc_els_rtv_detect ( struct fc_els *els __unused, const void *data,
1194                                size_t len __unused ) {
1195         const struct fc_rtv_request_frame *rtv = data;
1196
1197         /* Check for RTV */
1198         if ( rtv->command != FC_ELS_RTV )
1199                 return -EINVAL;
1200
1201         return 0;
1202 }
1203
1204 /** RTV ELS handler */
1205 struct fc_els_handler fc_els_rtv_handler __fc_els_handler = {
1206         .name           = "RTV",
1207         .tx             = fc_els_unknown_tx,
1208         .rx             = fc_els_rtv_rx,
1209         .detect         = fc_els_rtv_detect,
1210 };
1211
1212 /******************************************************************************
1213  *
1214  * ECHO
1215  *
1216  ******************************************************************************
1217  */
1218
1219 /** ECHO request data */
1220 struct fc_echo_request_frame {
1221         /** ECHO frame header */
1222         struct fc_echo_frame_header echo;
1223         /** Magic marker */
1224         uint32_t magic;
1225 } __attribute__ (( packed ));
1226
1227 /** ECHO magic marker */
1228 #define FC_ECHO_MAGIC 0x69505845
1229
1230 /**
1231  * Transmit ECHO
1232  *
1233  * @v els               Fibre Channel ELS transaction
1234  * @ret rc              Return status code
1235  */
1236 static int fc_els_echo_tx ( struct fc_els *els ) {
1237         struct fc_echo_request_frame echo;
1238
1239         /* Construct ECHO */
1240         memset ( &echo, 0, sizeof ( echo ) );
1241         echo.echo.command = FC_ELS_ECHO;
1242         echo.magic = htonl ( FC_ECHO_MAGIC );
1243
1244         /* Transmit ECHO */
1245         return fc_els_tx ( els, &echo, sizeof ( echo ) );
1246 }
1247
1248 /**
1249  * Receive ECHO request
1250  *
1251  * @v els               Fibre Channel ELS transaction
1252  * @v data              ELS frame
1253  * @v len               Length of ELS frame
1254  * @ret rc              Return status code
1255  */
1256 static int fc_els_echo_rx_request ( struct fc_els *els, void *data,
1257                                     size_t len ) {
1258         struct {
1259                 struct fc_echo_frame_header echo;
1260                 char payload[ len - sizeof ( struct fc_echo_frame_header ) ];
1261         } *echo = data;
1262         int rc;
1263
1264         DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
1265
1266         /* Transmit response */
1267         echo->echo.command = FC_ELS_LS_ACC;
1268         if ( ( rc = fc_els_tx ( els, echo, sizeof ( *echo ) ) ) != 0 )
1269                 return rc;
1270
1271         /* Nothing to do */
1272         return 0;
1273 }
1274
1275 /**
1276  * Receive ECHO response
1277  *
1278  * @v els               Fibre Channel ELS transaction
1279  * @v data              ELS frame
1280  * @v len               Length of ELS frame
1281  * @ret rc              Return status code
1282  */
1283 static int fc_els_echo_rx_response ( struct fc_els *els, void *data,
1284                                      size_t len ) {
1285         struct fc_echo_request_frame *echo = data;
1286
1287         DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
1288
1289         /* Check response is correct */
1290         if ( ( len != sizeof ( *echo ) ) ||
1291              ( echo->magic != htonl ( FC_ECHO_MAGIC ) ) ) {
1292                 DBGC ( els, FCELS_FMT " received bad echo response\n",
1293                        FCELS_ARGS ( els ) );
1294                 DBGC_HDA ( els, 0, data, len );
1295                 return -EIO;
1296         }
1297
1298         return 0;
1299 }
1300
1301 /**
1302  * Receive ECHO
1303  *
1304  * @v els               Fibre Channel ELS transaction
1305  * @v data              ELS frame
1306  * @v len               Length of ELS frame
1307  * @ret rc              Return status code
1308  */
1309 static int fc_els_echo_rx ( struct fc_els *els, void *data, size_t len ) {
1310
1311         if ( fc_els_is_request ( els ) ) {
1312                 return fc_els_echo_rx_response ( els, data, len );
1313         } else {
1314                 return fc_els_echo_rx_request ( els, data, len );
1315         }
1316 }
1317
1318 /**
1319  * Detect ECHO
1320  *
1321  * @v els               Fibre Channel ELS transaction
1322  * @v data              ELS frame
1323  * @v len               Length of ELS frame
1324  * @ret rc              Return status code
1325  */
1326 static int fc_els_echo_detect ( struct fc_els *els __unused, const void *data,
1327                                 size_t len __unused ) {
1328         const struct fc_echo_frame_header *echo = data;
1329
1330         /* Check for ECHO */
1331         if ( echo->command != FC_ELS_ECHO )
1332                 return -EINVAL;
1333
1334         return 0;
1335 }
1336
1337 /** ECHO ELS handler */
1338 struct fc_els_handler fc_els_echo_handler __fc_els_handler = {
1339         .name           = "ECHO",
1340         .tx             = fc_els_echo_tx,
1341         .rx             = fc_els_echo_rx,
1342         .detect         = fc_els_echo_detect,
1343 };