Real SCSI device passthrough
PATCH v2 1/1 Real SCSI device passthrough
This patch allows to connect the virtual SCSI interface of Qemu to a real SCSI device of the host.
Using the devices /dev/sg, it allows to send the SCSI commands from the virtual SCSI interface to the real device.
It has been tested with a SATA disk and an ATA CD burner with ide-scsi module and wodim to burn a CD-RW.
v2 improves Timeout and management of REQUEST_SENSE command.
Laurent
--- Makefile | 1 block-raw.c | 28 +- block.c | 17 + block.h | 2 block_int.h | 4 hw/esp.c | 20 - hw/lsi53c895a.c | 49 ++- hw/scsi-disk.c | 62 +++-- hw/scsi-disk.h | 29 +- hw/scsi-generic.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb-msd.c | 16 - 11 files changed, 820 insertions(+), 75 deletions(-)
block-raw.c +23 -5
@@ -52,6 +52,7 @@#include <sys/ioctl.h> #include <linux/cdrom.h> #include <linux/fd.h> +#include <linux/major.h> #endif #ifdef __FreeBSD__#include <sys/disk.h>
@@ -152,7 +153,7 @@ static int raw_pread(BlockDriverState *bif (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt <=10 ) { DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
@@ -205,7 +206,7 @@ static int raw_pwrite(BlockDriverState *if (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt) { DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%"
@@ -277,8 +278,8 @@ void qemu_aio_init(void)seems to fix the problem. */ struct aioinit ai;memset (&ai,0 ,sizeof (ai)); - ai.aio_threads =1 ; - ai.aio_num =1 ; + ai.aio_threads =16 ; + ai.aio_num =16 ; ai.aio_idle_time =365 *100000 ;aio_init (&ai); }
@@ -388,7 +389,10 @@ static RawAIOCB *raw_aio_setup(BlockDriv acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num; acb->aiocb.aio_sigevent.sigev_notify =SIGEV_SIGNAL ; acb->aiocb.aio_buf = buf; - acb->aiocb.aio_nbytes = nb_sectors *512 ; +if (nb_sectors <0 ) + acb->aiocb.aio_nbytes = -nb_sectors; +else + acb->aiocb.aio_nbytes = nb_sectors *512 ; acb->aiocb.aio_offset = sector_num *512 ; acb->next = first_aio; first_aio = acb;
@@ -680,6 +684,8 @@ static int hdev_open(BlockDriverState *b s->fd_open_flags = open_flags;/* open will not fail even if no floppy is inserted */ open_flags |= O_NONBLOCK; + }else if (strstart (filename,"/dev/sg" ,NULL )) { + bs->sg =1 ; }#endif fd =open (filename, open_flags,0644 );
@@ -859,6 +865,12 @@ static int raw_set_locked(BlockDriverSta return 0; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BDRVRawState *s = bs->opaque; + +return ioctl (s->fd, req, buf); +}#else static int raw_is_inserted (BlockDriverState *bs)
@@ -881,6 +893,10 @@ static int raw_set_locked(BlockDriverStareturn -ENOTSUP ; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +return -ENOTSUP ; +}#endif /* !linux */ BlockDriver bdrv_host_device = {
@@ -907,6 +923,8 @@ BlockDriver bdrv_host_device = { .bdrv_media_changed = raw_media_changed, .bdrv_eject = raw_eject, .bdrv_set_locked = raw_set_locked, +/* generic scsi device */ + .bdrv_ioctl = raw_ioctl, };#else /* _WIN32 */
block.c +16 -1
@@ -303,7 +303,6 @@ int bdrv_file_open(BlockDriverState **pb {BlockDriverState *bs;int ret; - bs =bdrv_new ("" );if (!bs)return -ENOMEM ;
@@ -796,6 +795,11 @@ int bdrv_is_cached(BlockDriverState *bs)return bs->cache; } +int bdrv_is_sg (BlockDriverState *bs) +{ +return bs->sg; +} +/* XXX: no longer used */ void bdrv_set_change_cb (BlockDriverState *bs,void (*change_cb)(void *opaque),void *opaque)
@@ -1404,3 +1408,14 @@ void bdrv_set_locked(BlockDriverState *b drv->bdrv_set_locked (bs, locked); } } + +/* needed for generic scsi interface */ + +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BlockDriver *drv = bs->drv; + +if (drv && drv->bdrv_ioctl) +return drv->bdrv_ioctl (bs, req, buf); +return -ENOTSUP ; +}
block.h +2 -0
@@ -120,6 +120,7 @@ int bdrv_get_type_hint(BlockDriverStateint bdrv_get_translation_hint (BlockDriverState *bs);int bdrv_is_removable (BlockDriverState *bs);int bdrv_is_read_only (BlockDriverState *bs); +int bdrv_is_sg (BlockDriverState *bs);int bdrv_is_inserted (BlockDriverState *bs);int bdrv_is_cached (BlockDriverState *bs);int bdrv_media_changed (BlockDriverState *bs);
@@ -150,6 +151,7 @@ int bdrv_snapshot_delete(BlockDriverStatint bdrv_snapshot_list (BlockDriverState *bs,QEMUSnapshotInfo **psn_info);char *bdrv_snapshot_dump (char *buf,int buf_size,QEMUSnapshotInfo *sn); +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf);char *get_human_readable_size (char *buf,int buf_size,int64_t size);int path_is_absolute (const char *path);
block_int.h +4 -0
@@ -82,6 +82,9 @@ struct BlockDriver {int (*bdrv_eject)(BlockDriverState *bs,int eject_flag);int (*bdrv_set_locked)(BlockDriverState *bs,int locked); +/* to control generic scsi devices */ +int (*bdrv_ioctl)(BlockDriverState *bs,unsigned long int req,char *buf); +BlockDriverAIOCB *free_aiocb;struct BlockDriver *next; };
@@ -93,6 +96,7 @@ struct BlockDriverState {int removable;/* if true, the media can be removed */ int locked;/* if true, the media cannot temporarily be ejected */ int encrypted;/* if true, the media is encrypted */ +int sg;/* if true, the device is a /dev/sg* */ /* event callback when inserting/removing */ void (*change_cb)(void *opaque);void *change_opaque;
hw/scsi-disk.c +38 -24
166 lines not on this page · raw message
hw/esp.c +11 -9
@@ -165,7 +165,7 @@ static int get_cmd(ESPState *s, uint8_tif (s->current_dev) {/* Started a new command before the old one finished. Cancel it. */ -scsi_cancel_io (s->current_dev,0 ); + s->current_dev->cancel_io (s->current_dev,0 ); s->async_len =0 ; }
@@ -188,7 +188,7 @@ static void do_cmd(ESPState *s, uint8_tDPRINTF ("do_cmd: busid 0x%x\n" , buf[0 ]); lun = buf[0 ] &7 ; - datalen =scsi_send_command (s->current_dev,0 , &buf[1 ], lun); + datalen = s->current_dev->send_command (s->current_dev,0 , &buf[1 ], lun); s->ti_size = datalen;if (datalen !=0 ) { s->rregs[ESP_RSTAT ] =STAT_IN |STAT_TC ;
@@ -196,10 +196,10 @@ static void do_cmd(ESPState *s, uint8_t s->dma_counter =0 ;if (datalen >0 ) { s->rregs[ESP_RSTAT ] |=STAT_DI ; -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 ); }else { s->rregs[ESP_RSTAT ] |=STAT_DO ; -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); } } s->rregs[ESP_RINTR ] =INTR_BS |INTR_FC ;
@@ -298,9 +298,9 @@ static void esp_do_dma(ESPState *s)if (s->async_len ==0 ) {if (to_device) {// ti_size is negative -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); }else { -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 );/* If there is still data to be read from the device then complete the DMA operation immeriately. Otherwise defer until the scsi layer has completed. */
@@ -335,7 +335,7 @@ static void esp_command_complete(void *o }else {DPRINTF ("transfer %d/%d\n" , s->dma_left, s->ti_size); s->async_len = arg; - s->async_buf =scsi_get_buf (s->current_dev,0 ); + s->async_buf = s->current_dev->get_buf (s->current_dev,0 );if (s->dma_left) {esp_do_dma (s); }else if (s->dma_counter !=0 && s->ti_size <=0 ) {
@@ -611,11 +611,13 @@ void esp_scsi_attach(void *opaque, Block }if (s->scsi_dev[id]) {DPRINTF ("Destroying device %d\n" , id); -scsi_disk_destroy (s->scsi_dev[id]); + s->scsi_dev[id]->destroy (s->scsi_dev[id]); }DPRINTF ("Attaching block device %d\n" , id);/* Command queueing is not implemented. */ - s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); + s->scsi_dev[id] =scsi_generic_init (bd,0 , esp_command_complete, s); +if (s->scsi_dev[id] ==NULL ) + s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); } void *esp_init(target_phys_addr_t espaddr,
hw/lsi53c895a.c +34 -15
112 lines not on this page · raw message
hw/usb-msd.c +8 -8
@@ -149,9 +149,9 @@ static void usb_msd_copy_data(MSDState * s->data_len -= len;if (s->scsi_len ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } }
@@ -204,7 +204,7 @@ static void usb_msd_command_complete(voireturn ; } s->scsi_len = arg; - s->scsi_buf =scsi_get_buf (s->scsi_dev, tag); + s->scsi_buf = s->scsi_dev->get_buf (s->scsi_dev, tag);if (p) {usb_msd_copy_data (s);if (s->usb_len ==0 ) {
@@ -342,7 +342,7 @@ static int usb_msd_handle_control(USBDevstatic void usb_msd_cancel_io (USBPacket *p,void *opaque) {MSDState *s = opaque; -scsi_cancel_io (s->scsi_dev, s->tag); + s->scsi_dev->cancel_io (s->scsi_dev, s->tag); s->packet =NULL ; s->scsi_len =0 ; }
@@ -390,14 +390,14 @@ static int usb_msd_handle_data(USBDeviceDPRINTF ("Command tag 0x%x flags %08x len %d data %d\n" , s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue =0 ; -scsi_send_command (s->scsi_dev, s->tag, cbw.cmd,0 ); + s->scsi_dev->send_command (s->scsi_dev, s->tag, cbw.cmd,0 );/* ??? Should check that USB and SCSI data transfer directions match. */ if (s->residue ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } ret = len;
@@ -508,7 +508,7 @@ static void usb_msd_handle_destroy(USBDe {MSDState *s = (MSDState *)dev; -scsi_disk_destroy (s->scsi_dev); + s->scsi_dev->destroy (s->scsi_dev);bdrv_delete (s->bs);qemu_free (s); }
hw/scsi-generic.c new file +667 -0
667 lines not on this page · raw message
Makefile +1 -0
@@ -56,6 +56,7 @@ OBJS+=irq.o OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o OBJS+=scsi-disk.o cdrom.o +OBJS+=scsi-generic.o OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o OBJS+=sd.o ssi-sd.o
hw/scsi-disk.h +16 -13
@@ -7,24 +7,27 @@ enum scsi_reason {SCSI_REASON_DATA /* Transfer complete, more data required. */ }; +typedef struct SCSIDeviceState SCSIDeviceState ;typedef struct SCSIDevice SCSIDevice ;typedef void (*scsi_completionfn )(void *opaque,int reason,uint32_t tag,uint32_t arg); -SCSIDevice *scsi_disk_init (BlockDriverState *bdrv, -int tcq, -scsi_completionfn completion, -void *opaque); -void scsi_disk_destroy (SCSIDevice *s); +struct SCSIDevice +{ +SCSIDeviceState *state; +void (*destroy)(SCSIDevice *s); +int32_t (*send_command)(SCSIDevice *s,uint32_t tag,uint8_t *buf, +int lun); +void (*read_data)(SCSIDevice *s,uint32_t tag); +int (*write_data)(SCSIDevice *s,uint32_t tag); +void (*cancel_io)(SCSIDevice *s,uint32_t tag); +uint8_t *(*get_buf)(SCSIDevice *s,uint32_t tag); +}; -int32_t scsi_send_command (SCSIDevice *s,uint32_t tag,uint8_t *buf,int lun); -/* SCSI data transfers are asynchrnonous. However, unlike the block IO -layer the completion routine may be called directly by -scsi_{read,write}_data. */ -void scsi_read_data (SCSIDevice *s,uint32_t tag); -int scsi_write_data (SCSIDevice *s,uint32_t tag); -void scsi_cancel_io (SCSIDevice *s,uint32_t tag); -uint8_t *scsi_get_buf (SCSIDevice *s,uint32_t tag); +SCSIDevice *scsi_disk_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque); +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque);/* cdrom.c */ int cdrom_read_toc (int nb_sectors,uint8_t *buf,int msf,int start_track);
Hi Laurent,
This patch is really neat. I'd like to do the same with virtio-blk.
Can you separate out the general cleanups from the function of this patch?
Regards,
Anthony Liguori
Quoted 1,378 lines · Laurent Vivier
This patch allows to connect the virtual SCSI interface of Qemu to a real SCSI device of the host.
Using the devices/dev/sg, it allows to send the SCSI commands from the virtual SCSI interface to the real device.It has been tested with a SATA disk and an ATA CD burner with ide-scsi module and wodim to burn a CD-RW.
v2 improves Timeout and management of
REQUEST_SENSEcommand.Laurent
--- Makefile | 1 block-raw.c | 28 +- block.c | 17 + block.h | 2 block_int.h | 4 hw/esp.c | 20 - hw/lsi53c895a.c | 49 ++- hw/scsi-disk.c | 62 +++-- hw/scsi-disk.h | 29 +- hw/scsi-generic.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb-msd.c | 16 - 11 files changed, 820 insertions(+), 75 deletions(-)Index:
qemu/block-raw.c
===================================================================block-raw.c +23
-5@@ -52,6 +52,7 @@#include <sys/ioctl.h> #include <linux/cdrom.h> #include <linux/fd.h> +#include <linux/major.h> #endif #ifdef __FreeBSD__#include <sys/disk.h> @@ -152,7 +153,7 @@ static int raw_pread(BlockDriverState *bif (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt <=10 ) { DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64@@ -205,7 +206,7 @@ static int raw_pwrite(BlockDriverState *if (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt) { DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%"@@ -277,8 +278,8 @@ void qemu_aio_init(void)seems to fix the problem. */ struct aioinit ai;memset (&ai,0 ,sizeof (ai)); - ai.aio_threads =1 ; - ai.aio_num =1 ; + ai.aio_threads =16 ; + ai.aio_num =16 ; ai.aio_idle_time =365 *100000 ;aio_init (&ai); }@@ -388,7 +389,10 @@ static RawAIOCB *raw_aio_setup(BlockDriv acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num; acb->aiocb.aio_sigevent.sigev_notify =SIGEV_SIGNAL ; acb->aiocb.aio_buf = buf; - acb->aiocb.aio_nbytes = nb_sectors *512 ; +if (nb_sectors <0 ) + acb->aiocb.aio_nbytes = -nb_sectors; +else + acb->aiocb.aio_nbytes = nb_sectors *512 ; acb->aiocb.aio_offset = sector_num *512 ; acb->next = first_aio; first_aio = acb;@@ -680,6 +684,8 @@ static int hdev_open(BlockDriverState *b s->fd_open_flags = open_flags;/* open will not fail even if no floppy is inserted */ open_flags |= O_NONBLOCK; + }else if (strstart (filename,"/dev/sg" ,NULL )) { + bs->sg =1 ; }#endif fd =open (filename, open_flags,0644 );@@ -859,6 +865,12 @@ static int raw_set_locked(BlockDriverSta return 0; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BDRVRawState *s = bs->opaque; + +return ioctl (s->fd, req, buf); +}#else static int raw_is_inserted (BlockDriverState *bs)@@ -881,6 +893,10 @@ static int raw_set_locked(BlockDriverStareturn -ENOTSUP ; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +return -ENOTSUP ; +}#endif /* !linux */ BlockDriver bdrv_host_device = {@@ -907,6 +923,8 @@ BlockDriver bdrv_host_device = { .bdrv_media_changed = raw_media_changed, .bdrv_eject = raw_eject, .bdrv_set_locked = raw_set_locked, +/* generic scsi device */ + .bdrv_ioctl = raw_ioctl, };#else /* _WIN32 */ Index:
qemu/block.c
===================================================================block.c +16
-1@@ -303,7 +303,6 @@ int bdrv_file_open(BlockDriverState **pb {BlockDriverState *bs;int ret; - bs =bdrv_new ("" );if (!bs)return -ENOMEM ;@@ -796,6 +795,11 @@ int bdrv_is_cached(BlockDriverState *bs)return bs->cache; } +int bdrv_is_sg (BlockDriverState *bs) +{ +return bs->sg; +} +/* XXX: no longer used */ void bdrv_set_change_cb (BlockDriverState *bs,void (*change_cb)(void *opaque),void *opaque)@@ -1404,3 +1408,14 @@ void bdrv_set_locked(BlockDriverState *b drv->bdrv_set_locked (bs, locked); } } + +/* needed for generic scsi interface */ + +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BlockDriver *drv = bs->drv; + +if (drv && drv->bdrv_ioctl) +return drv->bdrv_ioctl (bs, req, buf); +return -ENOTSUP ; +}Index:
qemu/block.h
===================================================================block.h +2
-0@@ -120,6 +120,7 @@ int bdrv_get_type_hint(BlockDriverStateint bdrv_get_translation_hint (BlockDriverState *bs);int bdrv_is_removable (BlockDriverState *bs);int bdrv_is_read_only (BlockDriverState *bs); +int bdrv_is_sg (BlockDriverState *bs);int bdrv_is_inserted (BlockDriverState *bs);int bdrv_is_cached (BlockDriverState *bs);int bdrv_media_changed (BlockDriverState *bs);@@ -150,6 +151,7 @@ int bdrv_snapshot_delete(BlockDriverStatint bdrv_snapshot_list (BlockDriverState *bs,QEMUSnapshotInfo **psn_info);char *bdrv_snapshot_dump (char *buf,int buf_size,QEMUSnapshotInfo *sn); +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf);char *get_human_readable_size (char *buf,int buf_size,int64_t size);int path_is_absolute (const char *path);Index:
qemu/block_int.h
===================================================================block_int.h +4
-0@@ -82,6 +82,9 @@ struct BlockDriver {int (*bdrv_eject)(BlockDriverState *bs,int eject_flag);int (*bdrv_set_locked)(BlockDriverState *bs,int locked); +/* to control generic scsi devices */ +int (*bdrv_ioctl)(BlockDriverState *bs,unsigned long int req,char *buf); +BlockDriverAIOCB *free_aiocb;struct BlockDriver *next; };@@ -93,6 +96,7 @@ struct BlockDriverState {int removable;/* if true, the media can be removed */ int locked;/* if true, the media cannot temporarily be ejected */ int encrypted;/* if true, the media is encrypted */ +int sg;/* if true, the device is a /dev/sg* */ /* event callback when inserting/removing */ void (*change_cb)(void *opaque);void *change_opaque;Index:
qemu/hw/scsi-disk.c
===================================================================hw/scsi-disk.c +38
-24@@ -37,7 +37,7 @@ do { fprintf(stderr, "scsi-disk: " fmt ,#define SCSI_DMA_BUF_SIZE 65536 typedef struct SCSIRequest { - SCSIDevice *dev; + SCSIDeviceState *dev; uint32_t tag; /* ??? We should probably keep track of whether the data trasfer is a read or a write. Currently we rely on the host getting it right. */@@ -51,7 +51,7 @@ typedef struct SCSIRequest {struct SCSIRequest *next; } SCSIRequest; -struct SCSIDevice +struct SCSIDeviceState {BlockDriverState *bdrv;SCSIRequest *requests;@@ -69,7 +69,7 @@ struct SCSIDevice/* Global pool of SCSIRequest structures. */ static SCSIRequest *free_requests =NULL ; -static SCSIRequest *scsi_new_request (SCSIDevice *s,uint32_t tag) +static SCSIRequest *scsi_new_request (SCSIDeviceState *s,uint32_t tag) {SCSIRequest *r;@@ -94,7 +94,7 @@ static SCSIRequest *scsi_new_request(SCSstatic void scsi_remove_request (SCSIRequest *r) {SCSIRequest *last; -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;if (s->requests == r) { s->requests = r->next;@@ -112,7 +112,7 @@ static void scsi_remove_request(SCSIRequ free_requests = r; } -static SCSIRequest *scsi_find_request (SCSIDevice *s,uint32_t tag) +static SCSIRequest *scsi_find_request (SCSIDeviceState *s,uint32_t tag) {SCSIRequest *r;@@ -126,7 +126,7 @@ static SCSIRequest *scsi_find_request(SC/* Helper function for command completion. */ static void scsi_command_complete (SCSIRequest *r,int sense) { -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;uint32_t tag;DPRINTF ("Command complete tag=0x%x sense=%d\n" , r->tag, sense); s->sense = sense;@@ -136,8 +136,9 @@ static void scsi_command_complete(SCSIRe }/* Cancel a pending data transfer. */ -void scsi_cancel_io (SCSIDevice *s,uint32_t tag) +static void scsi_cancel_io (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r;DPRINTF ("Cancel tag=0x%x\n" , tag); r =scsi_find_request (s, tag);@@ -152,7 +153,7 @@ void scsi_cancel_io(SCSIDevice *s, uint3static void scsi_read_complete (void * opaque,int ret) {SCSIRequest *r = (SCSIRequest *)opaque; -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;if (ret) {DPRINTF ("IO error\n" );@@ -165,8 +166,9 @@ static void scsi_read_complete(void * op }/* Read more data from scsi device into buffer. */ -void scsi_read_data (SCSIDevice *s,uint32_t tag) +static void scsi_read_data (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r;uint32_t n;@@ -205,7 +207,7 @@ void scsi_read_data(SCSIDevice *s, uint3static void scsi_write_complete (void * opaque,int ret) {SCSIRequest *r = (SCSIRequest *)opaque; -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;uint32_t len;if (ret) {@@ -229,8 +231,9 @@ static void scsi_write_complete(void * o/* Write data to a scsi device. Returns nonzero on failure. The transfer may complete asynchronously. */ -int scsi_write_data (SCSIDevice *s,uint32_t tag) +static int scsi_write_data (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r;uint32_t n;@@ -260,8 +263,9 @@ int scsi_write_data(SCSIDevice *s, uint3 }/* Return a pointer to the data buffer. */ -uint8_t *scsi_get_buf (SCSIDevice *s,uint32_t tag) +static uint8_t *scsi_get_buf (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r; r =scsi_find_request (s, tag);@@ -277,8 +281,10 @@ uint8_t *scsi_get_buf(SCSIDevice *s, uin(eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -int32_t scsi_send_command (SCSIDevice *s,uint32_t tag,uint8_t *buf,int lun) +static int32_t scsi_send_command (SCSIDevice *d,uint32_t tag, +uint8_t *buf,int lun) { +SCSIDeviceState *s = d->state;int64_t nb_sectors;uint32_t lba;uint32_t len;@@ -292,7 +298,7 @@ int32_t scsi_send_command(SCSIDevice *s, r =scsi_find_request (s, tag);if (r) {BADF ("Tag 0x%x already in use\n" , tag); -scsi_cancel_io (s, tag); +scsi_cancel_io (d, tag); }/* ??? Tags are not unique for different luns. We only implement a single lun, so this should not matter. */ @@ -577,19 +583,19 @@ int32_t scsi_send_command(SCSIDevice *s, } } -void scsi_disk_destroy (SCSIDevice *s) +static void scsi_destroy (SCSIDevice *d) { -qemu_free (s); +qemu_free (d->state); +qemu_free (d); } -SCSIDevice *scsi_disk_init (BlockDriverState *bdrv, -int tcq, -scsi_completionfn completion, -void *opaque) +SCSIDevice *scsi_disk_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque) { -SCSIDevice *s; +SCSIDevice *d; +SCSIDeviceState *s; - s = (SCSIDevice *)qemu_mallocz (sizeof (SCSIDevice)); + s = (SCSIDeviceState *)qemu_mallocz (sizeof (SCSIDeviceState)); s->bdrv = bdrv; s->tcq = tcq; s->completion = completion;@@ -600,6 +606,14 @@ SCSIDevice *scsi_disk_init(BlockDriverSt s->cluster_size =1 ; } -return s; -} + d = (SCSIDevice *)qemu_mallocz (sizeof (SCSIDevice)); + d->state = s; + d->destroy = scsi_destroy; + d->send_command = scsi_send_command; + d->read_data = scsi_read_data; + d->write_data = scsi_write_data; + d->cancel_io = scsi_cancel_io; + d->get_buf = scsi_get_buf; +return d; +}Index:
qemu/hw/esp.c
===================================================================hw/esp.c +11
-9@@ -165,7 +165,7 @@ static int get_cmd(ESPState *s, uint8_tif (s->current_dev) {/* Started a new command before the old one finished. Cancel it. */ -scsi_cancel_io (s->current_dev,0 ); + s->current_dev->cancel_io (s->current_dev,0 ); s->async_len =0 ; }@@ -188,7 +188,7 @@ static void do_cmd(ESPState *s, uint8_tDPRINTF ("do_cmd: busid 0x%x\n" , buf[0 ]); lun = buf[0 ] &7 ; - datalen =scsi_send_command (s->current_dev,0 , &buf[1 ], lun); + datalen = s->current_dev->send_command (s->current_dev,0 , &buf[1 ], lun); s->ti_size = datalen;if (datalen !=0 ) { s->rregs[ESP_RSTAT ] =STAT_IN |STAT_TC ;@@ -196,10 +196,10 @@ static void do_cmd(ESPState *s, uint8_t s->dma_counter =0 ;if (datalen >0 ) { s->rregs[ESP_RSTAT ] |=STAT_DI ; -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 ); }else { s->rregs[ESP_RSTAT ] |=STAT_DO ; -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); } } s->rregs[ESP_RINTR ] =INTR_BS |INTR_FC ;@@ -298,9 +298,9 @@ static void esp_do_dma(ESPState *s)if (s->async_len ==0 ) {if (to_device) {// ti_size is negative -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); }else { -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 );/* If there is still data to be read from the device then complete the DMA operation immeriately. Otherwise defer until the scsi layer has completed. */ @@ -335,7 +335,7 @@ static void esp_command_complete(void *o }else {DPRINTF ("transfer %d/%d\n" , s->dma_left, s->ti_size); s->async_len = arg; - s->async_buf =scsi_get_buf (s->current_dev,0 ); + s->async_buf = s->current_dev->get_buf (s->current_dev,0 );if (s->dma_left) {esp_do_dma (s); }else if (s->dma_counter !=0 && s->ti_size <=0 ) {@@ -611,11 +611,13 @@ void esp_scsi_attach(void *opaque, Block }if (s->scsi_dev[id]) {DPRINTF ("Destroying device %d\n" , id); -scsi_disk_destroy (s->scsi_dev[id]); + s->scsi_dev[id]->destroy (s->scsi_dev[id]); }DPRINTF ("Attaching block device %d\n" , id);/* Command queueing is not implemented. */ - s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); + s->scsi_dev[id] =scsi_generic_init (bd,0 , esp_command_complete, s); +if (s->scsi_dev[id] ==NULL ) + s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); } void *esp_init(target_phys_addr_t espaddr,Index:
qemu/hw/lsi53c895a.c
===================================================================hw/lsi53c895a.c +34
-15@@ -187,6 +187,7 @@ typedef struct {/* The tag is a combination of the device ID and the SCSI tag. */ uint32_t current_tag;uint32_t current_dma_len; +int command_complete;uint8_t *dma_buf;lsi_queue *queue;int queue_len;@@ -465,7 +466,8 @@ static void lsi_do_dma(LSIState *s, int s->dbc -= count;if (s->dma_buf ==NULL ) { - s->dma_buf =scsi_get_buf (s->current_dev, s->current_tag); + s->dma_buf = s->current_dev->get_buf (s->current_dev, + s->current_tag); }/* ??? Set SFBR to first data byte. */ @@ -479,10 +481,10 @@ static void lsi_do_dma(LSIState *s, int s->dma_buf =NULL ;if (out) {/* Write the data. */ -scsi_write_data (s->current_dev, s->current_tag); + s->current_dev->write_data (s->current_dev, s->current_tag); }else {/* Request any remaining data. */ -scsi_read_data (s->current_dev, s->current_tag); + s->current_dev->read_data (s->current_dev, s->current_tag); } }else { s->dma_buf += count;@@ -596,6 +598,7 @@ static void lsi_command_complete(void *oif (reason ==SCSI_REASON_DONE ) {DPRINTF ("Command complete sense=%d\n" , (int )arg); s->sense = arg; + s->command_complete =2 ;if (s->waiting && s->dbc !=0 ) {/* Raise phase mismatch for short transfers. */ lsi_bad_phase (s, out,PHASE_ST );@@ -612,6 +615,7 @@ static void lsi_command_complete(void *o }DPRINTF ("Data ready tag=0x%x len=%d\n" , tag, arg); s->current_dma_len = arg; + s->command_complete =1 ;if (!s->waiting)return ;if (s->waiting ==1 || s->dbc ==0 ) {@@ -631,21 +635,30 @@ static void lsi_do_command(LSIState *s) s->dbc =16 ;cpu_physical_memory_read (s->dnad, buf, s->dbc); s->sfbr = buf[0 ]; - n =scsi_send_command (s->current_dev, s->current_tag, buf, s->current_lun); + s->command_complete =0 ; + n = s->current_dev->send_command (s->current_dev, s->current_tag, buf, + s->current_lun);if (n >0 ) {lsi_set_phase (s,PHASE_DI ); -scsi_read_data (s->current_dev, s->current_tag); + s->current_dev->read_data (s->current_dev, s->current_tag); }else if (n <0 ) {lsi_set_phase (s,PHASE_DO ); -scsi_write_data (s->current_dev, s->current_tag); + s->current_dev->write_data (s->current_dev, s->current_tag); } -if (n && s->current_dma_len ==0 ) { -/* Command did not complete immediately so disconnect. */ -lsi_add_msg_byte (s,2 );/* SAVE DATA POINTER */ -lsi_add_msg_byte (s,4 );/* DISCONNECT */ -lsi_set_phase (s,PHASE_MI ); - s->msg_action =1 ; -lsi_queue_command (s); + +if (!s->command_complete) { +if (n) { +/* Command did not complete immediately so disconnect. */ +lsi_add_msg_byte (s,2 );/* SAVE DATA POINTER */ +lsi_add_msg_byte (s,4 );/* DISCONNECT */ +/* wait data */ +lsi_set_phase (s,PHASE_MI ); + s->msg_action =1 ; +lsi_queue_command (s); + }else { +/* wait command complete */ +lsi_set_phase (s,PHASE_DI ); + } } }@@ -1223,6 +1236,8 @@ static uint8_t lsi_reg_readb(LSIState *sreturn s->sdid;case 0x07 :/* GPREG0 */ return 0x7f ; +case 0x08 :/* Revision ID */ +return 0x00 ;case 0xa :/* SSID */ return s->ssid;case 0xb :/* SBCL */ @@ -1268,6 +1283,8 @@ static uint8_t lsi_reg_readb(LSIState *sreturn s->ctest4;case 0x22 :/* CTEST5 */ return s->ctest5; +case 0x23 :/* CTEST6 */ +return 0 ;case 0x24 :/* DBC[0:7] */ return s->dbc &0xff ;case 0x25 :/* DBC[8:15] */ @@ -1822,10 +1839,12 @@ void lsi_scsi_attach(void *opaque, Block }if (s->scsi_dev[id]) {DPRINTF ("Destroying device %d\n" , id); -scsi_disk_destroy (s->scsi_dev[id]); + s->scsi_dev[id]->destroy (s->scsi_dev[id]); }DPRINTF ("Attaching block device %d\n" , id); - s->scsi_dev[id] =scsi_disk_init (bd,1 , lsi_command_complete, s); + s->scsi_dev[id] =scsi_generic_init (bd,1 , lsi_command_complete, s); +if (s->scsi_dev[id] ==NULL ) + s->scsi_dev[id] =scsi_disk_init (bd,1 , lsi_command_complete, s); }void *lsi_scsi_init (PCIBus *bus,int devfn)Index:
qemu/hw/usb-msd.c
===================================================================hw/usb-msd.c +8
-8@@ -149,9 +149,9 @@ static void usb_msd_copy_data(MSDState * s->data_len -= len;if (s->scsi_len ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } }@@ -204,7 +204,7 @@ static void usb_msd_command_complete(voireturn ; } s->scsi_len = arg; - s->scsi_buf =scsi_get_buf (s->scsi_dev, tag); + s->scsi_buf = s->scsi_dev->get_buf (s->scsi_dev, tag);if (p) {usb_msd_copy_data (s);if (s->usb_len ==0 ) {@@ -342,7 +342,7 @@ static int usb_msd_handle_control(USBDevstatic void usb_msd_cancel_io (USBPacket *p,void *opaque) {MSDState *s = opaque; -scsi_cancel_io (s->scsi_dev, s->tag); + s->scsi_dev->cancel_io (s->scsi_dev, s->tag); s->packet =NULL ; s->scsi_len =0 ; }@@ -390,14 +390,14 @@ static int usb_msd_handle_data(USBDeviceDPRINTF ("Command tag 0x%x flags %08x len %d data %d\n" , s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue =0 ; -scsi_send_command (s->scsi_dev, s->tag, cbw.cmd,0 ); + s->scsi_dev->send_command (s->scsi_dev, s->tag, cbw.cmd,0 );/* ??? Should check that USB and SCSI data transfer directions match. */ if (s->residue ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } ret = len;@@ -508,7 +508,7 @@ static void usb_msd_handle_destroy(USBDe {MSDState *s = (MSDState *)dev; -scsi_disk_destroy (s->scsi_dev); + s->scsi_dev->destroy (s->scsi_dev);bdrv_delete (s->bs);qemu_free (s); }Index:
qemu/hw/scsi-generic.c
===================================================================hw/scsi-generic.c new file +667
-0@@ -0,0 +1,667 @@ +/* +* Generic SCSI Device support +* +* Copyright (c) 2007 Bull S.A.S. +* Based on code by Paul Brook +* Based on code by Fabrice Bellard +* +* Written by Laurent Vivier <[email protected]> +* +* This code is licenced under the LGPL. +* +*/ + +#include "qemu-common.h" +#include "block.h" +#include "scsi-disk.h" + +#ifndef __linux__ + +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque) +{ +return NULL ; +} + +#else /* __linux__ */ + +//#define DEBUG_SCSI + +#ifdef DEBUG_SCSI +#define DPRINTF(fmt, args...) \ +do { printf("scsi-generic: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#endif + +#define BADF(fmt, args...) \ +do { fprintf(stderr, "scsi-generic: " fmt , ##args); } while (0) + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <scsi/sg.h> +#include <scsi/scsi.h> + +#define LOAD_UNLOAD 0xa6 +#define SET_CD_SPEED 0xbb +#define BLANK 0xa1 + +#define SCSI_CMD_BUF_SIZE 16 +#define SCSI_SENSE_BUF_SIZE 32 + +#define SG_ERR_DRIVER_TIMEOUT 0x06 +#define SG_ERR_DRIVER_SENSE 0x08 + +#ifndef MAX_UINT +#define MAX_UINT ((unsigned int)-1) +#endif + +typedef struct SCSIRequest { +BlockDriverAIOCB *aiocb; +struct SCSIRequest *next; +SCSIDeviceState *dev; +uint32_t tag; +uint8_t cmd[SCSI_CMD_BUF_SIZE ]; +int cmdlen; +uint8_t *buf; +int buflen; +int len; +sg_io_hdr_t io_header; +}SCSIRequest ; + +struct SCSIDeviceState +{ +SCSIRequest *requests; +BlockDriverState *bdrv; +int blocksize; +int lun; +scsi_completionfn completion; +void *opaque; +int driver_status; +uint8_t sensebuf[SCSI_SENSE_BUF_SIZE ]; +}; + +/* Global pool of SCSIRequest structures. */ +static SCSIRequest *free_requests =NULL ; + +static SCSIRequest *scsi_new_request (SCSIDeviceState *s,uint32_t tag) +{ +SCSIRequest *r; + +if (free_requests) { + r = free_requests; + free_requests = r->next; + }else { + r =qemu_malloc (sizeof (SCSIRequest)); + r->buf =NULL ; + r->buflen =0 ; + } + r->dev = s; + r->tag = tag; +memset (r->cmd,0 ,sizeof (r->cmd)); +memset (&r->io_header,0 ,sizeof (r->io_header)); + r->cmdlen =0 ; + r->len =0 ; + r->aiocb =NULL ; + +/* link */ + + r->next = s->requests; + s->requests = r; +DPRINTF ("scsi_new_request tag=0x%x\n" , tag); +return r; +} + +static void scsi_remove_request (SCSIRequest *r) +{ +SCSIRequest *last; +SCSIDeviceState *s = r->dev; + +DPRINTF ("scsi_remove_request tag=0x%x\n" , r->tag); +if (s->requests == r) { + s->requests = r->next; + }else { + last = s->requests; +while (last && last->next != r) + last = last->next; +if (last) { + last->next = r->next; + }else { +BADF ("Orphaned request\n" ); + } + } + r->next = free_requests; + free_requests = r; +} + +static SCSIRequest *scsi_find_request (SCSIDeviceState *s,uint32_t tag) +{ +SCSIRequest *r; + + r = s->requests; +while (r && r->tag != tag) + r = r->next; + +return r; +} + +/* Helper function for command completion. */ +static void scsi_command_complete (void *opaque,int ret) +{ +SCSIRequest *r = (SCSIRequest *)opaque; +SCSIDeviceState *s = r->dev; +uint32_t tag; +int sense; + +if (r != r->io_header.usr_ptr) { +BADF ("Bad request\n" ); +exit (1 ); + } + + s->driver_status = r->io_header.driver_status; +if (ret !=0 ) + sense =HARDWARE_ERROR ; +else { +if (s->driver_status &SG_ERR_DRIVER_TIMEOUT ) { + sense =HARDWARE_ERROR ; +BADF ("Driver Timeout\n" ); + }else if ((s->driver_status &SG_ERR_DRIVER_SENSE ) ==0 ) + sense =NO_SENSE ; +else + sense = s->sensebuf[2 ] &0x0f ; + } + +DPRINTF ("Command complete 0x%p tag=0x%x sense=%d\n" , r, r->tag, sense); + tag = r->tag; +scsi_remove_request (r); + s->completion (s->opaque,SCSI_REASON_DONE , tag, sense); +} + +/* Cancel a pending data transfer. */ +static void scsi_cancel_io (SCSIDevice *d,uint32_t tag) +{ +DPRINTF ("scsi_cancel_io 0x%x\n" , tag); +SCSIDeviceState *s = d->state; +SCSIRequest *r; +DPRINTF ("Cancel tag=0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (r) { +if (r->aiocb) +bdrv_aio_cancel (r->aiocb); + r->aiocb =NULL ; +scsi_remove_request (r); + } +} + +static int execute_command (BlockDriverState *bdrv, +SCSIRequest *r,int direction, +BlockDriverCompletionFunc *complete) +{ + r->io_header.interface_id ='S' ; + r->io_header.dxfer_direction = direction; + r->io_header.dxfer_len = r->buflen; + r->io_header.dxferp = r->buf; + r->io_header.cmdp = r->cmd; + r->io_header.cmd_len = r->cmdlen; + r->io_header.mx_sb_len =sizeof (r->dev->sensebuf); + r->io_header.sbp = r->dev->sensebuf; + r->io_header.timeout =MAX_UINT ; + r->io_header.usr_ptr = r; + r->io_header.flags |=SG_FLAG_DIRECT_IO ; + +if (bdrv_pwrite (bdrv,-1 , &r->io_header,sizeof (r->io_header)) ==-1 ) { +BADF ("execute_command: write failed ! (%d)\n" , errno); +return -1 ; + } +if (complete ==NULL ) { +int ret; + r->aiocb =NULL ; +while ((ret =bdrv_pread (bdrv,-1 , &r->io_header, +sizeof (r->io_header))) ==-1 && + errno ==EINTR ); +if (ret ==-1 ) { +BADF ("execute_command: read failed !\n" ); +return -1 ; + } +return 0 ; + } + + r->aiocb =bdrv_aio_read (bdrv,0 , (uint8_t *)&r->io_header, + -(int64_t )sizeof (r->io_header), complete, r); +if (r->aiocb ==NULL ) { +BADF ("execute_command: read failed !\n" ); +return -1 ; + } + +return 0 ; +} + +static void scsi_read_complete (void * opaque,int ret) +{ +SCSIRequest *r = (SCSIRequest *)opaque; +SCSIDeviceState *s = r->dev; +int len; + +if (r != r->io_header.usr_ptr) { +BADF ("Bad request\n" ); +exit (1 ); + } + +if (ret) { +DPRINTF ("IO error\n" ); +scsi_command_complete (r, ret); +return ; + } + len = r->io_header.dxfer_len - r->io_header.resid; +DPRINTF ("Data ready tag=0x%x len=%d\n" , r->tag, len); + + r->len =-1 ; + s->completion (s->opaque,SCSI_REASON_DATA , r->tag, len); +} + +/* Read more data from scsi device into buffer. */ +static void scsi_read_data (SCSIDevice *d,uint32_t tag) +{ +SCSIDeviceState *s = d->state; +SCSIRequest *r; +int ret; + +DPRINTF ("scsi_read_data 0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (!r) { +BADF ("Bad read tag 0x%x\n" , tag); +/* ??? This is the wrong error. */ +scsi_command_complete (r, -EINVAL ); +return ; + } + +if (r->len ==-1 ) { +scsi_command_complete (r,0 ); +return ; + } + +if (r->cmd[0 ] ==REQUEST_SENSE && s->driver_status &SG_ERR_DRIVER_SENSE ) + { +memcpy (r->buf, s->sensebuf,16 ); + r->io_header.driver_status =0 ; + r->len =-1 ; + s->completion (s->opaque,SCSI_REASON_DATA , r->tag,16 ); +return ; + } + + ret =execute_command (s->bdrv, r,SG_DXFER_FROM_DEV , scsi_read_complete); +if (ret ==-1 ) { +scsi_command_complete (r, -EINVAL ); +return ; + } +} + +static void scsi_write_complete (void * opaque,int ret) +{ +SCSIRequest *r = (SCSIRequest *)opaque; + +if (r != r->io_header.usr_ptr) { +BADF ("Bad request\n" ); +exit (1 ); + } + +if (ret) { +DPRINTF ("IO error\n" ); +scsi_command_complete (r, ret); +return ; + } + +scsi_command_complete (r, ret); +} + +/* Write data to a scsi device. Returns nonzero on failure. +The transfer may complete asynchronously. */ +static int scsi_write_data (SCSIDevice *d,uint32_t tag) +{ +SCSIDeviceState *s = d->state; +SCSIRequest *r; +int ret; + +DPRINTF ("scsi_write_data 0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (!r) { +BADF ("Bad write tag 0x%x\n" , tag); +/* ??? This is the wrong error. */ +scsi_command_complete (r, -EINVAL ); +return 0 ; + } + +if (r->len ==0 ) { + r->len = r->buflen; + s->completion (s->opaque,SCSI_REASON_DATA , r->tag, r->len); +return 0 ; + } + + ret =execute_command (s->bdrv, r,SG_DXFER_TO_DEV , scsi_write_complete); +if (ret ==-1 ) { +scsi_command_complete (r, -EINVAL ); +return 1 ; + } + +return 0 ; +} + +/* Return a pointer to the data buffer. */ +static uint8_t *scsi_get_buf (SCSIDevice *d,uint32_t tag) +{ +SCSIDeviceState *s = d->state; +SCSIRequest *r; +DPRINTF ("scsi_get_buf 0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (!r) { +BADF ("Bad buffer tag 0x%x\n" , tag); +return NULL ; + } +return r->buf; +} + +static int scsi_length (uint8_t *cmd,int blocksize,int *cmdlen,uint32_t *len) +{ +switch (cmd[0 ] >>5 ) { +case 0 : + *len = cmd[4 ]; + *cmdlen =6 ; +break ; +case 1 : +case 2 : + *len = cmd[8 ] | (cmd[7 ] <<8 ); + *cmdlen =10 ; +break ; +case 4 : + *len = cmd[13 ] | (cmd[12 ] <<8 ) | (cmd[11 ] <<16 ) | (cmd[10 ] <<24 ); + *cmdlen =16 ; +break ; +case 5 : + *len = cmd[9 ] | (cmd[8 ] <<8 ) | (cmd[7 ] <<16 ) | (cmd[6 ] <<24 ); + *cmdlen =12 ; +break ; +default : +return -1 ; + } + +switch (cmd[0 ]) { +case TEST_UNIT_READY : +case REZERO_UNIT : +case START_STOP : +case SEEK_6 : +case WRITE_FILEMARKS : +case SPACE : +case ERASE : +case ALLOW_MEDIUM_REMOVAL : +case VERIFY : +case SEEK_10 : +case SYNCHRONIZE_CACHE : +case LOCK_UNLOCK_CACHE : +case LOAD_UNLOAD : +case SET_CD_SPEED : +case SET_LIMITS : +case WRITE_LONG : +case MOVE_MEDIUM : +case UPDATE_BLOCK : + *len =0 ; +break ; +case MODE_SENSE : +break ; +case WRITE_SAME : + *len =1 ; +break ; +case READ_CAPACITY : + *len =8 ; +break ; +case READ_BLOCK_LIMITS : + *len =6 ; +break ; +case READ_POSITION : + *len =20 ; +break ; +case SEND_VOLUME_TAG : + *len *=40 ; +break ; +case MEDIUM_SCAN : + *len *=8 ; +break ; +case WRITE_VERIFY : +case WRITE_6 : +case WRITE_10 : +case WRITE_12 : +case WRITE_VERIFY_12 : + *len *= blocksize; +break ; +case READ_6 : +case READ_REVERSE : +case RECOVER_BUFFERED_DATA : +case READ_10 : +case READ_12 : + *len *= blocksize; +break ; + } +return 0 ; +} + +static int is_write (int command) +{ +switch (command) { +case COPY : +case COPY_VERIFY : +case COMPARE : +case CHANGE_DEFINITION : +case LOG_SELECT : +case MODE_SELECT : +case MODE_SELECT_10 : +case SEND_DIAGNOSTIC : +case WRITE_BUFFER : +case FORMAT_UNIT : +case REASSIGN_BLOCKS : +case RESERVE : +case SEARCH_EQUAL : +case SEARCH_HIGH : +case SEARCH_LOW : +case WRITE_6 : +case WRITE_10 : +case WRITE_VERIFY : +case UPDATE_BLOCK : +case WRITE_LONG : +case WRITE_SAME : +case SEARCH_HIGH_12 : +case SEARCH_EQUAL_12 : +case SEARCH_LOW_12 : +case WRITE_12 : +case WRITE_VERIFY_12 : +case SET_WINDOW : +case MEDIUM_SCAN : +case SEND_VOLUME_TAG : +case WRITE_LONG_2 : +return 1 ; + } +return 0 ; +} + +/* Execute a scsi command. Returns the length of the data expected by the +command. This will be Positive for data transfers from the device +(eg. disk reads), negative for transfers to the device (eg. disk writes), +and zero if the command does not transfer any data. */ + +static int32_t scsi_send_command (SCSIDevice *d,uint32_t tag, +uint8_t *cmd,int lun) +{ +SCSIDeviceState *s = d->state; +uint32_t len; +int cmdlen; +SCSIRequest *r; +int ret; + +/* ??? Tags are not unique for different luns. We only implement a +single lun, so this should not matter. */ + +if (lun != s->lun || (cmd[1 ] >>5 ) != s->lun) { +DPRINTF ("Unimplemented LUN %d\n" , lun ? lun : cmd[1 ] >>5 ); + s->completion (s->opaque,SCSI_REASON_DONE , tag,HARDWARE_ERROR ); +return 0 ; + } + +if (scsi_length (cmd, s->blocksize, &cmdlen, &len) ==-1 ) { +BADF ("Unsupported command length, command %x\n" , cmd[0 ]); +return 0 ; + } + +DPRINTF ("Command: lun=%d tag=0x%x data=0x%02x len %d\n" , lun, tag, + cmd[0 ], len); + + r =scsi_find_request (s, tag); +if (r) { +BADF ("Tag 0x%x already in use %p\n" , tag, r); +scsi_cancel_io (d, tag); + } + r =scsi_new_request (s, tag); + +memcpy (r->cmd, cmd, cmdlen); + r->cmdlen = cmdlen; + +if (len ==0 ) { +if (r->buf !=NULL ) +free (r->buf); + r->buflen =0 ; + r->buf =NULL ; + ret =execute_command (s->bdrv, r,SG_DXFER_NONE , scsi_command_complete); +if (ret ==-1 ) { +scsi_command_complete (r, -EINVAL ); +return 0 ; + } +return 0 ; + } + +if (r->buflen != len) { +if (r->buf !=NULL ) +free (r->buf); + r->buf =qemu_memalign (512 , len); + r->buflen = len; + } + +memset (r->buf,0 , r->buflen); + r->len = len; +if (is_write (cmd[0 ])) { + r->len =0 ; +return -len; + } + +return len; +} + +static int get_blocksize (BlockDriverState *bdrv) +{ +uint8_t cmd[10 ]; +uint8_t buf[8 ]; +uint8_t sensebuf[8 ]; +sg_io_hdr_t io_header; +int ret; + +memset (cmd,sizeof (cmd),0 ); +memset (buf,sizeof (buf),0 ); + cmd[0 ] =READ_CAPACITY ; + +memset (&io_header,0 ,sizeof (io_header)); + io_header.interface_id ='S' ; + io_header.dxfer_direction =SG_DXFER_FROM_DEV ; + io_header.dxfer_len =sizeof (buf); + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len =sizeof (cmd); + io_header.mx_sb_len =sizeof (sensebuf); + io_header.sbp = sensebuf; + io_header.timeout =6000 ;/* XXX */ + + ret =bdrv_pwrite (bdrv,-1 , &io_header,sizeof (io_header)); +if (ret ==-1 ) +return -1 ; + +while ((ret =bdrv_pread (bdrv,-1 , &io_header,sizeof (io_header))) ==-1 && + errno ==EINTR ); + +if (ret ==-1 ) +return -1 ; + +return (buf[4 ] <<24 ) | (buf[5 ] <<16 ) | (buf[6 ] <<8 ) | buf[7 ]; +} + +static void scsi_destroy (SCSIDevice *d) +{ +SCSIRequest *r, *n; + + r = d->state->requests; +while (r) { + n = r->next; +qemu_free (r); + r = n; + } + + r = free_requests; +while (r) { + n = r->next; +qemu_free (r); + r = n; + } + +qemu_free (d->state); +qemu_free (d); +} + +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque) +{ +int sg_version; +SCSIDevice *d; +SCSIDeviceState *s; +struct sg_scsi_id scsiid; + +/* check we are really using a /dev/sg* file */ + +if (!bdrv_is_sg (bdrv)) +return NULL ; + +/* check we are using a driver managing SG_IO (version 3 and after */ + +if (bdrv_ioctl (bdrv,SG_GET_VERSION_NUM , (char *)&sg_version) <0 || + sg_version <30000 ) +return NULL ; + +/* get LUN of the /dev/sg? */ + +if (bdrv_ioctl (bdrv,SG_GET_SCSI_ID , &scsiid)) +return NULL ; + +/* define device state */ + + s = (SCSIDeviceState *)qemu_mallocz (sizeof (SCSIDeviceState)); + s->bdrv = bdrv; + s->requests =NULL ; + s->completion = completion; + s->opaque = opaque; + s->lun = scsiid.lun; + s->blocksize =get_blocksize (s->bdrv); + s->driver_status =0 ; +memset (s->sensebuf,0 ,sizeof (s->sensebuf)); +/* removable media returns 0 if not present */ +if (s->blocksize <=0 ) + s->blocksize =2048 ; + +/* define function to manage device */ + + d = (SCSIDevice *)qemu_mallocz (sizeof (SCSIDevice)); + d->state = s; + d->destroy = scsi_destroy; + d->send_command = scsi_send_command; + d->read_data = scsi_read_data; + d->write_data = scsi_write_data; + d->cancel_io = scsi_cancel_io; + d->get_buf = scsi_get_buf; + +return d; +} +#endif /* __linux__ */ Index: qemu/Makefile
===================================================================Makefile +1
-0@@ -56,6 +56,7 @@ OBJS+=irq.o OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o OBJS+=scsi-disk.o cdrom.o +OBJS+=scsi-generic.o OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o OBJS+=sd.o ssi-sd.oIndex:
qemu/hw/scsi-disk.h
===================================================================hw/scsi-disk.h +16
-13@@ -7,24 +7,27 @@ enum scsi_reason {SCSI_REASON_DATA /* Transfer complete, more data required. */ }; +typedef struct SCSIDeviceState SCSIDeviceState ;typedef struct SCSIDevice SCSIDevice ;typedef void (*scsi_completionfn )(void *opaque,int reason,uint32_t tag,uint32_t arg); -SCSIDevice *scsi_disk_init (BlockDriverState *bdrv, -int tcq, -scsi_completionfn completion, -void *opaque); -void scsi_disk_destroy (SCSIDevice *s); +struct SCSIDevice +{ +SCSIDeviceState *state; +void (*destroy)(SCSIDevice *s); +int32_t (*send_command)(SCSIDevice *s,uint32_t tag,uint8_t *buf, +int lun); +void (*read_data)(SCSIDevice *s,uint32_t tag); +int (*write_data)(SCSIDevice *s,uint32_t tag); +void (*cancel_io)(SCSIDevice *s,uint32_t tag); +uint8_t *(*get_buf)(SCSIDevice *s,uint32_t tag); +}; -int32_t scsi_send_command (SCSIDevice *s,uint32_t tag,uint8_t *buf,int lun); -/* SCSI data transfers are asynchrnonous. However, unlike the block IO -layer the completion routine may be called directly by -scsi_{read,write}_data. */ -void scsi_read_data (SCSIDevice *s,uint32_t tag); -int scsi_write_data (SCSIDevice *s,uint32_t tag); -void scsi_cancel_io (SCSIDevice *s,uint32_t tag); -uint8_t *scsi_get_buf (SCSIDevice *s,uint32_t tag); +SCSIDevice *scsi_disk_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque); +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque);/* cdrom.c */ int cdrom_read_toc (int nb_sectors,uint8_t *buf,int msf,int start_track);
Quoted 1 line · Anthony Liguori
Hi Laurent,
Hi Anthony,
Quoted 1 line
This patch is really neat. I'd like to do the same with virtio-blk.
Thank you,
Quoted 1 line
Can you separate out the general cleanups from the function of this patch?
Yes, of course. I merged all by laziness...
Quoted 3 lines
Regards,
Anthony Liguori
Regards,
Laurent
Quoted 1,381 lines
Quoted 1,379 lines · Laurent Vivier
This patch allows to connect the virtual SCSI interface of Qemu to a real SCSI device of the host.
Using the devices/dev/sg, it allows to send the SCSI commands from the virtual SCSI interface to the real device.It has been tested with a SATA disk and an ATA CD burner with ide-scsi module and wodim to burn a CD-RW.
v2 improves Timeout and management of
REQUEST_SENSEcommand.Laurent
--- Makefile | 1 block-raw.c | 28 +- block.c | 17 + block.h | 2 block_int.h | 4 hw/esp.c | 20 - hw/lsi53c895a.c | 49 ++- hw/scsi-disk.c | 62 +++-- hw/scsi-disk.h | 29 +- hw/scsi-generic.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb-msd.c | 16 - 11 files changed, 820 insertions(+), 75 deletions(-)Index:
qemu/block-raw.c
===================================================================block-raw.c +23
-5@@ -52,6 +52,7 @@#include <sys/ioctl.h> #include <linux/cdrom.h> #include <linux/fd.h> +#include <linux/major.h> #endif #ifdef __FreeBSD__#include <sys/disk.h> @@ -152,7 +153,7 @@ static int raw_pread(BlockDriverState *bif (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt <=10 ) { DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64@@ -205,7 +206,7 @@ static int raw_pwrite(BlockDriverState *if (ret <0 )return ret; -if (lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { +if (offset >=0 &&lseek (s->fd, offset,SEEK_SET ) == (off_t)-1 ) { ++(s->lseek_err_cnt);if (s->lseek_err_cnt) { DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%"@@ -277,8 +278,8 @@ void qemu_aio_init(void)seems to fix the problem. */ struct aioinit ai;memset (&ai,0 ,sizeof (ai)); - ai.aio_threads =1 ; - ai.aio_num =1 ; + ai.aio_threads =16 ; + ai.aio_num =16 ; ai.aio_idle_time =365 *100000 ;aio_init (&ai); }@@ -388,7 +389,10 @@ static RawAIOCB *raw_aio_setup(BlockDriv acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num; acb->aiocb.aio_sigevent.sigev_notify =SIGEV_SIGNAL ; acb->aiocb.aio_buf = buf; - acb->aiocb.aio_nbytes = nb_sectors *512 ; +if (nb_sectors <0 ) + acb->aiocb.aio_nbytes = -nb_sectors; +else + acb->aiocb.aio_nbytes = nb_sectors *512 ; acb->aiocb.aio_offset = sector_num *512 ; acb->next = first_aio; first_aio = acb;@@ -680,6 +684,8 @@ static int hdev_open(BlockDriverState *b s->fd_open_flags = open_flags;/* open will not fail even if no floppy is inserted */ open_flags |= O_NONBLOCK; + }else if (strstart (filename,"/dev/sg" ,NULL )) { + bs->sg =1 ; }#endif fd =open (filename, open_flags,0644 );@@ -859,6 +865,12 @@ static int raw_set_locked(BlockDriverSta return 0; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BDRVRawState *s = bs->opaque; + +return ioctl (s->fd, req, buf); +}#else static int raw_is_inserted (BlockDriverState *bs)@@ -881,6 +893,10 @@ static int raw_set_locked(BlockDriverStareturn -ENOTSUP ; } +static int raw_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +return -ENOTSUP ; +}#endif /* !linux */ BlockDriver bdrv_host_device = {@@ -907,6 +923,8 @@ BlockDriver bdrv_host_device = { .bdrv_media_changed = raw_media_changed, .bdrv_eject = raw_eject, .bdrv_set_locked = raw_set_locked, +/* generic scsi device */ + .bdrv_ioctl = raw_ioctl, };#else /* _WIN32 */ Index:
qemu/block.c
===================================================================block.c +16
-1@@ -303,7 +303,6 @@ int bdrv_file_open(BlockDriverState **pb {BlockDriverState *bs;int ret; - bs =bdrv_new ("" );if (!bs)return -ENOMEM ;@@ -796,6 +795,11 @@ int bdrv_is_cached(BlockDriverState *bs)return bs->cache; } +int bdrv_is_sg (BlockDriverState *bs) +{ +return bs->sg; +} +/* XXX: no longer used */ void bdrv_set_change_cb (BlockDriverState *bs,void (*change_cb)(void *opaque),void *opaque)@@ -1404,3 +1408,14 @@ void bdrv_set_locked(BlockDriverState *b drv->bdrv_set_locked (bs, locked); } } + +/* needed for generic scsi interface */ + +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf) +{ +BlockDriver *drv = bs->drv; + +if (drv && drv->bdrv_ioctl) +return drv->bdrv_ioctl (bs, req, buf); +return -ENOTSUP ; +}Index:
qemu/block.h
===================================================================block.h +2
-0@@ -120,6 +120,7 @@ int bdrv_get_type_hint(BlockDriverStateint bdrv_get_translation_hint (BlockDriverState *bs);int bdrv_is_removable (BlockDriverState *bs);int bdrv_is_read_only (BlockDriverState *bs); +int bdrv_is_sg (BlockDriverState *bs);int bdrv_is_inserted (BlockDriverState *bs);int bdrv_is_cached (BlockDriverState *bs);int bdrv_media_changed (BlockDriverState *bs);@@ -150,6 +151,7 @@ int bdrv_snapshot_delete(BlockDriverStatint bdrv_snapshot_list (BlockDriverState *bs,QEMUSnapshotInfo **psn_info);char *bdrv_snapshot_dump (char *buf,int buf_size,QEMUSnapshotInfo *sn); +int bdrv_ioctl (BlockDriverState *bs,unsigned long int req,void *buf);char *get_human_readable_size (char *buf,int buf_size,int64_t size);int path_is_absolute (const char *path);Index:
qemu/block_int.h
===================================================================block_int.h +4
-0@@ -82,6 +82,9 @@ struct BlockDriver {int (*bdrv_eject)(BlockDriverState *bs,int eject_flag);int (*bdrv_set_locked)(BlockDriverState *bs,int locked); +/* to control generic scsi devices */ +int (*bdrv_ioctl)(BlockDriverState *bs,unsigned long int req,char *buf); +BlockDriverAIOCB *free_aiocb;struct BlockDriver *next; };@@ -93,6 +96,7 @@ struct BlockDriverState {int removable;/* if true, the media can be removed */ int locked;/* if true, the media cannot temporarily be ejected */ int encrypted;/* if true, the media is encrypted */ +int sg;/* if true, the device is a /dev/sg* */ /* event callback when inserting/removing */ void (*change_cb)(void *opaque);void *change_opaque;Index:
qemu/hw/scsi-disk.c
===================================================================hw/scsi-disk.c +38
-24@@ -37,7 +37,7 @@ do { fprintf(stderr, "scsi-disk: " fmt ,#define SCSI_DMA_BUF_SIZE 65536 typedef struct SCSIRequest { - SCSIDevice *dev; + SCSIDeviceState *dev; uint32_t tag; /* ??? We should probably keep track of whether the data trasfer is a read or a write. Currently we rely on the host getting it right. */@@ -51,7 +51,7 @@ typedef struct SCSIRequest {struct SCSIRequest *next; } SCSIRequest; -struct SCSIDevice +struct SCSIDeviceState {BlockDriverState *bdrv;SCSIRequest *requests;@@ -69,7 +69,7 @@ struct SCSIDevice/* Global pool of SCSIRequest structures. */ static SCSIRequest *free_requests =NULL ; -static SCSIRequest *scsi_new_request (SCSIDevice *s,uint32_t tag) +static SCSIRequest *scsi_new_request (SCSIDeviceState *s,uint32_t tag) {SCSIRequest *r;@@ -94,7 +94,7 @@ static SCSIRequest *scsi_new_request(SCSstatic void scsi_remove_request (SCSIRequest *r) {SCSIRequest *last; -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;if (s->requests == r) { s->requests = r->next;@@ -112,7 +112,7 @@ static void scsi_remove_request(SCSIRequ free_requests = r; } -static SCSIRequest *scsi_find_request (SCSIDevice *s,uint32_t tag) +static SCSIRequest *scsi_find_request (SCSIDeviceState *s,uint32_t tag) {SCSIRequest *r;@@ -126,7 +126,7 @@ static SCSIRequest *scsi_find_request(SC/* Helper function for command completion. */ static void scsi_command_complete (SCSIRequest *r,int sense) { -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;uint32_t tag;DPRINTF ("Command complete tag=0x%x sense=%d\n" , r->tag, sense); s->sense = sense;@@ -136,8 +136,9 @@ static void scsi_command_complete(SCSIRe }/* Cancel a pending data transfer. */ -void scsi_cancel_io (SCSIDevice *s,uint32_t tag) +static void scsi_cancel_io (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r;DPRINTF ("Cancel tag=0x%x\n" , tag); r =scsi_find_request (s, tag);@@ -152,7 +153,7 @@ void scsi_cancel_io(SCSIDevice *s, uint3static void scsi_read_complete (void * opaque,int ret) {SCSIRequest *r = (SCSIRequest *)opaque; -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;if (ret) {DPRINTF ("IO error\n" );@@ -165,8 +166,9 @@ static void scsi_read_complete(void * op }/* Read more data from scsi device into buffer. */ -void scsi_read_data (SCSIDevice *s,uint32_t tag) +static void scsi_read_data (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r;uint32_t n;@@ -205,7 +207,7 @@ void scsi_read_data(SCSIDevice *s, uint3static void scsi_write_complete (void * opaque,int ret) {SCSIRequest *r = (SCSIRequest *)opaque; -SCSIDevice *s = r->dev; +SCSIDeviceState *s = r->dev;uint32_t len;if (ret) {@@ -229,8 +231,9 @@ static void scsi_write_complete(void * o/* Write data to a scsi device. Returns nonzero on failure. The transfer may complete asynchronously. */ -int scsi_write_data (SCSIDevice *s,uint32_t tag) +static int scsi_write_data (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r;uint32_t n;@@ -260,8 +263,9 @@ int scsi_write_data(SCSIDevice *s, uint3 }/* Return a pointer to the data buffer. */ -uint8_t *scsi_get_buf (SCSIDevice *s,uint32_t tag) +static uint8_t *scsi_get_buf (SCSIDevice *d,uint32_t tag) { +SCSIDeviceState *s = d->state;SCSIRequest *r; r =scsi_find_request (s, tag);@@ -277,8 +281,10 @@ uint8_t *scsi_get_buf(SCSIDevice *s, uin(eg. disk reads), negative for transfers to the device (eg. disk writes), and zero if the command does not transfer any data. */ -int32_t scsi_send_command (SCSIDevice *s,uint32_t tag,uint8_t *buf,int lun) +static int32_t scsi_send_command (SCSIDevice *d,uint32_t tag, +uint8_t *buf,int lun) { +SCSIDeviceState *s = d->state;int64_t nb_sectors;uint32_t lba;uint32_t len;@@ -292,7 +298,7 @@ int32_t scsi_send_command(SCSIDevice *s, r =scsi_find_request (s, tag);if (r) {BADF ("Tag 0x%x already in use\n" , tag); -scsi_cancel_io (s, tag); +scsi_cancel_io (d, tag); }/* ??? Tags are not unique for different luns. We only implement a single lun, so this should not matter. */ @@ -577,19 +583,19 @@ int32_t scsi_send_command(SCSIDevice *s, } } -void scsi_disk_destroy (SCSIDevice *s) +static void scsi_destroy (SCSIDevice *d) { -qemu_free (s); +qemu_free (d->state); +qemu_free (d); } -SCSIDevice *scsi_disk_init (BlockDriverState *bdrv, -int tcq, -scsi_completionfn completion, -void *opaque) +SCSIDevice *scsi_disk_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque) { -SCSIDevice *s; +SCSIDevice *d; +SCSIDeviceState *s; - s = (SCSIDevice *)qemu_mallocz (sizeof (SCSIDevice)); + s = (SCSIDeviceState *)qemu_mallocz (sizeof (SCSIDeviceState)); s->bdrv = bdrv; s->tcq = tcq; s->completion = completion;@@ -600,6 +606,14 @@ SCSIDevice *scsi_disk_init(BlockDriverSt s->cluster_size =1 ; } -return s; -} + d = (SCSIDevice *)qemu_mallocz (sizeof (SCSIDevice)); + d->state = s; + d->destroy = scsi_destroy; + d->send_command = scsi_send_command; + d->read_data = scsi_read_data; + d->write_data = scsi_write_data; + d->cancel_io = scsi_cancel_io; + d->get_buf = scsi_get_buf; +return d; +}Index:
qemu/hw/esp.c
===================================================================hw/esp.c +11
-9@@ -165,7 +165,7 @@ static int get_cmd(ESPState *s, uint8_tif (s->current_dev) {/* Started a new command before the old one finished. Cancel it. */ -scsi_cancel_io (s->current_dev,0 ); + s->current_dev->cancel_io (s->current_dev,0 ); s->async_len =0 ; }@@ -188,7 +188,7 @@ static void do_cmd(ESPState *s, uint8_tDPRINTF ("do_cmd: busid 0x%x\n" , buf[0 ]); lun = buf[0 ] &7 ; - datalen =scsi_send_command (s->current_dev,0 , &buf[1 ], lun); + datalen = s->current_dev->send_command (s->current_dev,0 , &buf[1 ], lun); s->ti_size = datalen;if (datalen !=0 ) { s->rregs[ESP_RSTAT ] =STAT_IN |STAT_TC ;@@ -196,10 +196,10 @@ static void do_cmd(ESPState *s, uint8_t s->dma_counter =0 ;if (datalen >0 ) { s->rregs[ESP_RSTAT ] |=STAT_DI ; -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 ); }else { s->rregs[ESP_RSTAT ] |=STAT_DO ; -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); } } s->rregs[ESP_RINTR ] =INTR_BS |INTR_FC ;@@ -298,9 +298,9 @@ static void esp_do_dma(ESPState *s)if (s->async_len ==0 ) {if (to_device) {// ti_size is negative -scsi_write_data (s->current_dev,0 ); + s->current_dev->write_data (s->current_dev,0 ); }else { -scsi_read_data (s->current_dev,0 ); + s->current_dev->read_data (s->current_dev,0 );/* If there is still data to be read from the device then complete the DMA operation immeriately. Otherwise defer until the scsi layer has completed. */ @@ -335,7 +335,7 @@ static void esp_command_complete(void *o }else {DPRINTF ("transfer %d/%d\n" , s->dma_left, s->ti_size); s->async_len = arg; - s->async_buf =scsi_get_buf (s->current_dev,0 ); + s->async_buf = s->current_dev->get_buf (s->current_dev,0 );if (s->dma_left) {esp_do_dma (s); }else if (s->dma_counter !=0 && s->ti_size <=0 ) {@@ -611,11 +611,13 @@ void esp_scsi_attach(void *opaque, Block }if (s->scsi_dev[id]) {DPRINTF ("Destroying device %d\n" , id); -scsi_disk_destroy (s->scsi_dev[id]); + s->scsi_dev[id]->destroy (s->scsi_dev[id]); }DPRINTF ("Attaching block device %d\n" , id);/* Command queueing is not implemented. */ - s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); + s->scsi_dev[id] =scsi_generic_init (bd,0 , esp_command_complete, s); +if (s->scsi_dev[id] ==NULL ) + s->scsi_dev[id] =scsi_disk_init (bd,0 , esp_command_complete, s); } void *esp_init(target_phys_addr_t espaddr,Index:
qemu/hw/lsi53c895a.c
===================================================================hw/lsi53c895a.c +34
-15@@ -187,6 +187,7 @@ typedef struct {/* The tag is a combination of the device ID and the SCSI tag. */ uint32_t current_tag;uint32_t current_dma_len; +int command_complete;uint8_t *dma_buf;lsi_queue *queue;int queue_len;@@ -465,7 +466,8 @@ static void lsi_do_dma(LSIState *s, int s->dbc -= count;if (s->dma_buf ==NULL ) { - s->dma_buf =scsi_get_buf (s->current_dev, s->current_tag); + s->dma_buf = s->current_dev->get_buf (s->current_dev, + s->current_tag); }/* ??? Set SFBR to first data byte. */ @@ -479,10 +481,10 @@ static void lsi_do_dma(LSIState *s, int s->dma_buf =NULL ;if (out) {/* Write the data. */ -scsi_write_data (s->current_dev, s->current_tag); + s->current_dev->write_data (s->current_dev, s->current_tag); }else {/* Request any remaining data. */ -scsi_read_data (s->current_dev, s->current_tag); + s->current_dev->read_data (s->current_dev, s->current_tag); } }else { s->dma_buf += count;@@ -596,6 +598,7 @@ static void lsi_command_complete(void *oif (reason ==SCSI_REASON_DONE ) {DPRINTF ("Command complete sense=%d\n" , (int )arg); s->sense = arg; + s->command_complete =2 ;if (s->waiting && s->dbc !=0 ) {/* Raise phase mismatch for short transfers. */ lsi_bad_phase (s, out,PHASE_ST );@@ -612,6 +615,7 @@ static void lsi_command_complete(void *o }DPRINTF ("Data ready tag=0x%x len=%d\n" , tag, arg); s->current_dma_len = arg; + s->command_complete =1 ;if (!s->waiting)return ;if (s->waiting ==1 || s->dbc ==0 ) {@@ -631,21 +635,30 @@ static void lsi_do_command(LSIState *s) s->dbc =16 ;cpu_physical_memory_read (s->dnad, buf, s->dbc); s->sfbr = buf[0 ]; - n =scsi_send_command (s->current_dev, s->current_tag, buf, s->current_lun); + s->command_complete =0 ; + n = s->current_dev->send_command (s->current_dev, s->current_tag, buf, + s->current_lun);if (n >0 ) {lsi_set_phase (s,PHASE_DI ); -scsi_read_data (s->current_dev, s->current_tag); + s->current_dev->read_data (s->current_dev, s->current_tag); }else if (n <0 ) {lsi_set_phase (s,PHASE_DO ); -scsi_write_data (s->current_dev, s->current_tag); + s->current_dev->write_data (s->current_dev, s->current_tag); } -if (n && s->current_dma_len ==0 ) { -/* Command did not complete immediately so disconnect. */ -lsi_add_msg_byte (s,2 );/* SAVE DATA POINTER */ -lsi_add_msg_byte (s,4 );/* DISCONNECT */ -lsi_set_phase (s,PHASE_MI ); - s->msg_action =1 ; -lsi_queue_command (s); + +if (!s->command_complete) { +if (n) { +/* Command did not complete immediately so disconnect. */ +lsi_add_msg_byte (s,2 );/* SAVE DATA POINTER */ +lsi_add_msg_byte (s,4 );/* DISCONNECT */ +/* wait data */ +lsi_set_phase (s,PHASE_MI ); + s->msg_action =1 ; +lsi_queue_command (s); + }else { +/* wait command complete */ +lsi_set_phase (s,PHASE_DI ); + } } }@@ -1223,6 +1236,8 @@ static uint8_t lsi_reg_readb(LSIState *sreturn s->sdid;case 0x07 :/* GPREG0 */ return 0x7f ; +case 0x08 :/* Revision ID */ +return 0x00 ;case 0xa :/* SSID */ return s->ssid;case 0xb :/* SBCL */ @@ -1268,6 +1283,8 @@ static uint8_t lsi_reg_readb(LSIState *sreturn s->ctest4;case 0x22 :/* CTEST5 */ return s->ctest5; +case 0x23 :/* CTEST6 */ +return 0 ;case 0x24 :/* DBC[0:7] */ return s->dbc &0xff ;case 0x25 :/* DBC[8:15] */ @@ -1822,10 +1839,12 @@ void lsi_scsi_attach(void *opaque, Block }if (s->scsi_dev[id]) {DPRINTF ("Destroying device %d\n" , id); -scsi_disk_destroy (s->scsi_dev[id]); + s->scsi_dev[id]->destroy (s->scsi_dev[id]); }DPRINTF ("Attaching block device %d\n" , id); - s->scsi_dev[id] =scsi_disk_init (bd,1 , lsi_command_complete, s); + s->scsi_dev[id] =scsi_generic_init (bd,1 , lsi_command_complete, s); +if (s->scsi_dev[id] ==NULL ) + s->scsi_dev[id] =scsi_disk_init (bd,1 , lsi_command_complete, s); }void *lsi_scsi_init (PCIBus *bus,int devfn)Index:
qemu/hw/usb-msd.c
===================================================================hw/usb-msd.c +8
-8@@ -149,9 +149,9 @@ static void usb_msd_copy_data(MSDState * s->data_len -= len;if (s->scsi_len ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } }@@ -204,7 +204,7 @@ static void usb_msd_command_complete(voireturn ; } s->scsi_len = arg; - s->scsi_buf =scsi_get_buf (s->scsi_dev, tag); + s->scsi_buf = s->scsi_dev->get_buf (s->scsi_dev, tag);if (p) {usb_msd_copy_data (s);if (s->usb_len ==0 ) {@@ -342,7 +342,7 @@ static int usb_msd_handle_control(USBDevstatic void usb_msd_cancel_io (USBPacket *p,void *opaque) {MSDState *s = opaque; -scsi_cancel_io (s->scsi_dev, s->tag); + s->scsi_dev->cancel_io (s->scsi_dev, s->tag); s->packet =NULL ; s->scsi_len =0 ; }@@ -390,14 +390,14 @@ static int usb_msd_handle_data(USBDeviceDPRINTF ("Command tag 0x%x flags %08x len %d data %d\n" , s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->residue =0 ; -scsi_send_command (s->scsi_dev, s->tag, cbw.cmd,0 ); + s->scsi_dev->send_command (s->scsi_dev, s->tag, cbw.cmd,0 );/* ??? Should check that USB and SCSI data transfer directions match. */ if (s->residue ==0 ) {if (s->mode ==USB_MSDM_DATAIN ) { -scsi_read_data (s->scsi_dev, s->tag); + s->scsi_dev->read_data (s->scsi_dev, s->tag); }else if (s->mode ==USB_MSDM_DATAOUT ) { -scsi_write_data (s->scsi_dev, s->tag); + s->scsi_dev->write_data (s->scsi_dev, s->tag); } } ret = len;@@ -508,7 +508,7 @@ static void usb_msd_handle_destroy(USBDe {MSDState *s = (MSDState *)dev; -scsi_disk_destroy (s->scsi_dev); + s->scsi_dev->destroy (s->scsi_dev);bdrv_delete (s->bs);qemu_free (s); }Index:
qemu/hw/scsi-generic.c
===================================================================hw/scsi-generic.c new file +667
-0@@ -0,0 +1,667 @@ +/* +* Generic SCSI Device support +* +* Copyright (c) 2007 Bull S.A.S. +* Based on code by Paul Brook +* Based on code by Fabrice Bellard +* +* Written by Laurent Vivier <[email protected]> +* +* This code is licenced under the LGPL. +* +*/ + +#include "qemu-common.h" +#include "block.h" +#include "scsi-disk.h" + +#ifndef __linux__ + +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque) +{ +return NULL ; +} + +#else /* __linux__ */ + +//#define DEBUG_SCSI + +#ifdef DEBUG_SCSI +#define DPRINTF(fmt, args...) \ +do { printf("scsi-generic: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) do {} while(0) +#endif + +#define BADF(fmt, args...) \ +do { fprintf(stderr, "scsi-generic: " fmt , ##args); } while (0) + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <scsi/sg.h> +#include <scsi/scsi.h> + +#define LOAD_UNLOAD 0xa6 +#define SET_CD_SPEED 0xbb +#define BLANK 0xa1 + +#define SCSI_CMD_BUF_SIZE 16 +#define SCSI_SENSE_BUF_SIZE 32 + +#define SG_ERR_DRIVER_TIMEOUT 0x06 +#define SG_ERR_DRIVER_SENSE 0x08 + +#ifndef MAX_UINT +#define MAX_UINT ((unsigned int)-1) +#endif + +typedef struct SCSIRequest { +BlockDriverAIOCB *aiocb; +struct SCSIRequest *next; +SCSIDeviceState *dev; +uint32_t tag; +uint8_t cmd[SCSI_CMD_BUF_SIZE ]; +int cmdlen; +uint8_t *buf; +int buflen; +int len; +sg_io_hdr_t io_header; +}SCSIRequest ; + +struct SCSIDeviceState +{ +SCSIRequest *requests; +BlockDriverState *bdrv; +int blocksize; +int lun; +scsi_completionfn completion; +void *opaque; +int driver_status; +uint8_t sensebuf[SCSI_SENSE_BUF_SIZE ]; +}; + +/* Global pool of SCSIRequest structures. */ +static SCSIRequest *free_requests =NULL ; + +static SCSIRequest *scsi_new_request (SCSIDeviceState *s,uint32_t tag) +{ +SCSIRequest *r; + +if (free_requests) { + r = free_requests; + free_requests = r->next; + }else { + r =qemu_malloc (sizeof (SCSIRequest)); + r->buf =NULL ; + r->buflen =0 ; + } + r->dev = s; + r->tag = tag; +memset (r->cmd,0 ,sizeof (r->cmd)); +memset (&r->io_header,0 ,sizeof (r->io_header)); + r->cmdlen =0 ; + r->len =0 ; + r->aiocb =NULL ; + +/* link */ + + r->next = s->requests; + s->requests = r; +DPRINTF ("scsi_new_request tag=0x%x\n" , tag); +return r; +} + +static void scsi_remove_request (SCSIRequest *r) +{ +SCSIRequest *last; +SCSIDeviceState *s = r->dev; + +DPRINTF ("scsi_remove_request tag=0x%x\n" , r->tag); +if (s->requests == r) { + s->requests = r->next; + }else { + last = s->requests; +while (last && last->next != r) + last = last->next; +if (last) { + last->next = r->next; + }else { +BADF ("Orphaned request\n" ); + } + } + r->next = free_requests; + free_requests = r; +} + +static SCSIRequest *scsi_find_request (SCSIDeviceState *s,uint32_t tag) +{ +SCSIRequest *r; + + r = s->requests; +while (r && r->tag != tag) + r = r->next; + +return r; +} + +/* Helper function for command completion. */ +static void scsi_command_complete (void *opaque,int ret) +{ +SCSIRequest *r = (SCSIRequest *)opaque; +SCSIDeviceState *s = r->dev; +uint32_t tag; +int sense; + +if (r != r->io_header.usr_ptr) { +BADF ("Bad request\n" ); +exit (1 ); + } + + s->driver_status = r->io_header.driver_status; +if (ret !=0 ) + sense =HARDWARE_ERROR ; +else { +if (s->driver_status &SG_ERR_DRIVER_TIMEOUT ) { + sense =HARDWARE_ERROR ; +BADF ("Driver Timeout\n" ); + }else if ((s->driver_status &SG_ERR_DRIVER_SENSE ) ==0 ) + sense =NO_SENSE ; +else + sense = s->sensebuf[2 ] &0x0f ; + } + +DPRINTF ("Command complete 0x%p tag=0x%x sense=%d\n" , r, r->tag, sense); + tag = r->tag; +scsi_remove_request (r); + s->completion (s->opaque,SCSI_REASON_DONE , tag, sense); +} + +/* Cancel a pending data transfer. */ +static void scsi_cancel_io (SCSIDevice *d,uint32_t tag) +{ +DPRINTF ("scsi_cancel_io 0x%x\n" , tag); +SCSIDeviceState *s = d->state; +SCSIRequest *r; +DPRINTF ("Cancel tag=0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (r) { +if (r->aiocb) +bdrv_aio_cancel (r->aiocb); + r->aiocb =NULL ; +scsi_remove_request (r); + } +} + +static int execute_command (BlockDriverState *bdrv, +SCSIRequest *r,int direction, +BlockDriverCompletionFunc *complete) +{ + r->io_header.interface_id ='S' ; + r->io_header.dxfer_direction = direction; + r->io_header.dxfer_len = r->buflen; + r->io_header.dxferp = r->buf; + r->io_header.cmdp = r->cmd; + r->io_header.cmd_len = r->cmdlen; + r->io_header.mx_sb_len =sizeof (r->dev->sensebuf); + r->io_header.sbp = r->dev->sensebuf; + r->io_header.timeout =MAX_UINT ; + r->io_header.usr_ptr = r; + r->io_header.flags |=SG_FLAG_DIRECT_IO ; + +if (bdrv_pwrite (bdrv,-1 , &r->io_header,sizeof (r->io_header)) ==-1 ) { +BADF ("execute_command: write failed ! (%d)\n" , errno); +return -1 ; + } +if (complete ==NULL ) { +int ret; + r->aiocb =NULL ; +while ((ret =bdrv_pread (bdrv,-1 , &r->io_header, +sizeof (r->io_header))) ==-1 && + errno ==EINTR ); +if (ret ==-1 ) { +BADF ("execute_command: read failed !\n" ); +return -1 ; + } +return 0 ; + } + + r->aiocb =bdrv_aio_read (bdrv,0 , (uint8_t *)&r->io_header, + -(int64_t )sizeof (r->io_header), complete, r); +if (r->aiocb ==NULL ) { +BADF ("execute_command: read failed !\n" ); +return -1 ; + } + +return 0 ; +} + +static void scsi_read_complete (void * opaque,int ret) +{ +SCSIRequest *r = (SCSIRequest *)opaque; +SCSIDeviceState *s = r->dev; +int len; + +if (r != r->io_header.usr_ptr) { +BADF ("Bad request\n" ); +exit (1 ); + } + +if (ret) { +DPRINTF ("IO error\n" ); +scsi_command_complete (r, ret); +return ; + } + len = r->io_header.dxfer_len - r->io_header.resid; +DPRINTF ("Data ready tag=0x%x len=%d\n" , r->tag, len); + + r->len =-1 ; + s->completion (s->opaque,SCSI_REASON_DATA , r->tag, len); +} + +/* Read more data from scsi device into buffer. */ +static void scsi_read_data (SCSIDevice *d,uint32_t tag) +{ +SCSIDeviceState *s = d->state; +SCSIRequest *r; +int ret; + +DPRINTF ("scsi_read_data 0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (!r) { +BADF ("Bad read tag 0x%x\n" , tag); +/* ??? This is the wrong error. */ +scsi_command_complete (r, -EINVAL ); +return ; + } + +if (r->len ==-1 ) { +scsi_command_complete (r,0 ); +return ; + } + +if (r->cmd[0 ] ==REQUEST_SENSE && s->driver_status &SG_ERR_DRIVER_SENSE ) + { +memcpy (r->buf, s->sensebuf,16 ); + r->io_header.driver_status =0 ; + r->len =-1 ; + s->completion (s->opaque,SCSI_REASON_DATA , r->tag,16 ); +return ; + } + + ret =execute_command (s->bdrv, r,SG_DXFER_FROM_DEV , scsi_read_complete); +if (ret ==-1 ) { +scsi_command_complete (r, -EINVAL ); +return ; + } +} + +static void scsi_write_complete (void * opaque,int ret) +{ +SCSIRequest *r = (SCSIRequest *)opaque; + +if (r != r->io_header.usr_ptr) { +BADF ("Bad request\n" ); +exit (1 ); + } + +if (ret) { +DPRINTF ("IO error\n" ); +scsi_command_complete (r, ret); +return ; + } + +scsi_command_complete (r, ret); +} + +/* Write data to a scsi device. Returns nonzero on failure. +The transfer may complete asynchronously. */ +static int scsi_write_data (SCSIDevice *d,uint32_t tag) +{ +SCSIDeviceState *s = d->state; +SCSIRequest *r; +int ret; + +DPRINTF ("scsi_write_data 0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (!r) { +BADF ("Bad write tag 0x%x\n" , tag); +/* ??? This is the wrong error. */ +scsi_command_complete (r, -EINVAL ); +return 0 ; + } + +if (r->len ==0 ) { + r->len = r->buflen; + s->completion (s->opaque,SCSI_REASON_DATA , r->tag, r->len); +return 0 ; + } + + ret =execute_command (s->bdrv, r,SG_DXFER_TO_DEV , scsi_write_complete); +if (ret ==-1 ) { +scsi_command_complete (r, -EINVAL ); +return 1 ; + } + +return 0 ; +} + +/* Return a pointer to the data buffer. */ +static uint8_t *scsi_get_buf (SCSIDevice *d,uint32_t tag) +{ +SCSIDeviceState *s = d->state; +SCSIRequest *r; +DPRINTF ("scsi_get_buf 0x%x\n" , tag); + r =scsi_find_request (s, tag); +if (!r) { +BADF ("Bad buffer tag 0x%x\n" , tag); +return NULL ; + } +return r->buf; +} + +static int scsi_length (uint8_t *cmd,int blocksize,int *cmdlen,uint32_t *len) +{ +switch (cmd[0 ] >>5 ) { +case 0 : + *len = cmd[4 ]; + *cmdlen =6 ; +break ; +case 1 : +case 2 : + *len = cmd[8 ] | (cmd[7 ] <<8 ); + *cmdlen =10 ; +break ; +case 4 : + *len = cmd[13 ] | (cmd[12 ] <<8 ) | (cmd[11 ] <<16 ) | (cmd[10 ] <<24 ); + *cmdlen =16 ; +break ; +case 5 : + *len = cmd[9 ] | (cmd[8 ] <<8 ) | (cmd[7 ] <<16 ) | (cmd[6 ] <<24 ); + *cmdlen =12 ; +break ; +default : +return -1 ; + } + +switch (cmd[0 ]) { +case TEST_UNIT_READY : +case REZERO_UNIT : +case START_STOP : +case SEEK_6 : +case WRITE_FILEMARKS : +case SPACE : +case ERASE : +case ALLOW_MEDIUM_REMOVAL : +case VERIFY : +case SEEK_10 : +case SYNCHRONIZE_CACHE : +case LOCK_UNLOCK_CACHE : +case LOAD_UNLOAD : +case SET_CD_SPEED : +case SET_LIMITS : +case WRITE_LONG : +case MOVE_MEDIUM : +case UPDATE_BLOCK : + *len =0 ; +break ; +case MODE_SENSE : +break ; +case WRITE_SAME : + *len =1 ; +break ; +case READ_CAPACITY : + *len =8 ; +break ; +case READ_BLOCK_LIMITS : + *len =6 ; +break ; +case READ_POSITION : + *len =20 ; +break ; +case SEND_VOLUME_TAG : + *len *=40 ; +break ; +case MEDIUM_SCAN : + *len *=8 ; +break ; +case WRITE_VERIFY : +case WRITE_6 : +case WRITE_10 : +case WRITE_12 : +case WRITE_VERIFY_12 : + *len *= blocksize; +break ; +case READ_6 : +case READ_REVERSE : +case RECOVER_BUFFERED_DATA : +case READ_10 : +case READ_12 : + *len *= blocksize; +break ; + } +return 0 ; +} + +static int is_write (int command) +{ +switch (command) { +case COPY : +case COPY_VERIFY : +case COMPARE : +case CHANGE_DEFINITION : +case LOG_SELECT : +case MODE_SELECT : +case MODE_SELECT_10 : +case SEND_DIAGNOSTIC : +case WRITE_BUFFER : +case FORMAT_UNIT : +case REASSIGN_BLOCKS : +case RESERVE : +case SEARCH_EQUAL : +case SEARCH_HIGH : +case SEARCH_LOW : +case WRITE_6 : +case WRITE_10 : +case WRITE_VERIFY : +case UPDATE_BLOCK : +case WRITE_LONG : +case WRITE_SAME : +case SEARCH_HIGH_12 : +case SEARCH_EQUAL_12 : +case SEARCH_LOW_12 : +case WRITE_12 : +case WRITE_VERIFY_12 : +case SET_WINDOW : +case MEDIUM_SCAN : +case SEND_VOLUME_TAG : +case WRITE_LONG_2 : +return 1 ; + } +return 0 ; +} + +/* Execute a scsi command. Returns the length of the data expected by the +command. This will be Positive for data transfers from the device +(eg. disk reads), negative for transfers to the device (eg. disk writes), +and zero if the command does not transfer any data. */ + +static int32_t scsi_send_command (SCSIDevice *d,uint32_t tag, +uint8_t *cmd,int lun) +{ +SCSIDeviceState *s = d->state; +uint32_t len; +int cmdlen; +SCSIRequest *r; +int ret; + +/* ??? Tags are not unique for different luns. We only implement a +single lun, so this should not matter. */ + +if (lun != s->lun || (cmd[1 ] >>5 ) != s->lun) { +DPRINTF ("Unimplemented LUN %d\n" , lun ? lun : cmd[1 ] >>5 ); + s->completion (s->opaque,SCSI_REASON_DONE , tag,HARDWARE_ERROR ); +return 0 ; + } + +if (scsi_length (cmd, s->blocksize, &cmdlen, &len) ==-1 ) { +BADF ("Unsupported command length, command %x\n" , cmd[0 ]); +return 0 ; + } + +DPRINTF ("Command: lun=%d tag=0x%x data=0x%02x len %d\n" , lun, tag, + cmd[0 ], len); + + r =scsi_find_request (s, tag); +if (r) { +BADF ("Tag 0x%x already in use %p\n" , tag, r); +scsi_cancel_io (d, tag); + } + r =scsi_new_request (s, tag); + +memcpy (r->cmd, cmd, cmdlen); + r->cmdlen = cmdlen; + +if (len ==0 ) { +if (r->buf !=NULL ) +free (r->buf); + r->buflen =0 ; + r->buf =NULL ; + ret =execute_command (s->bdrv, r,SG_DXFER_NONE , scsi_command_complete); +if (ret ==-1 ) { +scsi_command_complete (r, -EINVAL ); +return 0 ; + } +return 0 ; + } + +if (r->buflen != len) { +if (r->buf !=NULL ) +free (r->buf); + r->buf =qemu_memalign (512 , len); + r->buflen = len; + } + +memset (r->buf,0 , r->buflen); + r->len = len; +if (is_write (cmd[0 ])) { + r->len =0 ; +return -len; + } + +return len; +} + +static int get_blocksize (BlockDriverState *bdrv) +{ +uint8_t cmd[10 ]; +uint8_t buf[8 ]; +uint8_t sensebuf[8 ]; +sg_io_hdr_t io_header; +int ret; + +memset (cmd,sizeof (cmd),0 ); +memset (buf,sizeof (buf),0 ); + cmd[0 ] =READ_CAPACITY ; + +memset (&io_header,0 ,sizeof (io_header)); + io_header.interface_id ='S' ; + io_header.dxfer_direction =SG_DXFER_FROM_DEV ; + io_header.dxfer_len =sizeof (buf); + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len =sizeof (cmd); + io_header.mx_sb_len =sizeof (sensebuf); + io_header.sbp = sensebuf; + io_header.timeout =6000 ;/* XXX */ + + ret =bdrv_pwrite (bdrv,-1 , &io_header,sizeof (io_header)); +if (ret ==-1 ) +return -1 ; + +while ((ret =bdrv_pread (bdrv,-1 , &io_header,sizeof (io_header))) ==-1 && + errno ==EINTR ); + +if (ret ==-1 ) +return -1 ; + +return (buf[4 ] <<24 ) | (buf[5 ] <<16 ) | (buf[6 ] <<8 ) | buf[7 ]; +} + +static void scsi_destroy (SCSIDevice *d) +{ +SCSIRequest *r, *n; + + r = d->state->requests; +while (r) { + n = r->next; +qemu_free (r); + r = n; + } + + r = free_requests; +while (r) { + n = r->next; +qemu_free (r); + r = n; + } + +qemu_free (d->state); +qemu_free (d); +} + +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque) +{ +int sg_version; +SCSIDevice *d; +SCSIDeviceState *s; +struct sg_scsi_id scsiid; + +/* check we are really using a /dev/sg* file */ + +if (!bdrv_is_sg (bdrv)) +return NULL ; + +/* check we are using a driver managing SG_IO (version 3 and after */ + +if (bdrv_ioctl (bdrv,SG_GET_VERSION_NUM , (char *)&sg_version) <0 || + sg_version <30000 ) +return NULL ; + +/* get LUN of the /dev/sg? */ + +if (bdrv_ioctl (bdrv,SG_GET_SCSI_ID , &scsiid)) +return NULL ; + +/* define device state */ + + s = (SCSIDeviceState *)qemu_mallocz (sizeof (SCSIDeviceState)); + s->bdrv = bdrv; + s->requests =NULL ; + s->completion = completion; + s->opaque = opaque; + s->lun = scsiid.lun; + s->blocksize =get_blocksize (s->bdrv); + s->driver_status =0 ; +memset (s->sensebuf,0 ,sizeof (s->sensebuf)); +/* removable media returns 0 if not present */ +if (s->blocksize <=0 ) + s->blocksize =2048 ; + +/* define function to manage device */ + + d = (SCSIDevice *)qemu_mallocz (sizeof (SCSIDevice)); + d->state = s; + d->destroy = scsi_destroy; + d->send_command = scsi_send_command; + d->read_data = scsi_read_data; + d->write_data = scsi_write_data; + d->cancel_io = scsi_cancel_io; + d->get_buf = scsi_get_buf; + +return d; +} +#endif /* __linux__ */ Index: qemu/Makefile
===================================================================Makefile +1
-0@@ -56,6 +56,7 @@ OBJS+=irq.o OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o OBJS+=scsi-disk.o cdrom.o +OBJS+=scsi-generic.o OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o OBJS+=sd.o ssi-sd.oIndex:
qemu/hw/scsi-disk.h
===================================================================hw/scsi-disk.h +16
-13@@ -7,24 +7,27 @@ enum scsi_reason {SCSI_REASON_DATA /* Transfer complete, more data required. */ }; +typedef struct SCSIDeviceState SCSIDeviceState ;typedef struct SCSIDevice SCSIDevice ;typedef void (*scsi_completionfn )(void *opaque,int reason,uint32_t tag,uint32_t arg); -SCSIDevice *scsi_disk_init (BlockDriverState *bdrv, -int tcq, -scsi_completionfn completion, -void *opaque); -void scsi_disk_destroy (SCSIDevice *s); +struct SCSIDevice +{ +SCSIDeviceState *state; +void (*destroy)(SCSIDevice *s); +int32_t (*send_command)(SCSIDevice *s,uint32_t tag,uint8_t *buf, +int lun); +void (*read_data)(SCSIDevice *s,uint32_t tag); +int (*write_data)(SCSIDevice *s,uint32_t tag); +void (*cancel_io)(SCSIDevice *s,uint32_t tag); +uint8_t *(*get_buf)(SCSIDevice *s,uint32_t tag); +}; -int32_t scsi_send_command (SCSIDevice *s,uint32_t tag,uint8_t *buf,int lun); -/* SCSI data transfers are asynchrnonous. However, unlike the block IO -layer the completion routine may be called directly by -scsi_{read,write}_data. */ -void scsi_read_data (SCSIDevice *s,uint32_t tag); -int scsi_write_data (SCSIDevice *s,uint32_t tag); -void scsi_cancel_io (SCSIDevice *s,uint32_t tag); -uint8_t *scsi_get_buf (SCSIDevice *s,uint32_t tag); +SCSIDevice *scsi_disk_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque); +SCSIDevice *scsi_generic_init (BlockDriverState *bdrv,int tcq, +scsi_completionfn completion,void *opaque);/* cdrom.c */ int cdrom_read_toc (int nb_sectors,uint8_t *buf,int msf,int start_track);
Signature · 3 lines
------------- [email protected] -------------- "Any sufficiently advanced technology is indistinguishable from magic." - Arthur C. Clarke