text: #ifndef _IPXE_FC_H
  #define _IPXE_FC_H
  
  /**
   * @file
   *
   * Fibre Channel
   *
   */
  
  FILE_LICENCE ( GPL2_OR_LATER );
  
  #include <stdint.h>
  #include <ipxe/refcnt.h>
  #include <ipxe/list.h>
  #include <ipxe/tables.h>
  #include <ipxe/interface.h>
  #include <ipxe/retry.h>
  #include <ipxe/socket.h>
  
  /******************************************************************************
   *
   * 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__ (( 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 */
  