X-Git-Url: https://vcs.maemo.org/git/?a=blobdiff_plain;f=hw%2Flsi53c895a.c;h=e9866baaccef0c95cadc86770ff9d6dfc277f564;hb=cd346349b45ef056f138a184f660b8c34c3213cc;hp=24dff0eff5062f0858f8d02053b1c7e20fb2bfb8;hpb=7d8406be69ce936839300159fcf2a0c4863f7f08;p=qemu diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 24dff0e..e9866ba 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -1,4 +1,4 @@ -/* +/* * QEMU LSI53C895A SCSI Host Bus Adapter emulation * * Copyright (c) 2006 CodeSourcery. @@ -19,11 +19,11 @@ #define DPRINTF(fmt, args...) \ do { printf("lsi_scsi: " fmt , ##args); } while (0) #define BADF(fmt, args...) \ -do { fprintf(stderr, "lsi_scsi: " fmt , ##args); exit(1);} while (0) +do { fprintf(stderr, "lsi_scsi: error: " fmt , ##args); exit(1);} while (0) #else #define DPRINTF(fmt, args...) do {} while(0) #define BADF(fmt, args...) \ -do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0) +do { fprintf(stderr, "lsi_scsi: error: " fmt , ##args);} while (0) #endif #define LSI_SCNTL0_TRG 0x01 @@ -152,21 +152,46 @@ do { fprintf(stderr, "lsi_scsi: " fmt , ##args); } while (0) /* The HBA is ID 7, so for simplicitly limit to 7 devices. */ #define LSI_MAX_DEVS 7 +/* Maximum length of MSG IN data. */ +#define LSI_MAX_MSGIN_LEN 8 + +/* Flag set if this is a tagged command. */ +#define LSI_TAG_VALID (1 << 16) + +typedef struct { + uint32_t tag; + uint32_t pending; + int out; +} lsi_queue; + typedef struct { PCIDevice pci_dev; int mmio_io_addr; int ram_io_addr; uint32_t script_ram_base; - uint32_t data_len; int carry; /* ??? Should this be an a visible register somewhere? */ int sense; - uint8_t msg; - /* Nonzero if a Wait Reselect instruction has been issued. */ + /* Action to take at the end of a MSG IN phase. + 0 = COMMAND, 1 = disconect, 2 = DATA OUT, 3 = DATA IN. */ + int msg_action; + int msg_len; + uint8_t msg[LSI_MAX_MSGIN_LEN]; + /* 0 if SCRIPTS are running or stopped. + * 1 if a Wait Reselect instruction has been issued. + * 2 if processing DMA from lsi_execute_script. + * 3 if a DMA operation is in progress. */ int waiting; SCSIDevice *scsi_dev[LSI_MAX_DEVS]; SCSIDevice *current_dev; int current_lun; + /* The tag is a combination of the device ID and the SCSI tag. */ + uint32_t current_tag; + uint32_t current_dma_len; + uint8_t *dma_buf; + lsi_queue *queue; + int queue_len; + int active_commands; uint32_t dsa; uint32_t temp; @@ -203,10 +228,12 @@ typedef struct { uint8_t sxfer; uint8_t socl; uint8_t sdid; + uint8_t ssid; uint8_t sfbr; uint8_t stest1; uint8_t stest2; uint8_t stest3; + uint8_t sidl; uint8_t stime0; uint8_t respid0; uint8_t respid1; @@ -224,7 +251,7 @@ typedef struct { uint32_t ia; uint32_t sbc; uint32_t csbc; - uint32_t scratch[13]; /* SCRATCHA-SCRATCHR */ + uint32_t scratch[18]; /* SCRATCHA-SCRATCHR */ /* Script ram is stored as 32-bit words in host byteorder. */ uint32_t script_ram[2048]; @@ -274,6 +301,7 @@ static void lsi_soft_reset(LSIState *s) s->stest1 = 0; s->stest2 = 0; s->stest3 = 0; + s->sidl = 0; s->stime0 = 0; s->respid0 = 0x80; s->respid1 = 0; @@ -295,6 +323,7 @@ static void lsi_soft_reset(LSIState *s) static uint8_t lsi_reg_readb(LSIState *s, int offset); static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val); +static void lsi_execute_script(LSIState *s); static inline uint32_t read_dword(LSIState *s, uint32_t addr) { @@ -345,7 +374,7 @@ static void lsi_update_irq(LSIState *s) level, s->dstat, s->sist1, s->sist0); last_level = level; } - pci_set_irq(&s->pci_dev, 0, level); + qemu_set_irq(s->pci_dev.irq[0], level); } /* Stop SCRIPTS execution and raise a SCSI interrupt. */ @@ -402,44 +431,196 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase) lsi_set_phase(s, new_phase); } + +/* Resume SCRIPTS execution after a DMA operation. */ +static void lsi_resume_script(LSIState *s) +{ + if (s->waiting != 2) { + s->waiting = 0; + lsi_execute_script(s); + } else { + s->waiting = 0; + } +} + +/* Initiate a SCSI layer data transfer. */ static void lsi_do_dma(LSIState *s, int out) { - uint8_t buf[TARGET_PAGE_SIZE]; - uint32_t addr; uint32_t count; - int n; + uint32_t addr; - count = s->dbc; - addr = s->dnad; - DPRINTF("DMA %s addr=0x%08x len=%d avail=%d\n", out ? "out" : "in", - addr, count, s->data_len); - /* ??? Too long transfers are truncated. Don't know if this is the - correct behavior. */ - if (count > s->data_len) { - /* If the DMA length is greater then the device data length then - a phase mismatch will occur. */ - count = s->data_len; - s->dbc = count; - lsi_bad_phase(s, out, PHASE_ST); + if (!s->current_dma_len) { + /* Wait until data is available. */ + DPRINTF("DMA no data available\n"); + return; } + count = s->dbc; + if (count > s->current_dma_len) + count = s->current_dma_len; + DPRINTF("DMA addr=0x%08x len=%d\n", s->dnad, count); + + addr = s->dnad; s->csbc += count; + s->dnad += count; + s->dbc -= count; + + if (s->dma_buf == NULL) { + s->dma_buf = scsi_get_buf(s->current_dev, s->current_tag); + } /* ??? Set SFBR to first data byte. */ - while (count) { - n = (count > TARGET_PAGE_SIZE) ? TARGET_PAGE_SIZE : count; + if (out) { + cpu_physical_memory_read(addr, s->dma_buf, count); + } else { + cpu_physical_memory_write(addr, s->dma_buf, count); + } + s->current_dma_len -= count; + if (s->current_dma_len == 0) { + s->dma_buf = NULL; if (out) { - cpu_physical_memory_read(addr, buf, n); - scsi_write_data(s->current_dev, buf, n); + /* Write the data. */ + scsi_write_data(s->current_dev, s->current_tag); } else { - scsi_read_data(s->current_dev, buf, n); - cpu_physical_memory_write(addr, buf, n); + /* Request any remaining data. */ + scsi_read_data(s->current_dev, s->current_tag); } - addr += n; - count -= n; + } else { + s->dma_buf += count; + lsi_resume_script(s); + } +} + + +/* Add a command to the queue. */ +static void lsi_queue_command(LSIState *s) +{ + lsi_queue *p; + + DPRINTF("Queueing tag=0x%x\n", s->current_tag); + if (s->queue_len == s->active_commands) { + s->queue_len++; + s->queue = realloc(s->queue, s->queue_len * sizeof(lsi_queue)); } + p = &s->queue[s->active_commands++]; + p->tag = s->current_tag; + p->pending = 0; + p->out = (s->sstat1 & PHASE_MASK) == PHASE_DO; } +/* Queue a byte for a MSG IN phase. */ +static void lsi_add_msg_byte(LSIState *s, uint8_t data) +{ + if (s->msg_len >= LSI_MAX_MSGIN_LEN) { + BADF("MSG IN data too long\n"); + } else { + DPRINTF("MSG IN 0x%02x\n", data); + s->msg[s->msg_len++] = data; + } +} + +/* Perform reselection to continue a command. */ +static void lsi_reselect(LSIState *s, uint32_t tag) +{ + lsi_queue *p; + int n; + int id; + + p = NULL; + for (n = 0; n < s->active_commands; n++) { + p = &s->queue[n]; + if (p->tag == tag) + break; + } + if (n == s->active_commands) { + BADF("Reselected non-existant command tag=0x%x\n", tag); + return; + } + id = (tag >> 8) & 0xf; + s->ssid = id | 0x80; + DPRINTF("Reselected target %d\n", id); + s->current_dev = s->scsi_dev[id]; + s->current_tag = tag; + s->scntl1 |= LSI_SCNTL1_CON; + lsi_set_phase(s, PHASE_MI); + s->msg_action = p->out ? 2 : 3; + s->current_dma_len = p->pending; + s->dma_buf = NULL; + lsi_add_msg_byte(s, 0x80); + if (s->current_tag & LSI_TAG_VALID) { + lsi_add_msg_byte(s, 0x20); + lsi_add_msg_byte(s, tag & 0xff); + } + + s->active_commands--; + if (n != s->active_commands) { + s->queue[n] = s->queue[s->active_commands]; + } +} + +/* Record that data is available for a queued command. Returns zero if + the device was reselected, nonzero if the IO is deferred. */ +static int lsi_queue_tag(LSIState *s, uint32_t tag, uint32_t arg) +{ + lsi_queue *p; + int i; + for (i = 0; i < s->active_commands; i++) { + p = &s->queue[i]; + if (p->tag == tag) { + if (p->pending) { + BADF("Multiple IO pending for tag %d\n", tag); + } + p->pending = arg; + if (s->waiting == 1) { + /* Reselect device. */ + lsi_reselect(s, tag); + return 0; + } else { + DPRINTF("Queueing IO tag=0x%x\n", tag); + p->pending = arg; + return 1; + } + } + } + BADF("IO with unknown tag %d\n", tag); + return 1; +} + +/* Callback to indicate that the SCSI layer has completed a transfer. */ +static void lsi_command_complete(void *opaque, int reason, uint32_t tag, + uint32_t arg) +{ + LSIState *s = (LSIState *)opaque; + int out; + + out = (s->sstat1 & PHASE_MASK) == PHASE_DO; + if (reason == SCSI_REASON_DONE) { + DPRINTF("Command complete sense=%d\n", (int)arg); + s->sense = arg; + if (s->waiting && s->dbc != 0) { + /* Raise phase mismatch for short transfers. */ + lsi_bad_phase(s, out, PHASE_ST); + } else { + lsi_set_phase(s, PHASE_ST); + } + lsi_resume_script(s); + return; + } + + if (s->waiting == 1 || tag != s->current_tag) { + if (lsi_queue_tag(s, tag, arg)) + return; + } + DPRINTF("Data ready tag=0x%x len=%d\n", tag, arg); + s->current_dma_len = arg; + if (!s->waiting) + return; + if (s->waiting == 1 || s->dbc == 0) { + lsi_resume_script(s); + } else { + lsi_do_dma(s, out); + } +} static void lsi_do_command(LSIState *s) { @@ -451,36 +632,37 @@ static void lsi_do_command(LSIState *s) s->dbc = 16; cpu_physical_memory_read(s->dnad, buf, s->dbc); s->sfbr = buf[0]; - n = scsi_send_command(s->current_dev, 0, buf, s->current_lun); + n = scsi_send_command(s->current_dev, s->current_tag, buf, s->current_lun); if (n > 0) { - s->data_len = n; lsi_set_phase(s, PHASE_DI); + scsi_read_data(s->current_dev, s->current_tag); } else if (n < 0) { - s->data_len = -n; lsi_set_phase(s, PHASE_DO); + scsi_write_data(s->current_dev, s->current_tag); + } + if (n && s->current_dma_len == 0) { + /* Command did not complete immediately so disconnect. */ + lsi_add_msg_byte(s, 2); /* SAVE DATA POINTER */ + lsi_add_msg_byte(s, 4); /* DISCONNECT */ + lsi_set_phase(s, PHASE_MI); + s->msg_action = 1; + lsi_queue_command(s); } -} - -static void lsi_command_complete(void *opaque, uint32_t tag, int sense) -{ - LSIState *s = (LSIState *)opaque; - - DPRINTF("Command complete sense=%d\n", sense); - s->sense = sense; - lsi_set_phase(s, PHASE_ST); } static void lsi_do_status(LSIState *s) { + uint8_t sense; DPRINTF("Get status len=%d sense=%d\n", s->dbc, s->sense); if (s->dbc != 1) BADF("Bad Status move\n"); s->dbc = 1; - s->msg = s->sense; - cpu_physical_memory_write(s->dnad, &s->msg, 1); - s->sfbr = s->msg; + sense = s->sense; + s->sfbr = sense; + cpu_physical_memory_write(s->dnad, &sense, 1); lsi_set_phase(s, PHASE_MI); - s->msg = 0; /* COMMAND COMPLETE */ + s->msg_action = 1; + lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ } static void lsi_disconnect(LSIState *s) @@ -491,55 +673,114 @@ static void lsi_disconnect(LSIState *s) static void lsi_do_msgin(LSIState *s) { - DPRINTF("Message in len=%d\n", s->dbc); - s->dbc = 1; - s->sfbr = s->msg; - cpu_physical_memory_write(s->dnad, &s->msg, 1); - if (s->msg == 0) { - lsi_disconnect(s); + int len; + DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len); + s->sfbr = s->msg[0]; + len = s->msg_len; + if (len > s->dbc) + len = s->dbc; + cpu_physical_memory_write(s->dnad, s->msg, len); + /* Linux drivers rely on the last byte being in the SIDL. */ + s->sidl = s->msg[len - 1]; + s->msg_len -= len; + if (s->msg_len) { + memmove(s->msg, s->msg + len, s->msg_len); } else { /* ??? Check if ATN (not yet implemented) is asserted and maybe switch to PHASE_MO. */ - lsi_set_phase(s, PHASE_CMD); + switch (s->msg_action) { + case 0: + lsi_set_phase(s, PHASE_CMD); + break; + case 1: + lsi_disconnect(s); + break; + case 2: + lsi_set_phase(s, PHASE_DO); + break; + case 3: + lsi_set_phase(s, PHASE_DI); + break; + default: + abort(); + } } } +/* Read the next byte during a MSGOUT phase. */ +static uint8_t lsi_get_msgbyte(LSIState *s) +{ + uint8_t data; + cpu_physical_memory_read(s->dnad, &data, 1); + s->dnad++; + s->dbc--; + return data; +} + static void lsi_do_msgout(LSIState *s) { uint8_t msg; + int len; DPRINTF("MSG out len=%d\n", s->dbc); - if (s->dbc != 1) { - /* Multibyte messages not implemented. */ - s->msg = 7; /* MESSAGE REJECT */ - //s->dbc = 1; - //lsi_bad_phase(s, 1, PHASE_MI); - lsi_set_phase(s, PHASE_MI); - return; - } - cpu_physical_memory_read(s->dnad, &msg, 1); - s->sfbr = msg; - s->dnad++; - - switch (msg) { - case 0x00: - DPRINTF("Got Disconnect\n"); - lsi_disconnect(s); - return; - case 0x08: - DPRINTF("Got No Operation\n"); - lsi_set_phase(s, PHASE_CMD); - return; - } - if ((msg & 0x80) == 0) { - DPRINTF("Unimplemented message 0x%d\n", msg); - s->msg = 7; /* MESSAGE REJECT */ - lsi_bad_phase(s, 1, PHASE_MI); - return; + while (s->dbc) { + msg = lsi_get_msgbyte(s); + s->sfbr = msg; + + switch (msg) { + case 0x00: + DPRINTF("MSG: Disconnect\n"); + lsi_disconnect(s); + break; + case 0x08: + DPRINTF("MSG: No Operation\n"); + lsi_set_phase(s, PHASE_CMD); + break; + case 0x01: + len = lsi_get_msgbyte(s); + msg = lsi_get_msgbyte(s); + DPRINTF("Extended message 0x%x (len %d)\n", msg, len); + switch (msg) { + case 1: + DPRINTF("SDTR (ignored)\n"); + s->dbc -= 2; + break; + case 3: + DPRINTF("WDTR (ignored)\n"); + s->dbc -= 1; + break; + default: + goto bad; + } + break; + case 0x20: /* SIMPLE queue */ + s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; + DPRINTF("SIMPLE queue tag=0x%x\n", s->current_tag & 0xff); + break; + case 0x21: /* HEAD of queue */ + BADF("HEAD queue not implemented\n"); + s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; + break; + case 0x22: /* ORDERED queue */ + BADF("ORDERED queue not implemented\n"); + s->current_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; + break; + default: + if ((msg & 0x80) == 0) { + goto bad; + } + s->current_lun = msg & 7; + DPRINTF("Select LUN %d\n", s->current_lun); + lsi_set_phase(s, PHASE_CMD); + break; + } } - s->current_lun = msg & 7; - DPRINTF("Select LUN %d\n", s->current_lun); - lsi_set_phase(s, PHASE_CMD); + return; +bad: + BADF("Unimplemented message 0x%02x\n", msg); + lsi_set_phase(s, PHASE_MI); + lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */ + s->msg_action = 0; } /* Sign extend a 24-bit value. */ @@ -564,6 +805,23 @@ static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) } } +static void lsi_wait_reselect(LSIState *s) +{ + int i; + DPRINTF("Wait Reselect\n"); + if (s->current_dma_len) + BADF("Reselect with pending DMA\n"); + for (i = 0; i < s->active_commands; i++) { + if (s->queue[i].pending) { + lsi_reselect(s, s->queue[i].tag); + break; + } + } + if (s->current_dma_len == 0) { + s->waiting = 1; + } +} + static void lsi_execute_script(LSIState *s) { uint32_t insn; @@ -597,6 +855,7 @@ again: offset = sxt24(addr); cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8); s->dbc = cpu_to_le32(buf[0]); + s->rbc = s->dbc; addr = cpu_to_le32(buf[1]); } if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) { @@ -606,12 +865,20 @@ again: break; } s->dnad = addr; + /* ??? Set ESA. */ + s->ia = s->dsp - 8; switch (s->sstat1 & 0x7) { case PHASE_DO: + s->waiting = 2; lsi_do_dma(s, 1); + if (s->waiting) + s->waiting = 3; break; case PHASE_DI: + s->waiting = 2; lsi_do_dma(s, 0); + if (s->waiting) + s->waiting = 3; break; case PHASE_CMD: lsi_do_command(s); @@ -634,8 +901,6 @@ again: s->sbc = s->dbc; s->rbc -= s->dbc; s->ua = addr + s->dbc; - /* ??? Set ESA. */ - s->ia = s->dsp - 8; break; case 1: /* IO or Read/Write instruction. */ @@ -655,9 +920,13 @@ again: s->dnad = addr; switch (opcode) { case 0: /* Select */ + s->sdid = id; + if (s->current_dma_len && (s->ssid & 0xf) == id) { + DPRINTF("Already reselected by target %d\n", id); + break; + } s->sstat0 |= LSI_SSTAT0_WOA; s->scntl1 &= ~LSI_SCNTL1_IARB; - s->sdid = id; if (id >= LSI_MAX_DEVS || !s->scsi_dev[id]) { DPRINTF("Selected absent target %d\n", id); lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); @@ -670,6 +939,7 @@ again: it only applies in low-level mode (unimplemented). lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ s->current_dev = s->scsi_dev[id]; + s->current_tag = id << 8; s->scntl1 |= LSI_SCNTL1_CON; if (insn & (1 << 3)) { s->socl |= LSI_SOCL_ATN; @@ -681,8 +951,7 @@ again: s->scntl1 &= ~LSI_SCNTL1_CON; break; case 2: /* Wait Reselect */ - DPRINTF("Wait Reselect\n"); - s->waiting = 1; + lsi_wait_reselect(s); break; case 3: /* Set */ DPRINTF("Set%s%s%s%s\n", @@ -731,9 +1000,9 @@ again: data8 = (insn >> 8) & 0xff; opcode = (insn >> 27) & 7; operator = (insn >> 24) & 7; - DPRINTF("%s reg 0x%x %s data8 %d%s\n", + DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n", opcode_names[opcode - 5], reg, - operator_names[operator], data8, + operator_names[operator], data8, s->sfbr, (insn & (1 << 23)) ? " SFBR" : ""); op0 = op1 = 0; switch (opcode) { @@ -770,7 +1039,7 @@ again: op0 |= op1; break; case 3: /* XOR */ - op0 |= op1; + op0 ^= op1; break; case 4: /* AND */ op0 &= op1; @@ -778,6 +1047,7 @@ again: case 5: /* SHR */ op1 = op0 & 1; op0 = (op0 >> 1) | (s->carry << 7); + s->carry = op1; break; case 6: /* ADD */ op0 += op1; @@ -899,8 +1169,9 @@ again: n = (insn & 7); reg = (insn >> 16) & 0xff; if (insn & (1 << 24)) { - DPRINTF("Load reg 0x%x size %d addr 0x%08x\n", reg, n, addr); cpu_physical_memory_read(addr, data, n); + DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n, + addr, *(int *)data); for (i = 0; i < n; i++) { lsi_reg_writeb(s, reg + i, data[i]); } @@ -953,6 +1224,8 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) return s->sdid; case 0x07: /* GPREG0 */ return 0x7f; + case 0xa: /* SSID */ + return s->ssid; case 0xb: /* SBCL */ /* ??? This is not correct. However it's (hopefully) only used for diagnostics, so should be ok. */ @@ -1041,13 +1314,22 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) return s->stest2; case 0x4f: /* STEST3 */ return s->stest3; + case 0x50: /* SIDL */ + /* This is needed by the linux drivers. We currently only update it + during the MSG IN phase. */ + return s->sidl; case 0x52: /* STEST4 */ return 0xe0; case 0x56: /* CCNTL0 */ return s->ccntl0; case 0x57: /* CCNTL1 */ return s->ccntl1; - case 0x58: case 0x59: /* SBDL */ + case 0x58: /* SBDL */ + /* Some drivers peek at the data bus during the MSG IN phase. */ + if ((s->sstat1 & PHASE_MASK) == PHASE_MI) + return s->msg[0]; + return 0; + case 0x59: /* SBDL high */ return 0; CASE_GET_REG32(mmrs, 0xa0) CASE_GET_REG32(mmws, 0xa4) @@ -1108,7 +1390,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) break; case 0x02: /* SCNTL2 */ val &= ~(LSI_SCNTL2_WSR | LSI_SCNTL2_WSS); - s->scntl3 = val; + s->scntl2 = val; break; case 0x03: /* SCNTL3 */ s->scntl3 = val; @@ -1119,8 +1401,18 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) case 0x05: /* SXFER */ s->sxfer = val; break; + case 0x06: /* SDID */ + if ((val & 0xf) != (s->ssid & 0xf)) + BADF("Destination ID does not match SSID\n"); + s->sdid = val & 0xf; + break; case 0x07: /* GPREG0 */ break; + case 0x08: /* SFBR */ + /* The CPU is not allowed to write to this register. However the + SCRIPTS register move instructions are. */ + s->sfbr = val; + break; case 0x0c: case 0x0d: case 0x0e: case 0x0f: /* Linux writes to these readonly registers on startup. */ return; @@ -1134,7 +1426,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) s->istat0 &= ~LSI_ISTAT0_INTF; lsi_update_irq(s); } - if (s->waiting && val & LSI_ISTAT0_SIGP) { + if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) { DPRINTF("Woken by SIGP\n"); s->waiting = 0; s->dsp = s->dnad; @@ -1143,10 +1435,13 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) if (val & LSI_ISTAT0_SRST) { lsi_soft_reset(s); } + break; case 0x16: /* MBOX0 */ s->mbox0 = val; + break; case 0x17: /* MBOX1 */ s->mbox1 = val; + break; case 0x1b: /* CTEST3 */ s->ctest3 = val & 0x0f; break; @@ -1163,19 +1458,19 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) } s->ctest5 = val; break; - case 0x2c: /* DSPS[0:7] */ + case 0x2c: /* DSP[0:7] */ s->dsp &= 0xffffff00; s->dsp |= val; break; - case 0x2d: /* DSPS[8:15] */ + case 0x2d: /* DSP[8:15] */ s->dsp &= 0xffff00ff; s->dsp |= val << 8; break; - case 0x2e: /* DSPS[16:23] */ + case 0x2e: /* DSP[16:23] */ s->dsp &= 0xff00ffff; s->dsp |= val << 16; break; - case 0x2f: /* DSPS[14:31] */ + case 0x2f: /* DSP[24:31] */ s->dsp &= 0x00ffffff; s->dsp |= val << 24; if ((s->dmode & LSI_DMODE_MAN) == 0 @@ -1475,10 +1770,10 @@ static void lsi_io_writel(void *opaque, uint32_t addr, uint32_t val) lsi_reg_writeb(s, addr, val & 0xff); lsi_reg_writeb(s, addr + 1, (val >> 8) & 0xff); lsi_reg_writeb(s, addr + 2, (val >> 16) & 0xff); - lsi_reg_writeb(s, addr + 2, (val >> 24) & 0xff); + lsi_reg_writeb(s, addr + 3, (val >> 24) & 0xff); } -static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num, +static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { LSIState *s = (LSIState *)pci_dev; @@ -1493,7 +1788,7 @@ static void lsi_io_mapfunc(PCIDevice *pci_dev, int region_num, register_ioport_read(addr, 256, 4, lsi_io_readl, s); } -static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num, +static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { LSIState *s = (LSIState *)pci_dev; @@ -1503,7 +1798,7 @@ static void lsi_ram_mapfunc(PCIDevice *pci_dev, int region_num, cpu_register_physical_memory(addr + 0, 0x2000, s->ram_io_addr); } -static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num, +static void lsi_mmio_mapfunc(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type) { LSIState *s = (LSIState *)pci_dev; @@ -1531,7 +1826,7 @@ void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id) scsi_disk_destroy(s->scsi_dev[id]); } DPRINTF("Attaching block device %d\n", id); - s->scsi_dev[id] = scsi_disk_init(bd, lsi_command_complete, s); + s->scsi_dev[id] = scsi_disk_init(bd, 1, lsi_command_complete, s); } void *lsi_scsi_init(PCIBus *bus, int devfn) @@ -1563,6 +1858,9 @@ void *lsi_scsi_init(PCIBus *bus, int devfn) PCI_ADDRESS_SPACE_MEM, lsi_mmio_mapfunc); pci_register_io_region((struct PCIDevice *)s, 2, 0x2000, PCI_ADDRESS_SPACE_MEM, lsi_ram_mapfunc); + s->queue = qemu_malloc(sizeof(lsi_queue)); + s->queue_len = 1; + s->active_commands = 0; lsi_soft_reset(s);