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
- Lines: 1021
- Date:
Wed Dec 18 15:57:28 1996
- Orig file:
v2.1.16/linux/drivers/block/ez.c
- Orig date:
Thu Jan 1 02:00:00 1970
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