#define ERRFILE_fcels ( ERRFILE_NET | 0x002c0000 )
#define ERRFILE_fcp ( ERRFILE_NET | 0x002d0000 )
#define ERRFILE_fcoe ( ERRFILE_NET | 0x002e0000 )
+#define ERRFILE_fcns ( ERRFILE_NET | 0x002f0000 )
#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )
extern struct fc_port_id fc_empty_port_id;
extern struct fc_port_id fc_f_port_id;
+extern struct fc_port_id fc_gs_port_id;
extern struct fc_port_id fc_ptp_low_port_id;
extern struct fc_port_id fc_ptp_high_port_id;
FC_TYPE_BLS = 0x00, /**< Basic Link Service */
FC_TYPE_ELS = 0x01, /**< Extended Link Service */
FC_TYPE_FCP = 0x08, /**< Fibre Channel Protocol */
+ FC_TYPE_CT = 0x20, /**< Common Transport */
};
/** Fibre Channel Frame Control - Exchange and Sequence */
/** Link port ID (for point-to-point links only) */
struct fc_port_id ptp_link_port_id;
+ /** Name server PLOGI interface */
+ struct interface ns_plogi;
+
/** List of active exchanges */
struct list_head xchgs;
};
enum fc_port_flags {
/** Port is attached to a fabric */
FC_PORT_HAS_FABRIC = 0x0001,
+ /** Port is logged in to a name server */
+ FC_PORT_HAS_NS = 0x0002,
};
/**
--- /dev/null
+#ifndef _IPXE_FCNS_H
+#define _IPXE_FCNS_H
+
+/**
+ * @file
+ *
+ * Fibre Channel name server lookups
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <ipxe/fc.h>
+
+/** A Fibre Channel Common Transport header */
+struct fc_ct_header {
+ /** Revision */
+ uint8_t revision;
+ /** Original requestor ID */
+ struct fc_port_id in_id;
+ /** Generic service type */
+ uint8_t type;
+ /** Generic service subtype */
+ uint8_t subtype;
+ /** Options */
+ uint8_t options;
+ /** Reserved */
+ uint8_t reserved;
+ /** Command/response code */
+ uint16_t code;
+ /** Maximum/residual size */
+ uint16_t size;
+ /** Fragment ID */
+ uint8_t fragment;
+ /** Reason code */
+ uint8_t reason;
+ /** Reason code explanation */
+ uint8_t explanation;
+ /** Vendor specific */
+ uint8_t vendor;
+} __attribute__ (( packed ));
+
+/** Fibre Channel Common Transport revision */
+#define FC_CT_REVISION 1
+
+/** Fibre Channel generic service type */
+enum fc_gs_type {
+ /** Directory service */
+ FC_GS_TYPE_DS = 0xfc,
+};
+
+/** Fibre Channel generic service response codes */
+enum fc_gs_response_code {
+ /** Accepted */
+ FC_GS_ACCEPT = 0x8002,
+ /** Rejected */
+ FC_GS_REJECT = 0x8001,
+};
+
+/** Fibre Channel generic service rejection reason codes */
+enum fc_gs_reason_code {
+ /** Invalid command code */
+ FC_GS_BAD_COMMAND = 0x01,
+ /** Invalid version level */
+ FC_GS_BAD_VERSION = 0x02,
+ /** Logical error */
+ FC_GS_ERROR = 0x03,
+ /** Invalid CT_IU size */
+ FC_GS_BAD_SIZE = 0x04,
+ /** Logical busy */
+ FC_GS_BUSY = 0x05,
+ /** Protocol error */
+ FC_GS_EPROTO = 0x07,
+ /** Unable to perform command request */
+ FC_GS_UNABLE = 0x09,
+ /** Command not supported */
+ FC_GS_ENOTSUP = 0x0b,
+ /** Server not available */
+ FC_GS_UNAVAILABLE = 0x0d,
+ /** Session could not be established */
+ FC_GS_SESSION = 0x0e,
+};
+
+/** Fibre Channel directory service subtype */
+enum fc_ds_subtype {
+ /** Name server */
+ FC_DS_SUBTYPE_NAME = 0x02,
+};
+
+/** Fibre Channel name server commands */
+enum fc_ns_command_nibble {
+ /** Get */
+ FC_NS_GET = 0x1,
+ /** Register */
+ FC_NS_REGISTER = 0x2,
+ /** De-register */
+ FC_NS_DEREGISTER = 0x3,
+};
+
+/** Fibre Channel name server objects */
+enum fc_ns_object_nibble {
+ /** Port ID */
+ FC_NS_PORT_ID = 0x1,
+ /** Port name */
+ FC_NS_PORT_NAME = 0x2,
+ /** Node name */
+ FC_NS_NODE_NAME = 0x3,
+ /** FC-4 types */
+ FC_NS_FC4_TYPES = 0x7,
+ /** Symbolic port name */
+ FC_NS_SYM_PORT_NAME = 0x8,
+ /** Symbolic node name */
+ FC_NS_SYM_NODE_NAME = 0x9,
+ /** FC-4 features */
+ FC_NS_FC4_FEATURES = 0xf,
+};
+
+/** Construct Fibre Channel name server command code
+ *
+ * @v command Name server command
+ * @v key Name server key
+ * @v value Name server value
+ * @ret code Name server command code
+ */
+#define FC_NS_CODE( command, key, value ) \
+ ( ( (command) << 8 ) | ( (key) << 4 ) | ( (value) << 0 ) )
+
+/** Construct Fibre Channel name server "get" command code
+ *
+ * @v key Name server key
+ * @v value Name server value to get
+ * @ret code Name server command code
+ */
+#define FC_NS_GET( key, value ) FC_NS_CODE ( FC_NS_GET, key, value )
+
+/** Construct Fibre Channel name server "register" command code
+ *
+ * @v key Name server key
+ * @v value Name server value to register
+ * @ret code Name server command code
+ */
+#define FC_NS_REGISTER( key, value ) FC_NS_CODE ( FC_NS_REGISTER, key, value )
+
+/** Extract Fibre Channel name server command
+ *
+ * @v code Name server command code
+ * @ret command Name server command
+ */
+#define FC_NS_COMMAND( code ) ( ( (code) >> 8 ) & 0xf )
+
+/** Extract Fibre Channel name server key
+ *
+ * @v code Name server command code
+ * @ret key Name server key
+ */
+#define FC_NS_KEY( code ) ( ( (code) >> 4 ) & 0xf )
+
+/** Extract Fibre Channel name server value
+ *
+ * @v code Name server command code
+ * @ret value NAme server value
+ */
+#define FC_NS_VALUE( code ) ( ( (code) >> 0 ) & 0xf )
+
+/** A Fibre Channel name server port ID */
+struct fc_ns_port_id {
+ /** Reserved */
+ uint8_t reserved;
+ /** Port ID */
+ struct fc_port_id port_id;
+} __attribute__ (( packed ));
+
+/** A Fibre Channel name server GID_PN request */
+struct fc_ns_gid_pn_request {
+ /** Common Transport header */
+ struct fc_ct_header ct;
+ /** Port name */
+ struct fc_name port_wwn;
+} __attribute__ (( packed ));
+
+/** A Fibre Channel name server request */
+union fc_ns_request {
+ /** Get ID by port name */
+ struct fc_ns_gid_pn_request gid_pn;
+};
+
+/** A Fibre Channel name server rejection response */
+struct fc_ns_reject_response {
+ /** Common Transport header */
+ struct fc_ct_header ct;
+} __attribute__ (( packed ));
+
+/** A Fibre Channel name server GID_PN response */
+struct fc_ns_gid_pn_response {
+ /** Common Transport header */
+ struct fc_ct_header ct;
+ /** Port ID */
+ struct fc_ns_port_id port_id;
+} __attribute__ (( packed ));
+
+/** A Fibre Channel name server response */
+union fc_ns_response {
+ /** Common Transport header */
+ struct fc_ct_header ct;
+ /** Rejection */
+ struct fc_ns_reject_response reject;
+ /** Get ID by port name */
+ struct fc_ns_gid_pn_response gid_pn;
+};
+
+extern int fc_ns_query ( struct fc_peer *peer, struct fc_port *port,
+ int ( * done ) ( struct fc_peer *peer,
+ struct fc_port *port,
+ struct fc_port_id *peer_port_id ) );
+
+#endif /* _IPXE_FCNS_H */
#include <ipxe/iobuf.h>
#include <ipxe/fc.h>
#include <ipxe/fcels.h>
+#include <ipxe/fcns.h>
/** @file
*
/** F_Port contoller port ID */
struct fc_port_id fc_f_port_id = { .bytes = { 0xff, 0xff, 0xfe } };
+/** Generic services port ID */
+struct fc_port_id fc_gs_port_id = { .bytes = { 0xff, 0xff, 0xfc } };
+
/** Point-to-point low port ID */
struct fc_port_id fc_ptp_low_port_id = { .bytes = { 0x01, 0x01, 0x01 } };
}
/* Calculate routing control */
- if ( xchg->type == FC_TYPE_ELS ) {
+ switch ( xchg->type ) {
+ case FC_TYPE_ELS:
r_ctl = FC_R_CTL_ELS;
if ( meta->flags & XFER_FL_RESPONSE ) {
r_ctl |= FC_R_CTL_SOL_CTRL;
} else {
r_ctl |= FC_R_CTL_UNSOL_CTRL;
}
- } else {
+ break;
+ case FC_TYPE_CT:
+ r_ctl = FC_R_CTL_DATA;
+ if ( meta->flags & XFER_FL_RESPONSE ) {
+ r_ctl |= FC_R_CTL_SOL_CTRL;
+ } else {
+ r_ctl |= FC_R_CTL_UNSOL_CTRL;
+ }
+ break;
+ default:
r_ctl = FC_R_CTL_DATA;
switch ( meta->flags &
( XFER_FL_CMD_STAT | XFER_FL_RESPONSE ) ) {
r_ctl |= FC_R_CTL_UNSOL_DATA;
break;
}
+ break;
}
/* Calculate exchange and sequence control */
/* Shut down interfaces */
intf_shutdown ( &port->transport, rc );
intf_shutdown ( &port->flogi, rc );
+ intf_shutdown ( &port->ns_plogi, rc );
/* Shut down any remaining exchanges */
list_for_each_entry_safe ( xchg, tmp, &port->xchgs, list )
const struct fc_name *link_port_wwn, int has_fabric ) {
struct fc_peer *peer;
struct fc_peer *tmp;
+ int rc;
/* Perform implicit logout if logged in and details differ */
if ( fc_link_ok ( &port->link ) &&
fc_id_ntoa ( &port->port_id ) );
}
+ /* Log in to name server, if attached to a fabric */
+ if ( has_fabric && ! ( port->flags & FC_PORT_HAS_NS ) ) {
+
+ DBGC ( port, "FCPORT %s attempting login to name server\n",
+ port->name );
+
+ intf_restart ( &port->ns_plogi, -ECANCELED );
+ if ( ( rc = fc_els_plogi ( &port->ns_plogi, port,
+ &fc_gs_port_id ) ) != 0 ) {
+ DBGC ( port, "FCPORT %s could not initiate name "
+ "server PLOGI: %s\n",
+ port->name, strerror ( rc ) );
+ fc_port_logout ( port, rc );
+ return rc;
+ }
+ }
+
/* Record login */
fc_link_up ( &port->link );
/* Erase port details */
memset ( &port->port_id, 0, sizeof ( port->port_id ) );
+ port->flags = 0;
/* Record logout */
fc_link_err ( &port->link, rc );
fc_port_logout ( port, rc );
}
+/**
+ * Handle name server PLOGI completion
+ *
+ * @v port Fibre Channel port
+ * @v rc Reason for completion
+ */
+static void fc_port_ns_plogi_done ( struct fc_port *port, int rc ) {
+
+ intf_restart ( &port->ns_plogi, rc );
+
+ if ( rc == 0 ) {
+ port->flags |= FC_PORT_HAS_NS;
+ DBGC ( port, "FCPORT %s logged in to name server\n",
+ port->name );
+ } else {
+ DBGC ( port, "FCPORT %s could not log in to name server: %s\n",
+ port->name, strerror ( rc ) );
+ /* Absence of a name server is not a fatal error */
+ }
+}
+
/**
* Examine Fibre Channel port link state
*
static struct interface_descriptor fc_port_flogi_desc =
INTF_DESC ( struct fc_port, flogi, fc_port_flogi_op );
+/** Fibre Channel port name server PLOGI interface operations */
+static struct interface_operation fc_port_ns_plogi_op[] = {
+ INTF_OP ( intf_close, struct fc_port *, fc_port_ns_plogi_done ),
+};
+
+/** Fibre Channel port name server PLOGI interface descriptor */
+static struct interface_descriptor fc_port_ns_plogi_desc =
+ INTF_DESC ( struct fc_port, ns_plogi, fc_port_ns_plogi_op );
+
/**
* Create Fibre Channel port
*
intf_init ( &port->transport, &fc_port_transport_desc, &port->refcnt );
fc_link_init ( &port->link, fc_port_examine, &port->refcnt );
intf_init ( &port->flogi, &fc_port_flogi_desc, &port->refcnt );
+ intf_init ( &port->ns_plogi, &fc_port_ns_plogi_desc, &port->refcnt );
list_add_tail ( &port->list, &fc_ports );
INIT_LIST_HEAD ( &port->xchgs );
memcpy ( &port->node_wwn, node_wwn, sizeof ( port->node_wwn ) );
return NULL;
}
-/**
- * Find Fibre Channel port by link node name
- *
- * @v link_port_wwn Link node name
- * @ret port Fibre Channel port, or NULL
- */
-static struct fc_port *
-fc_port_find_link_wwn ( struct fc_name *link_port_wwn ) {
- struct fc_port *port;
-
- list_for_each_entry ( port, &fc_ports, list ) {
- if ( fc_link_ok ( &port->link ) &&
- ( memcmp ( &port->link_port_wwn, link_port_wwn,
- sizeof ( port->link_port_wwn ) ) == 0 ) ) {
- return port;
- }
- }
- return NULL;
-}
-
/******************************************************************************
*
* Fibre Channel peers
fc_peer_logout ( peer, rc );
}
+/**
+ * Initiate PLOGI
+ *
+ * @v peer Fibre Channel peer
+ * @v port Fibre Channel port
+ * @v peer_port_id Peer port ID
+ * @ret rc Return status code
+ */
+static int fc_peer_plogi ( struct fc_peer *peer, struct fc_port *port,
+ struct fc_port_id *peer_port_id ) {
+ int rc;
+
+ /* Try to create PLOGI ELS */
+ intf_restart ( &peer->plogi, -ECANCELED );
+ if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) {
+ DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n",
+ fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
+ fc_peer_logout ( peer, rc );
+ return rc;
+ }
+
+ return 0;
+}
+
/**
* Examine Fibre Channel peer link state
*
static void fc_peer_examine ( struct fc_link_state *link ) {
struct fc_peer *peer = container_of ( link, struct fc_peer, link );
struct fc_port *port;
- struct fc_port_id *peer_port_id;
int rc;
/* Check to see if underlying port link has gone down */
/* Sanity check */
assert ( peer->port == NULL );
- /* Look for a port with the peer attached via a point-to-point link */
- port = fc_port_find_link_wwn ( &peer->port_wwn );
- if ( ! port ) {
- DBGC ( peer, "FCPEER %s could not find a point-to-point "
- "link\n", fc_ntoa ( &peer->port_wwn ) );
- fc_peer_logout ( peer, -ENOENT );
- return;
+ /* First, look for a port with the peer attached via a
+ * point-to-point link.
+ */
+ list_for_each_entry ( port, &fc_ports, list ) {
+ if ( fc_link_ok ( &port->link ) &&
+ ( ! ( port->flags & FC_PORT_HAS_FABRIC ) ) &&
+ ( memcmp ( &peer->port_wwn, &port->link_port_wwn,
+ sizeof ( peer->port_wwn ) ) == 0 ) ) {
+ /* Use this peer port ID, and stop looking */
+ fc_peer_plogi ( peer, port, &port->ptp_link_port_id );
+ return;
+ }
}
- peer_port_id = &port->ptp_link_port_id;
- /* Try to create PLOGI ELS */
- intf_restart ( &peer->plogi, -ECANCELED );
- if ( ( rc = fc_els_plogi ( &peer->plogi, port, peer_port_id ) ) != 0 ) {
- DBGC ( peer, "FCPEER %s could not initiate PLOGI: %s\n",
- fc_ntoa ( &peer->port_wwn ), strerror ( rc ) );
- fc_peer_logout ( peer, rc );
- return;
+ /* If the peer is not directly attached, try initiating a name
+ * server lookup on any suitable ports.
+ */
+ list_for_each_entry ( port, &fc_ports, list ) {
+ if ( fc_link_ok ( &port->link ) &&
+ ( port->flags & FC_PORT_HAS_FABRIC ) &&
+ ( port->flags & FC_PORT_HAS_NS ) ) {
+ if ( ( rc = fc_ns_query ( peer, port,
+ fc_peer_plogi ) ) != 0 ) {
+ DBGC ( peer, "FCPEER %s could not attempt "
+ "name server lookup on %s: %s\n",
+ fc_ntoa ( &peer->port_wwn ), port->name,
+ strerror ( rc ) );
+ /* Non-fatal */
+ }
+ }
}
}
--- /dev/null
+/*
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/interface.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/process.h>
+#include <ipxe/xfer.h>
+#include <ipxe/fc.h>
+#include <ipxe/fcns.h>
+
+/** @file
+ *
+ * Fibre Channel name server lookups
+ *
+ */
+
+/** A Fibre Channel name server query */
+struct fc_ns_query {
+ /** Reference count */
+ struct refcnt refcnt;
+ /** Fibre Channel exchange */
+ struct interface xchg;
+
+ /** Fibre Channel peer */
+ struct fc_peer *peer;
+ /** Fibre Channel port */
+ struct fc_port *port;
+
+ /** Process */
+ struct process process;
+ /** Success handler
+ *
+ * @v peer Fibre Channel peer
+ * @v port Fibre Channel port
+ * @v peer_port_id Peer port ID
+ * @ret rc Return status code
+ */
+ int ( * done ) ( struct fc_peer *peer, struct fc_port *port,
+ struct fc_port_id *peer_port_id );
+};
+
+/**
+ * Free name server query
+ *
+ * @v refcnt Reference count
+ */
+static void fc_ns_query_free ( struct refcnt *refcnt ) {
+ struct fc_ns_query *query =
+ container_of ( refcnt, struct fc_ns_query, refcnt );
+
+ fc_peer_put ( query->peer );
+ fc_port_put ( query->port );
+ free ( query );
+}
+
+/**
+ * Close name server query
+ *
+ * @v query Name server query
+ * @v rc Reason for close
+ */
+static void fc_ns_query_close ( struct fc_ns_query *query, int rc ) {
+
+ /* Stop process */
+ process_del ( &query->process );
+
+ /* Shut down interfaces */
+ intf_shutdown ( &query->xchg, rc );
+}
+
+/**
+ * Receive name server query response
+ *
+ * @v query Name server query
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ */
+static int fc_ns_query_deliver ( struct fc_ns_query *query,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta __unused ) {
+ union fc_ns_response *resp = iobuf->data;
+ struct fc_port_id *peer_port_id;
+ int rc;
+
+ /* Sanity check */
+ if ( iob_len ( iobuf ) < sizeof ( resp->ct ) ) {
+ DBGC ( query, "FCNS %p received underlength response (%zd "
+ "bytes)\n", query, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto done;
+ }
+
+ /* Handle response */
+ switch ( ntohs ( resp->ct.code ) ) {
+ case FC_GS_ACCEPT:
+ if ( iob_len ( iobuf ) < sizeof ( resp->gid_pn ) ) {
+ DBGC ( query, "FCNS %p received underlength accept "
+ "response (%zd bytes)\n",
+ query, iob_len ( iobuf ) );
+ rc = -EINVAL;
+ goto done;
+ }
+ peer_port_id = &resp->gid_pn.port_id.port_id;
+ DBGC ( query, "FCNS %p resolved %s to %s via %s\n",
+ query, fc_ntoa ( &query->peer->port_wwn ),
+ fc_id_ntoa ( peer_port_id ), query->port->name );
+ if ( ( rc = query->done ( query->peer, query->port,
+ peer_port_id ) ) != 0 )
+ goto done;
+ break;
+ case FC_GS_REJECT:
+ DBGC ( query, "FCNS %p rejected (reason %02x explanation "
+ "%02x)\n", query, resp->reject.ct.reason,
+ resp->reject.ct.explanation );
+ break;
+ default:
+ DBGC ( query, "FCNS %p received invalid response code %04x\n",
+ query, ntohs ( resp->ct.code ) );
+ rc = -ENOTSUP;
+ goto done;
+ }
+
+ rc = 0;
+ done:
+ free_iob ( iobuf );
+ fc_ns_query_close ( query, rc );
+ return rc;
+}
+
+/**
+ * Name server query process
+ *
+ * @v process Process
+ */
+static void fc_ns_query_step ( struct process *process ) {
+ struct fc_ns_query *query =
+ container_of ( process, struct fc_ns_query, process );
+ struct xfer_metadata meta;
+ struct fc_ns_gid_pn_request gid_pn;
+ int xchg_id;
+ int rc;
+
+ /* Stop process */
+ process_del ( &query->process );
+
+ /* Create exchange */
+ if ( ( xchg_id = fc_xchg_originate ( &query->xchg, query->port,
+ &fc_gs_port_id,
+ FC_TYPE_CT ) ) < 0 ) {
+ rc = xchg_id;
+ DBGC ( query, "FCNS %p could not create exchange: %s\n",
+ query, strerror ( rc ) );
+ fc_ns_query_close ( query, rc );
+ return;
+ }
+
+ /* Construct query request */
+ memset ( &gid_pn, 0, sizeof ( gid_pn ) );
+ gid_pn.ct.revision = FC_CT_REVISION;
+ gid_pn.ct.type = FC_GS_TYPE_DS;
+ gid_pn.ct.subtype = FC_DS_SUBTYPE_NAME;
+ gid_pn.ct.code = htons ( FC_NS_GET ( FC_NS_PORT_NAME, FC_NS_PORT_ID ));
+ memcpy ( &gid_pn.port_wwn, &query->peer->port_wwn,
+ sizeof ( gid_pn.port_wwn ) );
+ memset ( &meta, 0, sizeof ( meta ) );
+ meta.flags = XFER_FL_OVER;
+
+ /* Send query */
+ if ( ( rc = xfer_deliver_raw_meta ( &query->xchg, &gid_pn,
+ sizeof ( gid_pn ), &meta ) ) != 0){
+ DBGC ( query, "FCNS %p could not deliver query: %s\n",
+ query, strerror ( rc ) );
+ fc_ns_query_close ( query, rc );
+ return;
+ }
+}
+
+/** Name server exchange interface operations */
+static struct interface_operation fc_ns_query_xchg_op[] = {
+ INTF_OP ( xfer_deliver, struct fc_ns_query *, fc_ns_query_deliver ),
+ INTF_OP ( intf_close, struct fc_ns_query *, fc_ns_query_close ),
+};
+
+/** Name server exchange interface descriptor */
+static struct interface_descriptor fc_ns_query_xchg_desc =
+ INTF_DESC ( struct fc_ns_query, xchg, fc_ns_query_xchg_op );
+
+/**
+ * Issue Fibre Channel name server query
+ *
+ * @v peer Fibre Channel peer
+ * @v port Fibre Channel port
+ * @ret rc Return status code
+ */
+int fc_ns_query ( struct fc_peer *peer, struct fc_port *port,
+ int ( * done ) ( struct fc_peer *peer, struct fc_port *port,
+ struct fc_port_id *peer_port_id ) ) {
+ struct fc_ns_query *query;
+
+ /* Allocate and initialise structure */
+ query = zalloc ( sizeof ( *query ) );
+ if ( ! query )
+ return -ENOMEM;
+ ref_init ( &query->refcnt, fc_ns_query_free );
+ intf_init ( &query->xchg, &fc_ns_query_xchg_desc, &query->refcnt );
+ process_init ( &query->process, fc_ns_query_step, &query->refcnt );
+ query->peer = fc_peer_get ( peer );
+ query->port = fc_port_get ( port );
+ query->done = done;
+
+ DBGC ( query, "FCNS %p querying %s via %s\n",
+ query, fc_ntoa ( &query->peer->port_wwn ), port->name );
+
+ /* Mortalise self and return */
+ ref_put ( &query->refcnt );
+ return 0;
+}