diff -u --recursive --new-file linux-2.0.30.orig/drivers/cdrom/Config.in linux/drivers/cdrom/Config.in --- linux-2.0.30.orig/drivers/cdrom/Config.in Thu May 16 08:35:39 1996 +++ linux/drivers/cdrom/Config.in Thu Sep 11 14:21:10 1997 @@ -13,6 +13,7 @@ fi fi fi +tristate 'MicroSolutions backpack CDROM support' CONFIG_BPCD tristate 'Mitsumi (standard) [no XA/Multisession] CDROM support' CONFIG_MCD tristate 'Mitsumi [XA/MultiSession] CDROM support' CONFIG_MCDX tristate 'Optics Storage DOLPHIN 8000AT CDROM support' CONFIG_OPTCD diff -u --recursive --new-file linux-2.0.30.orig/drivers/cdrom/Makefile linux/drivers/cdrom/Makefile --- linux-2.0.30.orig/drivers/cdrom/Makefile Sun May 12 00:13:07 1996 +++ linux/drivers/cdrom/Makefile Thu Sep 11 14:22:17 1997 @@ -122,6 +122,14 @@ endif endif #CONFIG_SJCD +ifeq ($(CONFIG_BPCD),y) +L_OBJS += bpcd.o +else + ifeq ($(CONFIG_BPCD),m) + M_OBJS += bpcd.o + endif +endif #CONFIG_BPCD + ifeq ($(CONFIG_CDI_INIT),y) L_OBJS += cdi.o endif #CONFIG_CDI_INIT diff -u --recursive --new-file linux-2.0.30.orig/drivers/cdrom/bpcd.c linux/drivers/cdrom/bpcd.c --- linux-2.0.30.orig/drivers/cdrom/bpcd.c Wed Dec 31 18:00:00 1969 +++ linux/drivers/cdrom/bpcd.c Thu Sep 11 14:23:10 1997 @@ -0,0 +1,1069 @@ +/* + bpcd.c (c) 1996,1997 Grant R. Guenther + Under the terms of the GNU public license. + + bpcd.c is a driver for the MicroSolutions "backpack" CDrom, + an external parallel port device. + + There are apparently two versions of the backpack protocol. This + driver knows about the version 2 protocol - as is used in the 4x + and 6x products. There is no support for the sound hardware that + is included in some models. It should not be difficult to add + support for the ATAPI audio play functions and the corresponding + ioctls. + + The driver was developed by reverse engineering the protocol + and testing it on the backpack model 164550. This model + is actually a stock ATAPI drive packaged with a custom + ASIC that implements the IDE over parallel protocol. + I tested with a backpack that happened to contain a Goldstar + drive, but I've seen reports of Sony and Mitsumi drives as well. + + If you have a copy of the driver that has not been integrated into + the kernel source tree, you can compile the driver manually, as a + module. Ensure that /usr/include/linux and /usr/include/asm are + links to the correct include files for the target system. Compile + the driver with + + cc -D__KERNEL__ -DMODULE -O2 -c bpcd.c + + You must then load it with insmod. If you are using + MODVERSIONS, add the following to the cc command: + + -DMODVERSIONS -include /usr/include/linux/modversions.h + + Before attempting to access the new driver, you will need to + create a new device special file. The following commands will + do that for you: + + mknod /dev/bpcd b 41 0 + chown root:disk /dev/bpcd + chmod 660 /dev/bpcd + + Afterward, you can mount a disk in the usual way: + + mount -t iso9660 /dev/bpcd /cdrom + + (assuming you have made a directory /cdrom to use as a + mount point). + + The driver will attempt to detect which parallel port your + backpack is connected to. If this fails for any reason, you + can override it by specifying a port on the LILO command line + (for built in drivers) or the insmod command (for drivers built + as modules). If your drive is on the port at 0x3bc, you would + use one of these commands: + + LILO: bpcd=0x3bc + + insmod: insmod bpcd bp_base=0x3bc + + The drive will also detect the appropriate transfer mode. + If necessary, you can force it to use a specific mode + by setting the variable bp_mode on the insmod or LILO command. + Mode 0 uses 4-bit reads, mode 1 is the standard bi-directional + mode and EPP ports use mode 2. To force the driver to use + 4-bit mode, you would specify + + LILO: bpcd=0x3bc,0 + + insmod: insmod bpcd bp_base=0x3bc bp_mode=0 + + (you must specify the correct port address if you use this method.) + + If you are loading bpcd as a module, you may also specify + + bp_debug=1 + + and the driver will log verbose messages as it probes + for the appropriate port and mode. If you have problems + loading the driver, please include this output with your + report. + + MicroSolutions' protocol allows for several drives to be + chained together off the same parallel port. Currently, this + driver will recognise only one of them. If you do have more + than one drive, it will choose the one with the lowest id number, + where the id number is the last two digits of the product's + serial number. + + It is not currently possible to connect a printer to the chained + port on the BackPack and expect Linux to use both devices at once. + + If you need to use this driver together with a printer on the + same port, build both the bpcd and lp drivers are modules. + + Keep an eye on http://www.torque.net/bpcd.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 BP_VERSION "0.24" + +#define BP_BASE 0 /* set to 0 for autoprobe */ +#define BP_HOLD 4 /* micro-second port settling time */ +#define BP_DEBUG 0 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* change to uaccess.h for 2.1 kernels ... */ + +#ifndef BPCD_MAJOR +#define BPCD_MAJOR 41 +#endif + +#define MAJOR_NR BPCD_MAJOR + +/* set up defines for blk.h, why don't all drivers do it this way ? */ + +#define DEVICE_NAME "BackPack" +#define DEVICE_REQUEST do_bp_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include + +#define BP_RETRIES 5 +#define BP_TMO 600 /* timeout in jiffies */ +#define BP_DELAY 50 /* spin delay in uS */ + +#define BP_SPIN (10000/BP_DELAY)*BP_TMO + +int bpcd_init(void); +void bpcd_setup(char * str, int * ints); +void cleanup_module( void ); + +static int bp_open(struct inode *inode, struct file *file); +static void do_bp_request(void); +static void do_bp_read(void); +static int bp_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg); +static void bp_release (struct inode *inode, struct file *file); + +static int bp_detect(void); +static void bp_lock(void); +static void bp_unlock(void); +static void bp_eject(void); +static void bp_interrupt(void *data); + +static int bp_base = BP_BASE; +static int bp_mode = 0; +static int bp_hold = BP_HOLD; +static int bp_debug = BP_DEBUG;; + +static int bp_unit_id; +static int bp_found_r0; +static int bp_retries; + +static int bp_access = 0; /* count of active opens ... */ +static int bp_busy = 0; /* request being processed ? */ +static int bp_timeout; /* "interrupt" loop limiter */ +static int bp_sector; /* address of next requested sector */ +static int bp_count; /* number of blocks still to do */ +static char * bp_buf; /* buffer for request in progress */ +static char bp_buffer[2048]; /* raw block buffer */ +static int bp_bufblk = -1; /* block in buffer, in CD units, + -1 for nothing there */ +static int nyb_map[256]; /* decodes a nybble */ +static int PortCache = 0; /* cache of the control port */ + +static struct tq_struct bp_tq = {0,0,bp_interrupt,NULL}; + +/* kernel glue structures */ + +static struct file_operations bp_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + bp_ioctl, /* ioctl */ + NULL, /* mmap */ + bp_open, /* open */ + bp_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* media change ? */ + NULL /* revalidate new media */ +}; + + +/* the MicroSolutions protocol uses bits 3,4,5 & 7 of the status port to + deliver a nybble in 4 bit mode. We use a table lookup to extract the + nybble value. The following function initialises the table. +*/ + +static void bp_init_nyb_map( void ) + +{ int i, j; + + for(i=0;i<256;i++) { + j = (i >> 3) & 0x7; + if (i & 0x80) j |= 8; + nyb_map[i] = j; + } +} + +int bpcd_init (void) /* preliminary initialisation */ + +{ bp_init_nyb_map(); + + if (bp_detect()) return -1; + + if (register_blkdev(MAJOR_NR,"bpcd",&bp_fops)) { + printk("bpcd: 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 */ + + return 0; +} + +static int bp_open (struct inode *inode, struct file *file) + +{ + if (file->f_mode & 2) return -EROFS; /* wants to write ? */ + + MOD_INC_USE_COUNT; + + bp_lock(); + + bp_access++; + return 0; +} + +static void do_bp_request (void) + +{ if (bp_busy) return; + while (1) { + if ((!CURRENT) || (CURRENT->rq_status == RQ_INACTIVE)) return; + INIT_REQUEST; + if (CURRENT->cmd == READ) { + bp_sector = CURRENT->sector; + bp_count = CURRENT->nr_sectors; + bp_buf = CURRENT->buffer; + do_bp_read(); + if (bp_busy) return; + } + else end_request(0); + } +} + +static int bp_ioctl(struct inode *inode,struct file *file, + unsigned int cmd, unsigned long arg) + +/* we currently support only the EJECT ioctl. */ + +{ switch (cmd) { + case CDROMEJECT: if (bp_access == 1) { + bp_eject(); + return 0; + } + default: + return -EINVAL; + } +} + +static void bp_release (struct inode *inode, struct file *file) + +{ kdev_t devp; + + bp_access--; + if (!bp_access) { + devp = inode->i_rdev; + fsync_dev(devp); + invalidate_inodes(devp); + invalidate_buffers(devp); + bp_unlock(); + } + MOD_DEC_USE_COUNT; +} + +#ifdef MODULE + +/* Glue for modules ... */ + +int init_module(void) + +{ int err; + long flags; + + save_flags(flags); + cli(); + + err = bpcd_init(); + + restore_flags(flags); + return err; +} + +void cleanup_module(void) + +{ long flags; + int range; + + if (bp_mode == 2) range = 8; else range = 3; + + save_flags(flags); + cli(); + unregister_blkdev(MAJOR_NR,"bpcd"); + release_region(bp_base,range); + restore_flags(flags); +} + +#else + +/* bpcd_setup: process lilo command parameters ... + + syntax: bpcd=base[,mode[,hold]] +*/ + +void bpcd_setup(char *str, int *ints) + +{ if (ints[0] > 0) bp_base = ints[1]; + if (ints[0] > 1) bp_mode = ints[2]; + if (ints[0] > 2) bp_hold = ints[3]; +} + +#endif + +#define out_p(port,byte) outb(byte,bp_base+port);udelay(bp_hold); +#define in_p(port) (udelay(bp_hold),inb(bp_base+port)) + +/* Unlike other PP devices I've worked on, the backpack protocol seems + to be driven by *changes* in the values of certain bits on the control + port, rather than their absolute value. Hence the unusual macros ... +*/ + +#define w0(byte) out_p(0,byte) +#define r0() (in_p(0) & 0xff) +#define w1(byte) out_p(1,byte) +#define r1() (in_p(1) & 0xff) +#define r2() (PortCache=(in_p(2) & 0xff)) +#define w2(byte) out_p(2,byte) ; PortCache = byte +#define t2(pat) PortCache ^= pat; out_p(2,PortCache) +#define e2() PortCache &= 0xfe; out_p(2,PortCache) +#define o2() PortCache |= 1; out_p(2,PortCache) +#define r4() (in_p(4) & 0xff) + + +static int bp_read_regr( char regr ) + +{ int r, l, h; + + switch (bp_mode) { + + case 0: w0(regr & 0xf); w0(regr); t2(2); t2(4); + l = nyb_map[r1()]; + t2(4); + h = nyb_map[r1()]; + return (h << 4) + l; + + case 1: w0(regr & 0xf); w0(regr); t2(2); + e2(); t2(0x20); + t2(4); r = r0(); + t2(1); t2(0x20); + return r; + + case 2: w0(regr); w2(9); w2(0); w2(0x20); + r = r4(); + w2(0); + return r; + + } + return -1; +} + +static void bp_write_regr( char regr, char val ) + +{ switch (bp_mode) { + + case 0: + case 1: w0(regr); + t2(2); + w0(val); + o2(); t2(4); t2(1); + break; + + case 2: w0(regr); w2(9); w2(0); + w0(val); w2(1); w2(3); w2(0); + break; + + } +} + +static void bp_write_cmd( char * cmd, int len ) + +{ int i; + + switch (bp_mode) { + + case 0: bp_write_regr(4,0x40); + w0(0x40); t2(2); t2(1); + for (i=0;i> 1); + } + for (j=0;j<2;j++) { + v = 0; + for (k=0;k<8;k++) { + bp_write_regr(6,0xc); + bp_write_regr(6,0xd); + bp_write_regr(6,0xc); + f = bp_read_regr(0); + v = 2*v + (f == 0x84); + } + buf[2*i+1-j] = v; + } + } + bp_write_regr(6,8); + bp_write_regr(6,0); + bp_write_regr(5,8); + + bp_disconnect(); + + if (om == 2) { + bp_connect(); + bp_write_regr(7,3); + bp_write_regr(4,8); + bp_disconnect(); + } + + bp_mode = om; + + printk("bpcd SERIAL: %8.8s\n",&(buf[110])); +} + +static int bp_wait_drq( char * fun, char * msg1, char * msg2 ) + +{ int j, r, e; + + j = 0; + while (!(bp_read_regr(0x47)&8)&&(j++= BP_SPIN) { + if (fun) + printk("bpcd: %s%s: DRQ timeout on %s, status: %x error: %x\n", + fun,msg1,msg2,r,e); + return -1; + } + return 0; +} + +static int bp_wait( char * fun, char * msg1, char * msg2 ) + +{ int j, r, e; + + j = 0; + while ((!(bp_read_regr(0xb)&0x80))&&(j++= BP_SPIN) || (r & 1)) { + if (j >= BP_SPIN) e |= 0x100; + if (fun) printk("bpcd: %s%s: Error %s, status: %x error: %x\n", + fun,msg1,msg2,r,e); + return -1; + } + return 0; +} + +static int bp_req_sense( char * fun, char * msg ) + +{ char rs_cmd[12] = { 0x03,0,0,0,18,0,0,0,0,0,0,0 }; + int n, r; + + bp_write_regr(0x44,18); + bp_write_regr(0x45,0); + bp_write_regr(0x46,0xa0); /* drive 0 */ + bp_write_regr(0x47,0xa0); /* ATAPI packet command */ + if (bp_wait_drq(fun,msg,"request sense command")) return -1; + bp_write_cmd(rs_cmd,12); + if (!(r=bp_wait(fun,msg,"getting request sense data"))) { + if (bp_read_regr(0x42) == 2) { + n = (bp_read_regr(0x44) + 256*bp_read_regr(0x45)); + bp_read_data(bp_buffer,n); + r=bp_wait(fun,msg,"on request sense done"); + if (!r) if (fun) + printk("bpcd: %s%s: sense key: %x, ASC: %x, ASQ: %x\n", + fun, msg, bp_buffer[2]&0xf, bp_buffer[12], bp_buffer[13]); + } + } + return r; +} + +static int bp_command( char * cmd, int dlen, char * fun ) + +{ int r; + + bp_connect(); + + bp_write_regr(0x44,dlen % 256); + bp_write_regr(0x45,dlen / 256); + bp_write_regr(0x46,0xa0); /* drive 0 */ + bp_write_regr(0x47,0xa0); /* ATAPI packet command */ + + if (bp_read_regr(0x47)&1) { + if (bp_req_sense(fun," before command")) { + bp_disconnect(); + return -1; + } + bp_write_regr(0x44,dlen % 256); + bp_write_regr(0x45,dlen / 256); + bp_write_regr(0x46,0xa0); /* drive 0 */ + bp_write_regr(0x47,0xa0); /* ATAPI packet command */ + } + + if ((r=bp_wait_drq(fun,"","sending command"))) { + bp_disconnect(); + return r; + } + + bp_write_cmd(cmd,12); + return 0; +} + +static int bp_completion( char * fun ) + +{ int r, n; + + if (!(r=bp_wait(fun,"","reading data"))) { + if (bp_read_regr(0x42) == 2) { + n = (bp_read_regr(0x44) + 256*bp_read_regr(0x45)); + bp_read_data(bp_buffer,n); + r=bp_wait(fun,"","waiting after data"); + } + } + if (r) r = bp_req_sense(fun," after command"); + + bp_disconnect(); + return r; +} + +static int bp_atapi( char * cmd, int dlen, char * fun ) + +{ int r; + + if (!(r=bp_command(cmd,dlen,fun))) + r = bp_completion(fun); + return r; +} + +static void bp_lock(void) + +{ char lo_cmd[12] = { 0x1e,0,0,0,1,0,0,0,0,0,0,0 }; + char cl_cmd[12] = { 0x1b,0,0,0,3,0,0,0,0,0,0,0 }; + + bp_atapi(cl_cmd,0,NULL); + bp_atapi(cl_cmd,0,NULL); + bp_atapi(cl_cmd,0,"close door"); + + bp_atapi(lo_cmd,0,NULL); + bp_atapi(lo_cmd,0,"lock door"); +} + +static void bp_unlock( void) + +{ char un_cmd[12] = { 0x1e,0,0,0,0,0,0,0,0,0,0,0 }; + + bp_atapi(un_cmd,0,"unlock door"); +} + +static void bp_eject( void) + +{ char ej_cmd[12] = { 0x1b,0,0,0,2,0,0,0,0,0,0,0 }; + + bp_unlock(); + bp_atapi(ej_cmd,0,"eject"); +} + +static int bp_reset( void ) + +/* the ATAPI standard actually specifies the contents of all 7 registers + after a reset, but the specification is ambiguous concerning the last + two bytes, and different drives interpret the standard differently. +*/ + +{ int i, flg; + int expect[5] = {1,1,1,0x14,0xeb}; + long flags; + + bp_connect(); + bp_write_regr(0x46,0xa0); + bp_write_regr(0x47,8); + + save_flags(flags); + sti(); + udelay(500000); /* delay 0.5 seconds */ + restore_flags(flags); + + flg = 1; + for(i=0;i<5;i++) flg &= (bp_read_regr(i+0x41) == expect[i]); + + if (bp_debug) { + printk("bpcd RESET: "); + for (i=0;i<5;i++) printk("%3x",bp_read_regr(i+0x41)); + printk("\n"); + } + + bp_disconnect(); + return flg-1; +} + +static int bp_identify( char * id ) + +{ int k; + char id_cmd[12] = {0x12,0,0,0,36,0,0,0,0,0,0,0}; + + bp_bufblk = -1; + if (bp_atapi(id_cmd,36,"identify")) return -1; + for (k=0;k<16;k++) id[k] = bp_buffer[16+k]; + id[16] = 0; + return 0; +} + +static int bp_port_check( void ) /* check for 8-bit port */ + +{ int i, r, m; + + w2(0x2c); i = r0(); w0(255-i); r = r0(); w0(i); + m = -1; + if (r == i) m = 1; + if (r == (255-i)) m = 0; + + w2(0xc); i = r0(); w0(255-i); r = r0(); w0(i); + if (r != (255-i)) m = -1; + + if (m == 0) { w2(6); w2(0xc); r = r0(); w0(0xaa); w0(r); w0(0xaa); } + if (m == 1) { w2(0x26); w2(0xc); } + + return m; +} + +static int bp_locate( void ) + +{ int k; + + for(k=0;k<256;k++) + if (!bp_check_id(k)) { + bp_unit_id = k; + return 0; + } + return -1; +} + +static void bp_probe ( int port, int max_mode ) + +{ int j, best, range; + + range = 3; + if (max_mode == 2) range = 8; + if (check_region(port,range)) { + max_mode = 1; + range = 3; + if (check_region(port,range)) return; + } + + bp_base = port; + + j = bp_port_check(); + if (j == -1) { + bp_base = 0; + return; + } + + if (bp_locate()) { + if (bp_debug) + printk("bpcd CHECK: no backpack adapter at 0x%x\n",port); + bp_base = 0; + return; + } + + best = -1; + /* if (j == 0) max_mode = 0; */ + + if (bp_debug) printk("bpcd PROBE: port 0x%x, id %d, max_mode %d\n", + port, bp_unit_id, max_mode); + + for (bp_mode=0;bp_mode<=max_mode;bp_mode++) + if (!bp_test_proto()) best = bp_mode; + + bp_mode = best; + if (bp_mode < 0) bp_base = 0; +} + +static int bp_detect( void ) + +{ char id[18]; + char *mode_str[3] = { "4-bit", "8-bit", "EPP" }; + int range; + + if (!bp_base) bp_probe(0x278,2); + if (!bp_base) bp_probe(0x378,2); + if (!bp_base) bp_probe(0x3bc,1); + + if (!bp_base) { + printk("bpcd: autoprobe failed\n"); + return -1; + } + + if (bp_mode == 2) range = 8; else range = 3; + if (check_region(bp_base,range)) { + printk("bpcd: Ports at 0x%x are not available\n",bp_base); + return -1; + } + + if (bp_port_check() < 0) { + printk("bpcd: No parallel port at 0x%x\n",bp_base); + return -1; + } + + if (bp_locate()) { + printk("bpcd: Couldn't find a backpack adapter at 0x%x\n", + bp_base); + return -1; + } + + if (bp_debug) printk("bpcd ID: unit %d\n",bp_unit_id); + + if (bp_debug) bp_read_eeprom(); + + if (bp_test_proto()) { + printk("bpcd: Can't access backpack in %s mode\n", + mode_str[bp_mode]); + return -1; + } + + printk("bpcd %s: backpack ID %d, using port 0x%x in %s mode\n", + BP_VERSION,bp_unit_id,bp_base,mode_str[bp_mode]); + + if (bp_reset()) { + printk("bpcd: Failed to reset CD drive\n"); + return -1; + } + + if (bp_identify(id)) { + printk("bpcd: ATAPI inquiry failed\n"); + return -1; + } + + if (bp_mode == 2) range = 8; else range = 3; + request_region(bp_base,range,"bpcd"); + + printk("bpcd: Found %s\n",id); + + return 0; +} + +static void bp_transfer( void ) + +{ int k, o; + + while (bp_count && (bp_sector/4 == bp_bufblk)) { + o = (bp_sector % 4) * 512; + for(k=0;k<512;k++) bp_buf[k] = bp_buffer[o+k]; + bp_count--; + bp_buf += 512; + bp_sector++; + } +} + +static void bp_start( void ) + +{ int b, i; + char rd_cmd[12] = {0xa8,0,0,0,0,0,0,0,0,1,0,0}; + + bp_bufblk = bp_sector / 4; + b = bp_bufblk; + for(i=0;i<4;i++) { + rd_cmd[5-i] = b & 0xff; + b = b >> 8; + } + + if (bp_command(rd_cmd,2048,"read block")) { + bp_bufblk = -1; + bp_busy = 0; + cli(); + end_request(0); + do_bp_request(); + return; + } + bp_timeout = jiffies + BP_TMO; + queue_task(&bp_tq,&tq_scheduler); +} + +static void do_bp_read( void ) + +{ bp_busy = 1; + bp_retries = 0; + bp_transfer(); + if (!bp_count) { + end_request(1); + bp_busy = 0; + return; + } + sti(); + + bp_start(); +} + +static void bp_interrupt( void *data) + +{ if (!(bp_read_regr(0xb) & 0x80)) { + if (jiffies > bp_timeout) { + bp_bufblk = -1; + bp_busy = 0; + end_request(0); + do_bp_request(); + return; + } + queue_task(&bp_tq,&tq_scheduler); + return; + } + sti(); + if (bp_completion("read block")) { + if (bp_retries < BP_RETRIES) { + udelay(100000); + bp_retries++; + + bp_start(); + + return; + } + cli(); + bp_busy = 0; + bp_bufblk = -1; + end_request(0); + do_bp_request(); + return; + } + do_bp_read(); + do_bp_request(); +} + +/* end of bpcd.c */ +