[zbin] Fix check for existence of most recent output byte
[ipxe.git] / 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 };