#ifndef _IPXE_FC_H #define _IPXE_FC_H /** * @file * * Fibre Channel * */ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include #include #include #include #include /****************************************************************************** * * Fibre Channel Names and identifiers * ****************************************************************************** */ /** A Fibre Channel name */ struct fc_name { uint8_t bytes[8]; } __attribute__ (( packed )); /** Length of Fibre Channel name text */ #define FC_NAME_STRLEN 23 /* "xx:xx:xx:xx:xx:xx:xx:xx" */ /** A Fibre Channel port identifier */ struct fc_port_id { uint8_t bytes[3]; } __attribute__ (( packed )); /** Length of Fibre Channel port identifier next */ #define FC_PORT_ID_STRLEN 9 /* "xx.xx.xx" */ /** * Fibre Channel socket address */ struct sockaddr_fc { /** Socket address family (part of struct @c sockaddr) * * Always set to @c AF_FC for Fibre Channel addresses */ sa_family_t sfc_family; /** Port ID */ struct fc_port_id sfc_port_id; /** Padding * * This ensures that a struct @c sockaddr_tcpip is large * enough to hold a socket address for any TCP/IP address * family. */ char pad[ sizeof ( struct sockaddr ) - sizeof ( sa_family_t ) - sizeof ( struct fc_port_id ) ]; } __attribute__ (( packed, may_alias )); 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; extern const char * fc_id_ntoa ( const struct fc_port_id *id ); extern int fc_id_aton ( const char *id_text, struct fc_port_id *id ); extern const char * fc_ntoa ( const struct fc_name *wwn ); extern int fc_aton ( const char *wwn_text, struct fc_name *wwn ); extern struct sockaddr * fc_fill_sockaddr ( struct sockaddr_fc *sa_fc, struct fc_port_id *id ); /****************************************************************************** * * Fibre Channel link state * ****************************************************************************** */ /** Delay between failed link-up attempts */ #define FC_LINK_RETRY_DELAY ( 2 * TICKS_PER_SEC ) /** A Fibre Channel link state nonitor */ struct fc_link_state { /** Retry timer */ struct retry_timer timer; /** Link state */ int rc; /** Examine link state * * @v link Fibre Channel link state monitor */ void ( * examine ) ( struct fc_link_state *link ); }; /** * Check Fibre Channel link state * * @v link Fibre Channel link state monitor * @ret link_up Link is up */ static inline __attribute__ (( always_inline )) int fc_link_ok ( struct fc_link_state *link ) { return ( link->rc == 0 ); } /****************************************************************************** * * Fibre Channel packet formats and exchanges * ****************************************************************************** */ /** A Fibre Channel Frame Header */ struct fc_frame_header { /** Routing control * * This is the bitwise OR of one @c fc_r_ctl_routing value and * one @c fc_r_ctl_info value. */ uint8_t r_ctl; /** Destination ID */ struct fc_port_id d_id; /** Class-specific control / Priority */ uint8_t cs_ctl_prio; /** Source ID */ struct fc_port_id s_id; /** Data structure type */ uint8_t type; /** Frame control - exchange and sequence */ uint8_t f_ctl_es; /** Frame control - acknowledgements */ uint8_t f_ctl_ack; /** Frame control - miscellaneous */ uint8_t f_ctl_misc; /** Sequence ID */ uint8_t seq_id; /** Data field control */ uint8_t df_ctl; /** Sequence count */ uint16_t seq_cnt; /** Originator exchange ID */ uint16_t ox_id; /** Responder exchange ID */ uint16_t rx_id; /** Parameter * * Contains the relative offset when @c FC_F_CTL_MISC_REL_OFF * is set. */ uint32_t parameter; } __attribute__ (( packed )); /** Fibre Channel Routing Control Routing */ enum fc_r_ctl_routing { FC_R_CTL_DATA = 0x00, /**< Device Data */ FC_R_CTL_ELS = 0x20, /**< Extended Link Services */ FC_R_CTL_FC4_LINK = 0x30, /**< FC-4 Link Data */ FC_R_CTL_VIDEO = 0x40, /**< Video Data */ FC_R_CTL_EH = 0x50, /**< Extended Headers */ FC_R_CTL_BLS = 0x80, /**< Basic Link Services */ FC_R_CTL_LINK_CTRL = 0xc0, /**< Link Control */ FC_R_CTL_EXT_ROUTE = 0xf0, /**< Extended Routing */ }; /** Fibre Channel Routing Control Routing mask */ #define FC_R_CTL_ROUTING_MASK 0xf0 /** Fibre Channel Routing Control Information */ enum fc_r_ctl_info { FC_R_CTL_UNCAT = 0x00, /**< Uncategorized */ FC_R_CTL_SOL_DATA = 0x01, /**< Solicited Data */ FC_R_CTL_UNSOL_CTRL = 0x02, /**< Unsolicited Control */ FC_R_CTL_SOL_CTRL = 0x03, /**< Solicited Control */ FC_R_CTL_UNSOL_DATA = 0x04, /**< Unsolicited Data */ FC_R_CTL_DATA_DESC = 0x05, /**< Data Descriptor */ FC_R_CTL_UNSOL_CMD = 0x06, /**< Unsolicited Command */ FC_R_CTL_CMD_STAT = 0x07, /**< Command Status */ }; /** Fibre Channel Routing Control Information mask */ #define FC_R_CTL_INFO_MASK 0x07 /** Fibre Channel Data Structure Type */ enum fc_type { 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 */ enum fc_f_ctl_es { FC_F_CTL_ES_RESPONDER = 0x80, /**< Responder of Exchange */ FC_F_CTL_ES_RECIPIENT = 0x40, /**< Sequence Recipient */ FC_F_CTL_ES_FIRST = 0x20, /**< First Sequence of Exchange */ FC_F_CTL_ES_LAST = 0x10, /**< Last Sequence of Exchange */ FC_F_CTL_ES_END = 0x08, /**< Last Data Frame of Sequence */ FC_F_CTL_ES_TRANSFER = 0x01, /**< Transfer Sequence Initiative */ }; /** Fibre Channel Frame Control - Miscellaneous */ enum fc_f_ctl_misc { FC_F_CTL_MISC_REL_OFF = 0x08, /**< Relative Offset Present */ }; /** Responder exchange identifier used before first response */ #define FC_RX_ID_UNKNOWN 0xffff struct fc_port; extern int fc_xchg_originate ( struct interface *parent, struct fc_port *port, struct fc_port_id *peer_port_id, unsigned int type ); /** A Fibre Channel responder */ struct fc_responder { /** Type */ unsigned int type; /** Respond to exchange * * @v xchg Exchange interface * @v port Fibre Channel port * @v port_id Local port ID * @v peer_port_id Peer port ID * @ret rc Return status code */ int ( * respond ) ( struct interface *xchg, struct fc_port *port, struct fc_port_id *port_id, struct fc_port_id *peer_port_id ); }; /** Fibre Channel responder table */ #define FC_RESPONDERS __table ( struct fc_responder, "fc_responders" ) /** Declare a Fibre Channel responder */ #define __fc_responder __table_entry ( FC_RESPONDERS, 01 ) /****************************************************************************** * * Fibre Channel ports * ****************************************************************************** */ /** A Fibre Channel port */ struct fc_port { /** Reference count */ struct refcnt refcnt; /** List of all ports */ struct list_head list; /** Name of this port */ char name[8]; /** Transport interface */ struct interface transport; /** Node name */ struct fc_name node_wwn; /** Port name */ struct fc_name port_wwn; /** Local port ID */ struct fc_port_id port_id; /** Flags */ unsigned int flags; /** Link state monitor */ struct fc_link_state link; /** FLOGI interface */ struct interface flogi; /** Link node name */ struct fc_name link_node_wwn; /** Link port name */ struct fc_name link_port_wwn; /** 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; }; /** Fibre Channel port flags */ 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, }; /** * Get reference to Fibre Channel port * * @v port Fibre Channel port * @ret port Fibre Channel port */ static inline __attribute__ (( always_inline )) struct fc_port * fc_port_get ( struct fc_port *port ) { ref_get ( &port->refcnt ); return port; } /** * Drop reference to Fibre Channel port * * @v port Fibre Channel port */ static inline __attribute__ (( always_inline )) void fc_port_put ( struct fc_port *port ) { ref_put ( &port->refcnt ); } extern struct list_head fc_ports; extern int fc_port_login ( struct fc_port *port, struct fc_port_id *port_id, const struct fc_name *link_node_wwn, const struct fc_name *link_port_wwn, int has_fabric ); extern void fc_port_logout ( struct fc_port *port, int rc ); extern int fc_port_open ( struct interface *transport, const struct fc_name *node_wwn, const struct fc_name *port_wwn, const char *name ); extern struct fc_port * fc_port_find ( const char *name ); /****************************************************************************** * * Fibre Channel peers * ****************************************************************************** */ /** A Fibre Channel peer */ struct fc_peer { /** Reference count */ struct refcnt refcnt; /** List of all peers */ struct list_head list; /** Port name */ struct fc_name port_wwn; /** Link state monitor */ struct fc_link_state link; /** PLOGI interface */ struct interface plogi; /** Fibre Channel port, if known */ struct fc_port *port; /** Peer port ID, if known */ struct fc_port_id port_id; /** List of upper-layer protocols */ struct list_head ulps; /** Active usage count * * A peer (and attached ULPs) may be created in response to * unsolicited login requests received via the fabric. We * track our own active usage count independently of the * existence of the peer, so that if the peer becomes logged * out (e.g. due to a link failure) then we know whether or * not we should attempt to relogin. */ unsigned int usage; }; /** * Get reference to Fibre Channel peer * * @v peer Fibre Channel peer * @ret peer Fibre Channel peer */ static inline __attribute__ (( always_inline )) struct fc_peer * fc_peer_get ( struct fc_peer *peer ) { ref_get ( &peer->refcnt ); return peer; } /** * Drop reference to Fibre Channel peer * * @v peer Fibre Channel peer */ static inline __attribute__ (( always_inline )) void fc_peer_put ( struct fc_peer *peer ) { ref_put ( &peer->refcnt ); } extern struct list_head fc_peers; extern struct fc_peer * fc_peer_get_wwn ( const struct fc_name *port_wwn ); extern struct fc_peer * fc_peer_get_port_id ( struct fc_port *port, const struct fc_port_id *peer_port_id ); extern int fc_peer_login ( struct fc_peer *peer, struct fc_port *port, struct fc_port_id *port_id ); extern void fc_peer_logout ( struct fc_peer *peer, int rc ); /****************************************************************************** * * Fibre Channel upper-layer protocols * ****************************************************************************** */ /** A Fibre Channel upper-layer protocol */ struct fc_ulp { /** Reference count */ struct refcnt refcnt; /** Fibre Channel peer */ struct fc_peer *peer; /** List of upper-layer protocols */ struct list_head list; /** Type */ unsigned int type; /** Flags */ unsigned int flags; /** Link state monitor */ struct fc_link_state link; /** PRLI interface */ struct interface prli; /** Service parameters, if any */ void *param; /** Service parameter length */ size_t param_len; /** Active users of this upper-layer protocol * * As with peers, an upper-layer protocol may be created in * response to an unsolicited login request received via the * fabric. This list records the number of active users of * the ULP; the number of entries in the list is equivalent to * the peer usage count. */ struct list_head users; }; /** Fibre Channel upper-layer protocol flags */ enum fc_ulp_flags { /** A login originated by us has succeeded */ FC_ULP_ORIGINATED_LOGIN_OK = 0x0001, }; /** A Fibre Channel upper-layer protocol user */ struct fc_ulp_user { /** Fibre Channel upper layer protocol */ struct fc_ulp *ulp; /** List of users */ struct list_head list; /** Containing object reference count, or NULL */ struct refcnt *refcnt; /** Examine link state * * @v user Fibre Channel upper-layer-protocol user */ void ( * examine ) ( struct fc_ulp_user *user ); }; /** * Get reference to Fibre Channel upper-layer protocol * * @v ulp Fibre Channel upper-layer protocol * @ret ulp Fibre Channel upper-layer protocol */ static inline __attribute__ (( always_inline )) struct fc_ulp * fc_ulp_get ( struct fc_ulp *ulp ) { ref_get ( &ulp->refcnt ); return ulp; } /** * Drop reference to Fibre Channel upper-layer protocol * * @v ulp Fibre Channel upper-layer protocol */ static inline __attribute__ (( always_inline )) void fc_ulp_put ( struct fc_ulp *ulp ) { ref_put ( &ulp->refcnt ); } /** * Get reference to Fibre Channel upper-layer protocol user * * @v user Fibre Channel upper-layer protocol user * @ret user Fibre Channel upper-layer protocol user */ static inline __attribute__ (( always_inline )) struct fc_ulp_user * fc_ulp_user_get ( struct fc_ulp_user *user ) { ref_get ( user->refcnt ); return user; } /** * Drop reference to Fibre Channel upper-layer protocol user * * @v user Fibre Channel upper-layer protocol user */ static inline __attribute__ (( always_inline )) void fc_ulp_user_put ( struct fc_ulp_user *user ) { ref_put ( user->refcnt ); } /** * Initialise Fibre Channel upper-layer protocol user * * @v user Fibre Channel upper-layer protocol user * @v examine Examine link state method * @v refcnt Containing object reference count, or NULL */ static inline __attribute__ (( always_inline )) void fc_ulp_user_init ( struct fc_ulp_user *user, void ( * examine ) ( struct fc_ulp_user *user ), struct refcnt *refcnt ) { user->examine = examine; user->refcnt = refcnt; } extern struct fc_ulp * fc_ulp_get_wwn_type ( const struct fc_name *port_wwn, unsigned int type ); extern struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port, const struct fc_port_id *peer_port_id, unsigned int type ); extern void fc_ulp_attach ( struct fc_ulp *ulp, struct fc_ulp_user *user ); extern void fc_ulp_detach ( struct fc_ulp_user *user ); extern int fc_ulp_login ( struct fc_ulp *ulp, const void *param, size_t param_len, int originated ); extern void fc_ulp_logout ( struct fc_ulp *ulp, int rc ); #endif /* _IPXE_FC_H */