ESP PIO mode, 2k CDROM sector size (Blue Swirl)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1577 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/hw/esp.c b/hw/esp.c
index e04539a..6890bd1 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -36,6 +36,9 @@
 #define ESPDMA_REGS 4
 #define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
 #define ESP_MAXREG 0x3f
+#define TI_BUFSZ 65536
+#define DMA_VER 0xa0000000
+#define DMA_LOADED 0x04000000
 
 typedef struct ESPState {
     BlockDriverState **bd;
@@ -44,8 +47,10 @@
     int irq;
     uint32_t espdmaregs[ESPDMA_REGS];
     uint32_t ti_size;
+    uint32_t ti_rptr, ti_wptr;
     int ti_dir;
-    uint8_t ti_buf[65536];
+    uint8_t ti_buf[TI_BUFSZ];
+    int dma;
 } ESPState;
 
 #define STAT_DO 0x00
@@ -73,17 +78,25 @@
     int64_t nb_sectors;
     int target;
 
-    dmaptr = iommu_translate(s->espdmaregs[1]);
     dmalen = s->wregs[0] | (s->wregs[1] << 8);
-    DPRINTF("Select with ATN at %8.8x len %d\n", dmaptr, dmalen);
-    DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
-    cpu_physical_memory_read(dmaptr, buf, dmalen);
+    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);
+	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;
-    target = s->wregs[4] & 7;
+    s->ti_rptr = 0;
+    s->ti_wptr = 0;
 
     if (target > 4 || !s->bd[target]) { // No such drive
 	s->rregs[4] = STAT_IN;
@@ -126,7 +139,10 @@
 	s->ti_buf[3] = nb_sectors & 0xff;
 	s->ti_buf[4] = 0;
 	s->ti_buf[5] = 0;
-	s->ti_buf[6] = 2;
+	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;
@@ -135,28 +151,42 @@
 	{
 	    int64_t offset, len;
 
-	    offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
-	    len = (buf[8] << 8) | buf[9];
+	    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);
 	    bdrv_read(s->bd[target], offset, s->ti_buf, len);
+	    // XXX error handling
 	    s->ti_dir = 1;
-	    s->ti_size = len * 512;
 	    break;
 	}
     case 0x2a:
 	{
 	    int64_t offset, len;
 
-	    offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
-	    len = (buf[8] << 8) | buf[9];
+	    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);
 	    bdrv_write(s->bd[target], offset, s->ti_buf, len);
+	    // XXX error handling
 	    s->ti_dir = 0;
-	    s->ti_size = len * 512;
 	    break;
 	}
     default:
-	DPRINTF("Unknown command (%2.2x)\n", buf[1]);
+	DPRINTF("Unknown SCSI command (%2.2x)\n", buf[1]);
 	break;
     }
     s->rregs[4] = STAT_IN | STAT_TC | STAT_DI;
@@ -170,17 +200,26 @@
 {
     uint32_t dmaptr, dmalen;
 
-    dmaptr = iommu_translate(s->espdmaregs[1]);
     dmalen = s->wregs[0] | (s->wregs[1] << 8);
-    DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? '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;
-    s->rregs[6] = SEQ_CD;
-    s->espdmaregs[0] |= 1;
+    DPRINTF("Transfer status len %d\n", dmalen);
+    if (s->dma) {
+	dmaptr = iommu_translate(s->espdmaregs[1]);
+	DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? '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;
+	s->rregs[6] = SEQ_CD;
+	s->espdmaregs[0] |= 1;
+    } else {
+	memcpy(s->ti_buf, buf, len);
+	s->ti_size = dmalen;
+	s->ti_rptr = 0;
+	s->ti_wptr = 0;
+    }
     pic_set_irq(s->irq, 1);
 
 }
+
 static const uint8_t okbuf[] = {0, 0};
 
 static void handle_ti(ESPState *s)
@@ -188,21 +227,27 @@
     uint32_t dmaptr, dmalen;
     unsigned int i;
 
-    dmaptr = iommu_translate(s->espdmaregs[1]);
     dmalen = s->wregs[0] | (s->wregs[1] << 8);
