* [Qemu-devel] [PATCH 1/2] SCSI cleanup
2007-12-07 14:23 [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough Laurent Vivier
@ 2007-12-07 14:23 ` Laurent Vivier
2007-12-07 14:23 ` [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3) Laurent Vivier
2007-12-09 23:16 ` [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough Thiemo Seufer
1 sibling, 1 reply; 8+ messages in thread
From: Laurent Vivier @ 2007-12-07 14:23 UTC (permalink / raw)
Cc: qemu-devel
This patch reworks the interface between SCSI controller and SCSI device.
It allows to connect something else than scsi-disk.c to the SCSI controller.
It is needed to use SCSI passthrough patch.
Laurent
---
hw/esp.c | 16 +++++++-------
hw/lsi53c895a.c | 41 ++++++++++++++++++++++++-------------
hw/scsi-disk.c | 62 ++++++++++++++++++++++++++++++++++----------------------
hw/scsi-disk.h | 27 ++++++++++++------------
hw/usb-msd.c | 16 +++++++-------
5 files changed, 95 insertions(+), 67 deletions(-)
Index: qemu/hw/scsi-disk.c
===================================================================
--- qemu.orig/hw/scsi-disk.c 2007-12-07 09:49:32.000000000 +0100
+++ qemu/hw/scsi-disk.c 2007-12-07 09:49:40.000000000 +0100
@@ -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(SCS
static 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, uint3
static 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, uint3
static 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
===================================================================
--- qemu.orig/hw/esp.c 2007-12-07 09:48:46.000000000 +0100
+++ qemu/hw/esp.c 2007-12-07 09:49:40.000000000 +0100
@@ -165,7 +165,7 @@ static int get_cmd(ESPState *s, uint8_t
if (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_t
DPRINTF("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,7 +611,7 @@ 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. */
Index: qemu/hw/lsi53c895a.c
===================================================================
--- qemu.orig/hw/lsi53c895a.c 2007-12-07 09:48:46.000000000 +0100
+++ qemu/hw/lsi53c895a.c 2007-12-07 09:49:40.000000000 +0100
@@ -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 *o
if (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);
+ }
}
}
@@ -1822,7 +1835,7 @@ 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);
Index: qemu/hw/usb-msd.c
===================================================================
--- qemu.orig/hw/usb-msd.c 2007-12-07 09:48:46.000000000 +0100
+++ qemu/hw/usb-msd.c 2007-12-07 09:49:40.000000000 +0100
@@ -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(voi
return;
}
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(USBDev
static 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(USBDevice
DPRINTF("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-disk.h
===================================================================
--- qemu.orig/hw/scsi-disk.h 2007-12-07 09:48:46.000000000 +0100
+++ qemu/hw/scsi-disk.h 2007-12-07 09:49:40.000000000 +0100
@@ -7,24 +7,25 @@ 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);
/* cdrom.c */
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3)
2007-12-07 14:23 ` [Qemu-devel] [PATCH 1/2] SCSI cleanup Laurent Vivier
@ 2007-12-07 14:23 ` Laurent Vivier
2007-12-07 15:22 ` Paul Brook
0 siblings, 1 reply; 8+ messages in thread
From: Laurent Vivier @ 2007-12-07 14:23 UTC (permalink / raw)
Cc: qemu-devel
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.
v3 has moved SCSI clenup to a separate patch.
Laurent
---
Makefile | 1
block-raw.c | 23 +
block.c | 16 +
block.h | 2
block_int.h | 4
hw/esp.c | 4
hw/lsi53c895a.c | 8
hw/scsi-disk.h | 2
hw/scsi-generic.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 722 insertions(+), 5 deletions(-)
Index: qemu/block-raw.c
===================================================================
--- qemu.orig/block-raw.c 2007-12-07 13:40:36.000000000 +0100
+++ qemu/block-raw.c 2007-12-07 14:22:05.000000000 +0100
@@ -152,7 +152,7 @@ static int raw_pread(BlockDriverState *b
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 <= 10) {
DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
@@ -205,7 +205,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) [%"
@@ -388,7 +388,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 +683,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 +864,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 +892,10 @@ static int raw_set_locked(BlockDriverSta
return -ENOTSUP;
}
+static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+ return -ENOTSUP;
+}
#endif /* !linux */
BlockDriver bdrv_host_device = {
@@ -907,6 +922,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
===================================================================
--- qemu.orig/block.c 2007-12-07 13:40:36.000000000 +0100
+++ qemu/block.c 2007-12-07 13:40:41.000000000 +0100
@@ -796,6 +796,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 +1409,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
===================================================================
--- qemu.orig/block.h 2007-12-07 13:40:36.000000000 +0100
+++ qemu/block.h 2007-12-07 13:40:41.000000000 +0100
@@ -120,6 +120,7 @@ int bdrv_get_type_hint(BlockDriverState
int 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(BlockDriverStat
int 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
===================================================================
--- qemu.orig/block_int.h 2007-12-07 13:40:36.000000000 +0100
+++ qemu/block_int.h 2007-12-07 13:40:41.000000000 +0100
@@ -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/esp.c
===================================================================
--- qemu.orig/hw/esp.c 2007-12-07 13:40:37.000000000 +0100
+++ qemu/hw/esp.c 2007-12-07 13:40:41.000000000 +0100
@@ -615,7 +615,9 @@ void esp_scsi_attach(void *opaque, Block
}
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
===================================================================
--- qemu.orig/hw/lsi53c895a.c 2007-12-07 13:40:36.000000000 +0100
+++ qemu/hw/lsi53c895a.c 2007-12-07 13:40:41.000000000 +0100
@@ -1236,6 +1236,8 @@ static uint8_t lsi_reg_readb(LSIState *s
return s->sdid;
case 0x07: /* GPREG0 */
return 0x7f;
+ case 0x08: /* Revision ID */
+ return 0x00;
case 0xa: /* SSID */
return s->ssid;
case 0xb: /* SBCL */
@@ -1281,6 +1283,8 @@ static uint8_t lsi_reg_readb(LSIState *s
return 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] */
@@ -1838,7 +1842,9 @@ void lsi_scsi_attach(void *opaque, Block
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/scsi-generic.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ qemu/hw/scsi-generic.c 2007-12-07 13:41:35.000000000 +0100
@@ -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 <Laurent.Vivier@bull.net>
+ *
+ * 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
===================================================================
--- qemu.orig/Makefile 2007-12-07 13:40:36.000000000 +0100
+++ qemu/Makefile 2007-12-07 13:40:41.000000000 +0100
@@ -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
Index: qemu/hw/scsi-disk.h
===================================================================
--- qemu.orig/hw/scsi-disk.h 2007-12-07 13:40:37.000000000 +0100
+++ qemu/hw/scsi-disk.h 2007-12-07 13:40:41.000000000 +0100
@@ -26,6 +26,8 @@ struct SCSIDevice
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);
^ permalink raw reply [flat|nested] 8+ messages in thread
* [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough
@ 2007-12-07 14:23 Laurent Vivier
2007-12-07 14:23 ` [Qemu-devel] [PATCH 1/2] SCSI cleanup Laurent Vivier
2007-12-09 23:16 ` [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough Thiemo Seufer
0 siblings, 2 replies; 8+ messages in thread
From: Laurent Vivier @ 2007-12-07 14:23 UTC (permalink / raw)
Cc: qemu-devel
This series of patches allows to connect real SCSI device to the virtual
SCSI controller of Qemu using the SCSI Generic interface (/dev/sg)
for instance:
qemu -hda my_disk.qcow2 -drive file=/dev/sg3,if=scsi
[PATCH 1/2] SCSI cleanup
This patch reworks the interface between SCSI controller and SCSI device.
[PATCH 2/2] Real SCSI device passthrough (v3)
This patch allows to connect the virtual SCSI interface of Qemu to
a real SCSI device of the host.
Laurent
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3)
2007-12-07 14:23 ` [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3) Laurent Vivier
@ 2007-12-07 15:22 ` Paul Brook
2007-12-07 15:29 ` Laurent Vivier
0 siblings, 1 reply; 8+ messages in thread
From: Paul Brook @ 2007-12-07 15:22 UTC (permalink / raw)
To: qemu-devel, Laurent Vivier
On Friday 07 December 2007, Laurent Vivier wrote:
> - 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;
Ugly hacks like this need at least a decent comment.
Paul
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3)
2007-12-07 15:22 ` Paul Brook
@ 2007-12-07 15:29 ` Laurent Vivier
2007-12-07 16:00 ` Anthony Liguori
0 siblings, 1 reply; 8+ messages in thread
From: Laurent Vivier @ 2007-12-07 15:29 UTC (permalink / raw)
To: Paul Brook; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 823 bytes --]
Le vendredi 07 décembre 2007 à 15:22 +0000, Paul Brook a écrit :
> On Friday 07 December 2007, Laurent Vivier wrote:
> > - 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;
>
> Ugly hacks like this need at least a decent comment.
Yes, you're right but I'm always wondering if it is the good way to do
what I want: read a number of bytes instead of a number of sectors.
Perhaps I should define a new function in the qemu AIO interface ?
There is another ugly hack, did you find it ? ;-)
Laurent
--
------------- Laurent.Vivier@bull.net --------------
"Any sufficiently advanced technology is
indistinguishable from magic." - Arthur C. Clarke
[-- Attachment #2: Ceci est une partie de message numériquement signée --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3)
2007-12-07 15:29 ` Laurent Vivier
@ 2007-12-07 16:00 ` Anthony Liguori
2007-12-07 16:13 ` Laurent Vivier
0 siblings, 1 reply; 8+ messages in thread
From: Anthony Liguori @ 2007-12-07 16:00 UTC (permalink / raw)
To: qemu-devel; +Cc: Paul Brook
Laurent Vivier wrote:
> Le vendredi 07 décembre 2007 à 15:22 +0000, Paul Brook a écrit :
>
>> On Friday 07 December 2007, Laurent Vivier wrote:
>>
>>> - 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;
>>>
>> Ugly hacks like this need at least a decent comment.
>>
>
> Yes, you're right but I'm always wondering if it is the good way to do
> what I want: read a number of bytes instead of a number of sectors.
> Perhaps I should define a new function in the qemu AIO interface ?
>
I think the crux of the problem is that you're using the BlockDriver API
for something that isn't actually a block driver.
Regards,
Anthony Liguori
> There is another ugly hack, did you find it ? ;-)
>
> Laurent
>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3)
2007-12-07 16:00 ` Anthony Liguori
@ 2007-12-07 16:13 ` Laurent Vivier
0 siblings, 0 replies; 8+ messages in thread
From: Laurent Vivier @ 2007-12-07 16:13 UTC (permalink / raw)
To: qemu-devel; +Cc: Paul Brook
[-- Attachment #1: Type: text/plain, Size: 1326 bytes --]
Le vendredi 07 décembre 2007 à 10:00 -0600, Anthony Liguori a écrit :
> Laurent Vivier wrote:
> > Le vendredi 07 décembre 2007 à 15:22 +0000, Paul Brook a écrit :
> >
> >> On Friday 07 December 2007, Laurent Vivier wrote:
> >>
> >>> - 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;
> >>>
> >> Ugly hacks like this need at least a decent comment.
> >>
> >
> > Yes, you're right but I'm always wondering if it is the good way to do
> > what I want: read a number of bytes instead of a number of sectors.
> > Perhaps I should define a new function in the qemu AIO interface ?
> >
>
> I think the crux of the problem is that you're using the BlockDriver API
> for something that isn't actually a block driver.
Yes, I know, but I don't want to rewrite all block-raw.c just to change
the granularity of the I/O access. It is the best solution I found, if
you have some better ideas (and I know you have lot of good ideas), I
take them.
Regards,
Laurent
--
------------- Laurent.Vivier@bull.net --------------
"Any sufficiently advanced technology is
indistinguishable from magic." - Arthur C. Clarke
[-- Attachment #2: Ceci est une partie de message numériquement signée --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough
2007-12-07 14:23 [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough Laurent Vivier
2007-12-07 14:23 ` [Qemu-devel] [PATCH 1/2] SCSI cleanup Laurent Vivier
@ 2007-12-09 23:16 ` Thiemo Seufer
1 sibling, 0 replies; 8+ messages in thread
From: Thiemo Seufer @ 2007-12-09 23:16 UTC (permalink / raw)
To: Laurent Vivier; +Cc: qemu-devel
Laurent Vivier wrote:
>
> This series of patches allows to connect real SCSI device to the virtual
> SCSI controller of Qemu using the SCSI Generic interface (/dev/sg)
>
> for instance:
>
> qemu -hda my_disk.qcow2 -drive file=/dev/sg3,if=scsi
Please update also the documentation to mention this example.
> [PATCH 1/2] SCSI cleanup
>
> This patch reworks the interface between SCSI controller and SCSI device.
>
> [PATCH 2/2] Real SCSI device passthrough (v3)
>
> This patch allows to connect the virtual SCSI interface of Qemu to
> a real SCSI device of the host.
>
> Laurent
>
>
>
>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2007-12-09 23:16 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-12-07 14:23 [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough Laurent Vivier
2007-12-07 14:23 ` [Qemu-devel] [PATCH 1/2] SCSI cleanup Laurent Vivier
2007-12-07 14:23 ` [Qemu-devel] [PATCH 2/2] Real SCSI device passthrough (v3) Laurent Vivier
2007-12-07 15:22 ` Paul Brook
2007-12-07 15:29 ` Laurent Vivier
2007-12-07 16:00 ` Anthony Liguori
2007-12-07 16:13 ` Laurent Vivier
2007-12-09 23:16 ` [Qemu-devel] [PATCH 0/2] Real SCSI device passthrough Thiemo Seufer
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.