Rearrange SCSI disk emulation code.

Add USB mass storage device emulation.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1940 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
pbrook 2006-05-25 23:58:51 +00:00
parent e6f3e5e016
commit 2e5d83bbef
13 changed files with 1080 additions and 470 deletions

View File

@ -306,8 +306,11 @@ ifdef CONFIG_ADLIB
SOUND_HW += fmopl.o adlib.o
endif
# SCSI layer
VL_OBJS+= scsi-disk.o cdrom.o
# USB layer
VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o
VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o
# PCI network cards
VL_OBJS+= ne2000.o rtl8139.o

156
hw/cdrom.c Normal file
View File

@ -0,0 +1,156 @@
/*
* QEMU ATAPI CD-ROM Emulator
*
* Copyright (c) 2006 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
here. */
#include <vl.h>
static void lba_to_msf(uint8_t *buf, int lba)
{
lba += 150;
buf[0] = (lba / 75) / 60;
buf[1] = (lba / 75) % 60;
buf[2] = lba % 75;
}
/* same toc as bochs. Return -1 if error or the toc length */
/* XXX: check this */
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
{
uint8_t *q;
int len;
if (start_track > 1 && start_track != 0xaa)
return -1;
q = buf + 2;
*q++ = 1; /* first session */
*q++ = 1; /* last session */
if (start_track <= 1) {
*q++ = 0; /* reserved */
*q++ = 0x14; /* ADR, control */
*q++ = 1; /* track number */
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, 0);
q += 3;
} else {
/* sector 0 */
cpu_to_be32wu((uint32_t *)q, 0);
q += 4;
}
}
/* lead out track */
*q++ = 0; /* reserved */
*q++ = 0x16; /* ADR, control */
*q++ = 0xaa; /* track number */
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, nb_sectors);
q += 3;
} else {
cpu_to_be32wu((uint32_t *)q, nb_sectors);
q += 4;
}
len = q - buf;
cpu_to_be16wu((uint16_t *)buf, len - 2);
return len;
}
/* mostly same info as PearPc */
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
{
uint8_t *q;
int len;
q = buf + 2;
*q++ = 1; /* first session */
*q++ = 1; /* last session */
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa0; /* lead-in */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
*q++ = 0;
*q++ = 1; /* first track */
*q++ = 0x00; /* disk type */
*q++ = 0x00;
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa1;
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
*q++ = 0;
*q++ = 1; /* last track */
*q++ = 0x00;
*q++ = 0x00;
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa2; /* lead-out */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, nb_sectors);
q += 3;
} else {
cpu_to_be32wu((uint32_t *)q, nb_sectors);
q += 4;
}
*q++ = 1; /* session number */
*q++ = 0x14; /* ADR, control */
*q++ = 0; /* track number */
*q++ = 1; /* point */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
if (msf) {
*q++ = 0;
lba_to_msf(q, 0);
q += 3;
} else {
*q++ = 0;
*q++ = 0;
*q++ = 0;
*q++ = 0;
}
len = q - buf;
cpu_to_be16wu((uint16_t *)buf, len - 2);
return len;
}

417
hw/esp.c
View File

