+ }
+
+ return 0;
+}
+
+/**
+ * Detach SCSI interface
+ *
+ * @v scsi SCSI interface
+ * @v rc Reason for close
+ */
+static void iscsi_scsi_detach ( struct scsi_interface *scsi, int rc ) {
+ struct iscsi_session *iscsi =
+ container_of ( scsi, struct iscsi_session, scsi );
+
+ iscsi_close_connection ( iscsi, rc );
+ process_del ( &iscsi->process );
+}
+
+/****************************************************************************
+ *
+ * Instantiator
+ *
+ */
+
+/** iSCSI root path components (as per RFC4173) */
+enum iscsi_root_path_component {
+ RP_LITERAL = 0,
+ RP_SERVERNAME,
+ RP_PROTOCOL,
+ RP_PORT,
+ RP_LUN,
+ RP_TARGETNAME,
+ NUM_RP_COMPONENTS
+};
+
+/**
+ * Parse iSCSI LUN
+ *
+ * @v iscsi iSCSI session
+ * @v lun_string LUN string representation (as per RFC4173)
+ * @ret rc Return status code
+ */
+static int iscsi_parse_lun ( struct iscsi_session *iscsi,
+ const char *lun_string ) {
+ char *p = ( char * ) lun_string;
+ union {
+ uint64_t u64;
+ uint16_t u16[4];
+ } lun;
+ int i;
+
+ for ( i = 0 ; i < 4 ; i++ ) {
+ lun.u16[i] = strtoul ( p, &p, 16 );
+ if ( *p != '-' )
+ return -EINVAL;
+ p++;
+ }
+ if ( *p )
+ return -EINVAL;
+
+ iscsi->lun = lun.u64;
+ return 0;
+}
+
+/**
+ * Parse iSCSI root path
+ *
+ * @v iscsi iSCSI session
+ * @v root_path iSCSI root path (as per RFC4173)
+ * @ret rc Return status code
+ */
+static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
+ const char *root_path ) {
+ const char *p = root_path;
+ char *fragment;
+ size_t len;
+ enum iscsi_root_path_component i;
+ int rc;
+
+ for ( i = 0 ; i < NUM_RP_COMPONENTS ; i++ ) {
+ len = strcspn ( p, ":" );
+ fragment = strndup ( p, len );
+ if ( ! fragment ) {
+ DBGC ( iscsi, "iSCSI %p could not duplicate root "
+ "path component at %s\n", iscsi, p );
+ return -ENOMEM;