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

📄 in2000.c

📁 基于组件方式开发操作系统的OSKIT源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *    in2000.c -  Linux device driver for the *                Always IN2000 ISA SCSI 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 provided * much of the inspiration and some of the code for this driver. * The Linux IN2000 driver distributed in the Linux kernels through * version 1.2.13 was an extremely valuable reference on the arcane * (and still mysterious) workings of the IN2000's fifo. It also * is where I lifted in2000_biosparam(), the gist of the card * detection scheme, and other bits of code. Many thanks to the * talented and courageous people who wrote, contributed to, and * maintained that driver (including Brad McLean, Shaun Savage, * Bill Earnest, Larry Doolittle, Roger Sunshine, John Luckey, * Matt Postiff, Peter Lu, zerucha@shell.portal.com, and Eric * Youngdale). I should also mention the driver written by * Hamish Macdonald for the (GASP!) Amiga A2091 card, included * in the Linux-m68k distribution; it gave me a good initial * understanding of the proper way to run a WD33c93 chip, and I * ended up stealing lots of code from it. * * _This_ driver is (I feel) an improvement over the old one in * several respects: *    -  All problems relating to the data size of a SCSI request are *          gone (as far as I know). The old driver couldn't handle *          swapping to partitions because that involved 4k blocks, nor *          could it deal with the st.c tape driver unmodified, because *          that usually involved 4k - 32k blocks. The old driver never *          quite got away from a morbid dependence on 2k block sizes - *          which of course is the size of the card's fifo. * *    -  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'm *          calling '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 there *          are a few devices (and many improperly terminated systems) *          that choke when doing sync, the default is sync DISABLED *          for all devices. This faster protocol can (and should!) *          be enabled on selected devices via the command-line. * *    -  Runtime operating parameters can now be specified through *       either the LILO or the 'insmod' command line. For LILO do: *          "in2000=blah,blah,blah" *       and with insmod go like: *          "insmod /usr/src/linux/modules/in2000.o setup_strings=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. * *    -  You can force detection of a card whose BIOS has been disabled. * *    -  Multiple IN2000 cards might almost be supported. I've tried to *       keep it in mind, but have no way to test... * * * TODO: *       tagged queuing. multiple cards. * * * NOTE: *       When using this or any other SCSI driver as a module, you'll *       find that with the stock kernel, at most _two_ SCSI hard *       drives will be linked into the device list (ie, usable). *       If your IN2000 card has more than 2 disks on its bus, you *       might want to change the define of 'SD_EXTRA_DEVS' in the *       'hosts.h' file from 2 to whatever is appropriate. It took *       me a while to track down this surprisingly obscure and *       undocumented little "feature". * * * 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". * */#include <linux/module.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <asm/io.h>#include <linux/ioport.h>#include <linux/blkdev.h>#include <linux/blk.h>#include <linux/stat.h>#include "scsi.h"#include "sd.h"#include "hosts.h"#define IN2000_VERSION    "1.33"#define IN2000_DATE       "26/August/1998"#include "in2000.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 a LILO or insmod command-line, those * settings are combined with 'setup_args[]'. Note that LILO command-lines * are prefixed with "in2000=" while insmod uses a "setup_strings=" prefix. * The driver recognizes the following keywords (lower case required) and * arguments: * * -  ioport:addr    -Where addr is IO address of a (usually ROM-less) card. * -  noreset        -No optional args. Prevents SCSI bus reset at boot time. * -  nosync:x       -x is a bitmask where the 1st 7 bits correspond with *                    the 7 possible SCSI devices (bit 0 for device #0, etc). *                    Set a bit to PREVENT sync negotiation on that device. *                    The driver default is sync DISABLED on all devices. * -  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 bitmask that causes *                    various types of debug output to printed - see the DB_xxx *                    defines in in2000.h * -  proc:x         -If 'PROC_INTERFACE' is defined, x is a bitmask that *                    determines how the /proc interface works and what it *                    does - see the PR_xxx defines in in2000.h * * 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. * * A few LILO examples (for insmod, use 'setup_strings' instead of 'in2000'): * -  in2000=ioport:0x220,noreset * -  in2000=period:250,disconnect:2,nosync:0x03 * -  in2000=debug:0x1e * -  in2000=proc:3 *//* 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 struct Scsi_Host *instance_list = 0;static inline uchar read_3393(struct IN2000_hostdata *hostdata, uchar reg_num){   write1_io(reg_num,IO_WD_ADDR);   return read1_io(IO_WD_DATA);}#define READ_AUX_STAT() read1_io(IO_WD_ASR)static inline void write_3393(struct IN2000_hostdata *hostdata, uchar reg_num, uchar value){   write1_io(reg_num,IO_WD_ADDR);   write1_io(value,IO_WD_DATA);}static inline void write_3393_cmd(struct IN2000_hostdata *hostdata, uchar cmd){/*   while (READ_AUX_STAT() & ASR_CIP)      printk("|");*/   write1_io(WD_COMMAND,IO_WD_ADDR);   write1_io(cmd,IO_WD_DATA);}static uchar read_1_byte(struct IN2000_hostdata *hostdata){uchar asr, x = 0;   write_3393(hostdata,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);   write_3393_cmd(hostdata,WD_CMD_TRANS_INFO|0x80);   do {      asr = READ_AUX_STAT();      if (asr & ASR_DBR)         x = read_3393(hostdata,WD_DATA);      } while (!(asr & ASR_INT));   return x;}static void write_3393_count(struct IN2000_hostdata *hostdata, unsigned long value){   write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);   write1_io((value >> 16),IO_WD_DATA);   write1_io((value >> 8),IO_WD_DATA);   write1_io(value,IO_WD_DATA);}static unsigned long read_3393_count(struct IN2000_hostdata *hostdata){unsigned long value;   write1_io(WD_TRANSFER_COUNT_MSB,IO_WD_ADDR);   value = read1_io(IO_WD_DATA) << 16;   value |= read1_io(IO_WD_DATA) << 8;   value |= read1_io(IO_WD_DATA);   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 in2000_execute(struct Scsi_Host *instance);int in2000_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)){struct IN2000_hostdata *hostdata;Scsi_Cmnd *tmp;unsigned long flags;   hostdata = (struct IN2000_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 helps keep track of >2048 byte transfers *  - 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;      }   cmd->SCp.have_data_in = 0;/* We don't set SCp.phase here - that's done in in2000_execute() *//* 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

⌨️ 快捷键说明

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