summary refs log tree commit diff stats
path: root/hw/esp.c
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-05-25 23:58:51 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-05-25 23:58:51 +0000
commit2e5d83bbef5a539f22970c2bccd19b125d82aab0 (patch)
treef733c22c2f4d19bd0a00f373b919e2e3ada86241 /hw/esp.c
parente6f3e5e016cc7473bc008f341d8e22bd989e03cb (diff)
downloadfocaccia-qemu-2e5d83bbef5a539f22970c2bccd19b125d82aab0.tar.gz
focaccia-qemu-2e5d83bbef5a539f22970c2bccd19b125d82aab0.zip
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
Diffstat (limited to 'hw/esp.c')
-rw-r--r--hw/esp.c417
1 files changed, 76 insertions, 341 deletions
diff --git a/hw/esp.c b/hw/esp.c
index 30708a1a0d..787892bfe0 100644
--- 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);
+        }
+    }
 }