📄 in2000.c
字号:
/* * 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 + -