@ -38,17 +38,14 @@ do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level)
#define ESPDMA_REGS 4
#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
#define ESP_MAXREG 0x3f
#define TI_BUFSZ 1024*1024 // XXX
#define TI_BUFSZ 32
#define DMA_VER 0xa0000000
#define DMA_INTR 1
#define DMA_INTREN 0x10
#define DMA_WRITE_MEM 0x100
#define DMA_LOADED 0x04000000
typedef struct ESPState ESPState;
typedef int ESPDMAFunc(ESPState *s,
target_phys_addr_t phys_addr,
int transfer_size1);
struct ESPState {
BlockDriverState **bd;
uint8_t rregs[ESP_MAXREG];
@ -57,12 +54,10 @@ struct ESPState {
uint32_t espdmaregs[ESPDMA_REGS];
uint32_t ti_size;
uint32_t ti_rptr, ti_wptr;
int ti_dir;
uint8_t ti_buf[TI_BUFSZ];
int dma;
ESPDMAFunc *dma_cb;
int64_t offset, len;
int target;
SCSIDevice *scsi_dev[MAX_DISKS];
SCSIDevice *current_dev;
};
#define STAT_DO 0x00
@ -83,195 +78,33 @@ struct ESPState {
#define SEQ_0 0x0
#define SEQ_CD 0x4
/* XXX: stolen from ide.c, move to common ATAPI/SCSI library */
static void lba_to_msf(uint8_t *buf, int lba)
{
lba += 150;
buf[0] = (lba / 75) / 60;
buf[1] = (lba / 75) % 60;
buf[2] = lba % 75;
}
static inline void cpu_to_ube16(uint8_t *buf, int val)
{
buf[0] = val >> 8;
buf[1] = val;
}
static inline void cpu_to_ube32(uint8_t *buf, unsigned int val)
{
buf[0] = val >> 24;
buf[1] = val >> 16;
buf[2] = val >> 8;
buf[3] = val;
}
/* same toc as bochs. Return -1 if error or the toc length */
/* XXX: check this */
static int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
{
uint8_t *q;
int len;
if (start_track > 1 && start_track != 0xaa)
return -1;
q = buf + 2;
*q++ = 1; /* first session */
*q++ = 1; /* last session */
if (start_track <= 1) {
*q++ = 0; /* reserved */
*q++ = 0x14; /* ADR, control */
*q++ = 1; /* track number */
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, 0);
q += 3;
} else {
/* sector 0 */
cpu_to_ube32(q, 0);
q += 4;
}
}
/* lead out track */
*q++ = 0; /* reserved */
*q++ = 0x16; /* ADR, control */
*q++ = 0xaa; /* track number */
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, nb_sectors);
q += 3;
} else {
cpu_to_ube32(q, nb_sectors);
q += 4;
}
len = q - buf;
cpu_to_ube16(buf, len - 2);
return len;
}
/* mostly same info as PearPc */
static int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf,
int session_num)
{
uint8_t *q;
int len;
q = buf + 2;
*q++ = 1; /* first session */
*q++ = 1; /* last session */
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa0; /* lead-in */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
*q++ = 0;
*q++ = 1; /* first track */
*q++ = 0x00; /* disk type */
*q++ = 0x00;
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa1;
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
*q++ = 0;
*q++ = 1; /* last track */
*q++ = 0x00;
*q++ = 0x00;
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa2; /* lead-out */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, nb_sectors);
q += 3;
} else {
cpu_to_ube32(q, nb_sectors);
q += 4;
}
*q++ = 1; /* session number */
*q++ = 0x14; /* ADR, control */
*q++ = 0; /* track number */
*q++ = 1; /* point */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
if (msf) {
*q++ = 0;
lba_to_msf(q, 0);
q += 3;
} else {
*q++ = 0;
*q++ = 0;
*q++ = 0;
*q++ = 0;
}
len = q - buf;
cpu_to_ube16(buf, len - 2);
return len;
}
static int esp_write_dma_cb(ESPState *s,
target_phys_addr_t phys_addr,
int transfer_size1)
{
int len;
if (bdrv_get_type_hint(s->bd[s->target]) == BDRV_TYPE_CDROM) {
len = transfer_size1/2048;
} else {
len = transfer_size1/512;
}
DPRINTF("Write callback (offset %lld len %lld size %d trans_size %d)\n",
s->offset, s->len, s->ti_size, transfer_size1);
bdrv_write(s->bd[s->target], s->offset, s->ti_buf+s->ti_rptr, len);
s->offset+=len;
return 0;
}
static void handle_satn(ESPState *s)
{
uint8_t buf[32];
uint32_t dmaptr, dmalen;
unsigned int i;
int64_t nb_sectors;
int target;
int32_t datalen;
dmalen = s->wregs[0] | (s->wregs[1] << 8);
target = s->wregs[4] & 7;
DPRINTF("Select with ATN len %d target %d\n", dmalen, target);
if (s->dma) {
dmaptr = iommu_translate(s->espdmaregs[1]);
DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr);
DPRINTF("DMA Direction: %c, addr 0x%8.8x\n",
s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', dmaptr);
cpu_physical_memory_read(dmaptr, buf, dmalen);
} else {
buf[0] = 0;
memcpy(&buf[1], s->ti_buf, dmalen);
dmalen++;
}
for (i = 0; i < dmalen; i++) {
DPRINTF("Command %2.2x\n", buf[i]);
}
s->ti_dir = 0;
s->ti_size = 0;
s->ti_rptr = 0;
s->ti_wptr = 0;
if (target >= 4 || !s->bd[target]) { // No such drive
if (target >= 4 || !s->scsi_dev[target]) {
// No such drive
s->rregs[4] = STAT_IN;
s->rregs[5] = INTR_DC;
s->rregs[6] = SEQ_0;
@ -279,141 +112,20 @@ static void handle_satn(ESPState *s)
pic_set_irq(s->irq, 1);
return;
}
switch (buf[1]) {
case 0x0:
DPRINTF("Test Unit Ready (len %d)\n", buf[5]);
break;
case 0x12:
DPRINTF("Inquiry (len %d)\n", buf[5]);
memset(s->ti_buf, 0, 36);
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
s->ti_buf[0] = 5;
memcpy(&s->ti_buf[16], "QEMU CDROM ", 16);
} else {
s->ti_buf[0] = 0;
memcpy(&s->ti_buf[16], "QEMU HARDDISK ", 16);
}
memcpy(&s->ti_buf[8], "QEMU ", 8);
s->ti_buf[2] = 1;
s->ti_buf[3] = 2;
s->ti_buf[4] = 32;
s->ti_dir = 1;
s->ti_size = 36;
break;
case 0x1a:
DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[3], buf[5]);
break;
case 0x25:
DPRINTF("Read Capacity (len %d)\n", buf[5]);
memset(s->ti_buf, 0, 8);
bdrv_get_geometry(s->bd[target], &nb_sectors);
s->ti_buf[0] = (nb_sectors >> 24) & 0xff;
s->ti_buf[1] = (nb_sectors >> 16) & 0xff;
s->ti_buf[2] = (nb_sectors >> 8) & 0xff;
s->ti_buf[3] = nb_sectors & 0xff;
s->ti_buf[4] = 0;
s->ti_buf[5] = 0;
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM)
s->ti_buf[6] = 8; // sector size 2048
else
s->ti_buf[6] = 2; // sector size 512
s->ti_buf[7] = 0;
s->ti_dir = 1;
s->ti_size = 8;
break;
case 0x28:
{
int64_t offset, len;
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4;
len = ((buf[8] << 8) | buf[9]) * 4;
s->ti_size = len * 2048;
} else {
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
len = (buf[8] << 8) | buf[9];
s->ti_size = len * 512;
}
DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len);
if (s->ti_size > TI_BUFSZ) {
DPRINTF("size too large %d\n", s->ti_size);
}
bdrv_read(s->bd[target], offset, s->ti_buf, len);
// XXX error handling
s->ti_dir = 1;
s->ti_rptr = 0;
break;
}
case 0x2a:
{
int64_t offset, len;
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4;
len = ((buf[8] << 8) | buf[9]) * 4;
s->ti_size = len * 2048;
} else {
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
len = (buf[8] << 8) | buf[9];
s->ti_size = len * 512;
}
DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len);
if (s->ti_size > TI_BUFSZ) {
DPRINTF("size too large %d\n", s->ti_size);
}
s->dma_cb = esp_write_dma_cb;
s->offset = offset;
s->len = len;
s->target = target;
s->ti_rptr = 0;
// XXX error handling
s->ti_dir = 0;
break;
}
case 0x43:
{
int start_track, format, msf, len;
msf = buf[2] & 2;
format = buf[3] & 0xf;
start_track = buf[7];
bdrv_get_geometry(s->bd[target], &nb_sectors);
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
switch(format) {
case 0:
len = cdrom_read_toc(nb_sectors, buf, msf, start_track);
if (len < 0)
goto error_cmd;
s->ti_size = len;
break;
case 1:
/* multi session : only a single session defined */
memset(buf, 0, 12);
buf[1] = 0x0a;
buf[2] = 0x01;
buf[3] = 0x01;
s->ti_size = 12;
break;
case 2:
len = cdrom_read_toc_raw(nb_sectors, buf, msf, start_track);
if (len < 0)
goto error_cmd;
s->ti_size = len;
break;
default:
error_cmd:
DPRINTF("Read TOC error\n");
// XXX error handling
break;
}
s->ti_dir = 1;
break;
s->current_dev = s->scsi_dev[target];
datalen = scsi_send_command(s->current_dev, 0, &buf[1]);
if (datalen == 0) {
s->ti_size = 0;
} else {
s->rregs[4] = STAT_IN | STAT_TC;
if (datalen > 0) {
s->rregs[4] |= STAT_DI;
s->ti_size = datalen;
} else {
s->rregs[4] |= STAT_DO;
s->ti_size = -datalen;
}
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[1]);
break;
}
s->rregs[4] = STAT_IN | STAT_TC | STAT_DI;
s->rregs[5] = INTR_BS | INTR_FC;
s->rregs[6] = SEQ_CD;
s->espdmaregs[0] |= DMA_INTR;
@ -427,7 +139,8 @@ static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
DPRINTF("Transfer status len %d\n", len);
if (s->dma) {
dmaptr = iommu_translate(s->espdmaregs[1]);
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
DPRINTF("DMA Direction: %c\n",
s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r');
cpu_physical_memory_write(dmaptr, buf, len);
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
s->rregs[5] = INTR_BS | INTR_FC;
@ -446,10 +159,26 @@ static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
static const uint8_t okbuf[] = {0, 0};
static void esp_command_complete(void *opaque, uint32_t tag, int fail)
{
ESPState *s = (ESPState *)opaque;
DPRINTF("SCSI Command complete\n");
if (s->ti_size != 0)
DPRINTF("SCSI command completed unexpectedly\n");
s->ti_size = 0;
/* ??? Report failures. */
if (fail)
DPRINTF("Command failed\n");
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
}
static void handle_ti(ESPState *s)
{
uint32_t dmaptr, dmalen, minlen, len, from, to;
unsigned int i;
int to_device;
uint8_t buf[TARGET_PAGE_SIZE];
dmalen = s->wregs[0] | (s->wregs[1] << 8);
if (dmalen==0) {
@ -460,7 +189,10 @@ static void handle_ti(ESPState *s)
DPRINTF("Transfer Information len %d\n", minlen);
if (s->dma) {
dmaptr = iommu_translate(s->espdmaregs[1]);
DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x %d %d\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr, s->ti_size, s->ti_rptr, s->ti_dir);
/* Check if the transfer writes to to reads from the device. */
to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x\n",
to_device ? 'r': 'w', dmaptr, s->ti_size);
from = s->espdmaregs[1];
to = from + minlen;
for (i = 0; i < minlen; i += len, from += len) {
@ -471,35 +203,23 @@ static void handle_ti(ESPState *s)
len = to - from;
}
DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
if (s->ti_dir)
cpu_physical_memory_write(dmaptr, &s->ti_buf[s->ti_rptr + i], len);
else
cpu_physical_memory_read(dmaptr, &s->ti_buf[s->ti_rptr + i], len);
s->ti_size -= len;
if (to_device) {
cpu_physical_memory_read(dmaptr, buf, len);
scsi_write_data(s->current_dev, buf, len);
} else {
scsi_read_data(s->current_dev, buf, len);
cpu_physical_memory_write(dmaptr, buf, len);
}
}
if (s->dma_cb) {
s->dma_cb(s, s->espdmaregs[1], minlen);
}
if (minlen < s->ti_size) {
s->rregs[4] = STAT_IN | STAT_TC | (s->ti_dir ? STAT_DO : STAT_DI);
if (s->ti_size) {
s->rregs[4] = STAT_IN | STAT_TC | (to_device ? STAT_DO : STAT_DI);
s->ti_size -= minlen;
s->ti_rptr += minlen;
} else {
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
s->dma_cb = NULL;
s->offset = 0;
s->len = 0;
s->target = 0;
s->ti_rptr = 0;
}
s->rregs[5] = INTR_BS;
s->rregs[6] = 0;
s->rregs[7] = 0;
s->espdmaregs[0] |= DMA_INTR;
} else {
s->ti_size = minlen;
s->ti_rptr = 0;
s->ti_wptr = 0;
s->rregs[7] = minlen;
}
pic_set_irq(s->irq, 1);
}
@ -514,9 +234,7 @@ static void esp_reset(void *opaque)
s->ti_size = 0;
s->ti_rptr = 0;
s->ti_wptr = 0;
s->ti_dir = 0;
s->dma = 0;
s->dma_cb = NULL;
}
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
@ -531,7 +249,12 @@ static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
// FIFO
if (s->ti_size > 0) {
s->ti_size--;
s->rregs[saddr] = s->ti_buf[s->ti_rptr++];
if ((s->rregs[4] & 6) == 0) {
/* Data in/out. */
scsi_read_data(s->current_dev, &s->rregs[2], 0);
} else {
s->rregs[2] = s->ti_buf[s->ti_rptr++];
}
pic_set_irq(s->irq, 1);
}
if (s->ti_size == 0) {
@ -566,8 +289,15 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
break;
case 2:
// FIFO
s->ti_size++;
s->ti_buf[s->ti_wptr++] = val & 0xff;
if ((s->rregs[4] & 6) == 0) {
uint8_t buf;
buf = val & 0xff;
s->ti_size--;
scsi_write_data(s->current_dev, &buf, 0);
} else {
s->ti_size++;
s->ti_buf[s->ti_wptr++] = val & 0xff;
}
break;
case 3:
s->rregs[saddr] = val;
@ -723,7 +453,6 @@ static void esp_save(QEMUFile *f, void *opaque)
qemu_put_be32s(f, &s->ti_size);
qemu_put_be32s(f, &s->ti_rptr);
qemu_put_be32s(f, &s->ti_wptr);
qemu_put_be32s(f, &s->ti_dir);
qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
qemu_put_be32s(f, &s->dma);
}
@ -744,7 +473,6 @@ static int esp_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_be32s(f, &s->ti_size);
qemu_get_be32s(f, &s->ti_rptr);
qemu_get_be32s(f, &s->ti_wptr);
qemu_get_be32s(f, &s->ti_dir);
qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
qemu_get_be32s(f, &s->dma);
@ -755,6 +483,7 @@ void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdadd
{
ESPState *s;
int esp_io_memory, espdma_io_memory;
int i;
s = qemu_mallocz(sizeof(ESPState));
if (!s)
@ -773,5 +502,11 @@ void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdadd
register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
qemu_register_reset(esp_reset, s);
for (i = 0; i < MAX_DISKS; i++) {
if (bs_table[i]) {
s->scsi_dev[i] =
scsi_disk_init(bs_table[i], esp_command_complete, s);
}
}
}

