⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wd33c93.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
   wd33c93_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. * * wd33c93_execute() is always called with interrupts disabled or from * the wd33c93_intr itself, which means that a wd33c93 interrupt * cannot occur while we are in here. */static void wd33c93_execute (struct Scsi_Host *instance){struct WD33C93_hostdata *hostdata;wd33c93_regs *regp;Scsi_Cmnd *cmd, *prev;int i;   hostdata = (struct WD33C93_hostdata *)instance->hostdata;   regp = hostdata->regp;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_wd33c93(regp, WD_DESTINATION_ID, cmd->target);   else      write_wd33c93(regp, 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_wd33c93(regp, WD_SOURCE_ID, ((cmd->SCp.phase)?SRCID_ER:0));   write_wd33c93(regp, WD_TARGET_LUN, cmd->lun);   write_wd33c93(regp,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 SX_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. * If we've been asked not to try synchronous transfers on this * target (and _all_ luns within it), we'll still send the SDTR message * later, but at that time we'll negotiate for async by specifying a * sync fifo depth of 0. */      if (hostdata->sync_stat[cmd->target] == SS_UNSET)            hostdata->sync_stat[cmd->target] = SS_FIRST;      hostdata->state = S_SELECTING;      write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */      write_wd33c93_cmd(regp, 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_wd33c93(regp, WD_COMMAND_PHASE, 0);   /* copy command_descriptor_block into WD chip    * (take advantage of auto-incrementing)    */      regp->SASR = WD_CDB_1;      for (i=0; i<cmd->cmd_len; i++)         regp->SCMD = cmd->cmnd[i];   /* 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_wd33c93(regp, WD_OWN_ID, cmd->cmd_len);   /* When doing a non-disconnect command with DMA, we can save    * ourselves a DATA phase interrupt later by setting everything    * up ahead of time.    */      if ((cmd->SCp.phase == 0) && (hostdata->no_dma == 0)) {         if (hostdata->dma_setup(cmd,                     (is_dir_out(cmd))?DATA_OUT_DIR:DATA_IN_DIR))            write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */         else {            write_wd33c93_count(regp, cmd->SCp.this_residual);            write_wd33c93(regp,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);            hostdata->dma = D_DMA_RUNNING;            }         }      else         write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */      hostdata->state = S_RUNNING_LEVEL2;      write_wd33c93_cmd(regp, 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(wd33c93_regs *regp, uchar *buf, int cnt,                  int data_in_dir, struct WD33C93_hostdata *hostdata){uchar asr;DB(DB_TRANSFER,printk("(%p,%d,%s:",buf,cnt,data_in_dir?"in":"out"))   write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);   write_wd33c93_count(regp,cnt);   write_wd33c93_cmd(regp, WD_CMD_TRANS_INFO);   if (data_in_dir) {      do {         asr = READ_AUX_STAT();         if (asr & ASR_DBR)            *buf++ = read_wd33c93(regp, WD_DATA);         } while (!(asr & ASR_INT));      }   else {      do {         asr = READ_AUX_STAT();         if (asr & ASR_DBR)            write_wd33c93(regp, 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(wd33c93_regs *regp, Scsi_Cmnd *cmd, int data_in_dir){struct WD33C93_hostdata *hostdata;unsigned long length;   hostdata = (struct WD33C93_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;      }   write_wd33c93(regp,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);/* 'hostdata->no_dma' is TRUE if we don't even want to try DMA. * Update 'this_residual' and 'ptr' after 'transfer_pio()' returns. */   if (hostdata->no_dma)      goto use_transfer_pio;/* 'dma_setup()' will return TRUE if we can't do DMA. * Update 'this_residual' and 'ptr' after 'transfer_pio()' returns. */   else if (hostdata->dma_setup(cmd, data_in_dir)) {use_transfer_pio:#ifdef PROC_STATISTICS      hostdata->pio_cnt++;#endif      transfer_pio(regp, (uchar *)cmd->SCp.ptr, cmd->SCp.this_residual,                         data_in_dir, hostdata);      length = cmd->SCp.this_residual;      cmd->SCp.this_residual = read_wd33c93_count(regp);      cmd->SCp.ptr += (length - cmd->SCp.this_residual);      }/* We are able to do DMA (in fact, the Amiga hardware is * already going!), so start up the wd33c93 in DMA mode. * We set 'hostdata->dma' = D_DMA_RUNNING so that when the * transfer completes and causes an interrupt, we're * reminded to tell the Amiga to shut down its end. We'll * postpone the updating of 'this_residual' and 'ptr' * until then. */   else {#ifdef PROC_STATISTICS      hostdata->dma_cnt++;#endif      write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);      write_wd33c93_count(regp,cmd->SCp.this_residual);      if ((hostdata->level2 >= L2_DATA) ||          (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {         write_wd33c93(regp, WD_COMMAND_PHASE, 0x45);         write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);         hostdata->state = S_RUNNING_LEVEL2;         }      else         write_wd33c93_cmd(regp, WD_CMD_TRANS_INFO);      hostdata->dma = D_DMA_RUNNING;      }}void wd33c93_intr (struct Scsi_Host *instance){struct WD33C93_hostdata *hostdata;Scsi_Cmnd *patch, *cmd;wd33c93_regs *regp;uchar asr, sr, phs, id, lun, *ucp, msg;unsigned long length, flags;   hostdata = (struct WD33C93_hostdata *)instance->hostdata;   regp = hostdata->regp;   asr = READ_AUX_STAT();   if (!(asr & ASR_INT) || (asr & ASR_BSY))      return;   save_flags(flags);#ifdef PROC_STATISTICS   hostdata->int_cnt++;#endif   cmd = (Scsi_Cmnd *)hostdata->connected;   /* assume we're connected */   sr = read_wd33c93(regp, WD_SCSI_STATUS);  /* clear the interrupt */   phs = read_wd33c93(regp, WD_COMMAND_PHASE);DB(DB_INTR,printk("{%02x:%02x-",asr,sr))/* After starting a DMA transfer, the next interrupt * is guaranteed to be in response to completion of * the transfer. Since the Amiga DMA hardware runs in * in an open-ended fashion, it needs to be told when * to stop; do that here if D_DMA_RUNNING is true. * Also, we have to update 'this_residual' and 'ptr' * based on the contents of the TRANSFER_COUNT register, * in case the device decided to do an intermediate * disconnect (a device may do this if it has to do a * seek, or just to be nice and let other devices have * some bus time during long transfers). After doing * whatever is needed, we go on and service the WD3393 * interrupt normally. */   if (hostdata->dma == D_DMA_RUNNING) {DB(DB_TRANSFER,printk("[%p/%d:",cmd->SCp.ptr,cmd->SCp.this_residual))      hostdata->dma_stop(cmd->host, cmd, 1);      hostdata->dma = D_DMA_OFF;      length = cmd->SCp.this_residual;      cmd->SCp.this_residual = read_wd33c93_count(regp);      cmd->SCp.ptr += (length - cmd->SCp.this_residual);DB(DB_TRANSFER,printk("%p/%d]",cmd->SCp.ptr,cmd->SCp.this_residual))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -