summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2010-11-04 03:49:07 +0100
committerMichael Brown2010-11-08 04:35:36 +0100
commit90930be8fe7d9991a6d31cd63c6c2da09e1d5f55 (patch)
tree9a313db32bce9c7f08685b8f16a73393bf59293c
parent[fcp] Use EINVAL for URI parsing errors and EPROTO for protocol errors (diff)
downloadipxe-90930be8fe7d9991a6d31cd63c6c2da09e1d5f55.tar.gz
ipxe-90930be8fe7d9991a6d31cd63c6c2da09e1d5f55.tar.xz
ipxe-90930be8fe7d9991a6d31cd63c6c2da09e1d5f55.zip
[fc] Support Fibre Channel ECHO
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/include/ipxe/fcels.h37
-rw-r--r--src/net/fcels.c295
-rw-r--r--src/net/fcp.c8
3 files changed, 237 insertions, 103 deletions
diff --git a/src/include/ipxe/fcels.h b/src/include/ipxe/fcels.h
index b35cb04c7..45fa69a4a 100644
--- a/src/include/ipxe/fcels.h
+++ b/src/include/ipxe/fcels.h
@@ -34,6 +34,7 @@ enum fc_els_command_code {
FC_ELS_FLOGI = 0x04, /**< Fabric Login */
FC_ELS_LOGO = 0x05, /**< Logout */
FC_ELS_RTV = 0x0e, /**< Read Timeout Value */
+ FC_ELS_ECHO = 0x10, /**< Echo */
FC_ELS_PRLI = 0x20, /**< Process Login */
FC_ELS_PRLO = 0x21, /**< Process Logout */
};
@@ -310,6 +311,14 @@ struct fc_rtv_response_frame {
/** Short R_T timeout */
#define FC_RTV_SHORT_R_T_TOV 0x0008
+/** A Fibre Channel ECHO frame */
+struct fc_echo_frame_header {
+ /** ELS command code */
+ uint8_t command;
+ /** Reserved */
+ uint8_t reserved[3];
+} __attribute__ (( packed ));
+
/** A Fibre Channel extended link services transaction */
struct fc_els {
/** Reference count */
@@ -343,37 +352,21 @@ enum fc_els_flags {
struct fc_els_handler {
/** Name */
const char *name;
- /** Transmit ELS request frame
- *
- * @v els Fibre Channel ELS transaction
- * @ret rc Return status code
- */
- int ( * tx_request ) ( struct fc_els *els );
- /** Transmit ELS response frame
- *
- * @v els Fibre Channel ELS transaction
- * @ret rc Return status code
- */
- int ( * tx_response ) ( struct fc_els *els );
- /** Receive ELS request frame
+ /** Transmit ELS frame
*
* @v els Fibre Channel ELS transaction
- * @v data ELS frame
- * @v len Length of ELS frame
* @ret rc Return status code
*/
- int ( * rx_request ) ( struct fc_els *els, const void *data,
- size_t len );
- /** Receive ELS response frame
+ int ( * tx ) ( struct fc_els *els );
+ /** Receive ELS frame
*
* @v els Fibre Channel ELS transaction
* @v data ELS frame
* @v len Length of ELS frame
* @ret rc Return status code
*/
- int ( * rx_response ) ( struct fc_els *els, const void *data,
- size_t len );
- /** Detect ELS request frame
+ int ( * rx ) ( struct fc_els *els, void *data, size_t len );
+ /** Detect ELS frame
*
* @v els Fibre Channel ELS transaction
* @v data ELS frame
@@ -444,7 +437,7 @@ extern int fc_els_prli_tx ( struct fc_els *els,
void *param );
extern int fc_els_prli_rx ( struct fc_els *els,
struct fc_els_prli_descriptor *descriptor,
- const void *data, size_t len );
+ void *data, size_t len );
extern int fc_els_prli_detect ( struct fc_els *els __unused,
struct fc_els_prli_descriptor *descriptor,
const void *data, size_t len );
diff --git a/src/net/fcels.c b/src/net/fcels.c
index 0ed682e3d..115442b67 100644
--- a/src/net/fcels.c
+++ b/src/net/fcels.c
@@ -158,7 +158,6 @@ static int fc_els_rx ( struct fc_els *els,
struct sockaddr_fc *src = ( ( struct sockaddr_fc * ) meta->src );
struct sockaddr_fc *dest = ( ( struct sockaddr_fc * ) meta->dest );
size_t len = iob_len ( iobuf );
- int ( * rx ) ( struct fc_els *els, const void *data, size_t len );
int rc;
/* Sanity check */
@@ -206,28 +205,13 @@ static int fc_els_rx ( struct fc_els *els,
DBGC2_HDA ( els, 0, frame, len );
/* Handle received frame */
- rx = ( fc_els_is_request ( els ) ?
- els->handler->rx_response : els->handler->rx_request );
- if ( ( rc = rx ( els, frame, len ) ) != 0 ) {
+ if ( ( rc = els->handler->rx ( els, frame, len ) ) != 0 ) {
DBGC ( els, FCELS_FMT " could not handle received frame: "
"%s\n", FCELS_ARGS ( els ), strerror ( rc ) );
DBGC_HDA ( els, 0, frame, len );
goto done;
}
- /* Free I/O buffer. Do this before transmitting response to
- * minimise memory consumption.
- */
- free_iob ( iob_disown ( iobuf ) );
-
- /* Transmit response if applicable */
- if ( ( ! fc_els_is_request ( els ) ) &&
- ( ( rc = els->handler->tx_response ( els ) ) != 0 ) ) {
- DBGC ( els, FCELS_FMT " could not transmit response: %s\n",
- FCELS_ARGS ( els ), strerror ( rc ) );
- goto done;
- }
-
done:
/* Free I/O buffer */
free_iob ( iobuf );
@@ -286,7 +270,7 @@ static void fc_els_step ( struct process *process ) {
}
/* Transmit request */
- if ( ( rc = els->handler->tx_request ( els ) ) != 0 ) {
+ if ( ( rc = els->handler->tx ( els ) ) != 0 ) {
DBGC ( els, FCELS_FMT " could not transmit request: %s\n",
FCELS_ARGS ( els ), strerror ( rc ) );
fc_els_close ( els, rc );
@@ -394,7 +378,7 @@ struct fc_responder fc_els_responder __fc_responder = {
* @v els Fibre Channel ELS transaction
* @ret rc Return status code
*/
-static int fc_els_unknown_tx_request ( struct fc_els *els __unused ) {
+static int fc_els_unknown_tx ( struct fc_els *els __unused ) {
return -ENOTSUP;
}
@@ -417,34 +401,26 @@ static int fc_els_unknown_tx_response ( struct fc_els *els ) {
}
/**
- * Receive unknown ELS request
+ * Receive unknown ELS
*
* @v els Fibre Channel ELS transaction
* @v data ELS frame
* @v len Length of ELS frame
* @ret rc Return status code
*/
-static int fc_els_unknown_rx_request ( struct fc_els *els, const void *data,
- size_t len ) {
+static int fc_els_unknown_rx ( struct fc_els *els, void *data, size_t len ) {
+ int rc;
DBGC ( els, FCELS_FMT ":\n", FCELS_ARGS ( els ) );
DBGC_HDA ( els, 0, data, len );
- return 0;
-}
-/**
- * Receive unknown ELS response
- *
- * @v els Fibre Channel ELS transaction
- * @v data ELS frame
- * @v len Length of ELS frame
- * @ret rc Return status code
- */
-static int fc_els_unknown_rx_response ( struct fc_els *els __unused,
- const void *data __unused,
- size_t len __unused ) {
- assert ( 0 );
- return -EINVAL;
+ /* Transmit response, if applicable */
+ if ( ! fc_els_is_request ( els ) ) {
+ if ( ( rc = fc_els_unknown_tx_response ( els ) ) != 0 )
+ return rc;
+ }
+
+ return 0;
}
/**
@@ -464,10 +440,8 @@ static int fc_els_unknown_detect ( struct fc_els *els __unused,
/** Unknown ELS handler */
struct fc_els_handler fc_els_unknown_handler __fc_els_handler = {
.name = "UNKNOWN",
- .tx_request = fc_els_unknown_tx_request,
- .tx_response = fc_els_unknown_tx_response,
- .rx_request = fc_els_unknown_rx_request,
- .rx_response = fc_els_unknown_rx_response,
+ .tx = fc_els_unknown_tx,
+ .rx = fc_els_unknown_rx,
.detect = fc_els_unknown_detect,
};
@@ -513,9 +487,8 @@ static int fc_els_flogi_tx ( struct fc_els *els ) {
* @v len Length of ELS frame
* @ret rc Return status code
*/
-static int fc_els_flogi_rx ( struct fc_els *els, const void *data,
- size_t len ) {
- const struct fc_login_frame *flogi = data;
+static int fc_els_flogi_rx ( struct fc_els *els, void *data, size_t len ) {
+ struct fc_login_frame *flogi = data;
int has_fabric;
int rc;
@@ -557,6 +530,12 @@ static int fc_els_flogi_rx ( struct fc_els *els, const void *data,
sizeof ( els->peer_port_id ) );
}
+ /* Transmit response, if applicable */
+ if ( ! fc_els_is_request ( els ) ) {
+ if ( ( rc = fc_els_flogi_tx ( els ) ) != 0 )
+ return rc;
+ }
+
return 0;
}
@@ -582,10 +561,8 @@ static int fc_els_flogi_detect ( struct fc_els *els __unused, const void *data,
/** FLOGI ELS handler */
struct fc_els_handler fc_els_flogi_handler __fc_els_handler = {
.name = "FLOGI",
- .tx_request = fc_els_flogi_tx,
- .tx_response = fc_els_flogi_tx,
- .rx_request = fc_els_flogi_rx,
- .rx_response = fc_els_flogi_rx,
+ .tx = fc_els_flogi_tx,
+ .rx = fc_els_flogi_rx,
.detect = fc_els_flogi_detect,
};
@@ -650,9 +627,8 @@ static int fc_els_plogi_tx ( struct fc_els *els ) {
* @v len Length of ELS frame
* @ret rc Return status code
*/
-static int fc_els_plogi_rx ( struct fc_els *els, const void *data,
- size_t len ) {
- const struct fc_login_frame *plogi = data;
+static int fc_els_plogi_rx ( struct fc_els *els, void *data, size_t len ) {
+ struct fc_login_frame *plogi = data;
struct fc_peer *peer;
int rc;
@@ -695,11 +671,18 @@ static int fc_els_plogi_rx ( struct fc_els *els, const void *data,
goto err_login;
}
+ /* Transmit response, if applicable */
+ if ( ! fc_els_is_request ( els ) ) {
+ if ( ( rc = fc_els_plogi_tx ( els ) ) != 0 )
+ goto err_plogi_tx;
+ }
+
/* Drop temporary reference to peer */
fc_peer_put ( peer );
return 0;
+ err_plogi_tx:
err_login:
fc_peer_put ( peer );
err_peer_get_wwn:
@@ -729,10 +712,8 @@ static int fc_els_plogi_detect ( struct fc_els *els __unused, const void *data,
/** PLOGI ELS handler */
struct fc_els_handler fc_els_plogi_handler __fc_els_handler = {
.name = "PLOGI",
- .tx_request = fc_els_plogi_tx,
- .tx_response = fc_els_plogi_tx,
- .rx_request = fc_els_plogi_rx,
- .rx_response = fc_els_plogi_rx,
+ .tx = fc_els_plogi_tx,
+ .rx = fc_els_plogi_rx,
.detect = fc_els_plogi_detect,
};
@@ -764,7 +745,7 @@ int fc_els_plogi ( struct interface *parent, struct fc_port *port,
* @v els Fibre Channel ELS transaction
* @ret rc Return status code
*/
-static int fc_els_logo_tx_request ( struct fc_els *els ) {
+static int fc_els_logo_tx ( struct fc_els *els ) {
struct fc_logout_request_frame logo;
/* Construct LOGO */
@@ -802,7 +783,7 @@ static int fc_els_logo_tx_response ( struct fc_els *els ) {
* @v port_id Peer port ID
*/
static void fc_els_logo_logout ( struct fc_els *els,
- const struct fc_port_id *peer_port_id ) {
+ struct fc_port_id *peer_port_id ) {
struct fc_peer *peer;
if ( ( memcmp ( peer_port_id, &fc_f_port_id,
@@ -827,9 +808,10 @@ static void fc_els_logo_logout ( struct fc_els *els,
* @v len Length of ELS frame
* @ret rc Return status code
*/
-static int fc_els_logo_rx_request ( struct fc_els *els, const void *data,
+static int fc_els_logo_rx_request ( struct fc_els *els, void *data,
size_t len ) {
- const struct fc_logout_request_frame *logo = data;
+ struct fc_logout_request_frame *logo = data;
+ int rc;
/* Sanity check */
if ( len < sizeof ( *logo ) ) {
@@ -845,6 +827,10 @@ static int fc_els_logo_rx_request ( struct fc_els *els, const void *data,
/* Log out individual peer or whole port as applicable */
fc_els_logo_logout ( els, &logo->port_id );
+ /* Transmit repsonse */
+ if ( ( rc = fc_els_logo_tx_response ( els ) ) != 0 )
+ return rc;
+
return 0;
}
@@ -856,8 +842,7 @@ static int fc_els_logo_rx_request ( struct fc_els *els, const void *data,
* @v len Length of ELS frame
* @ret rc Return status code
*/
-static int fc_els_logo_rx_response ( struct fc_els *els,
- const void *data __unused,
+static int fc_els_logo_rx_response ( struct fc_els *els, void *data __unused,
size_t len __unused ) {
/* Log out individual peer or whole port as applicable */
@@ -867,6 +852,23 @@ static int fc_els_logo_rx_response ( struct fc_els *els,
}
/**
+ * Receive LOGO
+ *
+ * @v els Fibre Channel ELS transaction
+ * @v data ELS frame
+ * @v len Length of ELS frame
+ * @ret rc Return status code
+ */
+static int fc_els_logo_rx ( struct fc_els *els, void *data, size_t len ) {
+
+ if ( fc_els_is_request ( els ) ) {
+ return fc_els_logo_rx_response ( els, data, len );
+ } else {
+ return fc_els_logo_rx_request ( els, data, len );
+ }
+}
+
+/**
* Detect LOGO
*
* @v els Fibre Channel ELS transaction
@@ -888,10 +890,8 @@ static int fc_els_logo_detect ( struct fc_els *els __unused, const void *data,
/** LOGO ELS handler */
struct fc_els_handler fc_els_logo_handler __fc_els_handler = {
.name = "LOGO",
- .tx_request = fc_els_logo_tx_request,
- .tx_response = fc_els_logo_tx_response,
- .rx_request = fc_els_logo_rx_request,
- .rx_response = fc_els_logo_rx_response,
+ .tx = fc_els_logo_tx,
+ .rx = fc_els_logo_rx,
.detect = fc_els_logo_detect,
};
@@ -1000,8 +1000,8 @@ int fc_els_prli_tx ( struct fc_els *els,
*/
int fc_els_prli_rx ( struct fc_els *els,
struct fc_els_prli_descriptor *descriptor,
- const void *data, size_t len ) {
- const struct {
+ void *data, size_t len ) {
+ struct {
struct fc_prli_frame frame;
uint8_t param[descriptor->param_len];
} __attribute__ (( packed )) *prli = data;
@@ -1055,11 +1055,18 @@ int fc_els_prli_rx ( struct fc_els *els,
}
}
+ /* Transmit response, if applicable */
+ if ( ! fc_els_is_request ( els ) ) {
+ if ( ( rc = els->handler->tx ( els ) ) != 0 )
+ goto err_tx;
+ }
+
/* Drop temporary reference to ULP */
fc_ulp_put ( ulp );
return 0;
+ err_tx:
err_login:
err_link:
fc_ulp_put ( ulp );
@@ -1148,20 +1155,25 @@ static int fc_els_rtv_tx_response ( struct fc_els *els ) {
}
/**
- * Receive RTV request
+ * Receive RTV
*
* @v els Fibre Channel ELS transaction
* @v data ELS frame
* @v len Length of ELS frame
* @ret rc Return status code
*/
-static int fc_els_rtv_rx_request ( struct fc_els *els,
- const void *data __unused,
- size_t len __unused ) {
+static int fc_els_rtv_rx ( struct fc_els *els, void *data __unused,
+ size_t len __unused ) {
+ int rc;
DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
- /* Nothing to do */
+ /* Transmit response */
+ if ( ! fc_els_is_request ( els ) ) {
+ if ( ( rc = fc_els_rtv_tx_response ( els ) ) != 0 )
+ return rc;
+ }
+
return 0;
}
@@ -1187,9 +1199,140 @@ static int fc_els_rtv_detect ( struct fc_els *els __unused, const void *data,
/** RTV ELS handler */
struct fc_els_handler fc_els_rtv_handler __fc_els_handler = {
.name = "RTV",
- .tx_request = fc_els_unknown_tx_request,
- .tx_response = fc_els_rtv_tx_response,
- .rx_request = fc_els_rtv_rx_request,
- .rx_response = fc_els_unknown_rx_response,
+ .tx = fc_els_unknown_tx,
+ .rx = fc_els_rtv_rx,
.detect = fc_els_rtv_detect,
};
+
+/******************************************************************************
+ *
+ * ECHO
+ *
+ ******************************************************************************
+ */
+
+/** ECHO request data */
+struct fc_echo_request_frame {
+ /** ECHO frame header */
+ struct fc_echo_frame_header echo;
+ /** Magic marker */
+ uint32_t magic;
+} __attribute__ (( packed ));
+
+/** ECHO magic marker */
+#define FC_ECHO_MAGIC 0x69505845
+
+/**
+ * Transmit ECHO
+ *
+ * @v els Fibre Channel ELS transaction
+ * @ret rc Return status code
+ */
+static int fc_els_echo_tx ( struct fc_els *els ) {
+ struct fc_echo_request_frame echo;
+
+ /* Construct ECHO */
+ memset ( &echo, 0, sizeof ( echo ) );
+ echo.echo.command = FC_ELS_ECHO;
+ echo.magic = htonl ( FC_ECHO_MAGIC );
+
+ /* Transmit ECHO */
+ return fc_els_tx ( els, &echo, sizeof ( echo ) );
+}
+
+/**
+ * Receive ECHO request
+ *
+ * @v els Fibre Channel ELS transaction
+ * @v data ELS frame
+ * @v len Length of ELS frame
+ * @ret rc Return status code
+ */
+static int fc_els_echo_rx_request ( struct fc_els *els, void *data,
+ size_t len ) {
+ struct {
+ struct fc_echo_frame_header echo;
+ char payload[ len - sizeof ( struct fc_echo_frame_header ) ];
+ } *echo = data;
+ int rc;
+
+ DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
+
+ /* Transmit response */
+ echo->echo.command = FC_ELS_LS_ACC;
+ if ( ( rc = fc_els_tx ( els, echo, sizeof ( *echo ) ) ) != 0 )
+ return rc;
+
+ /* Nothing to do */
+ return 0;
+}
+
+/**
+ * Receive ECHO response
+ *
+ * @v els Fibre Channel ELS transaction
+ * @v data ELS frame
+ * @v len Length of ELS frame
+ * @ret rc Return status code
+ */
+static int fc_els_echo_rx_response ( struct fc_els *els, void *data,
+ size_t len ) {
+ struct fc_echo_request_frame *echo = data;
+
+ DBGC ( els, FCELS_FMT "\n", FCELS_ARGS ( els ) );
+
+ /* Check response is correct */
+ if ( ( len != sizeof ( *echo ) ) ||
+ ( echo->magic != htonl ( FC_ECHO_MAGIC ) ) ) {
+ DBGC ( els, FCELS_FMT " received bad echo response\n",
+ FCELS_ARGS ( els ) );
+ DBGC_HDA ( els, 0, data, len );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * Receive ECHO
+ *
+ * @v els Fibre Channel ELS transaction
+ * @v data ELS frame
+ * @v len Length of ELS frame
+ * @ret rc Return status code
+ */
+static int fc_els_echo_rx ( struct fc_els *els, void *data, size_t len ) {
+
+ if ( fc_els_is_request ( els ) ) {
+ return fc_els_echo_rx_response ( els, data, len );
+ } else {
+ return fc_els_echo_rx_request ( els, data, len );
+ }
+}
+
+/**
+ * Detect ECHO
+ *
+ * @v els Fibre Channel ELS transaction
+ * @v data ELS frame
+ * @v len Length of ELS frame
+ * @ret rc Return status code
+ */
+static int fc_els_echo_detect ( struct fc_els *els __unused, const void *data,
+ size_t len __unused ) {
+ const struct fc_echo_frame_header *echo = data;
+
+ /* Check for ECHO */
+ if ( echo->command != FC_ELS_ECHO )
+ return -EINVAL;
+
+ return 0;
+}
+
+/** ECHO ELS handler */
+struct fc_els_handler fc_els_echo_handler __fc_els_handler = {
+ .name = "ECHO",
+ .tx = fc_els_echo_tx,
+ .rx = fc_els_echo_rx,
+ .detect = fc_els_echo_detect,
+};
diff --git a/src/net/fcp.c b/src/net/fcp.c
index 0da57cfcb..40cd057e6 100644
--- a/src/net/fcp.c
+++ b/src/net/fcp.c
@@ -103,7 +103,7 @@ static int fcp_prli_tx ( struct fc_els *els ) {
* @v len Length of ELS frame
* @ret rc Return status code
*/
-static int fcp_prli_rx ( struct fc_els *els, const void *data, size_t len ) {
+static int fcp_prli_rx ( struct fc_els *els, void *data, size_t len ) {
return fc_els_prli_rx ( els, &fcp_prli_descriptor, data, len );
}
@@ -123,10 +123,8 @@ static int fcp_prli_detect ( struct fc_els *els, const void *data,
/** FCP PRLI ELS handler */
struct fc_els_handler fcp_prli_handler __fc_els_handler = {
.name = "PRLI-FCP",
- .tx_request = fcp_prli_tx,
- .tx_response = fcp_prli_tx,
- .rx_request = fcp_prli_rx,
- .rx_response = fcp_prli_rx,
+ .tx = fcp_prli_tx,
+ .rx = fcp_prli_rx,
.detect = fcp_prli_detect,
};