125
hw/ide.c
View File

@ -1082,127 +1082,6 @@ static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
}
}
/* same toc as bochs. Return -1 if error or the toc length */
/* XXX: check this */
static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
{
uint8_t *q;
int nb_sectors, len;
if (start_track > 1 && start_track != 0xaa)
return -1;
q = buf + 2;
*q++ = 1; /* first session */
*q++ = 1; /* last session */
if (start_track <= 1) {
*q++ = 0; /* reserved */
*q++ = 0x14; /* ADR, control */
*q++ = 1; /* track number */
*q++ = 0; /* reserved */
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, 0);
q += 3;
} else {
/* sector 0 */
cpu_to_ube32(q, 0);
q += 4;
}
}
/* lead out track */
*q++ = 0; /* reserved */
*q++ = 0x16; /* ADR, control */
*q++ = 0xaa; /* track number */
*q++ = 0; /* reserved */
nb_sectors = s->nb_sectors >> 2;
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, nb_sectors);
q += 3;
} else {
cpu_to_ube32(q, nb_sectors);
q += 4;
}
len = q - buf;
cpu_to_ube16(buf, len - 2);
return len;
}
/* mostly same info as PearPc */
static int cdrom_read_toc_raw(IDEState *s, uint8_t *buf, int msf,
int session_num)
{
uint8_t *q;
int nb_sectors, len;
q = buf + 2;
*q++ = 1; /* first session */
*q++ = 1; /* last session */
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa0; /* lead-in */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
*q++ = 0;
*q++ = 1; /* first track */
*q++ = 0x00; /* disk type */
*q++ = 0x00;
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa1;
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
*q++ = 0;
*q++ = 1; /* last track */
*q++ = 0x00;
*q++ = 0x00;
*q++ = 1; /* session number */
*q++ = 0x14; /* data track */
*q++ = 0; /* track number */
*q++ = 0xa2; /* lead-out */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
nb_sectors = s->nb_sectors >> 2;
if (msf) {
*q++ = 0; /* reserved */
lba_to_msf(q, nb_sectors);
q += 3;
} else {
cpu_to_ube32(q, nb_sectors);
q += 4;
}
*q++ = 1; /* session number */
*q++ = 0x14; /* ADR, control */
*q++ = 0; /* track number */
*q++ = 1; /* point */
*q++ = 0; /* min */
*q++ = 0; /* sec */
*q++ = 0; /* frame */
if (msf) {
*q++ = 0;
lba_to_msf(q, 0);
q += 3;
} else {
*q++ = 0;
*q++ = 0;
*q++ = 0;
*q++ = 0;
}
len = q - buf;
cpu_to_ube16(buf, len - 2);
return len;
}
static void ide_atapi_cmd(IDEState *s)
{
const uint8_t *packet;
@ -1449,7 +1328,7 @@ static void ide_atapi_cmd(IDEState *s)
start_track = packet[6];
switch(format) {
case 0:
len = cdrom_read_toc(s, buf, msf, start_track);
len = cdrom_read_toc(s->nb_sectors >> 2, buf, msf, start_track);
if (len < 0)
goto error_cmd;
ide_atapi_cmd_reply(s, len, max_len);
@ -1463,7 +1342,7 @@ static void ide_atapi_cmd(IDEState *s)
ide_atapi_cmd_reply(s, 12, max_len);
break;
case 2:
len = cdrom_read_toc_raw(s, buf, msf, start_track);
len = cdrom_read_toc_raw(s->nb_sectors >> 2, buf, msf, start_track);
if (len < 0)
goto error_cmd;
ide_atapi_cmd_reply(s, len, max_len);

408
hw/scsi-disk.c Normal file
View File

@ -0,0 +1,408 @@
/*
* SCSI Device emulation
*
* Copyright (c) 2006 CodeSourcery.
* Based on code by Fabrice Bellard
*
* Written by Paul Brook
*
* This code is licenced under the LGPL.
*/
//#define DEBUG_SCSI
#ifdef DEBUG_SCSI
#define DPRINTF(fmt, args...) \
do { printf("scsi-disk: " fmt , ##args); } while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#endif
#define BADF(fmt, args...) \
do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
#include "vl.h"
#define SENSE_NO_SENSE 0
#define SENSE_ILLEGAL_REQUEST 5
struct SCSIDevice
{
int command;
uint32_t tag;
BlockDriverState *bdrv;
int sector_size;
/* When transfering data buf_pos and buf_len contain a partially
transferred block of data (or response to a command), and
sector/sector_count identify any remaining sectors. */
/* ??? 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. */
int sector;
int sector_count;
int buf_pos;
int buf_len;
int sense;
char buf[2048];
scsi_completionfn completion;
void *opaque;
};
static void scsi_command_complete(SCSIDevice *s, int sense)
{
s->sense = sense;
s->completion(s->opaque, s->tag, sense != SENSE_NO_SENSE);
}
/* Read data from a scsi device. Returns nonzero on failure. */
int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
{
uint32_t n;
DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
if (s->buf_len == 0 && s->sector_count == 0)
return 1;
if (s->buf_len) {
n = s->buf_len;
if (n > len)
n = len;
memcpy(data, s->buf + s->buf_pos, n);
s->buf_pos += n;
s->buf_len -= n;
data += n;
len -= n;
if (s->buf_len == 0)
s->buf_pos = 0;
}
n = len / s->sector_size;
if (n > s->sector_count)
n = s->sector_count;
if (n != 0) {
bdrv_read(s->bdrv, s->sector, data, n);
data += n * s->sector_size;
len -= n * s->sector_size;
s->sector += n;
s->sector_count -= n;
}
if (len && s->sector_count) {
bdrv_read(s->bdrv, s->sector, s->buf, 1);
s->sector++;
s->sector_count--;
s->buf_pos = 0;
s->buf_len = s->sector_size;
/* Recurse to complete the partial read. */
return scsi_read_data(s, data, len);
}
if (len != 0)
return 1;
if (s->buf_len == 0 && s->sector_count == 0)
scsi_command_complete(s, SENSE_NO_SENSE);
return 0;
}
/* Read data to a scsi device. Returns nonzero on failure. */
int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
{
uint32_t n;
DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
if (s->buf_pos != 0) {
BADF("Bad state on write\n");
return 1;
}
if (s->sector_count == 0)
return 1;
if (s->buf_len != 0 || len < s->sector_size) {
n = s->sector_size - s->buf_len;
if (n > len)
n = len;
memcpy(s->buf + s->buf_len, data, n);
data += n;
s->buf_len += n;
len -= n;
if (s->buf_len == s->sector_size) {
/* A full sector has been accumulated. Write it to disk. */
bdrv_write(s->bdrv, s->sector, s->buf, 1);
s->buf_len = 0;
s->sector++;
s->sector_count--;
}
}
n = len / s->sector_size;
if (n > s->sector_count)
n = s->sector_count;
if (n != 0) {
bdrv_write(s->bdrv, s->sector, data, n);
data += n * s->sector_size;
len -= n * s->sector_size;
s->sector += n;
s->sector_count -= n;
}
if (len >= s->sector_size)
return 1;
if (len && s->sector_count) {
/* Recurse to complete the partial write. */
return scsi_write_data(s, data, len);
}
if (len != 0)
return 1;
if (s->sector_count == 0)
scsi_command_complete(s, SENSE_NO_SENSE);
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. */
int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf)
{
int64_t nb_sectors;
uint32_t lba;
uint32_t len;
int cmdlen;
int is_write;
s->command = buf[0];
s->tag = tag;
s->sector_count = 0;
s->buf_pos = 0;
s->buf_len = 0;
is_write = 0;
DPRINTF("Command: 0x%02x", buf[0]);
switch (s->command >> 5) {
case 0:
lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
len = buf[4];
cmdlen = 6;
break;
case 1:
case 2:
lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
len = buf[8] | (buf[7] << 8);
cmdlen = 10;
break;
case 4:
lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
cmdlen = 16;
break;
case 5:
lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
cmdlen = 12;
break;
default:
BADF("Unsupported command length\n");
goto fail;
}
#ifdef DEBUG_SCSI
{
int i;
for (i = 1; i < cmdlen; i++) {
printf(" 0x%02x", buf[i]);
}
printf("\n");
}
#endif
if (buf[1] >> 5) {
/* Only LUN 0 supported. */
goto fail;
}
switch (s->command) {
case 0x0:
DPRINTF("Test Unit Ready\n");
break;
case 0x03:
DPRINTF("Request Sense (len %d)\n", len);
if (len < 4)
goto fail;
memset(buf, 0, 4);
s->buf[0] = 0xf0;
s->buf[1] = 0;
s->buf[2] = s->sense;
s->buf_len = 4;
break;
case 0x12:
DPRINTF("Inquiry (len %d)\n", len);
if (len < 36) {
BADF("Inquiry buffer too small (%d)\n", len);
}
memset(s->buf, 0, 36);
if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
s->buf[0] = 5;
s->buf[1] = 0x80;
memcpy(&s->buf[16], "QEMU CDROM ", 16);
} else {
s->buf[0] = 0;
memcpy(&s->buf[16], "QEMU HARDDISK ", 16);
}
memcpy(&s->buf[8], "QEMU ", 8);
s->buf[2] = 3; /* SCSI-3 */
s->buf[3] = 2; /* Format 2 */
s->buf[4] = 32;
s->buf_len = 36;
break;
case 0x16:
DPRINTF("Reserve(6)\n");
if (buf[1] & 1)
goto fail;
break;
case 0x17:
DPRINTF("Release(6)\n");
if (buf[1] & 1)
goto fail;
break;
case 0x1a:
DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[2], len);
memset(s->buf, 0, 4);
s->buf[0] = 0x16; /* Mode data length (4 + 0x12). */
s->buf[1] = 0; /* Default media type. */
s->buf[2] = 0; /* Write enabled. */
s->buf[3] = 0; /* Block descriptor length. */
/* Caching page. */
s->buf[4 + 0] = 8;
s->buf[4 + 1] = 0x12;
s->buf[4 + 2] = 4; /* WCE */
if (len > 0x16)
len = 0x16;
s->buf_len = len;
break;
case 0x25:
DPRINTF("Read Capacity\n");
/* The normal LEN field for this command is zero. */
memset(s->buf, 0, 8);
bdrv_get_geometry(s->bdrv, &nb_sectors);
s->buf[0] = (nb_sectors >> 24) & 0xff;
s->buf[1] = (nb_sectors >> 16) & 0xff;
s->buf[2] = (nb_sectors >> 8) & 0xff;
s->buf[3] = nb_sectors & 0xff;
s->buf[4] = 0;
s->buf[5] = 0;
s->buf[6] = s->sector_size >> 8;
s->buf[7] = s->sector_size & 0xff;
s->buf_len = 8;
break;
case 0x08:
case 0x28:
DPRINTF("Read (sector %d, count %d)\n", lba, len);
s->sector = lba;
s->sector_count = len;
break;
case 0x0a:
case 0x2a:
DPRINTF("Write (sector %d, count %d)\n", lba, len);
s->sector = lba;
s->sector_count = len;
is_write = 1;
break;
case 0x43:
{
int start_track, format, msf;
msf = buf[1] & 2;
format = buf[2] & 0xf;
start_track = buf[6];
bdrv_get_geometry(s->bdrv, &nb_sectors);
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
switch(format) {
case 0:
len = cdrom_read_toc(nb_sectors, s->buf, msf, start_track);
if (len < 0)
goto error_cmd;
s->buf_len = len;
break;
case 1:
/* multi session : only a single session defined */
memset(s->buf, 0, 12);
s->buf[1] = 0x0a;
s->buf[2] = 0x01;
s->buf[3] = 0x01;
s->buf_len = 12;
break;
case 2:
len = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track);
if (len < 0)
goto error_cmd;
s->buf_len = len;
break;
default:
error_cmd:
DPRINTF("Read TOC error\n");
goto fail;
}
break;
}
case 0x56:
DPRINTF("Reserve(10)\n");
if (buf[1] & 3)
goto fail;
break;
case 0x57:
DPRINTF("Release(10)\n");
if (buf[1] & 3)
goto fail;
break;
case 0xa0:
DPRINTF("Report LUNs (len %d)\n", len);
if (len < 16)
goto fail;
memset(s->buf, 0, 16);
s->buf[3] = 8;
s->buf_len = 16;
break;
default:
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
fail:
scsi_command_complete(s, SENSE_ILLEGAL_REQUEST);
return 0;
}
if (s->sector_count == 0 && s->buf_len == 0) {
scsi_command_complete(s, SENSE_NO_SENSE);
}
len = s->sector_count * s->sector_size + s->buf_len;
return is_write ? -len : len;
}
void scsi_disk_destroy(SCSIDevice *s)
{
bdrv_close(s->bdrv);
qemu_free(s);
}
SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
scsi_completionfn completion,
void *opaque)
{
SCSIDevice *s;
s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
s->bdrv = bdrv;
s->completion = completion;
s->opaque = opaque;
if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
s->sector_size = 2048;
} else {
s->sector_size = 512;
}
return s;
}

