📄 ncr53c9x.c
字号:
/* see how much we got ... */ fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES); if (!fifocnt) fifo_stuck++; else fifo_stuck = 0; ESPDATA(("\rgot %d st %x ph %x", fifocnt, esp->sreg, newphase)); /* read fifo */ for(j=0;j<fifocnt;j++) SCptr->SCp.ptr[i++] = esp_read(eregs->esp_fdata); ESPDATA(("(%d) ", i)); /* how many to go ?? */ hmuch -= fifocnt; /* break if status phase !! */ if(newphase == ESP_STATP) { /* clear int. */ esp->ireg = esp_read(eregs->esp_intrpt); break; } } else {#define MAX_FIFO 8 /* how much will fit ? */ int this_count = MAX_FIFO - fifocnt; if (this_count > hmuch) this_count = hmuch; /* fill fifo */ for(j=0;j<this_count;j++) esp_write(eregs->esp_fdata, SCptr->SCp.ptr[i++]); /* how many left if this goes out ?? */ hmuch -= this_count; /* 'go' ... */ esp_cmd(esp, eregs, ESP_CMD_TI); /* wait for 'got it' */ timeout = 1000000; while (!((esp->sreg=esp_read(eregs->esp_status)) & ESP_STAT_INTR) && --timeout) udelay(2); if (timeout == 0) printk("DRQ dataout timeout! \n"); newphase = esp->sreg & ESP_STAT_PMASK; /* need to check how much was sent ?? */ fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES); ESPDATA(("\rsent %d st %x ph %x", this_count - fifocnt, esp->sreg, newphase)); ESPDATA(("(%d) ", i)); /* break if status phase !! */ if(newphase == ESP_STATP) { /* clear int. */ esp->ireg = esp_read(eregs->esp_intrpt); break; } } /* clear int. */ esp->ireg = esp_read(eregs->esp_intrpt); ESPDATA(("ir %x ... ", esp->ireg)); if (hmuch == 0) ESPDATA(("done! \n")); restore_flags(flags); /* check new bus phase */ if (newphase != oldphase && i < esp->current_transfer_size) { /* something happened; disconnect ?? */ ESPDATA(("phase change, dropped out with %d done ... ", i)); break; } /* check int. status */ if (esp->ireg & ESP_INTR_DC) { /* disconnect */ ESPDATA(("disconnect; %d transfered ... ", i)); break; } else if (esp->ireg & ESP_INTR_FDONE) { /* function done */ ESPDATA(("function done; %d transfered ... ", i)); break; } /* XXX fixme: bail out on stall */ if (fifo_stuck > 10) { /* we're stuck */ ESPDATA(("fifo stall; %d transfered ... ", i)); break; } } ESPDATA(("\n")); /* check successful completion ?? */ if (thisphase == in_dataout) hmuch += fifocnt; /* stuck?? adjust data pointer ...*/ /* tell do_data_finale how much was transfered */ esp->current_transfer_size -= hmuch; /* still not completely sure on this one ... */ return /*do_intr_end*/ do_work_bus /*do_phase_determine*/ ; /* * end PIO */ } return do_intr_end;}/* See how successful the data transfer was. */static int esp_do_data_finale(struct NCR_ESP *esp, struct ESP_regs *eregs){ Scsi_Cmnd *SCptr = esp->current_SC; int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0; if(esp->dma_led_off) esp->dma_led_off(esp); ESPDATA(("esp_do_data_finale: ")); if(SCptr->SCp.phase == in_datain) { if(esp->sreg & ESP_STAT_PERR) { /* Yuck, parity error. The ESP asserts ATN * so that we can go to message out phase * immediately and inform the target that * something bad happened. */ ESPLOG(("esp%d: data bad parity detected.\n", esp->esp_id)); esp->cur_msgout[0] = INITIATOR_ERROR; esp->msgout_len = 1; } if(esp->dma_drain) esp->dma_drain(esp); } if(esp->dma_invalidate) esp->dma_invalidate(esp); /* This could happen for the above parity error case. */ if(!(esp->ireg == ESP_INTR_BSERV)) { /* Please go to msgout phase, please please please... */ ESPLOG(("esp%d: !BSERV after data, probably to msgout\n", esp->esp_id)); return esp_do_phase_determine(esp, eregs); } /* Check for partial transfers and other horrible events. */ fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES); ecount = esp_getcount(eregs); if(esp->fas_premature_intr_workaround) ecount -= 0x40; bytes_sent = esp->current_transfer_size; ESPDATA(("trans_sz=%d, ", bytes_sent)); if(!(esp->sreg & ESP_STAT_TCNT)) bytes_sent -= ecount; if(SCptr->SCp.phase == in_dataout) bytes_sent -= fifocnt; ESPDATA(("bytes_sent=%d (ecount=%d, fifocnt=%d), ", bytes_sent, ecount, fifocnt)); /* If we were in synchronous mode, check for peculiarities. */ if(SCptr->device->sync_max_offset) bogus_data = esp100_sync_hwbug(esp, eregs, SCptr, fifocnt); else esp_cmd(esp, eregs, ESP_CMD_FLUSH); /* Until we are sure of what has happened, we are certainly * in the dark. */ esp_advance_phase(SCptr, in_the_dark); /* Check for premature interrupt condition. Can happen on FAS2x6 * chips. QLogic recommends a workaround by overprogramming the * transfer counters, but this makes doing scatter-gather impossible. * Until there is a way to disable scatter-gather for a single target, * and not only for the entire host adapter as it is now, the workaround * is way to expensive performance wise. * Instead, it turns out that when this happens the target has disconnected * allready but it doesn't show in the interrupt register. Compensate for * that here to try and avoid a SCSI bus reset. */ if(!esp->fas_premature_intr_workaround && (fifocnt == 1) && sreg_dataoutp(esp->sreg)) { ESPLOG(("esp%d: Premature interrupt, enabling workaround\n", esp->esp_id));#if 0 /* Disable scatter-gather operations, they are not possible * when using this workaround. */ esp->ehost->sg_tablesize = 0; esp->ehost->use_clustering = ENABLE_CLUSTERING; esp->fas_premature_intr_workaround = 1; bytes_sent = 0; if(SCptr->use_sg) { ESPLOG(("esp%d: Aborting scatter-gather operation\n", esp->esp_id)); esp->cur_msgout[0] = ABORT; esp->msgout_len = 1; esp->msgout_ctr = 0; esp_cmd(esp, eregs, ESP_CMD_SATN); esp_setcount(eregs, 0xffff); esp_cmd(esp, eregs, ESP_CMD_NULL); esp_cmd(esp, eregs, ESP_CMD_TPAD | ESP_CMD_DMA); return do_intr_end; }#else /* Just set the disconnected bit. That's what appears to * happen anyway. The state machine will pick it up when * we return. */ esp->ireg |= ESP_INTR_DC;#endif } if(bytes_sent < 0) { /* I've seen this happen due to lost state in this * driver. No idea why it happened, but allowing * this value to be negative caused things to * lock up. This allows greater chance of recovery. * In fact every time I've seen this, it has been * a driver bug without question. */ ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id)); ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n", esp->esp_id, esp->current_transfer_size, fifocnt, ecount)); ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n", esp->esp_id, SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual)); ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id, SCptr->target)); SCptr->device->borken = 1; SCptr->device->sync = 0; bytes_sent = 0; } /* Update the state of our transfer. */ SCptr->SCp.ptr += bytes_sent; SCptr->SCp.this_residual -= bytes_sent; if(SCptr->SCp.this_residual < 0) { /* shit */ ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id)); SCptr->SCp.this_residual = 0; } /* Maybe continue. */ if(!bogus_data) { ESPDATA(("!bogus_data, ")); /* NO MATTER WHAT, we advance the scatterlist, * if the target should decide to disconnect * in between scatter chunks (which is common) * we could die horribly! I used to have the sg * advance occur only if we are going back into * (or are staying in) a data phase, you can * imagine the hell I went through trying to * figure this out. */ if(!SCptr->SCp.this_residual && SCptr->SCp.buffers_residual) advance_sg(esp, SCptr);#ifdef DEBUG_ESP_DATA if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) { ESPDATA(("to more data\n")); } else { ESPDATA(("to new phase\n")); }#endif return esp_do_phase_determine(esp, eregs); } /* Bogus data, just wait for next interrupt. */ ESPLOG(("esp%d: bogus_data during end of data phase\n", esp->esp_id)); return do_intr_end;}/* We received a non-good status return at the end of * running a SCSI command. This is used to decide if * we should clear our synchronous transfer state for * such a device when that happens. * * The idea is that when spinning up a disk or rewinding * a tape, we don't want to go into a loop re-negotiating * synchronous capabilities over and over. */static int esp_should_clear_sync(Scsi_Cmnd *sp){ unchar cmd1 = sp->cmnd[0]; unchar cmd2 = sp->data_cmnd[0]; /* These cases are for spinning up a disk and * waiting for that spinup to complete. */ if(cmd1 == START_STOP || cmd2 == START_STOP) return 0; if(cmd1 == TEST_UNIT_READY || cmd2 == TEST_UNIT_READY) return 0; /* One more special case for SCSI tape drives, * this is what is used to probe the device for * completion of a rewind or tape load operation. */ if(sp->device->type == TYPE_TAPE) { if(cmd1 == MODE_SENSE || cmd2 == MODE_SENSE) return 0; } return 1;}/* Either a command is completing or a target is dropping off the bus * to continue the command in the background so we can do other work. */static int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs){ Scsi_Cmnd *SCptr = esp->current_SC; int rval; rval = skipahead2(esp, eregs, SCptr, in_status, in_msgindone, in_freeing); if(rval) return rval; if(esp->ireg != ESP_INTR_DC) { ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id)); return do_reset_bus; /* target will not drop BSY... */ } esp->msgout_len = 0; esp->prevmsgout = NOP; if(esp->prevmsgin == COMMAND_COMPLETE) { /* Normal end of nexus. */ if(esp->disconnected_SC) esp_cmd(esp, eregs, ESP_CMD_ESEL); if(SCptr->SCp.Status != GOOD && SCptr->SCp.Status != CONDITION_GOOD && ((1<<SCptr->target) & esp->targets_present) && SCptr->device->sync && SCptr->device->sync_max_offset) { /* SCSI standard says that the synchronous capabilities * should be renegotiated at this point. Most likely * we are about to request sense from this target * in which case we want to avoid using sync * transfers until we are sure of the current target * state. */ ESPMISC(("esp: Status <%d> for target %d lun %d\n", SCptr->SCp.Status, SCptr->target, SCptr->lun)); /* But don't do this when spinning up a disk at * boot time while we poll for completion as it * fills up the console with messages. Also, tapes * can report not ready many times right after * loading up a tape. */ if(esp_should_clear_sync(SCptr) != 0) SCptr->device->sync = 0; } ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun)); esp_done(esp, ((SCptr->SCp.Status & 0xff) | ((SCptr->SCp.Message & 0xff)<<8) | (DID_OK << 16))); } else if(esp->prevmsgin == DISCONNECT) { /* Normal disconnect. */ esp_cmd(esp, eregs, ESP_CMD_ESEL); ESPDISC(("D<%02x,%02x>", SCptr->target, SCptr->lun)); append_SC(&esp->disconnected_SC, SCptr); esp->current_SC = NULL; if(esp->issue_SC) esp_exec_cmd(esp); } else { /* Driver bug, we do not expect a disconnect here * and should not have advanced the state engine * to in_freeing. */ ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n", esp->esp_id)); return do_reset_bus; } return do_intr_end;}/* When a reselect occurs, and we cannot find the command to * reconnect to in our queues, we do this. */static int esp_bad_reconnect(struct NCR_ESP *esp){ Scsi_Cmnd *sp; ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", esp->esp_id)); ESPLOG(("QUEUE DUMP\n")); sp = esp->issue_SC; ESPLOG(("esp%d: issue_SC[", esp->esp_id)); while(sp) { ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); sp = (Scsi_Cmnd *) sp->host_scribble; } ESPLOG(("]\n")); sp = esp->current_SC; ESPLOG(("esp%d: current_SC[", esp->esp_id)); while(sp) { ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); sp = (Scsi_Cmnd *) sp->host_scribble; } ESPLOG(("]\n")); sp = esp->disconnected_SC; ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); while(sp) { ESPLOG(("<%02x,%02x>", sp->target, sp->lun)); sp = (Scsi_Cmnd *) sp->host_scribble; } ESPLOG(("]\n")); return do_reset_bus;}/* Do the needy when a target tries to reconnect to us. */static int esp_do_reconnect(struct NCR_ESP *esp, struct ESP_regs *eregs){ int lun, target; Scsi_Cmnd *SCptr; /* Check for all bogus conditions first. */ target = reconnect_target(esp, eregs); if(target < 0) { ESPDISC(("bad bus bits\n")); return do_reset_bus; } lun = reconnect_lun(esp, eregs); if(lun < 0) { ESPDISC(("target=%2x, bad identify msg\n", target)); return do_reset_bus; } /* Things look ok... */ ESPDISC(("R<%02x,%02x>", target, lun)); esp_cmd(esp, eregs, ESP_CMD_FLUSH); if(esp100_reconnect_hwbug(esp, eregs)) return do_reset_bus; esp_cmd(esp, eregs
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -