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

📄 wd33c93.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *    wd33c93.c - Linux-68k device driver for the Commodore *                Amiga A2091/590 SCSI controller card * * Copyright (c) 1996 John Shifflett, GeoLog Consulting *    john@geolog.com *    jshiffle@netcom.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * * Drew Eckhardt's excellent 'Generic NCR5380' sources from Linux-PC * provided much of the inspiration and some of the code for this * driver. Everything I know about Amiga DMA was gleaned from careful * reading of Hamish Mcdonald's original wd33c93 driver; in fact, I * borrowed shamelessly from all over that source. Thanks Hamish! * * _This_ driver is (I feel) an improvement over the old one in * several respects: * *    -  Target Disconnection/Reconnection  is now supported. Any *          system with more than one device active on the SCSI bus *          will benefit from this. The driver defaults to what I *          call 'adaptive disconnect' - meaning that each command *          is evaluated individually as to whether or not it should *          be run with the option to disconnect/reselect (if the *          device chooses), or as a "SCSI-bus-hog". * *    -  Synchronous data transfers are now supported. Because of *          a few devices that choke after telling the driver that *          they can do sync transfers, we don't automatically use *          this faster protocol - it can be enabled via the command- *          line on a device-by-device basis. * *    -  Runtime operating parameters can now be specified through *       the 'amiboot' or the 'insmod' command line. For amiboot do: *          "amiboot [usual stuff] wd33c93=blah,blah,blah" *       The defaults should be good for most people. See the comment *       for 'setup_strings' below for more details. * *    -  The old driver relied exclusively on what the Western Digital *          docs call "Combination Level 2 Commands", which are a great *          idea in that the CPU is relieved of a lot of interrupt *          overhead. However, by accepting a certain (user-settable) *          amount of additional interrupts, this driver achieves *          better control over the SCSI bus, and data transfers are *          almost as fast while being much easier to define, track, *          and debug. * * * TODO: *       more speed. linked commands. * * * People with bug reports, wish-lists, complaints, comments, * or improvements are asked to pah-leeez email me (John Shifflett) * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get * this thing into as good a shape as possible, and I'm positive * there are lots of lurking bugs and "Stupid Places". * * Updates: * * Added support for pre -A chips, which don't have advanced features * and will generate CSR_RESEL rather than CSR_RESEL_AM. *	Richard Hirst <richard@sleepie.demon.co.uk>  August 2000 */#include <linux/config.h>#include <linux/module.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/version.h>#include <linux/init.h>#include <asm/irq.h>#include <linux/blk.h>#include "scsi.h"#include "hosts.h"#define WD33C93_VERSION    "1.25"#define WD33C93_DATE       "09/Jul/1997"/* NOTE: 1.25 for m68k is related to in2000-1.31 for x86 *//* * Note - the following defines have been moved to 'wd33c93.h': * *    PROC_INTERFACE *    PROC_STATISTICS *    SYNC_DEBUG *    DEBUGGING_ON *    DEBUG_DEFAULTS * */#include "wd33c93.h"/* * 'setup_strings' is a single string used to pass operating parameters and * settings from the kernel/module command-line to the driver. 'setup_args[]' * is an array of strings that define the compile-time default values for * these settings. If Linux boots with an amiboot or insmod command-line, * those settings are combined with 'setup_args[]'. Note that amiboot * command-lines are prefixed with "wd33c93=" while insmod uses a * "setup_strings=" prefix. The driver recognizes the following keywords * (lower case required) and arguments: * * -  nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with *                    the 7 possible SCSI devices. Set a bit to negotiate for *                    asynchronous transfers on that device. To maintain *                    backwards compatibility, a command-line such as *                    "wd33c93=255" will be automatically translated to *                    "wd33c93=nosync:0xff". * -  nodma:x        -x = 1 to disable DMA, x = 0 to enable it. Argument is *                    optional - if not present, same as "nodma:1". * -  period:ns      -ns is the minimum # of nanoseconds in a SCSI data transfer *                    period. Default is 500; acceptable values are 250 - 1000. * -  disconnect:x   -x = 0 to never allow disconnects, 2 to always allow them. *                    x = 1 does 'adaptive' disconnects, which is the default *                    and generally the best choice. * -  debug:x        -If 'DEBUGGING_ON' is defined, x is a bit mask that causes *                    various types of debug output to printed - see the DB_xxx *                    defines in wd33c93.h * -  clock:x        -x = clock input in MHz for WD33c93 chip. Normal values *                    would be from 8 through 20. Default is 8. * -  next           -No argument. Used to separate blocks of keywords when *                    there's more than one host adapter in the system. * * Syntax Notes: * -  Numeric arguments can be decimal or the '0x' form of hex notation. There *    _must_ be a colon between a keyword and its numeric argument, with no *    spaces. * -  Keywords are separated by commas, no spaces, in the standard kernel *    command-line manner. * -  A keyword in the 'nth' comma-separated command-line member will overwrite *    the 'nth' element of setup_args[]. A blank command-line member (in *    other words, a comma with no preceding keyword) will _not_ overwrite *    the corresponding setup_args[] element. * -  If a keyword is used more than once, the first one applies to the first *    SCSI host found, the second to the second card, etc, unless the 'next' *    keyword is used to change the order. * * Some amiboot examples (for insmod, use 'setup_strings' instead of 'wd33c93'): * -  wd33c93=nosync:255 * -  wd33c93=nodma * -  wd33c93=nodma:1 * -  wd33c93=disconnect:2,nosync:0x08,period:250 * -  wd33c93=debug:0x1c *//* Normally, no defaults are specified */static char *setup_args[] =      {"","","","","","","","",""};/* filled in by 'insmod' */static char *setup_strings = 0;#ifdef MODULE_PARMMODULE_PARM(setup_strings, "s");#endifstatic inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num){   regp->SASR = reg_num;   mb();   return(regp->SCMD);}#define READ_AUX_STAT() (regp->SASR)static inline void write_wd33c93(wd33c93_regs *regp,uchar reg_num, uchar value){   regp->SASR = reg_num;   mb();   regp->SCMD = value;   mb();}static inline void write_wd33c93_cmd(wd33c93_regs *regp, uchar cmd){   regp->SASR = WD_COMMAND;   mb();   regp->SCMD = cmd;   mb();}static inline uchar read_1_byte(wd33c93_regs *regp){uchar asr;uchar x = 0;   write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);   write_wd33c93_cmd(regp, WD_CMD_TRANS_INFO|0x80);   do {      asr = READ_AUX_STAT();      if (asr & ASR_DBR)         x = read_wd33c93(regp, WD_DATA);      } while (!(asr & ASR_INT));   return x;}static void write_wd33c93_count(wd33c93_regs *regp,unsigned long value){   regp->SASR = WD_TRANSFER_COUNT_MSB;   mb();   regp->SCMD = value >> 16;   regp->SCMD = value >> 8;   regp->SCMD = value;   mb();}static unsigned long read_wd33c93_count(wd33c93_regs *regp){unsigned long value;   regp->SASR = WD_TRANSFER_COUNT_MSB;   mb();   value = regp->SCMD << 16;   value |= regp->SCMD << 8;   value |= regp->SCMD;   mb();   return value;}/* The 33c93 needs to be told which direction a command transfers its * data; we use this function to figure it out. Returns true if there * will be a DATA_OUT phase with this command, false otherwise. * (Thanks to Joerg Dorchain for the research and suggestion.) */static int is_dir_out(Scsi_Cmnd *cmd){   switch (cmd->cmnd[0]) {      case WRITE_6:           case WRITE_10:          case WRITE_12:      case WRITE_LONG:        case WRITE_SAME:        case WRITE_BUFFER:      case WRITE_VERIFY:      case WRITE_VERIFY_12:            case COMPARE:           case COPY:              case COPY_VERIFY:      case SEARCH_EQUAL:      case SEARCH_HIGH:       case SEARCH_LOW:      case SEARCH_EQUAL_12:   case SEARCH_HIGH_12:    case SEARCH_LOW_12:            case FORMAT_UNIT:       case REASSIGN_BLOCKS:   case RESERVE:      case MODE_SELECT:       case MODE_SELECT_10:    case LOG_SELECT:      case SEND_DIAGNOSTIC:   case CHANGE_DEFINITION: case UPDATE_BLOCK:      case SET_WINDOW:        case MEDIUM_SCAN:       case SEND_VOLUME_TAG:      case 0xea:         return 1;      default:         return 0;      }}static struct sx_period sx_table[] = {   {  1, 0x20},   {252, 0x20},   {376, 0x30},   {500, 0x40},   {624, 0x50},   {752, 0x60},   {876, 0x70},   {1000,0x00},   {0,   0} };static int round_period(unsigned int period){int x;   for (x=1; sx_table[x].period_ns; x++) {      if ((period <= sx_table[x-0].period_ns) &&          (period >  sx_table[x-1].period_ns)) {         return x;         }      }   return 7;}static uchar calc_sync_xfer(unsigned int period, unsigned int offset){uchar result;   period *= 4;   /* convert SDTR code to ns */   result = sx_table[round_period(period)].reg_value;   result |= (offset < OPTIMUM_SX_OFF)?offset:OPTIMUM_SX_OFF;   return result;}static void wd33c93_execute(struct Scsi_Host *instance);int wd33c93_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)){   struct WD33C93_hostdata *hostdata;   Scsi_Cmnd *tmp;   unsigned long flags;   hostdata = (struct WD33C93_hostdata *)cmd->host->hostdata;DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld( ",cmd->target,cmd->cmnd[0],cmd->pid))/* Set up a few fields in the Scsi_Cmnd structure for our own use: *  - host_scribble is the pointer to the next cmd in the input queue *  - scsi_done points to the routine we call when a cmd is finished *  - result is what you'd expect */   cmd->host_scribble = NULL;   cmd->scsi_done = done;   cmd->result = 0;/* We use the Scsi_Pointer structure that's included with each command * as a scratchpad (as it's intended to be used!). The handy thing about * the SCp.xxx fields is that they're always associated with a given * cmd, and are preserved across disconnect-reselect. This means we * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages * if we keep all the critical pointers and counters in SCp: *  - SCp.ptr is the pointer into the RAM buffer *  - SCp.this_residual is the size of that buffer *  - SCp.buffer points to the current scatter-gather buffer *  - SCp.buffers_residual tells us how many S.G. buffers there are *  - SCp.have_data_in is not used *  - SCp.sent_command is not used *  - SCp.phase records this command's SRCID_ER bit setting */   if (cmd->use_sg) {      cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;      cmd->SCp.buffers_residual = cmd->use_sg - 1;      cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;      cmd->SCp.this_residual = cmd->SCp.buffer->length;      }   else {      cmd->SCp.buffer = NULL;      cmd->SCp.buffers_residual = 0;      cmd->SCp.ptr = (char *)cmd->request_buffer;      cmd->SCp.this_residual = cmd->request_bufflen;      }/* WD docs state that at the conclusion of a "LEVEL2" command, the * status byte can be retrieved from the LUN register. Apparently, * this is the case only for *uninterrupted* LEVEL2 commands! If * there are any unexpected phases entered, even if they are 100% * legal (different devices may choose to do things differently), * the LEVEL2 command sequence is exited. This often occurs prior * to receiving the status byte, in which case the driver does a * status phase interrupt and gets the status byte on its own. * While such a command can then be "resumed" (ie restarted to * finish up as a LEVEL2 command), the LUN register will NOT be * a valid status byte at the command's conclusion, and we must * use the byte obtained during the earlier interrupt. Here, we * preset SCp.Status to an illegal value (0xff) so that when * this command finally completes, we can tell where the actual * status byte is stored. */   cmd->SCp.Status = ILLEGAL_STATUS_BYTE;   /*    * 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.    */   save_flags(flags);   cli();   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! */

⌨️ 快捷键说明

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