View File

@ -323,10 +323,16 @@ static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len)
return l;
}
static void usb_mouse_handle_reset(USBDevice *dev)
static void usb_mouse_handle_reset(USBDevice *dev, int destroy)
{
USBMouseState *s = (USBMouseState *)dev;
if (destroy) {
qemu_add_mouse_event_handler(NULL, NULL, 0);
qemu_free(s);
return;
}
s->dx = 0;
s->dy = 0;
s->dz = 0;

View File

@ -199,9 +199,11 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
}
}
static void usb_hub_handle_reset(USBDevice *dev)
static void usb_hub_handle_reset(USBDevice *dev, int destroy)
{
/* XXX: do it */
if (destroy)
qemu_free(dev);
}
static int usb_hub_handle_control(USBDevice *dev, int request, int value,

395
hw/usb-msd.c Normal file
View File

@ -0,0 +1,395 @@
/*
* USB Mass Storage Device emulation
*
* Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook
*
* This code is licenced under the LGPL.
*/
#include "vl.h"
//#define DEBUG_MSD
#ifdef DEBUG_MSD
#define DPRINTF(fmt, args...) \
do { printf("usb-msd: " fmt , ##args); } while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#endif
/* USB requests. */
#define MassStorageReset 0xff
#define GetMaxLun 0xfe
enum USBMSDMode {
USB_MSDM_CBW, /* Command Block. */
USB_MSDM_DATAOUT, /* Tranfer data to device. */
USB_MSDM_DATAIN, /* Transfer data from device. */
USB_MSDM_CSW /* Command Status. */
};
typedef struct {
USBDevice dev;
enum USBMSDMode mode;
uint32_t data_len;
uint32_t tag;
SCSIDevice *scsi_dev;
int result;
} MSDState;
static const uint8_t qemu_msd_dev_descriptor[] = {
0x12, /* u8 bLength; */
0x01, /* u8 bDescriptorType; Device */
0x10, 0x00, /* u16 bcdUSB; v1.0 */
0x00, /* u8 bDeviceClass; */
0x00, /* u8 bDeviceSubClass; */
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
/* Vendor and product id are arbitrary. */
0x00, 0x00, /* u16 idVendor; */
0x00, 0x00, /* u16 idProduct; */
0x00, 0x00, /* u16 bcdDevice */
0x01, /* u8 iManufacturer; */
0x02, /* u8 iProduct; */
0x03, /* u8 iSerialNumber; */
0x01 /* u8 bNumConfigurations; */
};
static const uint8_t qemu_msd_config_descriptor[] = {
/* one configuration */
0x09, /* u8 bLength; */
0x02, /* u8 bDescriptorType; Configuration */
0x20, 0x00, /* u16 wTotalLength; */
0x01, /* u8 bNumInterfaces; (1) */
0x01, /* u8 bConfigurationValue; */
0x00, /* u8 iConfiguration; */
0xc0, /* u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* u8 MaxPower; */
/* one interface */
0x09, /* u8 if_bLength; */
0x04, /* u8 if_bDescriptorType; Interface */
0x00, /* u8 if_bInterfaceNumber; */
0x00, /* u8 if_bAlternateSetting; */
0x02, /* u8 if_bNumEndpoints; */
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
0x06, /* u8 if_bInterfaceSubClass; SCSI */
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
0x00, /* u8 if_iInterface; */
/* Bulk-In endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
0x02, /* u8 ep_bmAttributes; Bulk */
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
0x00, /* u8 ep_bInterval; */
/* Bulk-Out endpoint */
0x07, /* u8 ep_bLength; */
0x05, /* u8 ep_bDescriptorType; Endpoint */
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
0x02, /* u8 ep_bmAttributes; Bulk */
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
0x00 /* u8 ep_bInterval; */
};
static void usb_msd_command_complete(void *opaque, uint32_t tag, int fail)
{
MSDState *s = (MSDState *)opaque;
DPRINTF("Command complete\n");
s->result = fail;
s->mode = USB_MSDM_CSW;
}
static void usb_msd_handle_reset(USBDevice *dev, int destroy)
{
MSDState *s = (MSDState *)dev;
DPRINTF("Reset\n");
s->mode = USB_MSDM_CBW;
if (destroy) {
scsi_disk_destroy(s->scsi_dev);
qemu_free(s);
}
}
static int usb_msd_handle_control(USBDevice *dev, int request, int value,
int index, int length, uint8_t *data)
{
MSDState *s = (MSDState *)dev;
int ret = 0;
switch (request) {
case DeviceRequest | USB_REQ_GET_STATUS:
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
data[1] = 0x00;
ret = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 0;
} else {
goto fail;
}
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 1;
} else {
goto fail;
}
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
dev->addr = value;
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch(value >> 8) {
case USB_DT_DEVICE:
memcpy(data, qemu_msd_dev_descriptor,
sizeof(qemu_msd_dev_descriptor));
ret = sizeof(qemu_msd_dev_descriptor);
break;
case USB_DT_CONFIG:
memcpy(data, qemu_msd_config_descriptor,
sizeof(qemu_msd_config_descriptor));
ret = sizeof(qemu_msd_config_descriptor);
break;
case USB_DT_STRING:
switch(value & 0xff) {
case 0:
/* language ids */
data[0] = 4;
data[1] = 3;
data[2] = 0x09;
data[3] = 0x04;
ret = 4;
break;
case 1:
/* vendor description */
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
break;
case 2:
/* product description */
ret = set_usb_string(data, "QEMU USB HARDDRIVE");
break;
case 3:
/* serial number */
ret = set_usb_string(data, "1");
break;
default:
goto fail;
}
break;
default:
goto fail;
}
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
data[0] = 1;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0;
break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == 0 && index != 0x81) { /* clear ep halt */
goto fail;
}
ret = 0;
break;
/* Class specific requests. */
case MassStorageReset:
/* Reset state ready for the next CBW. */
s->mode = USB_MSDM_CBW;
ret = 0;
break;
case GetMaxLun:
data[0] = 0;
ret = 1;
break;
default:
fail:
ret = USB_RET_STALL;
break;
}
return ret;
}
struct usb_msd_cbw {
uint32_t sig;
uint32_t tag;
uint32_t data_len;
uint8_t flags;
uint8_t lun;
uint8_t cmd_len;
uint8_t cmd[16];
};
struct usb_msd_csw {
uint32_t sig;
uint32_t tag;
uint32_t residue;
uint8_t status;
};
static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
uint8_t *data, int len)
{
MSDState *s = (MSDState *)dev;
int ret = 0;
struct usb_msd_cbw cbw;
struct usb_msd_csw csw;
switch (pid) {
case USB_TOKEN_OUT:
if (devep != 2)
goto fail;
switch (s->mode) {
case USB_MSDM_CBW:
if (len != 31) {
fprintf(stderr, "usb-msd: Bad CBW size");
goto fail;
}
memcpy(&cbw, data, 31);
if (le32_to_cpu(cbw.sig) != 0x43425355) {
fprintf(stderr, "usb-msd: Bad signature %08x\n",
le32_to_cpu(cbw.sig));
goto fail;
}
DPRINTF("Command on LUN %d\n", cbw.lun);
if (cbw.lun != 0) {
fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
goto fail;
}
s->tag = le32_to_cpu(cbw.tag);
s->data_len = le32_to_cpu(cbw.data_len);
if (s->data_len == 0) {
s->mode = USB_MSDM_CSW;
} else if (cbw.flags & 0x80) {
s->mode = USB_MSDM_DATAIN;
} else {
s->mode = USB_MSDM_DATAOUT;
}
DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
scsi_send_command(s->scsi_dev, s->tag, cbw.cmd);
ret = len;
break;
case USB_MSDM_DATAOUT:
DPRINTF("Data out %d/%d\n", len, s->data_len);
if (len > s->data_len)
goto fail;
if (scsi_write_data(s->scsi_dev, data, len))
goto fail;
s->data_len -= len;
if (s->data_len == 0)
s->mode = USB_MSDM_CSW;
ret = len;
break;
default:
DPRINTF("Unexpected write (len %d)\n", len);
goto fail;
}
break;
case USB_TOKEN_IN:
if (devep != 1)
goto fail;
switch (s->mode) {
case USB_MSDM_CSW:
DPRINTF("Command status %d tag 0x%x, len %d\n",
s->result, s->tag, len);
if (len < 13)
goto fail;
csw.sig = cpu_to_le32(0x53425355);
csw.tag = cpu_to_le32(s->tag);
csw.residue = 0;
csw.status = s->result;
memcpy(data, &csw, 13);
ret = 13;
s->mode = USB_MSDM_CBW;
break;
case USB_MSDM_DATAIN:
DPRINTF("Data in %d/%d\n", len, s->data_len);
if (len > s->data_len)
len = s->data_len;
if (scsi_read_data(s->scsi_dev, data, len))
goto fail;
s->data_len -= len;
if (s->data_len == 0)
s->mode = USB_MSDM_CSW;
ret = len;
break;
default:
DPRINTF("Unexpected read (len %d)\n", len);
goto fail;
}
break;
default:
DPRINTF("Bad token\n");
fail:
ret = USB_RET_STALL;
break;
}
return ret;
}
USBDevice *usb_msd_init(const char *filename)
{
MSDState *s;
BlockDriverState *bdrv;
s = qemu_mallocz(sizeof(MSDState));
if (!s)
return NULL;
bdrv = bdrv_new("usb");
bdrv_open(bdrv, filename, 0);
s->dev.speed = USB_SPEED_FULL;
s->dev.handle_packet = usb_generic_handle_packet;
s->dev.handle_reset = usb_msd_handle_reset;
s->dev.handle_control = usb_msd_handle_control;
s->dev.handle_data = usb_msd_handle_data;
s->scsi_dev = scsi_disk_init(bdrv, usb_msd_command_complete, s);
usb_msd_handle_reset((USBDevice *)s, 0);
return (USBDevice *)s;
}