-    DPRINTF("Transfer Information at %8.8x len %d\n", dmaptr, dmalen);
-    DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
-    for (i = 0; i < s->ti_size; i++) {
-	dmaptr = iommu_translate(s->espdmaregs[1] + i);
-	if (s->ti_dir)
-	    cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1);
-	else
-	    cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1);
-    }
-    s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
-    s->rregs[5] = INTR_BS;
-    s->rregs[6] = 0;
-    s->espdmaregs[0] |= 1;
+    DPRINTF("Transfer Information len %d\n", dmalen);
+    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);
+	for (i = 0; i < s->ti_size; i++) {
+	    dmaptr = iommu_translate(s->espdmaregs[1] + i);
+	    if (s->ti_dir)
+		cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1);
+	    else
+		cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1);
+	}
+	s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
+	s->rregs[5] = INTR_BS;
+	s->rregs[6] = 0;
+	s->espdmaregs[0] |= 1;
+    } else {
+	s->ti_size = dmalen;
+	s->ti_rptr = 0;
+	s->ti_wptr = 0;
+    }	
     pic_set_irq(s->irq, 1);
 }
 
@@ -221,10 +266,23 @@
 
     saddr = (addr & ESP_MAXREG) >> 2;
     switch (saddr) {
+    case 2:
+	// FIFO
+	if (s->ti_size > 0) {
+	    s->ti_size--;
+	    s->rregs[saddr] = s->ti_buf[s->ti_rptr++];
+	    pic_set_irq(s->irq, 1);
+	}
+	if (s->ti_size == 0) {
+            s->ti_rptr = 0;
+            s->ti_wptr = 0;
+        }
+	break;
     default:
 	break;
     }
     DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
+
     return s->rregs[saddr];
 }
 
@@ -236,8 +294,23 @@
     saddr = (addr & ESP_MAXREG) >> 2;
     DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val);
     switch (saddr) {
+    case 0:
+    case 1:
+        s->rregs[saddr] = val;
+        break;
+    case 2:
+	// FIFO
+	s->ti_size++;
+	s->ti_buf[s->ti_wptr++] = val & 0xff;
+	break;
     case 3:
+        s->rregs[saddr] = val;
 	// Command
+	if (val & 0x80) {
+	    s->dma = 1;
+	} else {
+	    s->dma = 0;
+	}
 	switch(val & 0x7f) {
 	case 0:
 	    DPRINTF("NOP (%2.2x)\n", val);
@@ -278,13 +351,20 @@
 	    handle_satn(s);
 	    break;
 	default:
-	    DPRINTF("Unhandled command (%2.2x)\n", val);
+	    DPRINTF("Unhandled ESP command (%2.2x)\n", val);
 	    break;
 	}
 	break;
     case 4 ... 7:
-    case 9 ... 0xf:
 	break;
+    case 8:
+        s->rregs[saddr] = val;
+        break;
+    case 9 ... 10:
+        break;
+    case 11 ... 15:
+        s->rregs[saddr] = val;
+        break;
     default:
 	break;
     }
@@ -309,7 +389,8 @@
     uint32_t saddr;
 
     saddr = (addr & ESPDMA_MAXADDR) >> 2;
-    DPRINTF("read dmareg[%d]: 0x%2.2x\n", saddr, s->espdmaregs[saddr]);
+    DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]);
+
     return s->espdmaregs[saddr];
 }
 
@@ -319,12 +400,23 @@
     uint32_t saddr;
 
     saddr = (addr & ESPDMA_MAXADDR) >> 2;
-    DPRINTF("write dmareg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->espdmaregs[saddr], val);
+    DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val);
     switch (saddr) {
     case 0:
 	if (!(val & 0x10))
 	    pic_set_irq(s->irq, 0);
+	if (val & 0x80) {
+            esp_reset(s);
+        } else if (val & 0x40) {
+            val &= ~0x40;
+        } else if (val == 0)
+            val = 0x40;
+        val &= 0x0fffffff;
+        val |= DMA_VER;
 	break;
+    case 1:
+        s->espdmaregs[0] = DMA_LOADED;
+        break;
     default:
 	break;
     }
@@ -353,6 +445,12 @@
     qemu_put_be32s(f, &s->irq);
     for (i = 0; i < ESPDMA_REGS; i++)
 	qemu_put_be32s(f, &s->espdmaregs[i]);
+    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);
 }
 
 static int esp_load(QEMUFile *f, void *opaque, int version_id)
@@ -368,6 +466,12 @@
     qemu_get_be32s(f, &s->irq);
     for (i = 0; i < ESPDMA_REGS; i++)
 	qemu_get_be32s(f, &s->espdmaregs[i]);
+    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);
 
     return 0;
 }