in2000.c
来自「linux 内核源代码」· C语言 代码 · 共 2,045 行 · 第 1/5 页
C
2,045 行
/* * 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->device->host); DB(DB_QUEUE_COMMAND, printk(")Q-%ld ", cmd->serial_number)) 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 = NULL; while (cmd) { if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->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->device->id]++;#endif/* * Start the selection process */ if (is_dir_out(cmd)) write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id); else write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | 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->device->id != cmd->device->id) || (prev->device->lun != cmd->device->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->device->id]++;#endif no: write_3393(hostdata, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0)); write_3393(hostdata, WD_TARGET_LUN, cmd->device->lun); write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun); if ((hostdata->level2 <= L2_NONE) || (hostdata->sync_stat[cmd->device->id] == 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->device->id] == SS_UNSET) { if (hostdata->sync_off & (1 << cmd->device->id)) hostdata->sync_stat[cmd->device->id] = SS_SET; else hostdata->sync_stat[cmd->device->id] = 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 transferred 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->serial_number))}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->device->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 = sg_virt(cmd->SCp.buffer); }/* Set up hardware registers */ write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); 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); /* zero counter, assume write *//* Reading is easy. Just issue the command and return - we'll * get an interrupt later when we have actual data to worry about. */ if (data_in_dir) { write1_io(0, IO_FIFO_READ); if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_3393(hostdata, WD_COMMAND_PHASE, 0x45); write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; } else write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); hostdata->fifo = FI_FIFO_READING; cmd->SCp.have_data_in = 0; return; }/* Writing is more involved - we'll start the WD chip and write as * much data to the fifo as we can right now. Later interrupts will * write any bytes that don't make it at this stage. */ if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { write_3393(hostdata, WD_COMMAND_PHASE, 0x45); write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER); hostdata->state = S_RUNNING_LEVEL2; } else write_3393_cmd(hostdata, WD_CMD_TRANS_INFO); hostdata->fifo = FI_FIFO_WRITING; sp = (unsigned short *) cmd->SCp.ptr; if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE) i = IN2000_FIFO_SIZE; cmd->SCp.have_data_in = i; i >>= 1; /* Gulp. We assume this_residual is modulo 2 */ f = hostdata->io_base + IO_FIFO;#ifdef FAST_WRITE_IO FAST_WRITE2_IO();#else while (i--)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?