📄 acornscsi.c.2
字号:
if (len + offset > (1 << 12)) this_len = (1 << 12) - offset; else this_len = len; __acornscsi_out (host->card.io_ram + (offset << 1), ptr, this_len); offset += this_len; ptr += this_len; len -= this_len; if (offset == (1 << 12)) { offset = 0; page ++; outb ((page & 0x3f) | host->card.page_reg, host->card.io_page); } } outb (host->card.page_reg, host->card.io_page);}/* ========================================================================================= * On-board DMA routines *//* * Prototype: void acornscsi_dmastop (AS_Host *host) * Purpose : stop all DMA * Params : host - host on which to stop DMA * Notes : This is called when leaving DATA IN/OUT phase, * or when interface is RESET */static inlinevoid acornscsi_dma_stop (AS_Host *host){ dmac_write (host->dma.io_port, MASKREG, MASK_ON); dmac_clearintr (host->dma.io_intr_clear);#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma (host, "stop"));#endif}/* * Function: void acornscsi_dma_setup (AS_Host *host, dmadir_t direction) * Purpose : setup DMA controller for data transfer * Params : host - host to setup * direction - data transfer direction * Notes : This is called when entering DATA I/O phase, not * while we're in a DATA I/O phase */staticvoid acornscsi_dma_setup (AS_Host *host, dmadir_t direction){ unsigned int address, length, mode; host->dma.direction = direction; dmac_write (host->dma.io_port, MASKREG, MASK_ON); if (direction == DMA_OUT) {#if (DEBUG & DEBUG_NO_WRITE) if (NO_WRITE & (1 << host->SCpnt->target)) { printk (KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", host->host->host_no, acornscsi_target (host)); return; }#endif mode = DMAC_WRITE; } else mode = DMAC_READ; /* * Allocate some buffer space, limited to half the buffer size */ length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & (DMAC_BUFFER_SIZE - 1); /* * Transfer data to DMA memory */ if (direction == DMA_OUT) acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; dmac_write (host->dma.io_port, TXCNTLO, length); dmac_write (host->dma.io_port, TXCNTHI, length >> 8); dmac_write (host->dma.io_port, TXADRLO, address); dmac_write (host->dma.io_port, TXADRMD, address >> 8); dmac_write (host->dma.io_port, TXADRHI, 0); dmac_write (host->dma.io_port, MODECON, mode); dmac_write (host->dma.io_port, MASKREG, MASK_OFF);#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma (host, "strt"));#endif host->dma.xfer_setup = 1; }}/* * Function: void acornscsi_dma_cleanup (AS_Host *host) * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct * Params : host - host to finish * Notes : This is called when a command is: * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT * : This must not return until all transfers are completed. */staticvoid acornscsi_dma_cleanup (AS_Host *host){ dmac_write (host->dma.io_port, MASKREG, MASK_ON); dmac_clearintr (host->dma.io_intr_clear); /* * Check for a pending transfer */ if (host->dma.xfer_required) { host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) acornscsi_data_read (host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length); } /* * Has a transfer been setup? */ if (host->dma.xfer_setup) { unsigned int transferred; host->dma.xfer_setup = 0;#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma (host, "clup"));#endif /* * Calculate number of bytes transferred from DMA. */ transferred = dmac_address (host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; if (host->dma.direction == DMA_IN) acornscsi_data_read (host, host->scsi.SCp.ptr, host->dma.start_addr, transferred); /* * Update SCSI pointers */ acornscsi_data_updateptr (host, &host->scsi.SCp, transferred); }}/* * Function: void acornscsi_dmacintr (AS_Host *host) * Purpose : handle interrupts from DMAC device * Params : host - host to process * Notes : If reading, we schedule the read to main memory & * allow the transfer to continue. * : If writing, we fill the onboard DMA memory from main * memory. * : Called whenever DMAC finished it's current transfer. */staticvoid acornscsi_dma_intr (AS_Host *host){ unsigned int address, length, transferred;#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma (host, "inti"));#endif dmac_write (host->dma.io_port, MASKREG, MASK_ON); dmac_clearintr (host->dma.io_intr_clear); /* * Calculate amount transferred via DMA */ transferred = dmac_address (host->dma.io_port) - host->dma.start_addr; host->dma.transferred += transferred; /* * Schedule DMA transfer off board */ if (host->dma.direction == DMA_IN) { host->dma.xfer_start = host->dma.start_addr; host->dma.xfer_length = transferred; host->dma.xfer_ptr = host->scsi.SCp.ptr; host->dma.xfer_required = 1; } acornscsi_data_updateptr (host, &host->scsi.SCp, transferred); /* * Allocate some buffer space, limited to half the buffer size */ length = min (host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); if (length) { host->dma.start_addr = address = host->dma.free_addr; host->dma.free_addr = (host->dma.free_addr + length) & (DMAC_BUFFER_SIZE - 1); /* * Transfer data to DMA memory */ if (host->dma.direction == DMA_OUT) acornscsi_data_write (host, host->scsi.SCp.ptr, host->dma.start_addr, length); length -= 1; dmac_write (host->dma.io_port, TXCNTLO, length); dmac_write (host->dma.io_port, TXCNTHI, length >> 8); dmac_write (host->dma.io_port, TXADRLO, address); dmac_write (host->dma.io_port, TXADRMD, address >> 8); dmac_write (host->dma.io_port, TXADRHI, 0); dmac_write (host->dma.io_port, MASKREG, MASK_OFF);#if (DEBUG & DEBUG_DMA) DBG(host->SCpnt, acornscsi_dumpdma (host, "into"));#endif } else { host->dma.xfer_setup = 0;#if 0 /* * If the interface still wants more, then this is an error. * We give it another byte, but we also attempt to raise an * attention condition. We continue giving one byte until * the device recognises the attention. */ if (dmac_read (host->dma.io_port, STATUS) & STATUS_RQ0) { acornscsi_abortcmd (host, host->SCpnt->tag); dmac_write (host->dma.io_port, TXCNTLO, 0); dmac_write (host->dma.io_port, TXCNTHI, 0); dmac_write (host->dma.io_port, TXADRLO, 0); dmac_write (host->dma.io_port, TXADRMD, 0); dmac_write (host->dma.io_port, TXADRHI, 0); dmac_write (host->dma.io_port, MASKREG, MASK_OFF); }#endif }}/* * Function: void acornscsi_dma_xfer (AS_Host *host) * Purpose : transfer data between AcornSCSI and memory * Params : host - host to process */staticvoid acornscsi_dma_xfer (AS_Host *host){ host->dma.xfer_required = 0; if (host->dma.direction == DMA_IN) acornscsi_data_read (host, host->dma.xfer_ptr, host->dma.xfer_start, host->dma.xfer_length);}/* * Function: void acornscsi_dma_adjust (AS_Host *host) * Purpose : adjust DMA pointers & count for bytes transfered to * SBIC but not SCSI bus. * Params : host - host to adjust DMA count for */staticvoid acornscsi_dma_adjust (AS_Host *host){ if (host->dma.xfer_setup) { signed long transferred;#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) DBG(host->SCpnt, acornscsi_dumpdma (host, "adji"));#endif /* * Calculate correct DMA address - DMA is ahead of SCSI bus while * writing. * host->scsi.SCp.have_data_in is the number of bytes * actually transferred to/from the SCSI bus. * host->dma.transferred is the number of bytes transferred * over DMA since host->dma.start_addr was last set. * * real_dma_addr = host->dma.start_addr + host->scsi.SCp.have_data_in * - host->dma.transferred */ transferred = host->scsi.SCp.have_data_in - host->dma.transferred; if (transferred < 0) printk ("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", host->host->host_no, acornscsi_target (host), transferred); else if (transferred == 0) host->dma.xfer_setup = 0; else { transferred += host->dma.start_addr; dmac_write (host->dma.io_port, TXADRLO, transferred); dmac_write (host->dma.io_port, TXADRMD, transferred >> 8); dmac_write (host->dma.io_port, TXADRHI, transferred >> 16);#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) DBG(host->SCpnt, acornscsi_dumpdma (host, "adjo"));#endif } }}/* ========================================================================================= * Data I/O *//* * Function: void acornscsi_sendcommand (AS_Host *host) * Purpose : send a command to a target * Params : host - host which is connected to target */staticvoid acornscsi_sendcommand (AS_Host *host){ Scsi_Cmnd *SCpnt = host->SCpnt; unsigned int asr; unsigned char *cmdptr, *cmdend; sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0); sbic_arm_writenext (host->scsi.io_port, 0); sbic_arm_writenext (host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); sbic_arm_write (host->scsi.io_port, CMND, CMND_XFERINFO); cmdptr = SCpnt->cmnd + host->scsi.SCp.sent_command; cmdend = SCpnt->cmnd + SCpnt->cmd_len; while (cmdptr < cmdend) { asr = sbic_arm_read (host->scsi.io_port, ASR); if (asr & ASR_DBR) sbic_arm_write (host->scsi.io_port, DATA, *cmdptr++); else if (asr & ASR_INT) break; } if (cmdptr >= cmdend) host->scsi.SCp.sent_command = cmdptr - SCpnt->cmnd; host->scsi.phase = PHASE_COMMAND;}staticvoid acornscsi_sendmessage (AS_Host *host){ unsigned int message_length = msgqueue_msglength (&host->scsi.msgs); int msglen; char *msg;#if (DEBUG & DEBUG_MESSAGES) printk ("scsi%d.%c: sending message ", host->host->host_no, acornscsi_target (host));#endif switch (message_length) { case 0: sbic_arm_write (host->scsi.io_port, CMND, CMND_XFERINFO | CMND_SBT); while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); sbic_arm_write (host->scsi.io_port, DATA, NOP); host->scsi.last_message = NOP;#if (DEBUG & DEBUG_MESSAGES) printk ("NOP");#endif break; case 1: sbic_arm_write (host->scsi.io_port, CMND, CMND_XFERINFO | CMND_SBT); msg = msgqueue_getnextmsg (&host->scsi.msgs, &msglen); while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); sbic_arm_write (host->scsi.io_port, DATA, msg[0]); host->scsi.last_message = msg[0];#if (DEBUG & DEBUG_MESSAGES) print_msg (msg);#endif break; default: /* * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14) * 'When a target sends this (MESSAGE_REJECT) message, it * shall change to MESSAGE IN phase and send this message * prior to requesting additional message bytes from the * initiator. This provides an interlock so that the * initiator can determine which message byte is rejected. */ sbic_arm_write (host->scsi.io_port, TRANSCNTH, 0); sbic_arm_writenext (host->scsi.io_port, 0); sbic_arm_writenext (host->scsi.io_port, message_length); sbic_arm_write (host->scsi.io_port, CMND, CMND_XFERINFO); while ((msg = msgqueue_getnextmsg (&host->scsi.msgs, &msglen)) != NULL) { unsigned int asr, i;#if (DEBUG & DEBUG_MESSAGES) print_msg (msg);#endif for (i = 0; i < msglen;) { asr = sbic_arm_read (host->scsi.io_port, ASR); if (asr & ASR_DBR) sbic_arm_write (host->scsi.io_port, DATA, msg[i++]); if (asr & ASR_INT) break; } host->scsi.last_message = msg[0]; if (msg[0] == EXTENDED_MESSAGE) host->scsi.last_message |= msg[2] << 8; if (asr & ASR_INT) break; } break; }#if (DEBUG & DEBUG_MESSAGES) printk ("\n");#endif}/* * Function: void acornscsi_readstatusbyte (AS_Host *host) * Purpose : Read status byte from connected target * Params : host - host connected to target */staticvoid acornscsi_readstatusbyte (AS_Host *host){ sbic_arm_write (host->scsi.io_port, CMND, CMND_XFERINFO|CMND_SBT); while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); host->scsi.SCp.Status = sbic_arm_read (host->scsi.io_port, DATA); host->scsi.phase = PHASE_STATUSIN;}/* * Function: unsigned char acornscsi_readmessagebyte (AS_Host *host) * Purpose : Read one message byte from connected target * Params : host - host connected to target */staticunsigned char acornscsi_readmessagebyte (AS_Host *host){ unsigned char message; sbic_arm_write (host->scsi.io_port, CMND, CMND_XFERINFO | CMND_SBT); while ((sbic_arm_read (host->scsi.io_port, ASR) & ASR_DBR) == 0); message = sbic_arm_read (host->scsi.io_port, DATA);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -