in2000.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,042 行 · 第 1/5 页

C
2,042
字号
/* *    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. * * For the avoidance of doubt the "preferred form" of this code is one which * is in an open non patent encumbered format. Where cryptographic key signing * forms part of the process of creating an executable the information * including keys needed to generate an equivalently functional executable * are deemed to be part of the source code. * * 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". * * Updated for Linux 2.5 by Alan Cox <alan@redhat.com> *	- Using new_eh handler *	- Hopefully got all the locking right again *	See "FIXME" notes for items that could do with more work */#include <linux/module.h>#include <linux/blkdev.h>#include <linux/interrupt.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <linux/ioport.h>#include <linux/stat.h>#include <asm/io.h>#include <asm/system.h>#include "scsi.h"#include <scsi/scsi_host.h>#define IN2000_VERSION    "1.33-2.5"#define IN2000_DATE       "2002/11/03"#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;MODULE_PARM(setup_strings, "s");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);static int in2000_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *)){	struct Scsi_Host *instance;	struct IN2000_hostdata *hostdata;	Scsi_Cmnd *tmp;	instance = cmd->device->host;	hostdata = (struct IN2000_hostdata *) instance->hostdata;	DB(DB_QUEUE_COMMAND, printk("Q-%d-%02x-%ld(", cmd->device->id, 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 *) page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset;		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 * status byte is stored. */	cmd->SCp.Status = ILLEGAL_STATUS_BYTE;/* We need to disable interrupts before messing with the input * queue and calling in2000_execute(). */

⌨️ 快捷键说明

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