View File

@ -55,7 +55,10 @@ int usb_generic_handle_packet(USBDevice *s, int pid,
s->remote_wakeup = 0;
s->addr = 0;
s->state = USB_STATE_DEFAULT;
s->handle_reset(s);
s->handle_reset(s, 0);
break;
case USB_MSG_DESTROY:
s->handle_reset(s, 1);
break;
case USB_TOKEN_SETUP:
if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)

View File

@ -29,6 +29,7 @@
#define USB_MSG_ATTACH 0x100
#define USB_MSG_DETACH 0x101
#define USB_MSG_RESET 0x102
#define USB_MSG_DESTROY 0x103
#define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2)
@ -121,7 +122,7 @@ struct USBDevice {
/* The following fields are used by the generic USB device
layer. They are here just to avoid creating a new structure for
them. */
void (*handle_reset)(USBDevice *dev);
void (*handle_reset)(USBDevice *dev, int destroy);
int (*handle_control)(USBDevice *dev, int request, int value,
int index, int length, uint8_t *data);
int (*handle_data)(USBDevice *dev, int pid, uint8_t devep,
@ -170,3 +171,6 @@ void usb_host_info(void);
/* usb-hid.c */
USBDevice *usb_mouse_init(void);
USBDevice *usb_tablet_init(void);
/* usb-msd.c */
USBDevice *usb_msd_init(const char *filename);

View File

@ -55,7 +55,7 @@ typedef struct USBHostDevice {
int fd;
} USBHostDevice;
static void usb_host_handle_reset(USBDevice *dev)
static void usb_host_handle_reset(USBDevice *dev, int destroy)
{
#if 0
USBHostDevice *s = (USBHostDevice *)dev;

2
vl.c
View File

@ -3279,6 +3279,8 @@ static int usb_device_add(const char *devname)
dev = usb_mouse_init();
} else if (!strcmp(devname, "tablet")) {
dev = usb_tablet_init();
} else if (strstart(devname, "disk:", &p)) {
dev = usb_msd_init(p);
} else {
return -1;
}

17
vl.h
View File

@ -761,6 +761,10 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn);
int pmac_ide_init (BlockDriverState **hd_table,
SetIRQFunc *set_irq, void *irq_opaque, int irq);
/* cdrom.c */
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
/* es1370.c */
int es1370_init (PCIBus *bus, AudioState *s);
@ -1031,6 +1035,19 @@ void do_usb_add(const char *devname);
void do_usb_del(const char *devname);
void usb_info(void);
/* scsi-disk.c */
typedef struct SCSIDevice SCSIDevice;
typedef void (*scsi_completionfn)(void *, uint32_t, int);
SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
scsi_completionfn completion,
void *opaque);
void scsi_disk_destroy(SCSIDevice *s);
int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf);
int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len);
int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len);
/* integratorcp.c */
extern QEMUMachine integratorcp926_machine;
extern QEMUMachine integratorcp1026_machine;