Rearrange SCSI disk emulation code.
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Thu, 25 May 2006 23:58:51 +0000 (23:58 +0000)
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Thu, 25 May 2006 23:58:51 +0000 (23:58 +0000)
Add USB mass storage device emulation.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1940 c046a42c-6fe2-441c-8c8c-71466251a162

13 files changed:
Makefile.target
hw/cdrom.c [new file with mode: 0644]
hw/esp.c
hw/ide.c
hw/scsi-disk.c [new file with mode: 0644]
hw/usb-hid.c
hw/usb-hub.c
hw/usb-msd.c [new file with mode: 0644]
hw/usb.c
hw/usb.h
usb-linux.c
vl.c
vl.h

index 2b34617..4bf558f 100644 (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
diff --git a/hw/cdrom.c b/hw/cdrom.c
new file mode 100644 (file)
index 0000000..a43b417
--- /dev/null
@@ -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;
+}
+
+
index 30708a1..787892b 100644 (file)
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -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);
+        }
+    }
 }
 
index ed63573..ffe0230 100644 (file)
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -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);
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
new file mode 100644 (file)
index 0000000..937eb94
--- /dev/null
@@ -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;
+}
+
index 17160eb..883befc 100644 (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;
index e2cb283..c69d69c 100644 (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,
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
new file mode 100644 (file)
index 0000000..6b4cb81
--- /dev/null
@@ -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;
+}
index 34aac5f..a00d945 100644 (file)
--- a/hw/usb.c
+++ b/hw/usb.c
@@ -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)
index c92fd01..abdbb45 100644 (file)
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -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);
index 9cd543d..8009a1b 100644 (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;
diff --git a/vl.c b/vl.c
index b641299..5cbd164 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -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;
     }
diff --git a/vl.h b/vl.h
index a984065..f8414ea 100644 (file)
--- a/vl.h
+++ b/vl.h
@@ -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;