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

📄 imm.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* imm.c   --  low level driver for the IOMEGA MatchMaker * parallel port SCSI host adapter. *  * (The IMM is the embedded controller in the ZIP Plus drive.) *  * My unoffical company acronym list is 21 pages long: *      FLA:    Four letter acronym with built in facility for *              future expansion to five letters. */#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 <linux/delay.h>#include <asm/io.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>/* The following #define is to avoid a clash with hosts.c */#define IMM_PROBE_SPP   0x0001#define IMM_PROBE_PS2   0x0002#define IMM_PROBE_ECR   0x0010#define IMM_PROBE_EPP17 0x0100#define IMM_PROBE_EPP19 0x0200typedef struct {	struct pardevice *dev;	/* Parport device entry         */	int base;		/* Actual port address          */	int base_hi;		/* Hi Base address for ECP-ISA chipset */	int mode;		/* Transfer mode                */	struct scsi_cmnd *cur_cmd;	/* Current queued command       */	struct delayed_work imm_tq;	/* Polling interrupt stuff       */	unsigned long jstart;	/* Jiffies at start             */	unsigned failed:1;	/* Failure flag                 */	unsigned dp:1;		/* Data phase present           */	unsigned rd:1;		/* Read data in data phase      */	unsigned wanted:1;	/* Parport sharing busy flag    */	wait_queue_head_t *waiting;	struct Scsi_Host *host;	struct list_head list;} imm_struct;static void imm_reset_pulse(unsigned int base);static int device_check(imm_struct *dev);#include "imm.h"static inline imm_struct *imm_dev(struct Scsi_Host *host){	return *(imm_struct **)&host->hostdata;}static DEFINE_SPINLOCK(arbitration_lock);static void got_it(imm_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 imm_wakeup(void *ref){	imm_struct *dev = (imm_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);}static int imm_pb_claim(imm_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 imm_pb_dismiss(imm_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 imm_pb_release(imm_struct *dev){	parport_release(dev->dev);}/* This is to give the imm driver a way to modify the timings (and other * parameters) by writing to the /proc/scsi/imm/0 file. * Very simple method really... (Too 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 imm_proc_write(imm_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;	}	printk("imm /proc: invalid variable\n");	return (-EINVAL);}static int imm_proc_info(struct Scsi_Host *host, char *buffer, char **start,			off_t offset, int length, int inout){	imm_struct *dev = imm_dev(host);	int len = 0;	if (inout)		return imm_proc_write(dev, buffer, length);	len += sprintf(buffer + len, "Version : %s\n", IMM_VERSION);	len +=	    sprintf(buffer + len, "Parport : %s\n",		    dev->dev->port->name);	len +=	    sprintf(buffer + len, "Mode    : %s\n",		    IMM_MODE_STRING[dev->mode]);	/* Request for beyond end of buffer */	if (offset > len)		return 0;	*start = buffer + offset;	len -= offset;	if (len > length)		len = length;	return len;}#if IMM_DEBUG > 0#define imm_fail(x,y) printk("imm: imm_fail(%i) from %s at line %d\n",\	   y, __FUNCTION__, __LINE__); imm_fail_func(x,y);static inline voidimm_fail_func(imm_struct *dev, int error_code)#elsestatic inline voidimm_fail(imm_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 imm_wait(imm_struct *dev){	int k;	unsigned short ppb = dev->base;	unsigned char r;	w_ctr(ppb, 0x0c);	k = IMM_SPIN_TMO;	do {		r = r_str(ppb);		k--;		udelay(1);	}	while (!(r & 0x80) && (k));	/*	 * STR register (LPT base+1) to SCSI mapping:	 *	 * STR      imm     imm	 * ===================================	 * 0x80     S_REQ   S_REQ	 * 0x40     !S_BSY  (????)	 * 0x20     !S_CD   !S_CD	 * 0x10     !S_IO   !S_IO	 * 0x08     (????)  !S_BSY	 *	 * imm      imm     meaning	 * ==================================	 * 0xf0     0xb8    Bit mask	 * 0xc0     0x88    ZIP wants more data	 * 0xd0     0x98    ZIP wants to send more data	 * 0xe0     0xa8    ZIP is expecting SCSI command data	 * 0xf0     0xb8    end of transfer, ZIP is sending status	 */	w_ctr(ppb, 0x04);	if (k)		return (r & 0xb8);	/* Counter expired - Time out occurred */	imm_fail(dev, DID_TIME_OUT);	printk("imm timeout in imm_wait\n");	return 0;		/* command timed out */}static int imm_negotiate(imm_struct * tmp){	/*	 * The following is supposedly the IEEE 1284-1994 negotiate	 * sequence. I have yet to obtain a copy of the above standard	 * so this is a bit of a guess...	 *	 * A fair chunk of this is based on the Linux parport implementation	 * of IEEE 1284.	 *	 * Return 0 if data available	 *        1 if no data available	 */	unsigned short base = tmp->base;	unsigned char a, mode;	switch (tmp->mode) {	case IMM_NIBBLE:		mode = 0x00;		break;	case IMM_PS2:		mode = 0x01;		break;	default:		return 0;	}	w_ctr(base, 0x04);	udelay(5);	w_dtr(base, mode);	udelay(100);	w_ctr(base, 0x06);	udelay(5);	a = (r_str(base) & 0x20) ? 0 : 1;	udelay(5);	w_ctr(base, 0x07);	udelay(5);	w_ctr(base, 0x06);	if (a) {		printk		    ("IMM: IEEE1284 negotiate indicates no data available.\n");		imm_fail(tmp, DID_ERROR);	}	return a;}/*  * 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(imm_struct *dev){	int i, ppb_hi = dev->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("imm: ECP sync failed as data still present in FIFO.\n");	}}static int imm_byte_out(unsigned short base, const char *buffer, int len){	int i;	w_ctr(base, 0x4);	/* apparently a sane mode */	for (i = len >> 1; i; i--) {		w_dtr(base, *buffer++);		w_ctr(base, 0x5);	/* Drop STROBE low */		w_dtr(base, *buffer++);		w_ctr(base, 0x0);	/* STROBE high + INIT low */	}	w_ctr(base, 0x4);	/* apparently a sane mode */	return 1;		/* All went well - we hope! */}static int imm_nibble_in(unsigned short base, char *buffer, int len){	unsigned char l;	int i;	/*	 * The following is based on documented timing signals	 */	w_ctr(base, 0x4);	for (i = len; i; i--) {		w_ctr(base, 0x6);		l = (r_str(base) & 0xf0) >> 4;		w_ctr(base, 0x5);		*buffer++ = (r_str(base) & 0xf0) | l;		w_ctr(base, 0x4);	}	return 1;		/* All went well - we hope! */}static int imm_byte_in(unsigned short base, char *buffer, int len){	int i;	/*	 * The following is based on documented timing signals	 */	w_ctr(base, 0x4);	for (i = len; i; i--) {		w_ctr(base, 0x26);		*buffer++ = r_dtr(base);		w_ctr(base, 0x25);	}	return 1;		/* All went well - we hope! */}static int imm_out(imm_struct *dev, char *buffer, int len){	unsigned short ppb = dev->base;	int r = imm_wait(dev);	/*	 * Make sure that:	 * a) the SCSI bus is BUSY (device still listening)	 * b) the device is listening	 */	if ((r & 0x18) != 0x08) {		imm_fail(dev, DID_ERROR);		printk("IMM: returned SCSI status %2x\n", r);		return 0;	}	switch (dev->mode) {	case IMM_EPP_32:	case IMM_EPP_16:	case IMM_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;	case IMM_NIBBLE:	case IMM_PS2:		/* 8 bit output, with a loop */		r = imm_byte_out(ppb, buffer, len);		break;	default:		printk("IMM: bug in imm_out()\n");		r = 0;	}	return r;}static int imm_in(imm_struct *dev, char *buffer, int len){	unsigned short ppb = dev->base;	int r = imm_wait(dev);	/*	 * Make sure that:	 * a) the SCSI bus is BUSY (device still listening)	 * b) the device is sending data	 */	if ((r & 0x18) != 0x18) {		imm_fail(dev, DID_ERROR);		return 0;	}	switch (dev->mode) {	case IMM_NIBBLE:		/* 4 bit input, with a loop */		r = imm_nibble_in(ppb, buffer, len);		w_ctr(ppb, 0xc);		break;	case IMM_PS2:		/* 8 bit input, with a loop */		r = imm_byte_in(ppb, buffer, len);		w_ctr(ppb, 0xc);		break;	case IMM_EPP_32:	case IMM_EPP_16:	case IMM_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("IMM: bug in imm_ins()\n");		r = 0;		break;	}	return r;}static int imm_cpp(unsigned short ppb, unsigned char b){	/*	 * Comments on udelay values refer to the	 * Command Packet Protocol (CPP) timing diagram.	 */	unsigned char s1, s2, s3;	w_ctr(ppb, 0x0c);	udelay(2);		/* 1 usec - infinite */	w_dtr(ppb, 0xaa);	udelay(10);		/* 7 usec - infinite */	w_dtr(ppb, 0x55);	udelay(10);		/* 7 usec - infinite */	w_dtr(ppb, 0x00);	udelay(10);		/* 7 usec - infinite */	w_dtr(ppb, 0xff);	udelay(10);		/* 7 usec - infinite */	s1 = r_str(ppb) & 0xb8;	w_dtr(ppb, 0x87);	udelay(10);		/* 7 usec - infinite */	s2 = r_str(ppb) & 0xb8;	w_dtr(ppb, 0x78);	udelay(10);		/* 7 usec - infinite */	s3 = r_str(ppb) & 0x38;	/*	 * Values for b are:	 * 0000 00aa    Assign address aa to current device	 * 0010 00aa    Select device aa in EPP Winbond mode	 * 0010 10aa    Select device aa in EPP mode	 * 0011 xxxx    Deselect all devices	 * 0110 00aa    Test device aa	 * 1101 00aa    Select device aa in ECP mode	 * 1110 00aa    Select device aa in Compatible mode	 */	w_dtr(ppb, b);	udelay(2);		/* 1 usec - infinite */	w_ctr(ppb, 0x0c);	udelay(10);		/* 7 usec - infinite */	w_ctr(ppb, 0x0d);	udelay(2);		/* 1 usec - infinite */	w_ctr(ppb, 0x0c);	udelay(10);		/* 7 usec - infinite */	w_dtr(ppb, 0xff);	udelay(10);		/* 7 usec - infinite */	/*	 * The following table is electrical pin values.	 * (BSY is inverted at the CTR register)	 *	 *       BSY  ACK  POut SEL  Fault	 * S1    0    X    1    1    1	 * S2    1    X    0    1    1	 * S3    L    X    1    1    S	 *	 * L => Last device in chain	 * S => Selected	 *	 * Observered values for S1,S2,S3 are:	 * Disconnect => f8/58/78	 * Connect    => f8/58/70	 */	if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x30))		return 1;	/* Connected */	if ((s1 == 0xb8) && (s2 == 0x18) && (s3 == 0x38))		return 0;	/* Disconnected */	return -1;		/* No device present */}static inline int imm_connect(imm_struct *dev, int flag){	unsigned short ppb = dev->base;	imm_cpp(ppb, 0xe0);	/* Select device 0 in compatible mode */	imm_cpp(ppb, 0x30);	/* Disconnect all devices */	if ((dev->mode == IMM_EPP_8) ||	    (dev->mode == IMM_EPP_16) ||	    (dev->mode == IMM_EPP_32))		return imm_cpp(ppb, 0x28);	/* Select device 0 in EPP mode */	return imm_cpp(ppb, 0xe0);	/* Select device 0 in compatible mode */}static void imm_disconnect(imm_struct *dev){	imm_cpp(dev->base, 0x30);	/* Disconnect all devices */}static int imm_select(imm_struct *dev, int target){	int k;	unsigned short ppb = dev->base;	/*	 * Firstly we want to make sure there is nothing	 * holding onto the SCSI bus.	 */	w_ctr(ppb, 0xc);	k = IMM_SELECT_TMO;	do {		k--;	} while ((r_str(ppb) & 0x08) && (k));	if (!k)		return 0;	/*	 * Now assert the SCSI ID (HOST and TARGET) on the data bus	 */	w_ctr(ppb, 0x4);	w_dtr(ppb, 0x80 | (1 << target));	udelay(1);	/*	 * Deassert SELIN first followed by STROBE	 */	w_ctr(ppb, 0xc);	w_ctr(ppb, 0xd);	/*	 * ACK should drop low while SELIN is deasserted.	 * FAULT should drop low when the SCSI device latches the bus.	 */	k = IMM_SELECT_TMO;	do {		k--;	}	while (!(r_str(ppb) & 0x08) && (k));	/*	 * Place the interface back into a sane state (status mode)	 */	w_ctr(ppb, 0xc);	return (k) ? 1 : 0;}static int imm_init(imm_struct *dev){	if (imm_connect(dev, 0) != 1)		return -EIO;	imm_reset_pulse(dev->base);	mdelay(1);	/* Delay to allow devices to settle */	imm_disconnect(dev);	mdelay(1);	/* Another delay to allow devices to settle */	return device_check(dev);}static inline int imm_send_command(struct scsi_cmnd *cmd){	imm_struct *dev = imm_dev(cmd->device->host);	int k;	/* NOTE: IMM uses byte pairs */	for (k = 0; k < cmd->cmd_len; k += 2)		if (!imm_out(dev, &cmd->cmnd[k], 2))			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 imm_completion(struct scsi_cmnd *cmd){	/* Return codes:	 * -1     Error	 *  0     Told to schedule	 *  1     Finished data transfer	 */	imm_struct *dev = imm_dev(cmd->device->host);	unsigned short ppb = dev->base;

⌨️ 快捷键说明

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