patch-2.1.17 linux/drivers/block/ez.c

Next file: linux/drivers/block/ll_rw_blk.c
Previous file: linux/drivers/block/ataflop.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.16/linux/drivers/block/ez.c linux/drivers/block/ez.c
@@ -0,0 +1,1020 @@
+/* 
+	ez.c	(c) 1996  Grant R. Guenther <grant@torque.net>
+		          Under the terms of the GNU public license.
+
+	This is a driver for the parallel port versions of SyQuest's 
+        EZ135 and EZ230 removable media disk drives.  
+        
+        Special thanks go to Pedro Soria-Rodriguez for his help testing 
+	the EZFlyer 230 support.
+
+	The drive is actually SyQuest's IDE product with a
+        ShuttleTech IDE <-> parallel converter chip built in.
+
+	To compile the driver, ensure that /usr/include/linux and
+        /usr/include/asm are links to the correct include files for 
+        the target system. Then compile the driver with 
+
+		cc -D__KERNEL__ -DMODULE -O2 -c ez.c
+
+        If you are using MODVERSIONS, add the following to the cc command:
+
+		-DMODVERSIONS -I /usr/include/linux/modversions.h
+
+	You must then load it with insmod.
+
+	Before attempting to access the new driver, you will need to
+        create some device special files.  The following commands will
+	do that for you:
+
+		mknod /dev/eza  b 40 0
+		mknod /dev/eza1 b 40 1
+		mknod /dev/eza2 b 40 2
+		mknod /dev/eza3 b 40 3
+		mknod /dev/eza4 b 40 4
+		chown root:disk /dev/ez*
+		chmod 660 /dev/ez*
+
+	You can make devices for more partitions (up to 15) if you need to.
+
+	You can alter the port used by the driver in two ways:  either
+        change the definition of EZ_BASE or modify the ez_base variable
+        on the insmod command line, for example:
+
+		insmod ez ez_base=0x3bc
+
+	The driver can detect if the parallel port supports 8-bit
+        transfers.  If so, it will use them.  You can force it to use
+        4-bit (nybble) mode by setting the variable ez_nybble to 1.
+
+	The driver can be used with or without interrupts.  If an IRQ
+        is specified in the variable ez_irq, the driver will use it.
+        If ez_irq is set to 0, an alternative, polling-based, strategy 
+	will be used.
+
+	If you experience timeout errors while using this driver - and
+        you have enabled interrupts - try disabling the interrupt.  I
+        have heard reports of some parallel ports having exceptionally
+        unreliable interrupts.  This could happen on misconfigured 
+        systems in which an inactive sound card shares the same IRQ with 
+        the parallel port. (Remember that most people do not use the
+        parallel port interrupt for printing.)
+
+	It would be advantageous to use multiple mode transfers,
+        but ShuttleTech's driver does not appear to use them, so I'm not
+        sure that the converter can handle it.
+
+	It is not currently possible to connect a printer to the chained
+        port on the EZ135p and expect Linux to use both devices at once.
+
+	When the EZ230 powers on, the "standby timer" is set to about 6
+        minutes:  if the drive is idle for that length of time, it will
+        put itself into a low power standby mode.  It takes a couple of
+        seconds for the drive to come out of standby mode.  So, if you
+        load this driver while it is in standby mode, you will notice
+        a "freeze" of a second or two as the driver waits for the EZ230
+        to come back to life.  Once loaded, this driver disables the
+        standby timer (until you next power up the EZ230 ...)
+
+	Keep an eye on http://www.torque.net/ez135.html for news and
+        other information about the driver.  If you have any problems
+        with this driver, please send me, grant@torque.net, some mail 
+        directly before posting into the newsgroups or mailing lists.
+
+*/
+
+#define	EZ_VERSION	"0.11"
+
+#define	EZ_BASE		0x378
+#define EZ_IRQ		7
+#define EZ_REP		4
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/tqueue.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/ioport.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#define EZ_BITS	   4			/* compatible with SCSI version */
+#define EZ_MAJOR   40			/* as assigned by hpa */
+
+#define MAJOR_NR EZ_MAJOR
+
+/* set up defines for blk.h,  why don't all drivers do it this way ? */
+
+#define DEVICE_NAME "ez"
+#define DEVICE_REQUEST do_ez_request
+#define DEVICE_NR(device) (MINOR(device)>>EZ_BITS)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#define EZ_PARTNS  (1<<EZ_BITS)
+
+#define EZ_LOG_HEADS	64		
+#define EZ_LOG_SECTS	32		/* SCSI compatible logical geometry */
+
+#define EZ_SIGOFF	54
+#define EZ_SIG		"ySuQse tZE"
+#define EZ_SIGLEN	10
+#define EZ_ID_LEN	14
+
+#define EZ_TMO  	250		/* interrupt timeout in jiffies */
+
+#define EZ_SPIN_DEL     50		/* spin delay in micro-seconds  */
+
+#define EZ_SPIN		(10000/EZ_SPIN_DEL)*EZ_TMO  
+#define EZ_ISPIN	(10000/EZ_SPIN_DEL)*20
+#define EZ_DELAY        udelay(EZ_SPIN_DEL)
+
+#define STAT_ERR	0x00001
+#define STAT_INDEX	0x00002
+#define STAT_ECC	0x00004
+#define STAT_DRQ	0x00008
+#define STAT_SEEK	0x00010
+#define STAT_WRERR	0x00020
+#define STAT_READY	0x00040
+#define STAT_BUSY	0x00080
+
+#define ERR_AMNF	0x00100
+#define ERR_TK0NF	0x00200
+#define ERR_ABRT	0x00400
+#define ERR_MCR		0x00800
+#define ERR_IDNF	0x01000
+#define ERR_MC		0x02000
+#define ERR_UNC		0x04000
+#define ERR_TMO		0x10000
+
+#define IDE_READ	0x20
+#define IDE_WRITE	0x30
+#define IDE_STANDBY     0x96
+#define IDE_DOORLOCK	0xde
+#define IDE_DOORUNLOCK  0xdf
+#define IDE_ACKCHANGE   0xdb
+#define IDE_IDENTIFY    0xec
+
+int ez_init(void);
+void ez_setup(char * str, int * ints);
+#ifdef MODULE
+void cleanup_module( void );
+#endif
+static void ez_geninit(struct gendisk *ignored);
+static int ez_open(struct inode *inode, struct file *file);
+static void do_ez_request(void);
+static int ez_ioctl(struct inode *inode,struct file *file,
+                    unsigned int cmd, unsigned long arg);
+static void ez_release (struct inode *inode, struct file *file);
+static int ez_revalidate(kdev_t dev);
+static int ez_check_media(kdev_t dev);
+static void ez_get_capacity( void );
+static int ez_detect(void);
+static void do_ez_read(void);
+static void do_ez_write(void);
+static void ez_media_check(void);
+static void ez_doorlock(int func);
+static void ez_interrupt( int irq, void * dev_id, struct pt_regs * regs);
+static void ez_pseudo( void *data);
+static void ez_timer_int( unsigned long data);
+static void do_ez_read_drq( void );
+static void do_ez_write_done( void );
+
+static struct hd_struct ez[EZ_PARTNS];
+static int ez_sizes[EZ_PARTNS];
+static int ez_blocksizes[EZ_PARTNS];
+
+static int	ez_base = EZ_BASE;
+static int      ez_irq = EZ_IRQ;
+static int	ez_rep = EZ_REP;
+static int	ez_nybble = 0;		/* force 4-bit mode ? */
+
+static int ez_valid = 0;		/* OK to open */
+static int ez_access = 0;		/* count of active opens ... */
+static int ez_changed = 0;		/* Did we see new media on open ? */
+static int ez_capacity = 512*16*32;     /* Size of this volume in sectors */
+static int ez_heads = 16;		/* physical geometry */
+static int ez_sectors = 32;
+static int ez_mode = 1;			/* 4- or 8-bit mode */
+static int ez_loops = 0;		/* counter for pseudo-interrupts */
+static int ez_timeout = 0;		/* did the interrupt time out ? */
+static int ez_int_seen = 0;		/* have we ever seen an interrupt ? */
+static int ez_busy = 0;			/* request being processed ? */
+static int ez_block;			/* address of next requested block */
+static int ez_count;			/* number of blocks still to do */
+static char * ez_buf;			/* buffer for request in progress */
+static char ez_scratch[512];		/* scratch block buffer */
+static void (*ez_continuation)(void);	/* i/o completion handler */
+
+char	*ez_errs[17] = { "ERR","INDEX","ECC","DRQ","SEEK","WRERR",
+			 "READY","BUSY","AMNF","TK0NF","ABRT","MCR",
+			 "IDNF","MC","UNC","???","TMO"};
+
+static struct tq_struct ez_tq = {0,0,ez_pseudo,NULL};
+static struct timer_list ez_timer = {0,0,0,0,ez_timer_int};
+static struct wait_queue *ez_wait_open = NULL;
+
+/* kernel glue structures */
+
+static struct gendisk ez_gendisk = {
+	MAJOR_NR,	/* Major number */
+	"ez",		/* Major name */
+	EZ_BITS,	/* Bits to shift to get real from partition */
+	EZ_PARTNS,      /* Number of partitions per real */
+	1,		/* maximum number of real */
+	ez_geninit,	/* init function */
+	ez,		/* hd struct */
+	ez_sizes,	/* block sizes */
+	0,		/* number */
+        NULL,		/* internal */
+	NULL		/* next */
+};
+
+static struct file_operations ez_fops = {
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	ez_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	ez_open,		/* open */
+	ez_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	ez_check_media,         /* media change ? */
+	ez_revalidate		/* revalidate new media */
+};
+
+int ez_init (void)	/* preliminary initialisation */
+
+{	
+	if (register_blkdev(MAJOR_NR,"ez",&ez_fops)) {
+		printk("ez_init: unable to get major number %d\n",MAJOR_NR);
+		return -1;
+	}
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read ahead */
+	ez_gendisk.next = gendisk_head;
+	gendisk_head = &ez_gendisk;
+
+	return 0;
+}
+
+static void ez_geninit (struct gendisk *ignored)    /* real init */
+
+{	int i;
+
+	ez_gendisk.nr_real = 0;
+	
+	if (ez_detect()) {
+		ez_busy = 0;
+		ez_valid = 1;
+		ez_gendisk.nr_real = 1;
+		ez[0].nr_sects = ez_capacity;
+		for(i=0;i<EZ_PARTNS;i++) ez_blocksizes[i] = 1024;
+		blksize_size[MAJOR_NR] = ez_blocksizes;
+	} 
+#ifdef MODULE
+	  else cleanup_module();
+#endif
+}
+
+static int ez_open (struct inode *inode, struct file *file)
+
+{	int dev = DEVICE_NR(inode->i_rdev);
+
+	if (dev >= ez_gendisk.nr_real) return -ENODEV;
+
+	MOD_INC_USE_COUNT;
+
+	while (!ez_valid) sleep_on(&ez_wait_open);
+	ez_access++;
+	ez_media_check();
+	ez_doorlock(IDE_DOORLOCK);
+	return 0;
+}
+
+static void do_ez_request (void)
+
+{       int	dev;
+
+	if (ez_busy) return;
+repeat:
+	if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return;
+	INIT_REQUEST;
+
+	dev = MINOR(CURRENT->rq_dev);
+	ez_block = CURRENT->sector;
+	ez_count = CURRENT->nr_sectors;
+
+	if ((dev >= EZ_PARTNS) || ((ez_block+ez_count) > ez[dev].nr_sects)) {
+		end_request(0);
+		goto repeat;
+	}
+
+	ez_block += ez[dev].start_sect;
+	ez_buf = CURRENT->buffer;
+
+	if (CURRENT->cmd == READ) do_ez_read();
+	else if (CURRENT->cmd == WRITE) do_ez_write();
+	else {  end_request(0);
+		goto repeat;
+	}
+}
+
+static int ez_ioctl(struct inode *inode,struct file *file,
+		    unsigned int cmd, unsigned long arg)
+
+{	struct hd_geometry *geo = (struct hd_geometry *) arg;
+	int dev, err;
+
+	if ((!inode) || (!inode->i_rdev)) return -EINVAL;
+	dev = MINOR(inode->i_rdev);
+	if (dev >= EZ_PARTNS) return -EINVAL;
+
+	switch (cmd) {
+	    case HDIO_GETGEO:
+		if (!geo) return -EINVAL;
+		err = verify_area(VERIFY_WRITE,geo,sizeof(*geo));
+		if (err) return err;
+		put_user(ez_capacity/(EZ_LOG_HEADS*EZ_LOG_SECTS),
+			 (short *) &geo->cylinders);
+		put_user(EZ_LOG_HEADS, (char *) &geo->heads);
+		put_user(EZ_LOG_SECTS, (char *) &geo->sectors);
+	        put_user(ez[dev].start_sect,(long *)&geo->start);
+		return 0;
+	    case BLKRASET:
+		if(!suser()) return -EACCES;
+		if(!(inode->i_rdev)) return -EINVAL;
+		if(arg > 0xff) return -EINVAL;
+		read_ahead[MAJOR(inode->i_rdev)] = arg;
+		return 0;
+            case BLKRAGET:
+		if (!arg) return -EINVAL;
+		err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+		if (err) return (err);
+		put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
+		return (0);
+	    case BLKGETSIZE:
+		if (!arg) return -EINVAL;
+		err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long));
+		if (err) return (err);
+		put_user(ez[dev].nr_sects,(long *) arg);
+		return (0);
+	    case BLKFLSBUF:
+		if(!suser())  return -EACCES;
+		if(!(inode->i_rdev)) return -EINVAL;
+		fsync_dev(inode->i_rdev);
+	        invalidate_buffers(inode->i_rdev);
+		return 0;
+	    case BLKRRPART:
+		return ez_revalidate(inode->i_rdev);
+	    RO_IOCTLS(inode->i_rdev,arg);
+	    default:
+	        return -EINVAL;
+	}
+}
+
+static void ez_release (struct inode *inode, struct file *file)
+
+{	kdev_t devp;
+
+	devp = inode->i_rdev;
+	if (DEVICE_NR(devp) == 0)  {
+		fsync_dev(devp);
+		invalidate_inodes(devp);
+		invalidate_buffers(devp);
+		ez_access--;
+		if (!ez_access) ez_doorlock(IDE_DOORUNLOCK);
+		MOD_DEC_USE_COUNT;
+	}
+}
+
+static int ez_check_media( kdev_t dev)
+
+{       int	t;
+
+	t = ez_changed;
+	ez_changed = 0;
+	return t;
+}
+
+static int ez_revalidate(kdev_t dev)
+
+{	int p;
+	long flags;
+	kdev_t devp;
+
+	save_flags(flags);
+	cli(); 
+	if (ez_access > 1) {
+		restore_flags(flags);
+		return -EBUSY;
+	}
+	ez_valid = 0;
+	restore_flags(flags);	
+
+	for (p=(EZ_PARTNS-1);p>=0;p--) {
+		devp = MKDEV(MAJOR_NR, p);
+		fsync_dev(devp);
+		invalidate_inodes(devp);
+		invalidate_buffers(devp);
+		ez[p].start_sect = 0;
+		ez[p].nr_sects = 0;
+	}
+
+	ez_get_capacity();
+	ez[0].nr_sects = ez_capacity;
+	resetup_one_dev(&ez_gendisk,0);
+
+	ez_valid = 1;
+	wake_up(&ez_wait_open);
+
+	return 0;
+}
+
+#ifdef MODULE
+
+/* Glue for modules ... */
+
+void	cleanup_module(void);
+
+int	init_module(void)
+
+{	int	err;
+	long    flags;
+
+	save_flags(flags);
+	cli();
+
+	err = ez_init();
+	if (err) {
+	    restore_flags(flags);
+	    return err;
+	}
+	ez_geninit(&ez_gendisk);
+
+	if (!ez_gendisk.nr_real) {
+		restore_flags(flags);
+		return -1;
+	}
+
+	ez_valid = 0;
+	resetup_one_dev(&ez_gendisk,0);
+	ez_valid = 1;
+
+	restore_flags(flags);
+	return 0;
+}
+
+void	cleanup_module(void)
+
+{	struct gendisk **gdp;
+	long flags;
+
+	save_flags(flags);
+	cli();
+
+	unregister_blkdev(MAJOR_NR,"ez");
+
+	for(gdp=&gendisk_head;*gdp;gdp=&((*gdp)->next))
+		if (*gdp == &ez_gendisk) break;
+	if (*gdp) *gdp = (*gdp)->next;
+
+	if (ez_gendisk.nr_real) {
+		release_region(ez_base,3);
+		if (ez_irq) free_irq(ez_irq,NULL);
+	}
+
+	restore_flags(flags);
+}
+
+#else 
+
+/* ez_setup:  process lilo command parameters ...
+
+   syntax:	ez=base[,irq[,rep[,nybble]]]
+*/
+
+void    ez_setup(char *str, int *ints)
+
+{       if (ints[0] > 0) ez_base = ints[1];
+        if (ints[0] > 1) ez_irq = ints[2];
+        if (ints[0] > 2) ez_rep = ints[3];
+        if (ints[0] > 3) ez_nybble = ints[4];
+} 
+
+#endif
+
+/* Now the actual hardware interface to the EZ135p */
+
+static void    out_p( short port, char byte)
+
+{       int i;
+
+	for(i=0;i<ez_rep;i++) outb(byte,ez_base+port);
+}
+
+static int     in_p( short port)
+
+{       int i;
+	char c;
+
+	c=inb(ez_base+port);
+	for(i=1;i<ez_rep;i++) c=inb(ez_base+port);
+	return c & 0xff;
+}
+
+#define w0(byte)  out_p(0,byte)
+#define w2(byte)  out_p(2,byte)
+#define r0()      (in_p(0) & 0xff)
+#define r1()      (in_p(1) & 0xff)
+
+/*  register access functions */
+
+static int read_regr( char regr )
+
+{	int h, l;
+
+	if (ez_mode == 1) {	/* nybble mode */
+	    w0(regr);
+	    w2(1); w2(3);
+	    l = r1() >> 4;
+	    w2(4);
+	    h = r1() & 0xf0;
+	    return h + l;
+	} else {		/* byte mode */
+	    w0(regr+0x20);
+	    w2(1); w2(0x25);
+	    h = r0();
+	    w2(4);
+	    return h;
+	}
+}	
+
+static void write_regr( char regr, char val )
+
+{	w0(regr);
+	w2(1);
+	w0(val);
+	w2(4);
+}
+
+/* connect / disconnect code */
+
+static void prefix( char byte )
+
+{	w2(4); w0(0x22); w0(0xaa); w0(0x55); w0(0); 
+	w0(0xff); w0(0x87); w0(0x78); w0(byte);
+        w2(5); w2(4); w0(0xff); 
+}
+
+static void connect ( void  )
+
+{	prefix(0x40); prefix(0x50); prefix(0xe0);
+        w0(0); w2(1); w2(4);
+	read_regr(0xd);
+	write_regr(0x6d,0xe8);
+	write_regr(0x6c,0x1c);
+	write_regr(0x72,0x10);
+	write_regr(0x6a,0x38);
+	write_regr(0x68,0x10);
+	read_regr(0x12);
+	write_regr(0x72,0x10);
+	read_regr(0xd);
+	write_regr(0x6d,0xaa);
+	write_regr(0x6d,0xaa);
+}
+
+static void disconnect ( void )
+
+{	read_regr(0xd);
+	write_regr(0x6d,0xa8);
+	prefix(0x30);
+} 
+
+/* basic i/o */
+
+static void read_block( char * buf )
+
+/* the nybble mode read has a curious optimisation in it: there are actually
+   five bits available on each read.  The extra bit is used to signal that
+   the next nybble is identical ...  I wonder how much research went into
+   designing this use of the extra bit ?
+*/
+
+{	int	j, k, n0, n1, n2, n3;
+
+	read_regr(0xd); write_regr(0x6d,0xe9);
+
+	j = 0;
+	if (ez_mode == 1) {		/* nybble mode */
+
+	    w0(7); w2(1); w2(3); w0(0xff);
+	    for(k=0;k<256;k++) {
+		w2(6); n0 = r1();
+		if (n0 & 8) n1 = n0; else { w2(4); n1 = r1(); }
+		w2(7); n2 = r1();
+		if (n2 & 8) n3 = n2; else { w2(5); n3 = r1(); }
+		buf[j++] = (n0 >> 4) + (n1 & 0xf0);
+		buf[j++] = (n2 >> 4) + (n3 & 0xf0);
+	    }
+
+	} else {			/* byte mode */
+
+	    w0(0x27); w2(1); w2(0x25); w0(0);
+	    for(k=0;k<256;k++) {
+		w2(0x24); buf[j++] = r0();
+       		w2(0x25); buf[j++] = r0();
+	    }
+	    w2(0x26); w2(0x27); w0(0); w2(0x25); w2(4);
+
+	}
+}
+
+static void write_block( char * buf )
+
+{	int	j;
+
+	read_regr(0xd); write_regr(0x6d,0xe9);
+
+	w0(0x67); w2(1); w2(5);
+	for(j=0;j<256;j++) {
+		w0(buf[2*j]); w2(4);
+		w0(buf[2*j+1]); w2(5);
+	}
+	w2(7); w2(4);
+}
+
+/*  ide command interface */
+
+void	ez_print_error( char * msg, int status )
+
+{	char	*e, *p;
+        int	i;
+
+	e = ez_scratch;
+	for(i=0;i<18;i++) if (status & (1<<i)) {
+		p = ez_errs[i];
+		while ((*e++=*p++));
+		*(e-1) = ' ';
+	}
+	if (status) e--;
+	*e = 0;
+	printk("ez: %s: status = 0x%x (%s)\n",msg,status,ez_scratch);
+}
+
+static int wait_for( int w, char * msg )    /* polled wait */
+
+{	int	k, r, e;
+
+	k=0;
+	while(k < EZ_SPIN) { 
+	    r = read_regr(0x1f);
+            k++;
+	    if (ez_timeout) break;
+	    if (((r & w) == w) && !(r & STAT_BUSY)) break;
+	    EZ_DELAY;
+	}
+	e = (read_regr(0x19)<<8) + r;
+	if ((k >= EZ_SPIN) || ez_timeout) e |= (ERR_TMO|STAT_ERR);
+	if ((e & STAT_ERR) & (msg != NULL)) ez_print_error(msg,e);
+	return e;
+}
+
+static void send_command( int n, int s, int h, int c0, int c1, int func )
+
+{
+	read_regr(0xd); write_regr(0x6d,0xa9);
+
+	write_regr(0x76,0);		
+	write_regr(0x79,0);	/* the IDE task file */
+	write_regr(0x7a,n);
+	write_regr(0x7b,s);
+	write_regr(0x7c,c0);
+	write_regr(0x7d,c1);
+	write_regr(0x7e,0xa0+h);
+	write_regr(0x7f,func);
+
+	udelay(1);
+}
+
+static void ez_ide_command( int func, int block )
+
+{	int c1, c0, h, s;
+
+	s  = ( block % ez_sectors) + 1;
+	h  = ( block / ez_sectors) % ez_heads;
+	c0 = ( block / (ez_sectors*ez_heads)) % 256;
+	c1 = ( block / (ez_sectors*ez_heads*256));
+
+	send_command(1,s,h,c0,c1,func);
+}
+
+static void ez_gate_intr( int flag )
+
+{	if (flag) write_regr(0x6d,0x39);  /* gate interrupt line to bus */
+     	if (flag && ez_irq) w2(0x14);	  /* enable IRQ */
+	if (!flag) w2(4);	          /* disable IRQ */
+}
+
+static int check_int( void )	/* is the interrupt bit set  ?  */
+
+{	return (r1() & 0x40);
+}
+
+static void ez_doorlock( int func )
+
+{	connect();
+	if (wait_for(STAT_READY,"Lock") & STAT_ERR) {
+		disconnect();
+		return;
+	}
+	ez_ide_command(func,0);
+	wait_for(STAT_READY,"Lock done");
+	disconnect();
+}
+
+/* ez_media_check: check for and acknowledge the MC flag */
+
+static void ez_media_check( void )
+
+{	int r;
+
+	ez_changed = 0;
+	connect();
+	r = wait_for(STAT_READY,"Media check ready");
+	if (!(r & STAT_ERR)) {
+		ez_ide_command(IDE_READ,0);  /* try to read block 0 */
+		r = wait_for(STAT_DRQ,"Media check");
+		if (!(r & STAT_ERR)) read_block(ez_scratch);
+	} else ez_changed = 1;   /* say changed if other error */
+	if (r & ERR_MC) {
+		ez_changed = 1;
+		ez_ide_command(IDE_ACKCHANGE,0);
+		wait_for(STAT_READY,"Ack. media change");
+	}
+	disconnect();
+}
+
+static int ez_identify( void )
+
+
+{	int	k, r;
+
+	connect();
+	wait_for(0,NULL);  /* wait until not busy, quietly */
+	ez_ide_command(IDE_IDENTIFY,0);
+
+	if (ez_irq) {	  		/* check that the interrupt works */
+		ez_gate_intr(1);
+		k = 0;
+		while ((k++ < EZ_ISPIN) && !ez_int_seen) EZ_DELAY;
+		ez_gate_intr(0);
+		r = read_regr(0x1f);
+		if ((!ez_int_seen) || !(r & STAT_DRQ)) {
+			free_irq(ez_irq,NULL);
+			ez_irq = 0;
+		}
+	}
+
+        if (wait_for(STAT_DRQ,NULL) & STAT_ERR) {
+		disconnect();
+		return 0;
+	}
+	read_block(ez_scratch);
+	disconnect();
+	return 1;
+}
+
+#define  word_val(n) 	(ez_scratch[2*n]+256*ez_scratch[2*n+1])
+
+static void ez_get_capacity( void )
+
+{	int	ez_cylinders;
+
+	connect();
+	wait_for(0,NULL);
+	ez_ide_command(IDE_IDENTIFY,0);
+	if (wait_for(STAT_DRQ,"Get capacity") & STAT_ERR) {
+		disconnect();
+		return;
+	}
+	read_block(ez_scratch);
+	disconnect();
+	ez_sectors = word_val(6);
+	ez_heads = word_val(3);
+	ez_cylinders  = word_val(1);
+	ez_capacity = ez_sectors*ez_heads*ez_cylinders;
+	printk("ez: Capacity = %d, (%d/%d/%d)\n",ez_capacity,ez_cylinders,
+		ez_heads,ez_sectors);
+}
+
+static void ez_standby_off( void )
+
+{	connect();
+	wait_for(0,NULL);
+	send_command(0,0,0,0,0,IDE_STANDBY);
+	wait_for(0,NULL);
+	disconnect();
+}
+
+static int ez_port_check( void ) 	/* check for 8-bit port */
+
+{	int	r;
+
+        w2(0); 
+	w0(0x55); if (r0() != 0x55) return 0;
+	w0(0xaa); if (r0() != 0xaa) return 0;
+	w2(0x20); w0(0x55); r = r0(); w0(0xaa);
+	if (r0() == r) return 2;
+	if (r0() == 0xaa) return 1;
+	return 0;
+}
+
+static int ez_detect( void )
+
+{	int j, k;
+	char sig[EZ_SIGLEN] = EZ_SIG;
+	char id[EZ_ID_LEN+1];
+	long	flags;
+
+	if (check_region(ez_base,3)) {
+		printk("ez: Ports at 0x%x are not available\n",ez_base);
+		return 0;
+	}
+
+	ez_mode = ez_port_check();
+	if (!ez_mode) {
+		printk("ez: No parallel port at 0x%x\n",ez_base);
+		return 0;
+	}
+
+	if (ez_irq && request_irq(ez_irq,ez_interrupt,0,"ez",NULL)) ez_irq = 0;
+
+	if (ez_nybble) ez_mode = 1;
+	    
+	request_region(ez_base,3,"ez");
+
+	save_flags(flags);
+	sti();
+
+	k = 0;
+	if (ez_identify()) {
+		k = 1;
+		for(j=0;j<EZ_SIGLEN;j++) 
+		   k &= (ez_scratch[j+EZ_SIGOFF] == sig[j]);
+	}
+	if (k) { 
+	    for(j=0;j<EZ_ID_LEN;j++) id[j^1] = ez_scratch[j+EZ_SIGOFF];
+	    id[EZ_ID_LEN] = 0;
+	    if (!ez_irq) printk("ez %s: %s at 0x%x, %d-bit mode.\n",
+				EZ_VERSION,id,ez_base,4*ez_mode);
+	    else printk("ez %s: %s at 0x%x, IRQ %d, %d-bit mode.\n",
+                        EZ_VERSION,id,ez_base,ez_irq,4*ez_mode);
+	    ez_standby_off();
+	    ez_media_check();
+	    ez_get_capacity();
+	    restore_flags(flags);
+	    return 1;
+	}
+	restore_flags(flags);
+	release_region(ez_base,3);
+	if (ez_irq) free_irq(ez_irq,NULL);
+	printk("ez: Drive not detected\n");
+	return 0;
+}
+
+/* interrupt management */
+
+static void ez_set_intr( void (*continuation)(void) )
+
+{	ez_continuation = continuation;
+	ez_loops = 1;  ez_timeout = 0;
+	ez_gate_intr(1);
+	if (ez_irq) {
+		ez_timer.expires = jiffies + EZ_TMO;
+		add_timer(&ez_timer);
+	} else queue_task(&ez_tq,&tq_scheduler); 	
+}
+
+static void ez_pseudo( void *data )
+
+{	void (*con)(void);
+
+	ez_timeout = (ez_loops >= EZ_TMO);
+	if (check_int() || ez_timeout) {
+		con = ez_continuation;
+		ez_continuation = NULL;
+		if (con) con();
+	} else {	
+		ez_loops++;
+	        queue_task(&ez_tq,&tq_scheduler);
+	}
+}
+
+static void ez_timer_int( unsigned long data)
+
+{	void  (*con)(void);
+
+	con = ez_continuation;
+	if (!con) return;
+	ez_continuation = NULL;
+	ez_gate_intr(0);
+	ez_timeout = 1;
+	con();
+}
+
+static void ez_interrupt( int irq, void * dev_id, struct pt_regs * regs)
+
+{	void  (*con)(void);
+
+	ez_int_seen = 1;
+	con = ez_continuation;
+	if (!con) return;
+	ez_gate_intr(0);
+	del_timer(&ez_timer);
+	ez_continuation = NULL;
+	con();
+}
+
+/* The i/o request engine */
+
+#define EZ_DONE(s) { disconnect(); end_request(s); ez_busy = 0;\
+		     cli(); do_ez_request(); return; }
+
+static void do_ez_read( void )
+
+{	ez_busy = 1;
+	if (!ez_count) {
+		ez_busy = 0;
+		return;
+	}
+	sti();
+	connect();
+	if (wait_for(STAT_READY,"do_ez_read") & STAT_ERR) EZ_DONE(0);
+	ez_ide_command(IDE_READ,ez_block);
+	ez_set_intr(do_ez_read_drq);
+}
+
+static void do_ez_read_drq( void )
+
+{	sti();
+	if (wait_for(STAT_DRQ,"do_ez_read_drq") & STAT_ERR) EZ_DONE(0);
+	read_block(ez_buf);
+	ez_count--;
+	if (ez_count) {
+		ez_buf += 512;
+		ez_block++;
+		disconnect();
+		do_ez_read();
+		return;
+	}
+	EZ_DONE(1);
+}
+
+static void do_ez_write( void )
+
+{	ez_busy = 1;
+	if (!ez_count) {
+		ez_busy = 0;
+		return;
+	}
+	sti();
+	connect();
+	if (wait_for(STAT_READY,"do_ez_write") & STAT_ERR) 
+	   EZ_DONE(0);
+	ez_ide_command(IDE_WRITE,ez_block);
+	if (wait_for(STAT_DRQ,"do_ez_write_drq") & STAT_ERR) 
+	   EZ_DONE(0);
+	write_block(ez_buf);
+	ez_set_intr(do_ez_write_done);
+}
+
+static void do_ez_write_done( void )
+
+{	sti();
+	if (wait_for(STAT_READY,"do_ez_write_done") & STAT_ERR) EZ_DONE(0);
+	ez_count--;
+	if (ez_count) {
+		ez_buf += 512;
+		ez_block++;
+		disconnect();
+		do_ez_write();
+		return;
+	}
+	EZ_DONE(1);
+}
+
+/* end of ez.c */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov