123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- /*
- * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER );
-
- #include <stddef.h>
- #include <stdlib.h>
- #include <string.h>
- #include <byteswap.h>
- #include <errno.h>
- #include <ipxe/list.h>
- #include <ipxe/process.h>
- #include <ipxe/xfer.h>
- #include <ipxe/blockdev.h>
- #include <ipxe/scsi.h>
-
- /** @file
- *
- * SCSI block device
- *
- */
-
- /** Maximum number of command retries */
- #define SCSICMD_MAX_RETRIES 10
-
- /* Error numbers generated by SCSI sense data */
- #define EIO_NO_SENSE __einfo_error ( EINFO_EIO_NO_SENSE )
- #define EINFO_EIO_NO_SENSE \
- __einfo_uniqify ( EINFO_EIO, 0x00, "No sense" )
- #define EIO_RECOVERED_ERROR __einfo_error ( EINFO_EIO_RECOVERED_ERROR )
- #define EINFO_EIO_RECOVERED_ERROR \
- __einfo_uniqify ( EINFO_EIO, 0x01, "Recovered error" )
- #define EIO_NOT_READY __einfo_error ( EINFO_EIO_NOT_READY )
- #define EINFO_EIO_NOT_READY \
- __einfo_uniqify ( EINFO_EIO, 0x02, "Not ready" )
- #define EIO_MEDIUM_ERROR __einfo_error ( EINFO_EIO_MEDIUM_ERROR )
- #define EINFO_EIO_MEDIUM_ERROR \
- __einfo_uniqify ( EINFO_EIO, 0x03, "Medium error" )
- #define EIO_HARDWARE_ERROR __einfo_error ( EINFO_EIO_HARDWARE_ERROR )
- #define EINFO_EIO_HARDWARE_ERROR \
- __einfo_uniqify ( EINFO_EIO, 0x04, "Hardware error" )
- #define EIO_ILLEGAL_REQUEST __einfo_error ( EINFO_EIO_ILLEGAL_REQUEST )
- #define EINFO_EIO_ILLEGAL_REQUEST \
- __einfo_uniqify ( EINFO_EIO, 0x05, "Illegal request" )
- #define EIO_UNIT_ATTENTION __einfo_error ( EINFO_EIO_UNIT_ATTENTION )
- #define EINFO_EIO_UNIT_ATTENTION \
- __einfo_uniqify ( EINFO_EIO, 0x06, "Unit attention" )
- #define EIO_DATA_PROTECT __einfo_error ( EINFO_EIO_DATA_PROTECT )
- #define EINFO_EIO_DATA_PROTECT \
- __einfo_uniqify ( EINFO_EIO, 0x07, "Data protect" )
- #define EIO_BLANK_CHECK __einfo_error ( EINFO_EIO_BLANK_CHECK )
- #define EINFO_EIO_BLANK_CHECK \
- __einfo_uniqify ( EINFO_EIO, 0x08, "Blank check" )
- #define EIO_VENDOR_SPECIFIC __einfo_error ( EINFO_EIO_VENDOR_SPECIFIC )
- #define EINFO_EIO_VENDOR_SPECIFIC \
- __einfo_uniqify ( EINFO_EIO, 0x09, "Vendor specific" )
- #define EIO_COPY_ABORTED __einfo_error ( EINFO_EIO_COPY_ABORTED )
- #define EINFO_EIO_COPY_ABORTED \
- __einfo_uniqify ( EINFO_EIO, 0x0a, "Copy aborted" )
- #define EIO_ABORTED_COMMAND __einfo_error ( EINFO_EIO_ABORTED_COMMAND )
- #define EINFO_EIO_ABORTED_COMMAND \
- __einfo_uniqify ( EINFO_EIO, 0x0b, "Aborted command" )
- #define EIO_RESERVED __einfo_error ( EINFO_EIO_RESERVED )
- #define EINFO_EIO_RESERVED \
- __einfo_uniqify ( EINFO_EIO, 0x0c, "Reserved" )
- #define EIO_VOLUME_OVERFLOW __einfo_error ( EINFO_EIO_VOLUME_OVERFLOW )
- #define EINFO_EIO_VOLUME_OVERFLOW \
- __einfo_uniqify ( EINFO_EIO, 0x0d, "Volume overflow" )
- #define EIO_MISCOMPARE __einfo_error ( EINFO_EIO_MISCOMPARE )
- #define EINFO_EIO_MISCOMPARE \
- __einfo_uniqify ( EINFO_EIO, 0x0e, "Miscompare" )
- #define EIO_COMPLETED __einfo_error ( EINFO_EIO_COMPLETED )
- #define EINFO_EIO_COMPLETED \
- __einfo_uniqify ( EINFO_EIO, 0x0f, "Completed" )
- #define EIO_SENSE( key ) \
- EUNIQ ( EINFO_EIO, (key), EIO_NO_SENSE, EIO_RECOVERED_ERROR, \
- EIO_NOT_READY, EIO_MEDIUM_ERROR, EIO_HARDWARE_ERROR, \
- EIO_ILLEGAL_REQUEST, EIO_UNIT_ATTENTION, \
- EIO_DATA_PROTECT, EIO_BLANK_CHECK, EIO_VENDOR_SPECIFIC, \
- EIO_COPY_ABORTED, EIO_ABORTED_COMMAND, EIO_RESERVED, \
- EIO_VOLUME_OVERFLOW, EIO_MISCOMPARE, EIO_COMPLETED )
-
- /******************************************************************************
- *
- * Utility functions
- *
- ******************************************************************************
- */
-
- /**
- * Parse SCSI LUN
- *
- * @v lun_string LUN string representation
- * @v lun LUN to fill in
- * @ret rc Return status code
- */
- int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun ) {
- char *p;
- int i;
-
- memset ( lun, 0, sizeof ( *lun ) );
- if ( lun_string ) {
- p = ( char * ) lun_string;
- for ( i = 0 ; i < 4 ; i++ ) {
- lun->u16[i] = htons ( strtoul ( p, &p, 16 ) );
- if ( *p == '\0' )
- break;
- if ( *p != '-' )
- return -EINVAL;
- p++;
- }
- if ( *p )
- return -EINVAL;
- }
-
- return 0;
- }
-
- /******************************************************************************
- *
- * Interface methods
- *
- ******************************************************************************
- */
-
- /**
- * Issue SCSI command
- *
- * @v control SCSI control interface
- * @v data SCSI data interface
- * @v command SCSI command
- * @ret tag Command tag, or negative error
- */
- int scsi_command ( struct interface *control, struct interface *data,
- struct scsi_cmd *command ) {
- struct interface *dest;
- scsi_command_TYPE ( void * ) *op =
- intf_get_dest_op ( control, scsi_command, &dest );
- void *object = intf_object ( dest );
- int tap;
-
- if ( op ) {
- tap = op ( object, data, command );
- } else {
- /* Default is to fail to issue the command */
- tap = -EOPNOTSUPP;
- }
-
- intf_put ( dest );
- return tap;
- }
-
- /**
- * Report SCSI response
- *
- * @v interface SCSI command interface
- * @v response SCSI response
- */
- void scsi_response ( struct interface *intf, struct scsi_rsp *response ) {
- struct interface *dest;
- scsi_response_TYPE ( void * ) *op =
- intf_get_dest_op ( intf, scsi_response, &dest );
- void *object = intf_object ( dest );
-
- if ( op ) {
- op ( object, response );
- } else {
- /* Default is to ignore the response */
- }
-
- intf_put ( dest );
- }
-
- /******************************************************************************
- *
- * SCSI devices and commands
- *
- ******************************************************************************
- */
-
- /** A SCSI device */
- struct scsi_device {
- /** Reference count */
- struct refcnt refcnt;
- /** Block control interface */
- struct interface block;
- /** SCSI control interface */
- struct interface scsi;
-
- /** SCSI LUN */
- struct scsi_lun lun;
- /** Flags */
- unsigned int flags;
-
- /** TEST UNIT READY interface */
- struct interface ready;
- /** TEST UNIT READY process */
- struct process process;
-
- /** List of commands */
- struct list_head cmds;
- };
-
- /** SCSI device flags */
- enum scsi_device_flags {
- /** TEST UNIT READY has been issued */
- SCSIDEV_UNIT_TESTED = 0x0001,
- /** TEST UNIT READY has completed successfully */
- SCSIDEV_UNIT_READY = 0x0002,
- };
-
- /** A SCSI command */
- struct scsi_command {
- /** Reference count */
- struct refcnt refcnt;
- /** SCSI device */
- struct scsi_device *scsidev;
- /** List of SCSI commands */
- struct list_head list;
-
- /** Block data interface */
- struct interface block;
- /** SCSI data interface */
- struct interface scsi;
-
- /** Command type */
- struct scsi_command_type *type;
- /** Starting logical block address */
- uint64_t lba;
- /** Number of blocks */
- unsigned int count;
- /** Data buffer */
- userptr_t buffer;
- /** Length of data buffer */
- size_t len;
- /** Command tag */
- uint32_t tag;
-
- /** Retry count */
- unsigned int retries;
-
- /** Private data */
- uint8_t priv[0];
- };
-
- /** A SCSI command type */
- struct scsi_command_type {
- /** Name */
- const char *name;
- /** Additional working space */
- size_t priv_len;
- /**
- * Construct SCSI command IU
- *
- * @v scsicmd SCSI command
- * @v command SCSI command IU
- */
- void ( * cmd ) ( struct scsi_command *scsicmd,
- struct scsi_cmd *command );
- /**
- * Handle SCSI command completion
- *
- * @v scsicmd SCSI command
- * @v rc Reason for completion
- */
- void ( * done ) ( struct scsi_command *scsicmd, int rc );
- };
-
- /**
- * Get reference to SCSI device
- *
- * @v scsidev SCSI device
- * @ret scsidev SCSI device
- */
- static inline __attribute__ (( always_inline )) struct scsi_device *
- scsidev_get ( struct scsi_device *scsidev ) {
- ref_get ( &scsidev->refcnt );
- return scsidev;
- }
-
- /**
- * Drop reference to SCSI device
- *
- * @v scsidev SCSI device
- */
- static inline __attribute__ (( always_inline )) void
- scsidev_put ( struct scsi_device *scsidev ) {
- ref_put ( &scsidev->refcnt );
- }
-
- /**
- * Get reference to SCSI command
- *
- * @v scsicmd SCSI command
- * @ret scsicmd SCSI command
- */
- static inline __attribute__ (( always_inline )) struct scsi_command *
- scsicmd_get ( struct scsi_command *scsicmd ) {
- ref_get ( &scsicmd->refcnt );
- return scsicmd;
- }
-
- /**
- * Drop reference to SCSI command
- *
- * @v scsicmd SCSI command
- */
- static inline __attribute__ (( always_inline )) void
- scsicmd_put ( struct scsi_command *scsicmd ) {
- ref_put ( &scsicmd->refcnt );
- }
-
- /**
- * Get SCSI command private data
- *
- * @v scsicmd SCSI command
- * @ret priv Private data
- */
- static inline __attribute__ (( always_inline )) void *
- scsicmd_priv ( struct scsi_command *scsicmd ) {
- return scsicmd->priv;
- }
-
- /**
- * Free SCSI command
- *
- * @v refcnt Reference count
- */
- static void scsicmd_free ( struct refcnt *refcnt ) {
- struct scsi_command *scsicmd =
- container_of ( refcnt, struct scsi_command, refcnt );
-
- /* Remove from list of commands */
- list_del ( &scsicmd->list );
- scsidev_put ( scsicmd->scsidev );
-
- /* Free command */
- free ( scsicmd );
- }
-
- /**
- * Close SCSI command
- *
- * @v scsicmd SCSI command
- * @v rc Reason for close
- */
- static void scsicmd_close ( struct scsi_command *scsicmd, int rc ) {
- struct scsi_device *scsidev = scsicmd->scsidev;
-
- if ( rc != 0 ) {
- DBGC ( scsidev, "SCSI %p tag %08x closed: %s\n",
- scsidev, scsicmd->tag, strerror ( rc ) );
- }
-
- /* Shut down interfaces */
- intf_shutdown ( &scsicmd->scsi, rc );
- intf_shutdown ( &scsicmd->block, rc );
- }
-
- /**
- * Construct and issue SCSI command
- *
- * @ret rc Return status code
- */
- static int scsicmd_command ( struct scsi_command *scsicmd ) {
- struct scsi_device *scsidev = scsicmd->scsidev;
- struct scsi_cmd command;
- int tag;
- int rc;
-
- /* Construct command */
- memset ( &command, 0, sizeof ( command ) );
- memcpy ( &command.lun, &scsidev->lun, sizeof ( command.lun ) );
- scsicmd->type->cmd ( scsicmd, &command );
-
- /* Issue command */
- if ( ( tag = scsi_command ( &scsidev->scsi, &scsicmd->scsi,
- &command ) ) < 0 ) {
- rc = tag;
- DBGC ( scsidev, "SCSI %p could not issue command: %s\n",
- scsidev, strerror ( rc ) );
- return rc;
- }
-
- /* Record tag */
- if ( scsicmd->tag ) {
- DBGC ( scsidev, "SCSI %p tag %08x is now tag %08x\n",
- scsidev, scsicmd->tag, tag );
- }
- scsicmd->tag = tag;
- DBGC2 ( scsidev, "SCSI %p tag %08x %s " SCSI_CDB_FORMAT "\n",
- scsidev, scsicmd->tag, scsicmd->type->name,
- SCSI_CDB_DATA ( command.cdb ) );
-
- return 0;
- }
-
- /**
- * Handle SCSI command completion
- *
- * @v scsicmd SCSI command
- * @v rc Reason for close
- */
- static void scsicmd_done ( struct scsi_command *scsicmd, int rc ) {
- struct scsi_device *scsidev = scsicmd->scsidev;
-
- /* Restart SCSI interface */
- intf_restart ( &scsicmd->scsi, rc );
-
- /* SCSI targets have an annoying habit of returning occasional
- * pointless "error" messages such as "power-on occurred", so
- * we have to be prepared to retry commands.
- */
- if ( ( rc != 0 ) && ( scsicmd->retries++ < SCSICMD_MAX_RETRIES ) ) {
- /* Retry command */
- DBGC ( scsidev, "SCSI %p tag %08x failed: %s\n",
- scsidev, scsicmd->tag, strerror ( rc ) );
- DBGC ( scsidev, "SCSI %p tag %08x retrying (retry %d)\n",
- scsidev, scsicmd->tag, scsicmd->retries );
- if ( ( rc = scsicmd_command ( scsicmd ) ) == 0 )
- return;
- }
-
- /* If we didn't (successfully) reissue the command, hand over
- * to the command completion handler.
- */
- scsicmd->type->done ( scsicmd, rc );
- }
-
- /**
- * Handle SCSI response
- *
- * @v scsicmd SCSI command
- * @v response SCSI response
- */
- static void scsicmd_response ( struct scsi_command *scsicmd,
- struct scsi_rsp *response ) {
- struct scsi_device *scsidev = scsicmd->scsidev;
- size_t overrun;
- size_t underrun;
- int rc;
-
- if ( response->status == 0 ) {
- scsicmd_done ( scsicmd, 0 );
- } else {
- DBGC ( scsidev, "SCSI %p tag %08x status %02x",
- scsidev, scsicmd->tag, response->status );
- if ( response->overrun > 0 ) {
- overrun = response->overrun;
- DBGC ( scsidev, " overrun +%zd", overrun );
- } else if ( response->overrun < 0 ) {
- underrun = -(response->overrun);
- DBGC ( scsidev, " underrun -%zd", underrun );
- }
- DBGC ( scsidev, " sense %02x:%02x:%08x\n",
- response->sense.code, response->sense.key,
- ntohl ( response->sense.info ) );
-
- /* Construct error number from sense data */
- rc = -EIO_SENSE ( response->sense.key & SCSI_SENSE_KEY_MASK );
- scsicmd_done ( scsicmd, rc );
- }
- }
-
- /**
- * Construct SCSI READ command
- *
- * @v scsicmd SCSI command
- * @v command SCSI command IU
- */
- static void scsicmd_read_cmd ( struct scsi_command *scsicmd,
- struct scsi_cmd *command ) {
-
- if ( ( scsicmd->lba + scsicmd->count ) > SCSI_MAX_BLOCK_10 ) {
- /* Use READ (16) */
- command->cdb.read16.opcode = SCSI_OPCODE_READ_16;
- command->cdb.read16.lba = cpu_to_be64 ( scsicmd->lba );
- command->cdb.read16.len = cpu_to_be32 ( scsicmd->count );
- } else {
- /* Use READ (10) */
- command->cdb.read10.opcode = SCSI_OPCODE_READ_10;
- command->cdb.read10.lba = cpu_to_be32 ( scsicmd->lba );
- command->cdb.read10.len = cpu_to_be16 ( scsicmd->count );
- }
- command->data_in = scsicmd->buffer;
- command->data_in_len = scsicmd->len;
- }
-
- /** SCSI READ command type */
- static struct scsi_command_type scsicmd_read = {
- .name = "READ",
- .cmd = scsicmd_read_cmd,
- .done = scsicmd_close,
- };
-
- /**
- * Construct SCSI WRITE command
- *
- * @v scsicmd SCSI command
- * @v command SCSI command IU
- */
- static void scsicmd_write_cmd ( struct scsi_command *scsicmd,
- struct scsi_cmd *command ) {
-
- if ( ( scsicmd->lba + scsicmd->count ) > SCSI_MAX_BLOCK_10 ) {
- /* Use WRITE (16) */
- command->cdb.write16.opcode = SCSI_OPCODE_WRITE_16;
- command->cdb.write16.lba = cpu_to_be64 ( scsicmd->lba );
- command->cdb.write16.len = cpu_to_be32 ( scsicmd->count );
- } else {
- /* Use WRITE (10) */
- command->cdb.write10.opcode = SCSI_OPCODE_WRITE_10;
- command->cdb.write10.lba = cpu_to_be32 ( scsicmd->lba );
- command->cdb.write10.len = cpu_to_be16 ( scsicmd->count );
- }
- command->data_out = scsicmd->buffer;
- command->data_out_len = scsicmd->len;
- }
-
- /** SCSI WRITE command type */
- static struct scsi_command_type scsicmd_write = {
- .name = "WRITE",
- .cmd = scsicmd_write_cmd,
- .done = scsicmd_close,
- };
-
- /** SCSI READ CAPACITY private data */
- struct scsi_read_capacity_private {
- /** Use READ CAPACITY (16) */
- int use16;
- /** Data buffer for READ CAPACITY commands */
- union {
- /** Data buffer for READ CAPACITY (10) */
- struct scsi_capacity_10 capacity10;
- /** Data buffer for READ CAPACITY (16) */
- struct scsi_capacity_16 capacity16;
- } capacity;
- };
-
- /**
- * Construct SCSI READ CAPACITY command
- *
- * @v scsicmd SCSI command
- * @v command SCSI command IU
- */
- static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd,
- struct scsi_cmd *command ) {
- struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd );
- struct scsi_cdb_read_capacity_16 *readcap16 = &command->cdb.readcap16;
- struct scsi_cdb_read_capacity_10 *readcap10 = &command->cdb.readcap10;
- struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16;
- struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10;
-
- if ( priv->use16 ) {
- /* Use READ CAPACITY (16) */
- readcap16->opcode = SCSI_OPCODE_SERVICE_ACTION_IN;
- readcap16->service_action =
- SCSI_SERVICE_ACTION_READ_CAPACITY_16;
- readcap16->len = cpu_to_be32 ( sizeof ( *capacity16 ) );
- command->data_in = virt_to_user ( capacity16 );
- command->data_in_len = sizeof ( *capacity16 );
- } else {
- /* Use READ CAPACITY (10) */
- readcap10->opcode = SCSI_OPCODE_READ_CAPACITY_10;
- command->data_in = virt_to_user ( capacity10 );
- command->data_in_len = sizeof ( *capacity10 );
- }
- }
-
- /**
- * Handle SCSI READ CAPACITY command completion
- *
- * @v scsicmd SCSI command
- * @v rc Reason for completion
- */
- static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
- int rc ) {
- struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd );
- struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16;
- struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10;
- struct block_device_capacity capacity;
-
- /* Close if command failed */
- if ( rc != 0 ) {
- scsicmd_close ( scsicmd, rc );
- return;
- }
-
- /* Extract capacity */
- if ( priv->use16 ) {
- capacity.blocks = ( be64_to_cpu ( capacity16->lba ) + 1 );
- capacity.blksize = be32_to_cpu ( capacity16->blksize );
- } else {
- capacity.blocks = ( be32_to_cpu ( capacity10->lba ) + 1 );
- capacity.blksize = be32_to_cpu ( capacity10->blksize );
-
- /* If capacity range was exceeded (i.e. capacity.lba
- * was 0xffffffff, meaning that blockdev->blocks is
- * now zero), use READ CAPACITY (16) instead. READ
- * CAPACITY (16) is not mandatory, so we can't just
- * use it straight off.
- */
- if ( capacity.blocks == 0 ) {
- priv->use16 = 1;
- if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 ) {
- scsicmd_close ( scsicmd, rc );
- return;
- }
- return;
- }
- }
- capacity.max_count = -1U;
-
- /* Return capacity to caller */
- block_capacity ( &scsicmd->block, &capacity );
-
- /* Close command */
- scsicmd_close ( scsicmd, 0 );
- }
-
- /** SCSI READ CAPACITY command type */
- static struct scsi_command_type scsicmd_read_capacity = {
- .name = "READ CAPACITY",
- .priv_len = sizeof ( struct scsi_read_capacity_private ),
- .cmd = scsicmd_read_capacity_cmd,
- .done = scsicmd_read_capacity_done,
- };
-
- /**
- * Construct SCSI TEST UNIT READY command
- *
- * @v scsicmd SCSI command
- * @v command SCSI command IU
- */
- static void scsicmd_test_unit_ready_cmd ( struct scsi_command *scsicmd __unused,
- struct scsi_cmd *command ) {
- struct scsi_cdb_test_unit_ready *testready = &command->cdb.testready;
-
- testready->opcode = SCSI_OPCODE_TEST_UNIT_READY;
- }
-
- /** SCSI TEST UNIT READY command type */
- static struct scsi_command_type scsicmd_test_unit_ready = {
- .name = "TEST UNIT READY",
- .cmd = scsicmd_test_unit_ready_cmd,
- .done = scsicmd_close,
- };
-
- /** SCSI command block interface operations */
- static struct interface_operation scsicmd_block_op[] = {
- INTF_OP ( intf_close, struct scsi_command *, scsicmd_close ),
- };
-
- /** SCSI command block interface descriptor */
- static struct interface_descriptor scsicmd_block_desc =
- INTF_DESC_PASSTHRU ( struct scsi_command, block,
- scsicmd_block_op, scsi );
-
- /** SCSI command SCSI interface operations */
- static struct interface_operation scsicmd_scsi_op[] = {
- INTF_OP ( intf_close, struct scsi_command *, scsicmd_done ),
- INTF_OP ( scsi_response, struct scsi_command *, scsicmd_response ),
- };
-
- /** SCSI command SCSI interface descriptor */
- static struct interface_descriptor scsicmd_scsi_desc =
- INTF_DESC_PASSTHRU ( struct scsi_command, scsi,
- scsicmd_scsi_op, block );
-
- /**
- * Create SCSI command
- *
- * @v scsidev SCSI device
- * @v block Block data interface
- * @v type SCSI command type
- * @v lba Starting logical block address
- * @v count Number of blocks to transfer
- * @v buffer Data buffer
- * @v len Length of data buffer
- * @ret rc Return status code
- */
- static int scsidev_command ( struct scsi_device *scsidev,
- struct interface *block,
- struct scsi_command_type *type,
- uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len ) {
- struct scsi_command *scsicmd;
- int rc;
-
- /* Allocate and initialise structure */
- scsicmd = zalloc ( sizeof ( *scsicmd ) + type->priv_len );
- if ( ! scsicmd ) {
- rc = -ENOMEM;
- goto err_zalloc;
- }
- ref_init ( &scsicmd->refcnt, scsicmd_free );
- intf_init ( &scsicmd->block, &scsicmd_block_desc, &scsicmd->refcnt );
- intf_init ( &scsicmd->scsi, &scsicmd_scsi_desc,
- &scsicmd->refcnt );
- scsicmd->scsidev = scsidev_get ( scsidev );
- list_add ( &scsicmd->list, &scsidev->cmds );
- scsicmd->type = type;
- scsicmd->lba = lba;
- scsicmd->count = count;
- scsicmd->buffer = buffer;
- scsicmd->len = len;
-
- /* Issue SCSI command */
- if ( ( rc = scsicmd_command ( scsicmd ) ) != 0 )
- goto err_command;
-
- /* Attach to parent interface, mortalise self, and return */
- intf_plug_plug ( &scsicmd->block, block );
- ref_put ( &scsicmd->refcnt );
- return 0;
-
- err_command:
- scsicmd_close ( scsicmd, rc );
- ref_put ( &scsicmd->refcnt );
- err_zalloc:
- return rc;
- }
-
- /**
- * Issue SCSI block read
- *
- * @v scsidev SCSI device
- * @v block Block data interface
- * @v lba Starting logical block address
- * @v count Number of blocks to transfer
- * @v buffer Data buffer
- * @v len Length of data buffer
- * @ret rc Return status code
-
- */
- static int scsidev_read ( struct scsi_device *scsidev,
- struct interface *block,
- uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len ) {
- return scsidev_command ( scsidev, block, &scsicmd_read,
- lba, count, buffer, len );
- }
-
- /**
- * Issue SCSI block write
- *
- * @v scsidev SCSI device
- * @v block Block data interface
- * @v lba Starting logical block address
- * @v count Number of blocks to transfer
- * @v buffer Data buffer
- * @v len Length of data buffer
- * @ret rc Return status code
- */
- static int scsidev_write ( struct scsi_device *scsidev,
- struct interface *block,
- uint64_t lba, unsigned int count,
- userptr_t buffer, size_t len ) {
- return scsidev_command ( scsidev, block, &scsicmd_write,
- lba, count, buffer, len );
- }
-
- /**
- * Read SCSI device capacity
- *
- * @v scsidev SCSI device
- * @v block Block data interface
- * @ret rc Return status code
- */
- static int scsidev_read_capacity ( struct scsi_device *scsidev,
- struct interface *block ) {
- return scsidev_command ( scsidev, block, &scsicmd_read_capacity,
- 0, 0, UNULL, 0 );
- }
-
- /**
- * Test to see if SCSI device is ready
- *
- * @v scsidev SCSI device
- * @v block Block data interface
- * @ret rc Return status code
- */
- static int scsidev_test_unit_ready ( struct scsi_device *scsidev,
- struct interface *block ) {
- return scsidev_command ( scsidev, block, &scsicmd_test_unit_ready,
- 0, 0, UNULL, 0 );
- }
-
- /**
- * Check SCSI device flow-control window
- *
- * @v scsidev SCSI device
- * @ret len Length of window
- */
- static size_t scsidev_window ( struct scsi_device *scsidev ) {
-
- /* Refuse commands until unit is confirmed ready */
- if ( ! ( scsidev->flags & SCSIDEV_UNIT_READY ) )
- return 0;
-
- return xfer_window ( &scsidev->scsi );
- }
-
- /**
- * Close SCSI device
- *
- * @v scsidev SCSI device
- * @v rc Reason for close
- */
- static void scsidev_close ( struct scsi_device *scsidev, int rc ) {
- struct scsi_command *scsicmd;
- struct scsi_command *tmp;
-
- /* Stop process */
- process_del ( &scsidev->process );
-
- /* Shut down interfaces */
- intf_shutdown ( &scsidev->block, rc );
- intf_shutdown ( &scsidev->scsi, rc );
- intf_shutdown ( &scsidev->ready, rc );
-
- /* Shut down any remaining commands */
- list_for_each_entry_safe ( scsicmd, tmp, &scsidev->cmds, list ) {
- scsicmd_get ( scsicmd );
- scsicmd_close ( scsicmd, rc );
- scsicmd_put ( scsicmd );
- }
- }
-
- /** SCSI device block interface operations */
- static struct interface_operation scsidev_block_op[] = {
- INTF_OP ( xfer_window, struct scsi_device *, scsidev_window ),
- INTF_OP ( block_read, struct scsi_device *, scsidev_read ),
- INTF_OP ( block_write, struct scsi_device *, scsidev_write ),
- INTF_OP ( block_read_capacity, struct scsi_device *,
- scsidev_read_capacity ),
- INTF_OP ( intf_close, struct scsi_device *, scsidev_close ),
- };
-
- /** SCSI device block interface descriptor */
- static struct interface_descriptor scsidev_block_desc =
- INTF_DESC_PASSTHRU ( struct scsi_device, block,
- scsidev_block_op, scsi );
-
- /**
- * Handle SCSI TEST UNIT READY response
- *
- * @v scsidev SCSI device
- * @v rc Reason for close
- */
- static void scsidev_ready ( struct scsi_device *scsidev, int rc ) {
-
- /* Shut down interface */
- intf_shutdown ( &scsidev->ready, rc );
-
- /* Close device on failure */
- if ( rc != 0 ) {
- DBGC ( scsidev, "SCSI %p not ready: %s\n",
- scsidev, strerror ( rc ) );
- scsidev_close ( scsidev, rc );
- return;
- }
-
- /* Mark device as ready */
- scsidev->flags |= SCSIDEV_UNIT_READY;
- xfer_window_changed ( &scsidev->block );
- DBGC ( scsidev, "SCSI %p unit is ready\n", scsidev );
- }
-
- /** SCSI device TEST UNIT READY interface operations */
- static struct interface_operation scsidev_ready_op[] = {
- INTF_OP ( intf_close, struct scsi_device *, scsidev_ready ),
- };
-
- /** SCSI device TEST UNIT READY interface descriptor */
- static struct interface_descriptor scsidev_ready_desc =
- INTF_DESC ( struct scsi_device, ready, scsidev_ready_op );
-
- /**
- * SCSI TEST UNIT READY process
- *
- * @v scsidev SCSI device
- */
- static void scsidev_step ( struct scsi_device *scsidev ) {
- int rc;
-
- /* Do nothing if we have already issued TEST UNIT READY */
- if ( scsidev->flags & SCSIDEV_UNIT_TESTED )
- return;
-
- /* Wait until underlying SCSI device is ready */
- if ( xfer_window ( &scsidev->scsi ) == 0 )
- return;
-
- DBGC ( scsidev, "SCSI %p waiting for unit to become ready\n",
- scsidev );
-
- /* Mark TEST UNIT READY as sent */
- scsidev->flags |= SCSIDEV_UNIT_TESTED;
-
- /* Issue TEST UNIT READY command */
- if ( ( rc = scsidev_test_unit_ready ( scsidev, &scsidev->ready )) !=0){
- scsidev_close ( scsidev, rc );
- return;
- }
- }
-
- /** SCSI device SCSI interface operations */
- static struct interface_operation scsidev_scsi_op[] = {
- INTF_OP ( xfer_window_changed, struct scsi_device *, scsidev_step ),
- INTF_OP ( intf_close, struct scsi_device *, scsidev_close ),
- };
-
- /** SCSI device SCSI interface descriptor */
- static struct interface_descriptor scsidev_scsi_desc =
- INTF_DESC_PASSTHRU ( struct scsi_device, scsi,
- scsidev_scsi_op, block );
-
- /** SCSI device process descriptor */
- static struct process_descriptor scsidev_process_desc =
- PROC_DESC_ONCE ( struct scsi_device, process, scsidev_step );
-
- /**
- * Open SCSI device
- *
- * @v block Block control interface
- * @v scsi SCSI control interface
- * @v lun SCSI LUN
- * @ret rc Return status code
- */
- int scsi_open ( struct interface *block, struct interface *scsi,
- struct scsi_lun *lun ) {
- struct scsi_device *scsidev;
-
- /* Allocate and initialise structure */
- scsidev = zalloc ( sizeof ( *scsidev ) );
- if ( ! scsidev )
- return -ENOMEM;
- ref_init ( &scsidev->refcnt, NULL );
- intf_init ( &scsidev->block, &scsidev_block_desc, &scsidev->refcnt );
- intf_init ( &scsidev->scsi, &scsidev_scsi_desc, &scsidev->refcnt );
- intf_init ( &scsidev->ready, &scsidev_ready_desc, &scsidev->refcnt );
- process_init ( &scsidev->process, &scsidev_process_desc,
- &scsidev->refcnt );
- INIT_LIST_HEAD ( &scsidev->cmds );
- memcpy ( &scsidev->lun, lun, sizeof ( scsidev->lun ) );
- DBGC ( scsidev, "SCSI %p created for LUN " SCSI_LUN_FORMAT "\n",
- scsidev, SCSI_LUN_DATA ( scsidev->lun ) );
-
- /* Attach to SCSI and parent interfaces, mortalise self, and return */
- intf_plug_plug ( &scsidev->scsi, scsi );
- intf_plug_plug ( &scsidev->block, block );
- ref_put ( &scsidev->refcnt );
- return 0;
- }
|