iPXE
fcels.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdint.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <errno.h>
00030 #include <assert.h>
00031 #include <byteswap.h>
00032 #include <ipxe/interface.h>
00033 #include <ipxe/xfer.h>
00034 #include <ipxe/iobuf.h>
00035 #include <ipxe/process.h>
00036 #include <ipxe/fc.h>
00037 #include <ipxe/fcels.h>
00038 
00039 /** @file
00040  *
00041  * Fibre Channel Extended Link Services
00042  *
00043  */
00044 
00045 /** Fibre Channel ELS transaction debug message format */
00046 #define FCELS_FMT "FCELS %s %s %s %s"
00047 
00048 /** Fibre Channel ELS transaction debug message arguments */
00049 #define FCELS_ARGS( els )                                               \
00050         (els)->port->name,                                              \
00051         ( (els)->handler ? (els)->handler->name : "unknown ELS" ),      \
00052         ( fc_els_is_request ( els ) ? "to" : "from" ),                  \
00053         fc_id_ntoa ( &(els)->peer_port_id )
00054 
00055 struct fc_els_handler fc_els_unknown_handler __fc_els_handler;
00056 
00057 /**
00058  * Free Fibre Channel ELS transaction
00059  *
00060  * @v refcnt            Reference count
00061  */
00062 static void fc_els_free ( struct refcnt *refcnt ) {
00063         struct fc_els *els = container_of ( refcnt, struct fc_els, refcnt );
00064 
00065         assert ( ! process_running ( &els->process ) );
00066         fc_port_put ( els->port );
00067         free ( els );
00068 }
00069 
00070 /**
00071  * Close Fibre Channel ELS transaction
00072  *
00073  * @v els               Fibre Channel ELS transaction
00074  * @v rc                Reason for close
00075  */
00076 static void fc_els_close ( struct fc_els *els, int rc ) {
00077 
00078         if ( rc != 0 ) {
00079                 DBGC ( els, FCELS_FMT " complete (%s)\n",
00080                        FCELS_ARGS ( els ), strerror ( rc ) );
00081         }
00082 
00083         /* Stop process */
00084         process_del ( &els->process );
00085 
00086         /* Shut down interfaces */
00087         intf_shutdown ( &els->xchg, rc );
00088         intf_shutdown ( &els->job, rc );
00089 }
00090 
00091 /**
00092  * Detect Fibre Channel ELS frame handler
00093  *
00094  * @v els               Fibre Channel ELS transaction
00095  * @v command           ELS command code
00096  * @ret handler         ELS handler, or NULL
00097  */
00098 static struct fc_els_handler * fc_els_detect ( struct fc_els *els,
00099                                                const void *data,
00100                                                size_t len ) {
00101         const struct fc_els_frame_common *frame = data;
00102         struct fc_els_handler *handler;
00103         int rc;
00104 
00105         /* Sanity check */
00106         if ( len < sizeof ( *frame ) )
00107                 return NULL;
00108 
00109         /* Try each handler in turn */
00110         for_each_table_entry ( handler, FC_ELS_HANDLERS ) {
00111                 if ( ( rc = handler->detect ( els, data, len ) ) == 0 )
00112                         return handler;
00113         }
00114 
00115         return NULL;
00116 }
00117 
00118 /**
00119  * Transmit Fibre Channel ELS frame
00120  *
00121  * @v els               Fibre Channel ELS transaction
00122  * @v data              Data to transmit
00123  * @v len               Length of data
00124  * @ret rc              Return status code
00125  */
00126 int fc_els_tx ( struct fc_els *els, const void *data, size_t len ) {
00127         struct xfer_metadata meta;
00128         struct sockaddr_fc dest;
00129         int rc;
00130 
00131         DBGC2 ( els, FCELS_FMT " transmitting:\n", FCELS_ARGS ( els ) );
00132         DBGC2_HDA ( els, 0, data, len );
00133 
00134         /* Construct metadata */
00135         memset ( &meta, 0, sizeof ( meta ) );
00136         meta.flags = ( fc_els_is_request ( els ) ?
00137                        XFER_FL_OVER : ( XFER_FL_RESPONSE | XFER_FL_OUT ) );
00138         meta.dest = fc_fill_sockaddr ( &dest, &els->peer_port_id );
00139 
00140         /* Transmit frame */
00141         if ( ( rc = xfer_deliver_raw_meta ( &els->xchg, data, len,
00142                                             &meta ) ) != 0 ) {
00143                 DBGC ( els, FCELS_FMT " could not deliver frame: %s\n",
00144                        FCELS_ARGS ( els ), strerror ( rc ) );
00145                 return rc;
00146         }
00147 
00148         return 0;
00149 }
00150 
00151 /**
00152  * Receive Fibre Channel ELS frame
00153  *
00154  * @v els               Fibre Channel ELS transaction
00155  * @v iobuf             I/O buffer
00156  * @v meta              Data transfer metadata
00157  * @ret rc              Return status code
00158  */
00159 static int fc_els_rx ( struct fc_els *els,
00160                        struct io_buffer *iobuf,
00161                        struct xfer_metadata *meta ) {
00162         struct fc_els_frame_common *frame = iobuf->data;
00163         struct sockaddr_fc *src = ( ( struct sockaddr_fc * ) meta->src );
00164         struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest );
00165         size_t len = iob_len ( iobuf );
00166         int rc;
00167 
00168         /* Sanity check */
00169         if ( len < sizeof ( *frame ) ) {
00170                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
00171                        FCELS_ARGS ( els ) );
00172                 DBGC_HDA ( els, 0, frame, len );
00173                 rc = -EINVAL;
00174                 goto done;
00175         }
00176         if ( ! src ) {
00177                 DBGC ( els, FCELS_FMT " received frame missing source "
00178                        "address:\n", FCELS_ARGS ( els ) );
00179                 rc = -EINVAL;
00180                 goto done;
00181         }
00182         if ( ! dest ) {
00183                 DBGC ( els, FCELS_FMT " received frame missing destination "
00184                        "address:\n", FCELS_ARGS ( els ) );
00185                 rc = -EINVAL;
00186                 goto done;
00187         }
00188 
00189         /* Check for rejection responses */
00190         if ( fc_els_is_request ( els ) &&
00191              ( frame->command != FC_ELS_LS_ACC ) ) {
00192                 DBGC ( els, FCELS_FMT " rejected:\n", FCELS_ARGS ( els ) );
00193                 DBGC_HDA ( els, 0, frame, len );
00194                 rc = -EACCES;
00195                 goto done;
00196         }
00197 
00198         /* Update port IDs */
00199         memcpy ( &els->port_id, &dest->sfc_port_id, sizeof ( els->port_id ) );
00200         memcpy ( &els->peer_port_id, &src->sfc_port_id,
00201                  sizeof ( els->peer_port_id ) );
00202 
00203         /* Determine handler, if necessary */
00204         if ( ! els->handler )
00205                 els->handler = fc_els_detect ( els, frame, len );
00206         if ( ! els->handler )
00207                 els->handler = &fc_els_unknown_handler;
00208 
00209         DBGC2 ( els, FCELS_FMT " received:\n", FCELS_ARGS ( els ) );
00210         DBGC2_HDA ( els, 0, frame, len );
00211 
00212         /* Handle received frame */
00213         if ( ( rc = els->handler->rx ( els, frame, len ) ) != 0 ) {
00214                 DBGC ( els, FCELS_FMT " could not handle received frame: "
00215                        "%s\n", FCELS_ARGS ( els ), strerror ( rc ) );
00216                 DBGC_HDA ( els, 0, frame, len );
00217                 goto done;
00218         }
00219 
00220  done:
00221         /* Free I/O buffer */
00222         free_iob ( iobuf );
00223 
00224         /* Close transaction */
00225         fc_els_close ( els, rc );
00226 
00227         return rc;
00228 }
00229 
00230 /** Fibre Channel ELS exchange interface operations */
00231 static struct interface_operation fc_els_xchg_op[] = {
00232         INTF_OP ( xfer_deliver, struct fc_els *, fc_els_rx ),
00233         INTF_OP ( intf_close, struct fc_els *, fc_els_close ),
00234 };
00235 
00236 /** Fibre Channel ELS exchange interface descriptor */
00237 static struct interface_descriptor fc_els_xchg_desc =
00238         INTF_DESC ( struct fc_els, xchg, fc_els_xchg_op );
00239 
00240 /** Fibre Channel ELS job control interface operations */
00241 static struct interface_operation fc_els_job_op[] = {
00242         INTF_OP ( intf_close, struct fc_els *, fc_els_close ),
00243 };
00244 
00245 /** Fibre Channel ELS job control interface descriptor */
00246 static struct interface_descriptor fc_els_job_desc =
00247         INTF_DESC ( struct fc_els, job, fc_els_job_op );
00248 
00249 /**
00250  * Fibre Channel ELS process
00251  *
00252  * @v els               Fibre Channel ELS transaction
00253  */
00254 static void fc_els_step ( struct fc_els *els ) {
00255         int xchg_id;
00256         int rc;
00257 
00258         /* Sanity check */
00259         assert ( fc_els_is_request ( els ) );
00260 
00261         /* Create exchange */
00262         if ( ( xchg_id = fc_xchg_originate ( &els->xchg, els->port,
00263                                              &els->peer_port_id,
00264                                              FC_TYPE_ELS ) ) < 0 ) {
00265                 rc = xchg_id;
00266                 DBGC ( els, FCELS_FMT " could not create exchange: %s\n",
00267                        FCELS_ARGS ( els ), strerror ( rc ) );
00268                 fc_els_close ( els, rc );
00269                 return;
00270         }
00271 
00272         /* Transmit request */
00273         if ( ( rc = els->handler->tx ( els ) ) != 0 ) {
00274                 DBGC ( els, FCELS_FMT " could not transmit request: %s\n",
00275                        FCELS_ARGS ( els ), strerror ( rc ) );
00276                 fc_els_close ( els, rc );
00277                 return;
00278         }
00279 }
00280 
00281 /** Fibre Channel ELS process descriptor */
00282 static struct process_descriptor fc_els_process_desc =
00283         PROC_DESC_ONCE ( struct fc_els, process, fc_els_step );
00284 
00285 /**
00286  * Create ELS transaction
00287  *
00288  * @v port              Fibre Channel port
00289  * @v port_id           Local port ID
00290  * @v peer_port_id      Peer port ID
00291  * @ret els             Fibre Channel ELS transaction, or NULL
00292  */
00293 static struct fc_els * fc_els_create ( struct fc_port *port,
00294                                        struct fc_port_id *port_id,
00295                                        struct fc_port_id *peer_port_id ) {
00296         struct fc_els *els;
00297 
00298         /* Allocate and initialise structure */
00299         els = zalloc ( sizeof ( *els ) );
00300         if ( ! els )
00301                 return NULL;
00302         ref_init ( &els->refcnt, fc_els_free );
00303         intf_init ( &els->job, &fc_els_job_desc, &els->refcnt );
00304         intf_init ( &els->xchg, &fc_els_xchg_desc, &els->refcnt );
00305         process_init_stopped ( &els->process, &fc_els_process_desc,
00306                                &els->refcnt );
00307         els->port = fc_port_get ( port );
00308         memcpy ( &els->port_id, port_id, sizeof ( els->port_id ) );
00309         memcpy ( &els->peer_port_id, peer_port_id,
00310                  sizeof ( els->peer_port_id ) );
00311         return els;
00312 }
00313 
00314 /**
00315  * Create ELS request
00316  *
00317  * @v job               Parent job-control interface
00318  * @v port              Fibre Channel port
00319  * @v peer_port_id      Peer port ID
00320  * @v handler           ELS handler
00321  * @ret rc              Return status code
00322  */
00323 int fc_els_request ( struct interface *job, struct fc_port *port,
00324                      struct fc_port_id *peer_port_id,
00325                      struct fc_els_handler *handler ) {
00326         struct fc_els *els;
00327 
00328         /* Allocate and initialise structure */
00329         els = fc_els_create ( port, &port->port_id, peer_port_id );
00330         if ( ! els )
00331                 return -ENOMEM;
00332         els->handler = handler;
00333         els->flags = FC_ELS_REQUEST;
00334         process_add ( &els->process );
00335 
00336         /* Attach to parent job interface, mortalise self, and return */
00337         intf_plug_plug ( &els->job, job );
00338         ref_put ( &els->refcnt );
00339         return 0;
00340 }
00341 
00342 /**
00343  * Create ELS response
00344  *
00345  * @v xchg              Exchange interface
00346  * @v port              Fibre Channel port
00347  * @v port_id           Local port ID
00348  * @v peer_port_id      Peer port ID
00349  * @ret rc              Return status code
00350  */
00351 static int fc_els_respond ( struct interface *xchg, struct fc_port *port,
00352                             struct fc_port_id *port_id,
00353                             struct fc_port_id *peer_port_id ) {
00354         struct fc_els *els;
00355 
00356         /* Allocate and initialise structure */
00357         els = fc_els_create ( port, port_id, peer_port_id );
00358         if ( ! els )
00359                 return -ENOMEM;
00360 
00361         /* Attach to exchange interface, mortalise self, and return */
00362         intf_plug_plug ( &els->xchg, xchg );
00363         ref_put ( &els->refcnt );
00364         return 0;
00365 }
00366 
00367 /** Fibre Channel ELS responder */
00368 struct fc_responder fc_els_responder __fc_responder = {
00369         .type = FC_TYPE_ELS,
00370         .respond = fc_els_respond,
00371 };
00372 
00373 /******************************************************************************
00374  *
00375  * Unknown ELS handler
00376  *
00377  ******************************************************************************
00378  */
00379 
00380 /**
00381  * Transmit unknown ELS request
00382  *
00383  * @v els               Fibre Channel ELS transaction
00384  * @ret rc              Return status code
00385  */
00386 static int fc_els_unknown_tx ( struct fc_els *els __unused ) {
00387         return -ENOTSUP;
00388 }
00389 
00390 /**
00391  * Transmit unknown ELS response
00392  *
00393  * @v els               Fibre Channel ELS transaction
00394  * @ret rc              Return status code
00395  */
00396 static int fc_els_unknown_tx_response ( struct fc_els *els ) {
00397         struct fc_ls_rjt_frame ls_rjt;
00398 
00399         /* Construct LS_RJT */
00400         memset ( &ls_rjt, 0, sizeof ( ls_rjt ) );
00401         ls_rjt.command = FC_ELS_LS_RJT;
00402         ls_rjt.reason = FC_ELS_RJT_UNSUPPORTED;
00403 
00404         /* Transmit LS_RJT */
00405         return fc_els_tx ( els, &ls_rjt, sizeof ( ls_rjt ) );
00406 }
00407 
00408 /**
00409  * Receive unknown ELS
00410  *
00411  * @v els               Fibre Channel ELS transaction
00412  * @v data              ELS frame
00413  * @v len               Length of ELS frame
00414  * @ret rc              Return status code
00415  */
00416 static int fc_els_unknown_rx ( struct fc_els *els, void *data, size_t len ) {
00417         int rc;
00418 
00419         DBGC ( els, FCELS_FMT ":\n", FCELS_ARGS ( els ) );
00420         DBGC_HDA ( els, 0, data, len );
00421 
00422         /* Transmit response, if applicable */
00423         if ( ! fc_els_is_request ( els ) ) {
00424                 if ( ( rc = fc_els_unknown_tx_response ( els ) ) != 0 )
00425                         return rc;
00426         }
00427 
00428         return 0;
00429 }
00430 
00431 /**
00432  * Detect unknown ELS
00433  *
00434  * @v els               Fibre Channel ELS transaction
00435  * @v data              ELS frame
00436  * @v len               Length of ELS frame
00437  * @ret rc              Return status code
00438  */
00439 static int fc_els_unknown_detect ( struct fc_els *els __unused,
00440                                    const void *data __unused,
00441                                    size_t len __unused ) {
00442         return -ENOTSUP;
00443 }
00444 
00445 /** Unknown ELS handler */
00446 struct fc_els_handler fc_els_unknown_handler __fc_els_handler = {
00447         .name           = "UNKNOWN",
00448         .tx             = fc_els_unknown_tx,
00449         .rx             = fc_els_unknown_rx,
00450         .detect         = fc_els_unknown_detect,
00451 };
00452 
00453 /******************************************************************************
00454  *
00455  * FLOGI
00456  *
00457  ******************************************************************************
00458  */
00459 
00460 /**
00461  * Transmit FLOGI
00462  *
00463  * @v els               Fibre Channel ELS transaction
00464  * @ret rc              Return status code
00465  */
00466 static int fc_els_flogi_tx ( struct fc_els *els ) {
00467         struct fc_login_frame flogi;
00468 
00469         /* Construct FLOGI */
00470         memset ( &flogi, 0, sizeof ( flogi ) );
00471         flogi.command = fc_els_tx_command ( els, FC_ELS_FLOGI );
00472         flogi.common.version = htons ( FC_LOGIN_VERSION );
00473         flogi.common.credit = htons ( FC_LOGIN_DEFAULT_B2B );
00474         flogi.common.flags = htons ( FC_LOGIN_CONTINUOUS_OFFSET );
00475         flogi.common.mtu = htons ( FC_LOGIN_DEFAULT_MTU );
00476         memcpy ( &flogi.port_wwn, &els->port->port_wwn,
00477                  sizeof ( flogi.port_wwn ) );
00478         memcpy ( &flogi.node_wwn, &els->port->node_wwn,
00479                  sizeof ( flogi.node_wwn ) );
00480         flogi.class3.flags = htons ( FC_LOGIN_CLASS_VALID |
00481                                      FC_LOGIN_CLASS_SEQUENTIAL );
00482 
00483         /* Transmit FLOGI */
00484         return fc_els_tx ( els, &flogi, sizeof ( flogi ) );
00485 }
00486 
00487 /**
00488  * Receive FLOGI
00489  *
00490  * @v els               Fibre Channel ELS transaction
00491  * @v data              ELS frame
00492  * @v len               Length of ELS frame
00493  * @ret rc              Return status code
00494  */
00495 static int fc_els_flogi_rx ( struct fc_els *els, void *data, size_t len ) {
00496         struct fc_login_frame *flogi = data;
00497         int has_fabric;
00498         int rc;
00499 
00500         /* Sanity check */
00501         if ( len < sizeof ( *flogi ) ) {
00502                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
00503                        FCELS_ARGS ( els ) );
00504                 DBGC_HDA ( els, 0, data, len );
00505                 return -EINVAL;
00506         }
00507 
00508         /* Extract parameters */
00509         has_fabric = ( flogi->common.flags & htons ( FC_LOGIN_F_PORT ) );
00510         DBGC ( els, FCELS_FMT " has node %s\n", FCELS_ARGS ( els ),
00511                fc_ntoa ( &flogi->node_wwn ) );
00512         DBGC ( els, FCELS_FMT " has port %s\n", FCELS_ARGS ( els ),
00513                fc_ntoa ( &flogi->port_wwn ) );
00514         if ( has_fabric ) {
00515                 DBGC ( els, FCELS_FMT " has fabric with", FCELS_ARGS ( els ) );
00516                 DBGC ( els, " local ID %s\n", fc_id_ntoa ( &els->port_id ) );
00517         } else {
00518                 DBGC ( els, FCELS_FMT " has point-to-point link\n",
00519                        FCELS_ARGS ( els ) );
00520         }
00521 
00522         /* Log in port */
00523         if ( ( rc = fc_port_login ( els->port, &els->port_id, &flogi->node_wwn,
00524                                     &flogi->port_wwn, has_fabric ) ) != 0 ) {
00525                 DBGC ( els, FCELS_FMT " could not log in port: %s\n",
00526                        FCELS_ARGS ( els ), strerror ( rc ) );
00527                 return rc;
00528         }
00529 
00530         /* Send any responses to the newly-assigned peer port ID, if
00531          * applicable.
00532          */
00533         if ( ! has_fabric ) {
00534                 memcpy ( &els->peer_port_id, &els->port->ptp_link_port_id,
00535                          sizeof ( els->peer_port_id ) );
00536         }
00537 
00538         /* Transmit response, if applicable */
00539         if ( ! fc_els_is_request ( els ) ) {
00540                 if ( ( rc = fc_els_flogi_tx ( els ) ) != 0 )
00541                         return rc;
00542         }
00543 
00544         return 0;
00545 }
00546 
00547 /**
00548  * Detect FLOGI
00549  *
00550  * @v els               Fibre Channel ELS transaction
00551  * @v data              ELS frame
00552  * @v len               Length of ELS frame
00553  * @ret rc              Return status code
00554  */
00555 static int fc_els_flogi_detect ( struct fc_els *els __unused, const void *data,
00556                                  size_t len __unused ) {
00557         const struct fc_login_frame *flogi = data;
00558 
00559         /* Check for FLOGI */
00560         if ( flogi->command != FC_ELS_FLOGI )
00561                 return -EINVAL;
00562 
00563         return 0;
00564 }
00565 
00566 /** FLOGI ELS handler */
00567 struct fc_els_handler fc_els_flogi_handler __fc_els_handler = {
00568         .name           = "FLOGI",
00569         .tx             = fc_els_flogi_tx,
00570         .rx             = fc_els_flogi_rx,
00571         .detect         = fc_els_flogi_detect,
00572 };
00573 
00574 /**
00575  * Create FLOGI request
00576  *
00577  * @v parent            Parent interface
00578  * @v port              Fibre Channel port
00579  * @ret rc              Return status code
00580  */
00581 int fc_els_flogi ( struct interface *parent, struct fc_port *port ) {
00582 
00583         return fc_els_request ( parent, port, &fc_f_port_id,
00584                                 &fc_els_flogi_handler );
00585 }
00586 
00587 /******************************************************************************
00588  *
00589  * PLOGI
00590  *
00591  ******************************************************************************
00592  */
00593 
00594 /**
00595  * Transmit PLOGI
00596  *
00597  * @v els               Fibre Channel ELS transaction
00598  * @ret rc              Return status code
00599  */
00600 static int fc_els_plogi_tx ( struct fc_els *els ) {
00601         struct fc_login_frame plogi;
00602 
00603         /* Construct PLOGI */
00604         memset ( &plogi, 0, sizeof ( plogi ) );
00605         plogi.command = fc_els_tx_command ( els, FC_ELS_PLOGI );
00606         plogi.common.version = htons ( FC_LOGIN_VERSION );
00607         plogi.common.credit = htons ( FC_LOGIN_DEFAULT_B2B );
00608         plogi.common.flags = htons ( FC_LOGIN_CONTINUOUS_OFFSET );
00609         plogi.common.mtu = htons ( FC_LOGIN_DEFAULT_MTU );
00610         plogi.common.u.plogi.max_seq = htons ( FC_LOGIN_DEFAULT_MAX_SEQ );
00611         plogi.common.u.plogi.rel_offs = htons ( FC_LOGIN_DEFAULT_REL_OFFS );
00612         plogi.common.e_d_tov = htonl ( FC_LOGIN_DEFAULT_E_D_TOV );
00613         memcpy ( &plogi.port_wwn, &els->port->port_wwn,
00614                  sizeof ( plogi.port_wwn ) );
00615         memcpy ( &plogi.node_wwn, &els->port->node_wwn,
00616                  sizeof ( plogi.node_wwn ) );
00617         plogi.class3.flags = htons ( FC_LOGIN_CLASS_VALID |
00618                                      FC_LOGIN_CLASS_SEQUENTIAL );
00619         plogi.class3.mtu = htons ( FC_LOGIN_DEFAULT_MTU );
00620         plogi.class3.max_seq = htons ( FC_LOGIN_DEFAULT_MAX_SEQ );
00621         plogi.class3.max_seq_per_xchg = 1;
00622 
00623         /* Transmit PLOGI */
00624         return fc_els_tx ( els, &plogi, sizeof ( plogi ) );
00625 }
00626 
00627 /**
00628  * Receive PLOGI
00629  *
00630  * @v els               Fibre Channel ELS transaction
00631  * @v data              ELS frame
00632  * @v len               Length of ELS frame
00633  * @ret rc              Return status code
00634  */
00635 static int fc_els_plogi_rx ( struct fc_els *els, void *data, size_t len ) {
00636         struct fc_login_frame *plogi = data;
00637         struct fc_peer *peer;
00638         int rc;
00639 
00640         /* Sanity checks */
00641         if ( len < sizeof ( *plogi ) ) {
00642                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
00643                        FCELS_ARGS ( els ) );
00644                 DBGC_HDA ( els, 0, data, len );
00645                 rc = -EINVAL;
00646                 goto err_sanity;
00647         }
00648         if ( ! fc_link_ok ( &els->port->link ) ) {
00649                 DBGC ( els, FCELS_FMT " received while port link is down\n",
00650                        FCELS_ARGS ( els ) );
00651                 rc = -EINVAL;
00652                 goto err_sanity;
00653         }
00654 
00655         /* Extract parameters */
00656         DBGC ( els, FCELS_FMT " has node %s\n", FCELS_ARGS ( els ),
00657                fc_ntoa ( &plogi->node_wwn ) );
00658         DBGC ( els, FCELS_FMT " has port %s as %s\n",
00659                FCELS_ARGS ( els ), fc_ntoa ( &plogi->port_wwn ),
00660                fc_id_ntoa ( &els->peer_port_id ) );
00661 
00662         /* Get peer */
00663         peer = fc_peer_get_wwn ( &plogi->port_wwn );
00664         if ( ! peer ) {
00665                 DBGC ( els, FCELS_FMT " could not create peer\n",
00666                        FCELS_ARGS ( els ) );
00667                 rc = -ENOMEM;
00668                 goto err_peer_get_wwn;
00669         }
00670 
00671         /* Record login */
00672         if ( ( rc = fc_peer_login ( peer, els->port,
00673                                     &els->peer_port_id ) ) != 0 ) {
00674                 DBGC ( els, FCELS_FMT " could not log in peer: %s\n",
00675                        FCELS_ARGS ( els ), strerror ( rc ) );
00676                 goto err_login;
00677         }
00678 
00679         /* Transmit response, if applicable */
00680         if ( ! fc_els_is_request ( els ) ) {
00681                 if ( ( rc = fc_els_plogi_tx ( els ) ) != 0 )
00682                         goto err_plogi_tx;
00683         }
00684 
00685         /* Drop temporary reference to peer */
00686         fc_peer_put ( peer );
00687 
00688         return 0;
00689 
00690  err_plogi_tx:
00691  err_login:
00692         fc_peer_put ( peer );
00693  err_peer_get_wwn:
00694  err_sanity:
00695         return rc;
00696 }
00697 
00698 /**
00699  * Detect PLOGI
00700  *
00701  * @v els               Fibre Channel ELS transaction
00702  * @v data              ELS frame
00703  * @v len               Length of ELS frame
00704  * @ret rc              Return status code
00705  */
00706 static int fc_els_plogi_detect ( struct fc_els *els __unused, const void *data,
00707                                  size_t len __unused ) {
00708         const struct fc_login_frame *plogi = data;
00709 
00710         /* Check for PLOGI */
00711         if ( plogi->command != FC_ELS_PLOGI )
00712                 return -EINVAL;
00713 
00714         return 0;
00715 }
00716 
00717 /** PLOGI ELS handler */
00718 struct fc_els_handler fc_els_plogi_handler __fc_els_handler = {
00719         .name           = "PLOGI",
00720         .tx             = fc_els_plogi_tx,
00721         .rx             = fc_els_plogi_rx,
00722         .detect         = fc_els_plogi_detect,
00723 };
00724 
00725 /**
00726  * Create PLOGI request
00727  *
00728  * @v parent            Parent interface
00729  * @v port              Fibre Channel port
00730  * @v peer_port_id      Peer port ID
00731  * @ret rc              Return status code
00732  */
00733 int fc_els_plogi ( struct interface *parent, struct fc_port *port,
00734                    struct fc_port_id *peer_port_id ) {
00735 
00736         return fc_els_request ( parent, port, peer_port_id,
00737                                 &fc_els_plogi_handler );
00738 }
00739 
00740 /******************************************************************************
00741  *
00742  * LOGO
00743  *
00744  ******************************************************************************
00745  */
00746 
00747 /**
00748  * Transmit LOGO request
00749  *
00750  * @v els               Fibre Channel ELS transaction
00751  * @ret rc              Return status code
00752  */
00753 static int fc_els_logo_tx ( struct fc_els *els ) {
00754         struct fc_logout_request_frame logo;
00755 
00756         /* Construct LOGO */
00757         memset ( &logo, 0, sizeof ( logo ) );
00758         logo.command = FC_ELS_LOGO;
00759         memcpy ( &logo.port_id, &els->port->port_id, sizeof ( logo.port_id ) );
00760         memcpy ( &logo.port_wwn, &els->port->port_wwn,
00761                  sizeof ( logo.port_wwn ) );
00762 
00763         /* Transmit LOGO */
00764         return fc_els_tx ( els, &logo, sizeof ( logo ) );
00765 }
00766 
00767 /**
00768  * Transmit LOGO response
00769  *
00770  * @v els               Fibre Channel ELS transaction
00771  * @ret rc              Return status code
00772  */
00773 static int fc_els_logo_tx_response ( struct fc_els *els ) {
00774         struct fc_logout_response_frame logo;
00775 
00776         /* Construct LOGO */
00777         memset ( &logo, 0, sizeof ( logo ) );
00778         logo.command = FC_ELS_LS_ACC;
00779 
00780         /* Transmit LOGO */
00781         return fc_els_tx ( els, &logo, sizeof ( logo ) );
00782 }
00783 
00784 /**
00785  * Log out individual peer or whole port as applicable
00786  *
00787  * @v els               Fibre Channel ELS transaction
00788  * @v port_id           Peer port ID
00789  */
00790 static void fc_els_logo_logout ( struct fc_els *els,
00791                                  struct fc_port_id *peer_port_id ) {
00792         struct fc_peer *peer;
00793 
00794         if ( ( memcmp ( peer_port_id, &fc_f_port_id,
00795                         sizeof ( *peer_port_id ) ) == 0 ) ||
00796              ( memcmp ( peer_port_id, &els->port->port_id,
00797                         sizeof ( *peer_port_id ) ) == 0 ) ) {
00798                 fc_port_logout ( els->port, 0 );
00799         } else {
00800                 peer = fc_peer_get_port_id ( els->port, peer_port_id );
00801                 if ( peer ) {
00802                         fc_peer_logout ( peer, 0 );
00803                         fc_peer_put ( peer );
00804                 }
00805         }
00806 }
00807 
00808 /**
00809  * Receive LOGO request
00810  *
00811  * @v els               Fibre Channel ELS transaction
00812  * @v data              ELS frame
00813  * @v len               Length of ELS frame
00814  * @ret rc              Return status code
00815  */
00816 static int fc_els_logo_rx_request ( struct fc_els *els, void *data,
00817                                     size_t len ) {
00818         struct fc_logout_request_frame *logo = data;
00819         int rc;
00820 
00821         /* Sanity check */
00822         if ( len < sizeof ( *logo ) ) {
00823                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
00824                        FCELS_ARGS ( els ) );
00825                 DBGC_HDA ( els, 0, data, len );
00826                 return -EINVAL;
00827         }
00828 
00829         DBGC ( els, FCELS_FMT " has port %s as %s\n", FCELS_ARGS ( els ),
00830                fc_ntoa ( &logo->port_wwn ), fc_id_ntoa ( &logo->port_id ) );
00831 
00832         /* Log out individual peer or whole port as applicable */
00833         fc_els_logo_logout ( els, &logo->port_id );
00834 
00835         /* Transmit repsonse */
00836         if ( ( rc = fc_els_logo_tx_response ( els ) ) != 0 )
00837                 return rc;
00838 
00839         return 0;
00840 }
00841 
00842 /**
00843  * Receive LOGO response
00844  *
00845  * @v els               Fibre Channel ELS transaction
00846  * @v data              ELS frame
00847  * @v len               Length of ELS frame
00848  * @ret rc              Return status code
00849  */
00850 static int fc_els_logo_rx_response ( struct fc_els *els, void *data __unused,
00851                                      size_t len __unused ) {
00852 
00853         /* Log out individual peer or whole port as applicable */
00854         fc_els_logo_logout ( els, &els->peer_port_id );
00855 
00856         return 0;
00857 }
00858 
00859 /**
00860  * Receive LOGO
00861  *
00862  * @v els               Fibre Channel ELS transaction
00863  * @v data              ELS frame
00864  * @v len               Length of ELS frame
00865  * @ret rc              Return status code
00866  */
00867 static int fc_els_logo_rx ( struct fc_els *els, void *data, size_t len ) {
00868 
00869         if ( fc_els_is_request ( els ) ) {
00870                 return fc_els_logo_rx_response ( els, data, len );
00871         } else {
00872                 return fc_els_logo_rx_request ( els, data, len );
00873         }
00874 }
00875 
00876 /**
00877  * Detect LOGO
00878  *
00879  * @v els               Fibre Channel ELS transaction
00880  * @v data              ELS frame
00881  * @v len               Length of ELS frame
00882  * @ret rc              Return status code
00883  */
00884 static int fc_els_logo_detect ( struct fc_els *els __unused, const void *data,
00885                                 size_t len __unused ) {
00886         const struct fc_logout_request_frame *logo = data;
00887 
00888         /* Check for LOGO */
00889         if ( logo->command != FC_ELS_LOGO )
00890                 return -EINVAL;
00891 
00892         return 0;
00893 }
00894 
00895 /** LOGO ELS handler */
00896 struct fc_els_handler fc_els_logo_handler __fc_els_handler = {
00897         .name           = "LOGO",
00898         .tx             = fc_els_logo_tx,
00899         .rx             = fc_els_logo_rx,
00900         .detect         = fc_els_logo_detect,
00901 };
00902 
00903 /**
00904  * Create LOGO request
00905  *
00906  * @v parent            Parent interface
00907  * @v port              Fibre Channel port
00908  * @v peer_port_id      Peer port ID
00909  * @ret rc              Return status code
00910  */
00911 int fc_els_logo ( struct interface *parent, struct fc_port *port,
00912                   struct fc_port_id *peer_port_id ) {
00913 
00914         return fc_els_request ( parent, port, peer_port_id,
00915                                 &fc_els_logo_handler );
00916 }
00917 
00918 /******************************************************************************
00919  *
00920  * PRLI
00921  *
00922  ******************************************************************************
00923  */
00924 
00925 /**
00926  * Find PRLI descriptor
00927  *
00928  * @v type              Upper-layer protocol type
00929  * @ret descriptor      PRLI descriptor, or NULL
00930  */
00931 static struct fc_els_prli_descriptor *
00932 fc_els_prli_descriptor ( unsigned int type ) {
00933         struct fc_els_prli_descriptor *descriptor;
00934 
00935         for_each_table_entry ( descriptor, FC_ELS_PRLI_DESCRIPTORS ) {
00936                 if ( descriptor->type == type )
00937                         return descriptor;
00938         }
00939         return NULL;
00940 }
00941 
00942 /**
00943  * Transmit PRLI
00944  *
00945  * @v els               Fibre Channel ELS transaction
00946  * @v descriptor        ELS PRLI descriptor
00947  * @v param             Service parameters
00948  * @ret rc              Return status code
00949  */
00950 int fc_els_prli_tx ( struct fc_els *els,
00951                      struct fc_els_prli_descriptor *descriptor, void *param ) {
00952         struct {
00953                 struct fc_prli_frame frame;
00954                 uint8_t param[descriptor->param_len];
00955         } __attribute__ (( packed )) prli;
00956         struct fc_ulp *ulp;
00957         int rc;
00958 
00959         /* Get ULP */
00960         ulp = fc_ulp_get_port_id_type ( els->port, &els->peer_port_id,
00961                                         descriptor->type );
00962         if ( ! ulp ) {
00963                 rc = -ENOMEM;
00964                 goto err_get_port_id_type;
00965         }
00966 
00967         /* Build frame for transmission */
00968         memset ( &prli, 0, sizeof ( prli ) );
00969         prli.frame.command = fc_els_tx_command ( els, FC_ELS_PRLI );
00970         prli.frame.page_len =
00971                 ( sizeof ( prli.frame.page ) + sizeof ( prli.param ) );
00972         prli.frame.len = htons ( sizeof ( prli ) );
00973         prli.frame.page.type = descriptor->type;
00974         if ( fc_els_is_request ( els ) ) {
00975                 prli.frame.page.flags |= htons ( FC_PRLI_ESTABLISH );
00976         } else if ( fc_link_ok ( &ulp->link ) ) {
00977                 prli.frame.page.flags |= htons ( FC_PRLI_ESTABLISH |
00978                                                     FC_PRLI_RESPONSE_SUCCESS );
00979         }
00980         memcpy ( &prli.param, param, sizeof ( prli.param ) );
00981 
00982         /* Transmit frame */
00983         if ( ( rc = fc_els_tx ( els, &prli, sizeof ( prli ) ) ) != 0 )
00984                 goto err_tx;
00985 
00986         /* Drop temporary reference to ULP */
00987         fc_ulp_put ( ulp );
00988 
00989         return 0;
00990 
00991  err_tx:
00992         fc_ulp_put ( ulp );
00993  err_get_port_id_type:
00994         return rc;
00995 }
00996 
00997 /**
00998  * Receive PRLI
00999  *
01000  * @v els               Fibre Channel ELS transaction
01001  * @v descriptor        ELS PRLI descriptor
01002  * @v frame             ELS frame
01003  * @v len               Length of ELS frame
01004  * @ret rc              Return status code
01005  */
01006 int fc_els_prli_rx ( struct fc_els *els,
01007                      struct fc_els_prli_descriptor *descriptor,
01008                      void *data, size_t len ) {
01009         struct {
01010                 struct fc_prli_frame frame;
01011                 uint8_t param[descriptor->param_len];
01012         } __attribute__ (( packed )) *prli = data;
01013         struct fc_ulp *ulp;
01014         int rc;
01015 
01016         /* Sanity check */
01017         if ( len < sizeof ( *prli ) ) {
01018                 DBGC ( els, FCELS_FMT " received underlength frame:\n",
01019                        FCELS_ARGS ( els ) );
01020                 DBGC_HDA ( els, 0, data, len );
01021                 rc = -EINVAL;
01022                 goto err_sanity;
01023         }
01024 
01025         DBGC ( els, FCELS_FMT " has parameters:\n", FCELS_ARGS ( els ) );
01026         DBGC_HDA ( els, 0, prli->param, sizeof ( prli->param ) );
01027 
01028         /* Get ULP */
01029         ulp = fc_ulp_get_port_id_type ( els->port, &els->peer_port_id,
01030                                         descriptor->type );
01031         if ( ! ulp ) {
01032                 rc = -ENOMEM;
01033                 goto err_get_port_id_type;
01034         }
01035 
01036         /* Sanity check */
01037         if ( ! fc_link_ok ( &ulp->peer->link ) ) {
01038                 DBGC ( els, FCELS_FMT " received while peer link is down\n",
01039                        FCELS_ARGS ( els ) );
01040                 rc = -EINVAL;
01041                 goto err_link;
01042         }
01043 
01044         /* Log in ULP, if applicable */
01045         if ( prli->frame.page.flags & htons ( FC_PRLI_ESTABLISH ) ) {
01046                 if ( ( rc = fc_ulp_login ( ulp, prli->param,
01047                                            sizeof ( prli->param ),
01048                                            fc_els_is_request ( els ) ) ) != 0 ){
01049                         DBGC ( els, FCELS_FMT " could not log in ULP: %s\n",
01050                                FCELS_ARGS ( els ), strerror ( rc ) );
01051                         goto err_login;
01052                 }
01053         } else {
01054                 if ( fc_els_is_request ( els ) ) {
01055                         fc_ulp_logout ( ulp, -EACCES );
01056                 } else {
01057                         /* This is just an information-gathering PRLI; do not
01058                          * log in or out
01059                          */
01060                 }
01061         }
01062 
01063         /* Transmit response, if applicable */
01064         if ( ! fc_els_is_request ( els ) ) {
01065                 if ( ( rc = els->handler->tx ( els ) ) != 0 )
01066                         goto err_tx;
01067         }
01068 
01069         /* Drop temporary reference to ULP */
01070         fc_ulp_put ( ulp );
01071 
01072         return 0;
01073 
01074  err_tx:
01075  err_login:
01076  err_link:
01077         fc_ulp_put ( ulp );
01078  err_get_port_id_type:
01079  err_sanity:
01080         return rc;
01081 }
01082 
01083 /**
01084  * Detect PRLI
01085  *
01086  * @v els               Fibre Channel ELS transaction
01087  * @v descriptor        ELS PRLI descriptor
01088  * @v data              ELS frame
01089  * @v len               Length of ELS frame
01090  * @ret rc              Return status code
01091  */
01092 int fc_els_prli_detect ( struct fc_els *els __unused,
01093                          struct fc_els_prli_descriptor *descriptor,
01094                          const void *data, size_t len ) {
01095         const struct {
01096                 struct fc_prli_frame frame;
01097                 uint8_t param[descriptor->param_len];
01098         } __attribute__ (( packed )) *prli = data;
01099 
01100         /* Check for PRLI */
01101         if ( prli->frame.command != FC_ELS_PRLI )
01102                 return -EINVAL;
01103 
01104         /* Check for sufficient length to contain service parameter page */
01105         if ( len < sizeof ( *prli ) )
01106                 return -EINVAL;
01107 
01108         /* Check for upper-layer protocol type */
01109         if ( prli->frame.page.type != descriptor->type )
01110                 return -EINVAL;
01111 
01112         return 0;
01113 }
01114 
01115 /**
01116  * Create PRLI request
01117  *
01118  * @v parent            Parent interface
01119  * @v port              Fibre Channel port
01120  * @v peer_port_id      Peer port ID
01121  * @v type              Upper-layer protocol type
01122  * @ret rc              Return status code
01123  */
01124 int fc_els_prli ( struct interface *parent, struct fc_port *port,
01125                   struct fc_port_id *peer_port_id, unsigned int type ) {
01126         struct fc_els_prli_descriptor *descriptor;
01127 
01128         /* Find a PRLI descriptor */
01129         descriptor = fc_els_prli_descriptor ( type );
01130         if ( ! descriptor )
01131                 return -ENOTSUP;
01132 
01133         return fc_els_request ( parent, port, peer_port_id,
01134                                 descriptor->handler );
01135 }
01136 
01137 /******************************************************************************
01138  *
01139  * RTV
01140  *
01141  ******************************************************************************
01142  */
01143 
01144 /**
01145  * Transmit RTV response
01146  *
01147  * @v els               Fibre Channel ELS transaction
01148  * @ret rc              Return status code
01149  */
01150 static int fc_els_rtv_tx_response ( struct fc_els *els ) {
01151         struct fc_rtv_response_frame rtv;
01152 
01153         /* Construct RTV */
01154         memset ( &rtv, 0, sizeof ( rtv ) );
01155         rtv.command = FC_ELS_LS_ACC;
01156         rtv.e_d_tov = htonl ( FC_LOGIN_DEFAULT_E_D_TOV );
01157 
01158         /* Transmit RTV */
01159         return fc_els_tx ( els, &rtv, sizeof ( rtv ) );
01160 }
01161 
01162 /**
01163  * Receive RTV
01164  *
01165  * @v els               Fibre Channel ELS transaction
01166  * @v data              ELS frame
01167  * @v len               Length of ELS frame
01168  * @ret rc              Return status code
01169  */
01170 static int fc_els_rtv_rx ( struct fc_els *els, void *data __unused,
01171                            size_t len __unused ) {
01172         int rc;
01173 
01174         DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
01175 
01176         /* Transmit response */
01177         if ( ! fc_els_is_request ( els ) ) {
01178                 if ( ( rc = fc_els_rtv_tx_response ( els ) ) != 0 )
01179                         return rc;
01180         }
01181 
01182         return 0;
01183 }
01184 
01185 /**
01186  * Detect RTV
01187  *
01188  * @v els               Fibre Channel ELS transaction
01189  * @v data              ELS frame
01190  * @v len               Length of ELS frame
01191  * @ret rc              Return status code
01192  */
01193 static int fc_els_rtv_detect ( struct fc_els *els __unused, const void *data,
01194                                size_t len __unused ) {
01195         const struct fc_rtv_request_frame *rtv = data;
01196 
01197         /* Check for RTV */
01198         if ( rtv->command != FC_ELS_RTV )
01199                 return -EINVAL;
01200 
01201         return 0;
01202 }
01203 
01204 /** RTV ELS handler */
01205 struct fc_els_handler fc_els_rtv_handler __fc_els_handler = {
01206         .name           = "RTV",
01207         .tx             = fc_els_unknown_tx,
01208         .rx             = fc_els_rtv_rx,
01209         .detect         = fc_els_rtv_detect,
01210 };
01211 
01212 /******************************************************************************
01213  *
01214  * ECHO
01215  *
01216  ******************************************************************************
01217  */
01218 
01219 /** ECHO request data */
01220 struct fc_echo_request_frame {
01221         /** ECHO frame header */
01222         struct fc_echo_frame_header echo;
01223         /** Magic marker */
01224         uint32_t magic;
01225 } __attribute__ (( packed ));
01226 
01227 /** ECHO magic marker */
01228 #define FC_ECHO_MAGIC 0x69505845
01229 
01230 /**
01231  * Transmit ECHO
01232  *
01233  * @v els               Fibre Channel ELS transaction
01234  * @ret rc              Return status code
01235  */
01236 static int fc_els_echo_tx ( struct fc_els *els ) {
01237         struct fc_echo_request_frame echo;
01238 
01239         /* Construct ECHO */
01240         memset ( &echo, 0, sizeof ( echo ) );
01241         echo.echo.command = FC_ELS_ECHO;
01242         echo.magic = htonl ( FC_ECHO_MAGIC );
01243 
01244         /* Transmit ECHO */
01245         return fc_els_tx ( els, &echo, sizeof ( echo ) );
01246 }
01247 
01248 /**
01249  * Receive ECHO request
01250  *
01251  * @v els               Fibre Channel ELS transaction
01252  * @v data              ELS frame
01253  * @v len               Length of ELS frame
01254  * @ret rc              Return status code
01255  */
01256 static int fc_els_echo_rx_request ( struct fc_els *els, void *data,
01257                                     size_t len ) {
01258         struct {
01259                 struct fc_echo_frame_header echo;
01260                 char payload[ len - sizeof ( struct fc_echo_frame_header ) ];
01261         } *echo = data;
01262         int rc;
01263 
01264         DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
01265 
01266         /* Transmit response */
01267         echo->echo.command = FC_ELS_LS_ACC;
01268         if ( ( rc = fc_els_tx ( els, echo, sizeof ( *echo ) ) ) != 0 )
01269                 return rc;
01270 
01271         /* Nothing to do */
01272         return 0;
01273 }
01274 
01275 /**
01276  * Receive ECHO response
01277  *
01278  * @v els               Fibre Channel ELS transaction
01279  * @v data              ELS frame
01280  * @v len               Length of ELS frame
01281  * @ret rc              Return status code
01282  */
01283 static int fc_els_echo_rx_response ( struct fc_els *els, void *data,
01284                                      size_t len ) {
01285         struct fc_echo_request_frame *echo = data;
01286 
01287         DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
01288 
01289         /* Check response is correct */
01290         if ( ( len != sizeof ( *echo ) ) ||
01291              ( echo->magic != htonl ( FC_ECHO_MAGIC ) ) ) {
01292                 DBGC ( els, FCELS_FMT " received bad echo response\n",
01293                        FCELS_ARGS ( els ) );
01294                 DBGC_HDA ( els, 0, data, len );
01295                 return -EIO;
01296         }
01297 
01298         return 0;
01299 }
01300 
01301 /**
01302  * Receive ECHO
01303  *
01304  * @v els               Fibre Channel ELS transaction
01305  * @v data              ELS frame
01306  * @v len               Length of ELS frame
01307  * @ret rc              Return status code
01308  */
01309 static int fc_els_echo_rx ( struct fc_els *els, void *data, size_t len ) {
01310 
01311         if ( fc_els_is_request ( els ) ) {
01312                 return fc_els_echo_rx_response ( els, data, len );
01313         } else {
01314                 return fc_els_echo_rx_request ( els, data, len );
01315         }
01316 }
01317 
01318 /**
01319  * Detect ECHO
01320  *
01321  * @v els               Fibre Channel ELS transaction
01322  * @v data              ELS frame
01323  * @v len               Length of ELS frame
01324  * @ret rc              Return status code
01325  */
01326 static int fc_els_echo_detect ( struct fc_els *els __unused, const void *data,
01327                                 size_t len __unused ) {
01328         const struct fc_echo_frame_header *echo = data;
01329 
01330         /* Check for ECHO */
01331         if ( echo->command != FC_ELS_ECHO )
01332                 return -EINVAL;
01333 
01334         return 0;
01335 }
01336 
01337 /** ECHO ELS handler */
01338 struct fc_els_handler fc_els_echo_handler __fc_els_handler = {
01339         .name           = "ECHO",
01340         .tx             = fc_els_echo_tx,
01341         .rx             = fc_els_echo_rx,
01342         .detect         = fc_els_echo_detect,
01343 };