📄 lsi53c895a.c
字号:
if (s->dma_buf == NULL) { s->dma_buf = scsi_get_buf(s->current_dev, s->current_tag); } /* ??? Set SFBR to first data byte. */ 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) { /* Write the data. */ scsi_write_data(s->current_dev, s->current_tag); } else { /* Request any remaining data. */ scsi_read_data(s->current_dev, s->current_tag); } } 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){ uint8_t buf[16]; int n; DPRINTF("Send command len=%d\n", s->dbc); if (s->dbc > 16) s->dbc = 16; cpu_physical_memory_read(s->dnad, buf, s->dbc); s->sfbr = buf[0]; n = scsi_send_command(s->current_dev, s->current_tag, buf, s->current_lun); if (n > 0) { lsi_set_phase(s, PHASE_DI); scsi_read_data(s->current_dev, s->current_tag); } else if (n < 0) { 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_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; sense = s->sense; s->sfbr = sense; cpu_physical_memory_write(s->dnad, &sense, 1); lsi_set_phase(s, PHASE_MI); s->msg_action = 1; lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */}static void lsi_disconnect(LSIState *s){ s->scntl1 &= ~LSI_SCNTL1_CON; s->sstat1 &= ~PHASE_MASK;}static void lsi_do_msgin(LSIState *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. */ 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); 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; } } 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. */static inline int32_t sxt24(int32_t n){ return (n << 8) >> 8;}static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count){ int n; uint8_t buf[TARGET_PAGE_SIZE]; DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count); while (count) { n = (count > TARGET_PAGE_SIZE) ? TARGET_PAGE_SIZE : count; cpu_physical_memory_read(src, buf, n); cpu_physical_memory_write(dest, buf, n); src += n; dest += n; count -= n; }}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; uint32_t addr; int opcode; s->istat1 |= LSI_ISTAT1_SRUN;again: insn = read_dword(s, s->dsp); addr = read_dword(s, s->dsp + 4); DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr); s->dsps = addr; s->dcmd = insn >> 24; s->dsp += 8; switch (insn >> 30) { case 0: /* Block move. */ if (s->sist1 & LSI_SIST1_STO) { DPRINTF("Delayed select timeout\n"); lsi_stop_script(s); break; } s->dbc = insn & 0xffffff; s->rbc = s->dbc; if (insn & (1 << 29)) { /* Indirect addressing. */ addr = read_dword(s, addr); } else if (insn & (1 << 28)) { uint32_t buf[2]; int32_t offset; /* Table indirect addressing. */ offset = sxt24(addr); cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8); s->dbc = cpu_to_le32(buf[0]); addr = cpu_to_le32(buf[1]); } if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) { DPRINTF("Wrong phase got %d expected %d\n", s->sstat1 & PHASE_MASK, (insn >> 24) & 7); lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); break; } s->dnad = addr; 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); break; case PHASE_ST: lsi_do_status(s); break; case PHASE_MO: lsi_do_msgout(s); break; case PHASE_MI: lsi_do_msgin(s); break; default: BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK); exit(1); } s->dfifo = s->dbc & 0xff; s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3); 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. */ opcode = (insn >> 27) & 7; if (opcode < 5) { uint32_t id; if (insn & (1 << 25)) { id = read_dword(s, s->dsa + sxt24(insn)); } else { id = addr; } id = (id >> 16) & 0xf; if (insn & (1 << 26)) { addr = s->dsp + sxt24(addr); } 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; 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); lsi_disconnect(s); break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -