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