ppa.c

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

C
1,151
字号
/* ppa.c   --  low level driver for the IOMEGA PPA3  * parallel port SCSI host adapter. *  * (The PPA3 is the embedded controller in the ZIP drive.) *  * (c) 1995,1996 Grant R. Guenther, grant@torque.net, * under the terms of the GNU General Public License. *  * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800) *                     campbell@torque.net */#include <linux/config.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/blkdev.h>#include <linux/parport.h>#include <linux/workqueue.h>#include <asm/io.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>static void ppa_reset_pulse(unsigned int base);typedef struct {	struct pardevice *dev;	/* Parport device entry         */	int base;		/* Actual port address          */	int mode;		/* Transfer mode                */	struct scsi_cmnd *cur_cmd;	/* Current queued command       */	struct work_struct ppa_tq;	/* Polling interrupt stuff       */	unsigned long jstart;	/* Jiffies at start             */	unsigned long recon_tmo;	/* How many usecs to wait for reconnection (6th bit) */	unsigned int failed:1;	/* Failure flag                 */	unsigned wanted:1;	/* Parport sharing busy flag    */	wait_queue_head_t *waiting;	struct Scsi_Host *host;	struct list_head list;} ppa_struct;#include  "ppa.h"static inline ppa_struct *ppa_dev(struct Scsi_Host *host){	return *(ppa_struct **)&host->hostdata;}static spinlock_t arbitration_lock = SPIN_LOCK_UNLOCKED;static void got_it(ppa_struct *dev){	dev->base = dev->dev->port->base;	if (dev->cur_cmd)		dev->cur_cmd->SCp.phase = 1;	else		wake_up(dev->waiting);}static void ppa_wakeup(void *ref){	ppa_struct *dev = (ppa_struct *) ref;	unsigned long flags;	spin_lock_irqsave(&arbitration_lock, flags);	if (dev->wanted) {		parport_claim(dev->dev);		got_it(dev);		dev->wanted = 0;	}	spin_unlock_irqrestore(&arbitration_lock, flags);	return;}static int ppa_pb_claim(ppa_struct *dev){	unsigned long flags;	int res = 1;	spin_lock_irqsave(&arbitration_lock, flags);	if (parport_claim(dev->dev) == 0) {		got_it(dev);		res = 0;	}	dev->wanted = res;	spin_unlock_irqrestore(&arbitration_lock, flags);	return res;}static void ppa_pb_dismiss(ppa_struct *dev){	unsigned long flags;	int wanted;	spin_lock_irqsave(&arbitration_lock, flags);	wanted = dev->wanted;	dev->wanted = 0;	spin_unlock_irqrestore(&arbitration_lock, flags);	if (!wanted)		parport_release(dev->dev);}static inline void ppa_pb_release(ppa_struct *dev){	parport_release(dev->dev);}/* * Start of Chipset kludges *//* This is to give the ppa driver a way to modify the timings (and other * parameters) by writing to the /proc/scsi/ppa/0 file. * Very simple method really... (To simple, no error checking :( ) * Reason: Kernel hackers HATE having to unload and reload modules for * testing... * Also gives a method to use a script to obtain optimum timings (TODO) */static inline int ppa_proc_write(ppa_struct *dev, char *buffer, int length){	unsigned long x;	if ((length > 5) && (strncmp(buffer, "mode=", 5) == 0)) {		x = simple_strtoul(buffer + 5, NULL, 0);		dev->mode = x;		return length;	}	if ((length > 10) && (strncmp(buffer, "recon_tmo=", 10) == 0)) {		x = simple_strtoul(buffer + 10, NULL, 0);		dev->recon_tmo = x;		printk("ppa: recon_tmo set to %ld\n", x);		return length;	}	printk("ppa /proc: invalid variable\n");	return (-EINVAL);}static int ppa_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, int length, int inout){	int len = 0;	ppa_struct *dev = ppa_dev(host);	if (inout)		return ppa_proc_write(dev, buffer, length);	len += sprintf(buffer + len, "Version : %s\n", PPA_VERSION);	len +=	    sprintf(buffer + len, "Parport : %s\n",		    dev->dev->port->name);	len +=	    sprintf(buffer + len, "Mode    : %s\n",		    PPA_MODE_STRING[dev->mode]);#if PPA_DEBUG > 0	len +=	    sprintf(buffer + len, "recon_tmo : %lu\n", dev->recon_tmo);#endif	/* Request for beyond end of buffer */	if (offset > length)		return 0;	*start = buffer + offset;	len -= offset;	if (len > length)		len = length;	return len;}static int device_check(ppa_struct *dev);#if PPA_DEBUG > 0#define ppa_fail(x,y) printk("ppa: ppa_fail(%i) from %s at line %d\n",\	   y, __FUNCTION__, __LINE__); ppa_fail_func(x,y);static inline void ppa_fail_func(ppa_struct *dev, int error_code)#elsestatic inline void ppa_fail(ppa_struct *dev, int error_code)#endif{	/* If we fail a device then we trash status / message bytes */	if (dev->cur_cmd) {		dev->cur_cmd->result = error_code << 16;		dev->failed = 1;	}}/* * Wait for the high bit to be set. *  * In principle, this could be tied to an interrupt, but the adapter * doesn't appear to be designed to support interrupts.  We spin on * the 0x80 ready bit.  */static unsigned char ppa_wait(ppa_struct *dev){	int k;	unsigned short ppb = dev->base;	unsigned char r;	k = PPA_SPIN_TMO;	/* Wait for bit 6 and 7 - PJC */	for (r = r_str(ppb); ((r & 0xc0) != 0xc0) && (k); k--) {		udelay(1);		r = r_str(ppb);	}	/*	 * return some status information.	 * Semantics: 0xc0 = ZIP wants more data	 *            0xd0 = ZIP wants to send more data	 *            0xe0 = ZIP is expecting SCSI command data	 *            0xf0 = end of transfer, ZIP is sending status	 */	if (k)		return (r & 0xf0);	/* Counter expired - Time out occurred */	ppa_fail(dev, DID_TIME_OUT);	printk("ppa timeout in ppa_wait\n");	return 0;		/* command timed out */}/* * Clear EPP Timeout Bit  */static inline void epp_reset(unsigned short ppb){	int i;	i = r_str(ppb);	w_str(ppb, i);	w_str(ppb, i & 0xfe);}/*  * Wait for empty ECP fifo (if we are in ECP fifo mode only) */static inline void ecp_sync(ppa_struct *dev){	int i, ppb_hi = dev->dev->port->base_hi;	if (ppb_hi == 0)		return;	if ((r_ecr(ppb_hi) & 0xe0) == 0x60) {	/* mode 011 == ECP fifo mode */		for (i = 0; i < 100; i++) {			if (r_ecr(ppb_hi) & 0x01)				return;			udelay(5);		}		printk("ppa: ECP sync failed as data still present in FIFO.\n");	}}static int ppa_byte_out(unsigned short base, const char *buffer, int len){	int i;	for (i = len; i; i--) {		w_dtr(base, *buffer++);		w_ctr(base, 0xe);		w_ctr(base, 0xc);	}	return 1;		/* All went well - we hope! */}static int ppa_byte_in(unsigned short base, char *buffer, int len){	int i;	for (i = len; i; i--) {		*buffer++ = r_dtr(base);		w_ctr(base, 0x27);		w_ctr(base, 0x25);	}	return 1;		/* All went well - we hope! */}static int ppa_nibble_in(unsigned short base, char *buffer, int len){	for (; len; len--) {		unsigned char h;		w_ctr(base, 0x4);		h = r_str(base) & 0xf0;		w_ctr(base, 0x6);		*buffer++ = h | ((r_str(base) & 0xf0) >> 4);	}	return 1;		/* All went well - we hope! */}static int ppa_out(ppa_struct *dev, char *buffer, int len){	int r;	unsigned short ppb = dev->base;	r = ppa_wait(dev);	if ((r & 0x50) != 0x40) {		ppa_fail(dev, DID_ERROR);		return 0;	}	switch (dev->mode) {	case PPA_NIBBLE:	case PPA_PS2:		/* 8 bit output, with a loop */		r = ppa_byte_out(ppb, buffer, len);		break;	case PPA_EPP_32:	case PPA_EPP_16:	case PPA_EPP_8:		epp_reset(ppb);		w_ctr(ppb, 0x4);#ifdef CONFIG_SCSI_IZIP_EPP16		if (!(((long) buffer | len) & 0x01))			outsw(ppb + 4, buffer, len >> 1);#else		if (!(((long) buffer | len) & 0x03))			outsl(ppb + 4, buffer, len >> 2);#endif		else			outsb(ppb + 4, buffer, len);		w_ctr(ppb, 0xc);		r = !(r_str(ppb) & 0x01);		w_ctr(ppb, 0xc);		ecp_sync(dev);		break;	default:		printk("PPA: bug in ppa_out()\n");		r = 0;	}	return r;}static int ppa_in(ppa_struct *dev, char *buffer, int len){	int r;	unsigned short ppb = dev->base;	r = ppa_wait(dev);	if ((r & 0x50) != 0x50) {		ppa_fail(dev, DID_ERROR);		return 0;	}	switch (dev->mode) {	case PPA_NIBBLE:		/* 4 bit input, with a loop */		r = ppa_nibble_in(ppb, buffer, len);		w_ctr(ppb, 0xc);		break;	case PPA_PS2:		/* 8 bit input, with a loop */		w_ctr(ppb, 0x25);		r = ppa_byte_in(ppb, buffer, len);		w_ctr(ppb, 0x4);		w_ctr(ppb, 0xc);		break;	case PPA_EPP_32:	case PPA_EPP_16:	case PPA_EPP_8:		epp_reset(ppb);		w_ctr(ppb, 0x24);#ifdef CONFIG_SCSI_IZIP_EPP16		if (!(((long) buffer | len) & 0x01))			insw(ppb + 4, buffer, len >> 1);#else		if (!(((long) buffer | len) & 0x03))			insl(ppb + 4, buffer, len >> 2);#endif		else			insb(ppb + 4, buffer, len);		w_ctr(ppb, 0x2c);		r = !(r_str(ppb) & 0x01);		w_ctr(ppb, 0x2c);		ecp_sync(dev);		break;	default:		printk("PPA: bug in ppa_ins()\n");		r = 0;		break;	}	return r;}/* end of ppa_io.h */static inline void ppa_d_pulse(unsigned short ppb, unsigned char b){	w_dtr(ppb, b);	w_ctr(ppb, 0xc);	w_ctr(ppb, 0xe);	w_ctr(ppb, 0xc);	w_ctr(ppb, 0x4);	w_ctr(ppb, 0xc);}static void ppa_disconnect(ppa_struct *dev){	unsigned short ppb = dev->base;	ppa_d_pulse(ppb, 0);	ppa_d_pulse(ppb, 0x3c);	ppa_d_pulse(ppb, 0x20);	ppa_d_pulse(ppb, 0xf);}static inline void ppa_c_pulse(unsigned short ppb, unsigned char b){	w_dtr(ppb, b);	w_ctr(ppb, 0x4);	w_ctr(ppb, 0x6);	w_ctr(ppb, 0x4);	w_ctr(ppb, 0xc);}static inline void ppa_connect(ppa_struct *dev, int flag){	unsigned short ppb = dev->base;	ppa_c_pulse(ppb, 0);	ppa_c_pulse(ppb, 0x3c);	ppa_c_pulse(ppb, 0x20);	if ((flag == CONNECT_EPP_MAYBE) && IN_EPP_MODE(dev->mode))		ppa_c_pulse(ppb, 0xcf);	else		ppa_c_pulse(ppb, 0x8f);}static int ppa_select(ppa_struct *dev, int target){	int k;	unsigned short ppb = dev->base;	/*	 * Bit 6 (0x40) is the device selected bit.	 * First we must wait till the current device goes off line...	 */	k = PPA_SELECT_TMO;	do {		k--;		udelay(1);	} while ((r_str(ppb) & 0x40) && (k));	if (!k)		return 0;	w_dtr(ppb, (1 << target));	w_ctr(ppb, 0xe);	w_ctr(ppb, 0xc);	w_dtr(ppb, 0x80);	/* This is NOT the initator */	w_ctr(ppb, 0x8);	k = PPA_SELECT_TMO;	do {		k--;		udelay(1);	}	while (!(r_str(ppb) & 0x40) && (k));	if (!k)		return 0;	return 1;}/*  * This is based on a trace of what the Iomega DOS 'guest' driver does. * I've tried several different kinds of parallel ports with guest and * coded this to react in the same ways that it does. *  * The return value from this function is just a hint about where the * handshaking failed. *  */static int ppa_init(ppa_struct *dev){	int retv;	unsigned short ppb = dev->base;	ppa_disconnect(dev);	ppa_connect(dev, CONNECT_NORMAL);	retv = 2;		/* Failed */	w_ctr(ppb, 0xe);	if ((r_str(ppb) & 0x08) == 0x08)		retv--;	w_ctr(ppb, 0xc);	if ((r_str(ppb) & 0x08) == 0x00)		retv--;	if (!retv)		ppa_reset_pulse(ppb);	udelay(1000);		/* Allow devices to settle down */	ppa_disconnect(dev);	udelay(1000);		/* Another delay to allow devices to settle */	if (retv)		return -EIO;	return device_check(dev);}static inline int ppa_send_command(struct scsi_cmnd *cmd){	ppa_struct *dev = ppa_dev(cmd->device->host);	int k;	w_ctr(dev->base, 0x0c);	for (k = 0; k < cmd->cmd_len; k++)		if (!ppa_out(dev, &cmd->cmnd[k], 1))			return 0;	return 1;}/* * The bulk flag enables some optimisations in the data transfer loops, * it should be true for any command that transfers data in integral * numbers of sectors. *  * The driver appears to remain stable if we speed up the parallel port * i/o in this function, but not elsewhere. */static int ppa_completion(struct scsi_cmnd *cmd){	/* Return codes:	 * -1     Error	 *  0     Told to schedule	 *  1     Finished data transfer	 */	ppa_struct *dev = ppa_dev(cmd->device->host);	unsigned short ppb = dev->base;	unsigned long start_jiffies = jiffies;	unsigned char r, v;	int fast, bulk, status;	v = cmd->cmnd[0];	bulk = ((v == READ_6) ||		(v == READ_10) || (v == WRITE_6) || (v == WRITE_10));	/*	 * We only get here if the drive is ready to comunicate,	 * hence no need for a full ppa_wait.	 */	r = (r_str(ppb) & 0xf0);	while (r != (unsigned char) 0xf0) {		/*		 * If we have been running for more than a full timer tick		 * then take a rest.		 */		if (time_after(jiffies, start_jiffies + 1))			return 0;		if ((cmd->SCp.this_residual <= 0)) {			ppa_fail(dev, DID_ERROR);			return -1;	/* ERROR_RETURN */		}		/* On some hardware we have SCSI disconnected (6th bit low)		 * for about 100usecs. It is too expensive to wait a 		 * tick on every loop so we busy wait for no more than		 * 500usecs to give the drive a chance first. We do not 		 * change things for "normal" hardware since generally 		 * the 6th bit is always high.		 * This makes the CPU load higher on some hardware 		 * but otherwise we can not get more than 50K/secs 		 * on this problem hardware.		 */

⌨️ 快捷键说明

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