📄 in2000.c
字号:
* status byte is stored. */ cmd->SCp.Status = ILLEGAL_STATUS_BYTE;/* We need to disable interrupts before messing with the input * queue and calling in2000_execute(). */ save_flags(flags); cli(); /* * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE * commands are added to the head of the queue so that the desired * sense data is not lost before REQUEST_SENSE executes. */ if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) { cmd->host_scribble = (uchar *)hostdata->input_Q; hostdata->input_Q = cmd; } else { /* find the end of the queue */ for (tmp=(Scsi_Cmnd *)hostdata->input_Q; tmp->host_scribble; tmp=(Scsi_Cmnd *)tmp->host_scribble) ; tmp->host_scribble = (uchar *)cmd; }/* We know that there's at least one command in 'input_Q' now. * Go see if any of them are runnable! */ in2000_execute(cmd->host);DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid)) restore_flags(flags); return 0;}/* * This routine attempts to start a scsi command. If the host_card is * already connected, we give up immediately. Otherwise, look through * the input_Q, using the first command we find that's intended * for a currently non-busy target/lun. * Note that this function is always called with interrupts already * disabled (either from in2000_queuecommand() or in2000_intr()). */static void in2000_execute (struct Scsi_Host *instance){struct IN2000_hostdata *hostdata;Scsi_Cmnd *cmd, *prev;int i;unsigned short *sp;unsigned short f;unsigned short flushbuf[16]; hostdata = (struct IN2000_hostdata *)instance->hostdata;DB(DB_EXECUTE,printk("EX(")) if (hostdata->selecting || hostdata->connected) {DB(DB_EXECUTE,printk(")EX-0 ")) return; } /* * Search through the input_Q for a command destined * for an idle target/lun. */ cmd = (Scsi_Cmnd *)hostdata->input_Q; prev = 0; while (cmd) { if (!(hostdata->busy[cmd->target] & (1 << cmd->lun))) break; prev = cmd; cmd = (Scsi_Cmnd *)cmd->host_scribble; } /* quit if queue empty or all possible targets are busy */ if (!cmd) {DB(DB_EXECUTE,printk(")EX-1 ")) return; } /* remove command from queue */ if (prev) prev->host_scribble = cmd->host_scribble; else hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;#ifdef PROC_STATISTICS hostdata->cmd_cnt[cmd->target]++;#endif/* * Start the selection process */ if (is_dir_out(cmd)) write_3393(hostdata,WD_DESTINATION_ID, cmd->target); else write_3393(hostdata,WD_DESTINATION_ID, cmd->target | DSTID_DPD);/* Now we need to figure out whether or not this command is a good * candidate for disconnect/reselect. We guess to the best of our * ability, based on a set of hierarchical rules. When several * devices are operating simultaneously, disconnects are usually * an advantage. In a single device system, or if only 1 device * is being accessed, transfers usually go faster if disconnects * are not allowed: * * + Commands should NEVER disconnect if hostdata->disconnect = * DIS_NEVER (this holds for tape drives also), and ALWAYS * disconnect if hostdata->disconnect = DIS_ALWAYS. * + Tape drive commands should always be allowed to disconnect. * + Disconnect should be allowed if disconnected_Q isn't empty. * + Commands should NOT disconnect if input_Q is empty. * + Disconnect should be allowed if there are commands in input_Q * for a different target/lun. In this case, the other commands * should be made disconnect-able, if not already. * * I know, I know - this code would flunk me out of any * "C Programming 101" class ever offered. But it's easy * to change around and experiment with for now. */ cmd->SCp.phase = 0; /* assume no disconnect */ if (hostdata->disconnect == DIS_NEVER) goto no; if (hostdata->disconnect == DIS_ALWAYS) goto yes; if (cmd->device->type == 1) /* tape drive? */ goto yes; if (hostdata->disconnected_Q) /* other commands disconnected? */ goto yes; if (!(hostdata->input_Q)) /* input_Q empty? */ goto no; for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev; prev=(Scsi_Cmnd *)prev->host_scribble) { if ((prev->target != cmd->target) || (prev->lun != cmd->lun)) { for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev; prev=(Scsi_Cmnd *)prev->host_scribble) prev->SCp.phase = 1; goto yes; } } goto no;yes: cmd->SCp.phase = 1;#ifdef PROC_STATISTICS hostdata->disc_allowed_cnt[cmd->target]++;#endifno: write_3393(hostdata,WD_SOURCE_ID,((cmd->SCp.phase)?SRCID_ER:0)); write_3393(hostdata,WD_TARGET_LUN, cmd->lun); write_3393(hostdata,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]); hostdata->busy[cmd->target] |= (1 << cmd->lun); if ((hostdata->level2 <= L2_NONE) || (hostdata->sync_stat[cmd->target] == SS_UNSET)) { /* * Do a 'Select-With-ATN' command. This will end with * one of the following interrupts: * CSR_RESEL_AM: failure - can try again later. * CSR_TIMEOUT: failure - give up. * CSR_SELECT: success - proceed. */ hostdata->selecting = cmd;/* Every target has its own synchronous transfer setting, kept in * the sync_xfer array, and a corresponding status byte in sync_stat[]. * Each target's sync_stat[] entry is initialized to SS_UNSET, and its * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET * means that the parameters are undetermined as yet, and that we * need to send an SDTR message to this device after selection is * complete. We set SS_FIRST to tell the interrupt routine to do so, * unless we don't want to even _try_ synchronous transfers: In this * case we set SS_SET to make the defaults final. */ if (hostdata->sync_stat[cmd->target] == SS_UNSET) { if (hostdata->sync_off & (1 << cmd->target)) hostdata->sync_stat[cmd->target] = SS_SET; else hostdata->sync_stat[cmd->target] = SS_FIRST; } hostdata->state = S_SELECTING; write_3393_count(hostdata,0); /* this guarantees a DATA_PHASE interrupt */ write_3393_cmd(hostdata,WD_CMD_SEL_ATN); } else { /* * Do a 'Select-With-ATN-Xfer' command. This will end with * one of the following interrupts: * CSR_RESEL_AM: failure - can try again later. * CSR_TIMEOUT: failure - give up. * anything else: success - proceed. */ hostdata->connected = cmd; write_3393(hostdata,WD_COMMAND_PHASE, 0); /* copy command_descriptor_block into WD chip * (take advantage of auto-incrementing) */ write1_io(WD_CDB_1, IO_WD_ADDR); for (i=0; i<cmd->cmd_len; i++) write1_io(cmd->cmnd[i], IO_WD_DATA); /* The wd33c93 only knows about Group 0, 1, and 5 commands when * it's doing a 'select-and-transfer'. To be safe, we write the * size of the CDB into the OWN_ID register for every case. This * way there won't be problems with vendor-unique, audio, etc. */ write_3393(hostdata, WD_OWN_ID, cmd->cmd_len); /* When doing a non-disconnect command, we can save ourselves a DATA * phase interrupt later by setting everything up now. With writes we * need to pre-fill the fifo; if there's room for the 32 flush bytes, * put them in there too - that'll avoid a fifo interrupt. Reads are * somewhat simpler. * KLUDGE NOTE: It seems that you can't completely fill the fifo here: * This results in the IO_FIFO_COUNT register rolling over to zero, * and apparently the gate array logic sees this as empty, not full, * so the 3393 chip is never signalled to start reading from the * fifo. Or maybe it's seen as a permanent fifo interrupt condition. * Regardless, we fix this by temporarily pretending that the fifo * is 16 bytes smaller. (I see now that the old driver has a comment * about "don't fill completely" in an analogous place - must be the * same deal.) This results in CDROM, swap partitions, and tape drives * needing an extra interrupt per write command - I think we can live * with that! */ if (!(cmd->SCp.phase)) { write_3393_count(hostdata, cmd->SCp.this_residual); write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS); write1_io(0, IO_FIFO_WRITE); /* clear fifo counter, write mode */ if (is_dir_out(cmd)) { hostdata->fifo = FI_FIFO_WRITING; if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16) ) i = IN2000_FIFO_SIZE - 16; cmd->SCp.have_data_in = i; /* this much data in fifo */ i >>= 1; /* Gulp. Assuming modulo 2. */ sp = (unsigned short *)cmd->SCp.ptr; f = hostdata->io_base + IO_FIFO;#ifdef FAST_WRITE_IO FAST_WRITE2_IO();#else while (i--) write2_io(*sp++,IO_FIFO);#endif /* Is there room for the flush bytes? */ if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) { sp = flushbuf; i = 16;#ifdef FAST_WRITE_IO FAST_WRITE2_IO();#else while (i--) write2_io(0,IO_FIFO);#endif } } else { write1_io(0, IO_FIFO_READ); /* put fifo in read mode */ hostdata->fifo = FI_FIFO_READING; cmd->SCp.have_data_in = 0; /* nothing transfered yet */ } } else { write_3393_count(hostdata,0); /* this guarantees a DATA_PHASE interrupt */ } hostdata->state = S_RUNNING_LEVEL2; write_3393_cmd(hostdata,WD_CMD_SEL_ATN_XFER); } /* * Since the SCSI bus can handle only 1 connection at a time, * we get out of here now. If the selection fails, or when * the command disconnects, we'll come back to this routine * to search the input_Q again... */ DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))}static void transfer_pio(uchar *buf, int cnt, int data_in_dir, struct IN2000_hostdata *hostdata){uchar asr;DB(DB_TRANSFER,printk("(%p,%d,%s)",buf,cnt,data_in_dir?"in":"out")) write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); write_3393_count(hostdata,cnt); write_3393_cmd(hostdata,WD_CMD_TRANS_INFO); if (data_in_dir) { do { asr = READ_AUX_STAT(); if (asr & ASR_DBR) *buf++ = read_3393(hostdata,WD_DATA); } while (!(asr & ASR_INT)); } else { do { asr = READ_AUX_STAT(); if (asr & ASR_DBR) write_3393(hostdata,WD_DATA, *buf++); } while (!(asr & ASR_INT)); } /* Note: we are returning with the interrupt UN-cleared. * Since (presumably) an entire I/O operation has * completed, the bus phase is probably different, and * the interrupt routine will discover this when it * responds to the uncleared int. */}static void transfer_bytes(Scsi_Cmnd *cmd, int data_in_dir){struct IN2000_hostdata *hostdata;unsigned short *sp;unsigned short f;int i; hostdata = (struct IN2000_hostdata *)cmd->host->hostdata;/* Normally, you'd expect 'this_residual' to be non-zero here. * In a series of scatter-gather transfers, however, this * routine will usually be called with 'this_residual' equal * to 0 and 'buffers_residual' non-zero. This means that a * previous transfer completed, clearing 'this_residual', and * now we need to setup the next scatter-gather buffer as the * source or destination for THIS transfer. */ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { ++cmd->SCp.buffer; --cmd->SCp.buffers_residual; cmd->SCp.this_residual = cmd->SCp.buffer->length; cmd->SCp.ptr = cmd->SCp.buffer->address; }/* Set up hardware registers */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -