extern inline void finish_req(SC_REQ *reqp) { int sps; struct scsipi_xfer *xs = reqp->xs; #ifdef REAL_DMA /* * If we bounced, free the bounce buffer */ if (reqp->dr_flag & DRIVER_BOUNCING) free_bounceb(reqp->bounceb); #endif /* REAL_DMA */ #ifdef DBG_REQ if (dbg_target_mask & (1 << reqp->targ_id)) show_request(reqp, "DONE"); #endif #ifdef DBG_ERR_RET if (reqp->xs->error != 0) show_request(reqp, "ERR_RET"); #endif /* * Return request to free-q */ sps = splbio(); reqp->next = free_head; free_head = reqp; splx(sps); xs->xs_status |= XS_STS_DONE; if (!(reqp->dr_flag & DRIVER_LINKCHK)) scsipi_done(xs); }
BOOL DR_ClearFeature(void) { #ifdef DEBUG show_request("CF"); #endif return(TRUE); }
BOOL DR_SetFeature(void) { #ifdef DEBUG show_request("SF"); #endif return(TRUE); }
BOOL DR_GetDescriptor(void) { #ifdef DEBUG show_request("GD"); #endif return(TRUE); }
BOOL DR_SetInterface(void) // Called when a Set Interface command is received { #ifdef DEBUG show_request("SI"); #endif AlternateSetting = SETUPDAT[2]; return(TRUE); // Handled by user code }
BOOL DR_SetConfiguration(void) // Called when a Set Configuration command is received { #ifdef DEBUG show_request("SC"); #endif Configuration = SETUPDAT[2]; return(TRUE); // Handled by user code }
BOOL DR_GetInterface(void) // Called when a Set Interface command is received { #ifdef DEBUG show_request("GI"); #endif EP0BUF[0] = AlternateSetting; EP0BCH = 0; EP0BCL = 1; return(TRUE); // Handled by user code }
BOOL DR_GetConfiguration(void) // Called when a Get Configuration command is received { #ifdef DEBUG show_request("GC"); #endif EP0BUF[0] = Configuration; EP0BCH = 0; EP0BCL = 1; return(TRUE); // Handled by user code }
/* * The body of the driver. */ static void scsi_main(struct ncr_softc *sc) { SC_REQ *req, *prev; int itype; int sps; /* * While running in the driver SCSI-interrupts are disabled. */ scsi_idisable(); ENABLE_NCR5380(sc); PID("scsi_main1"); for (;;) { sps = splbio(); if (!connected) { /* * Check if it is fair keep any exclusive access to DMA * claimed. If not, stop queueing new jobs so the discon_q * will be eventually drained and DMA can be given up. */ if (!fair_to_keep_dma()) goto main_exit; /* * Search through the issue-queue for a command * destined for a target that isn't busy. */ prev = NULL; for (req=issue_q; req != NULL; prev = req, req = req->next) { if (!(busy & (1 << req->targ_id))) { /* * Found one, remove it from the issue queue */ if (prev == NULL) issue_q = req->next; else prev->next = req->next; req->next = NULL; break; } } /* * When a request has just ended, we get here before an other * device detects that the bus is free and that it can * reconnect. The problem is that when this happens, we always * baffle the device because our (initiator) id is higher. This * can cause a sort of starvation on slow devices. So we check * for a pending reselection here. * Note that 'connected' will be non-null if the reselection * succeeds. */ if ((GET_5380_REG(NCR5380_IDSTAT) & (SC_S_SEL|SC_S_IO)) == (SC_S_SEL|SC_S_IO)){ if (req != NULL) { req->next = issue_q; issue_q = req; } splx(sps); reselect(sc); scsi_clr_ipend(); goto connected; } /* * The host is not connected and there is no request * pending, exit. */ if (req == NULL) { PID("scsi_main2"); goto main_exit; } /* * Re-enable interrupts before handling the request. */ splx(sps); #ifdef DBG_REQ if (dbg_target_mask & (1 << req->targ_id)) show_request(req, "TARGET"); #endif /* * We found a request. Try to connect to the target. If the * initiator fails arbitration, the command is put back in the * issue queue. */ if (scsi_select(req, 0)) { sps = splbio(); req->next = issue_q; issue_q = req; splx(sps); #ifdef DBG_REQ if (dbg_target_mask & (1 << req->targ_id)) ncr_tprint(req, "Select failed\n"); #endif } } else splx(sps); connected: if (connected) { /* * If the host is currently connected but a 'real-DMA' transfer * is in progress, the 'end-of-DMA' interrupt restarts main. * So quit. */ sps = splbio(); if (connected && (connected->dr_flag & DRIVER_IN_DMA)) { PID("scsi_main3"); goto main_exit; } splx(sps); /* * Let the target guide us through the bus-phases */ while (information_transfer(sc) == -1) ; } } /* NEVER TO REACH HERE */ panic("ncr5380-SCSI: not designed to come here"); main_exit: /* * We enter here with interrupts disabled. We are about to exit main * so interrupts should be re-enabled. Because interrupts are edge * triggered, we could already have missed the interrupt. Therefore * we check the IRQ-line here and re-enter when we really missed a * valid interrupt. */ PID("scsi_main4"); scsi_ienable(); /* * If we're not currently connected, enable reselection * interrupts. */ if (!connected) SET_5380_REG(NCR5380_IDSTAT, SC_HOST_ID); if (scsi_ipending()) { if ((itype = check_intr(sc)) != INTR_SPURIOUS) { scsi_idisable(); splx(sps); if (itype == INTR_RESEL) reselect(sc); #ifdef REAL_DMA else dma_ready(); #else else { if (pdma_ready()) goto connected; panic("Got DMA interrupt without DMA"); } #endif scsi_clr_ipend(); goto connected; }
/* * Carry out a request from the high level driver. */ static void ncr5380_scsi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) { struct scsipi_xfer *xs; struct ncr_softc *sc = device_private(chan->chan_adapter->adapt_dev); int sps, flags; SC_REQ *reqp, *link, *tmp; switch (req) { case ADAPTER_REQ_RUN_XFER: xs = arg; flags = xs->xs_control; /* * We do not queue RESET commands */ if (flags & XS_CTL_RESET) { scsi_reset_verbose(sc, "Got reset-command"); scsipi_done(xs); return; } /* * Get a request block */ sps = splbio(); if ((reqp = free_head) == 0) { xs->error = XS_RESOURCE_SHORTAGE; scsipi_done(xs); return; } free_head = reqp->next; reqp->next = NULL; splx(sps); /* * Initialize our private fields */ reqp->dr_flag = (flags & XS_CTL_POLL) ? DRIVER_NOINT : 0; reqp->phase = NR_PHASE; reqp->msgout = MSG_NOOP; reqp->status = SCSGOOD; reqp->message = 0xff; reqp->link = NULL; reqp->xs = xs; reqp->targ_id = xs->xs_periph->periph_target; reqp->targ_lun = xs->xs_periph->periph_lun; reqp->xdata_ptr = (u_char*)xs->data; reqp->xdata_len = xs->datalen; memcpy(&reqp->xcmd, xs->cmd, xs->cmdlen); reqp->xcmd_len = xs->cmdlen; reqp->xcmd.bytes[0] |= reqp->targ_lun << 5; #ifdef REAL_DMA /* * Check if DMA can be used on this request */ if (scsi_dmaok(reqp)) reqp->dr_flag |= DRIVER_DMAOK; #endif /* REAL_DMA */ /* * Insert the command into the issue queue. Note that * 'REQUEST SENSE' commands are inserted at the head of the * queue since any command will clear the existing contingent * allegience condition and the sense data is only valid while * the condition exists. * When possible, link the command to a previous command to * the same target. This is not very sensible when AUTO_SENSE * is not defined! Interrupts are disabled while we are * fiddling with the issue-queue. */ sps = splbio(); link = NULL; if ((issue_q == NULL) || (reqp->xcmd.opcode == SCSI_REQUEST_SENSE)) { reqp->next = issue_q; issue_q = reqp; } else { tmp = issue_q; do { if (!link && (tmp->targ_id == reqp->targ_id) && !tmp->link) link = tmp; } while (tmp->next && (tmp = tmp->next)); tmp->next = reqp; #ifdef AUTO_SENSE if (link && (ncr_will_link & (1<<reqp->targ_id))) { link->link = reqp; link->xcmd.bytes[link->xs->cmdlen-2] |= 1; } #endif } #ifdef AUTO_SENSE /* * If we haven't already, check the target for link support. * Do this by prefixing the current command with a dummy * Request_Sense command, link the dummy to the current * command, and insert the dummy command at the head of the * issue queue. Set the DRIVER_LINKCHK flag so that we'll * ignore the results of the dummy command, since we only * care about whether it was accepted or not. */ if (!link && !(ncr_test_link & (1<<reqp->targ_id)) && (tmp = free_head) && !(reqp->dr_flag & DRIVER_NOINT)) { free_head = tmp->next; tmp->dr_flag = (reqp->dr_flag & ~DRIVER_DMAOK) | DRIVER_LINKCHK; tmp->phase = NR_PHASE; tmp->msgout = MSG_NOOP; tmp->status = SCSGOOD; tmp->xs = reqp->xs; tmp->targ_id = reqp->targ_id; tmp->targ_lun = reqp->targ_lun; memcpy(&tmp->xcmd, sense_cmd, sizeof(sense_cmd)); tmp->xcmd_len = sizeof(sense_cmd); tmp->xdata_ptr = (u_char *)&tmp->xs->sense.scsi_sense; tmp->xdata_len = sizeof(tmp->xs->sense.scsi_sense); ncr_test_link |= 1<<tmp->targ_id; tmp->link = reqp; tmp->xcmd.bytes[sizeof(sense_cmd)-2] |= 1; tmp->next = issue_q; issue_q = tmp; #ifdef DBG_REQ if (dbg_target_mask & (1 << tmp->targ_id)) show_request(tmp, "LINKCHK"); #endif } #endif splx(sps); #ifdef DBG_REQ if (dbg_target_mask & (1 << reqp->targ_id)) show_request(reqp, (reqp->xcmd.opcode == SCSI_REQUEST_SENSE) ? "HEAD":"TAIL"); #endif run_main(sc); return; case ADAPTER_REQ_GROW_RESOURCES: /* XXX Not supported. */ return; case ADAPTER_REQ_SET_XFER_MODE: /* XXX Not supported. */ return; } }
BOOL DR_VendorCmnd(void) { #ifdef DEBUG show_request("VC"); #endif switch(SETUPDAT[1]) { case 0x00: // 0x40, 0x00: reset { VC_Reset_IN(); VC_Reset_OUT(); break; } // 0x40, 0x01: set modem control // 0x40, 0x02: set flow control // 0x40, 0x03: set baud rate // 0x40, 0x04: set line property case 0x05: // 0xC0, 0x05: ? get status? reset? { #if 0 VC_Reset_IN(); VC_Reset_OUT(); #endif EP0BUF[0] = 0x36; EP0BUF[1] = 0x83; EP0BCH = 0; EP0BCL = 2; // Arm endpoint with # bytes to transfer break; } // 0x40, 0x09: set latency timer // 0xC0, 0x0A: get latency timer // 0x40, 0x0B: set bitbang mode (mode << 8) // 0xC0, 0x0C: get pins case 0x90: // 0xC0, 0x90: read eeprom ([4] has word addr) { BYTE addr = (SETUPDAT[4]<<1) & 0x7F; EP0BUF[0] = PROM[addr]; EP0BUF[1] = PROM[addr+1]; EP0BCH = 0; EP0BCL = 2; // Arm endpoint with # bytes to transfer break; } // 0x40, 0x91: write eeprom // 0x40, 0x92: erase eeprom default: { if(SETUPDAT[0] == 0xC0) { EP0BUF[0] = 0x36; EP0BUF[1] = 0x83; EP0BCH = 0; EP0BCL = 2; // Arm endpoint with # bytes to transfer }; } }; EP0CS |= bmHSNAK; // Acknowledge handshake phase of device request return(FALSE); // no error; command handled OK }