diff -u --recursive --new-file linux-1.1.55+new_quota/ANNOUNCE.eata linux/ANNOUNCE.eata --- linux-1.1.55+new_quota/ANNOUNCE.eata Wed Dec 31 18:00:00 1969 +++ linux/ANNOUNCE.eata Thu Oct 20 18:15:32 1994 @@ -0,0 +1,39 @@ +This is the announcement for the EATA SCSI driver + +The driver supports all EATA-DMA Protocol compliant SCSI +ISA and EISA controllers (PCI rsn.) + +Those are for example: + DPT Smartcache : PM2011 (ISA), PM2012A (EISA), + PM2012B (EISA) + Smartcache III: PM2021 (ISA), PM2022 (EISA), + PM2122 (EISA), PM2322 (EISA) + controllers from NEC and AT&T. + +The driver has been tested on the PM2011, PM2021 and PM2022 + +I call it still ALPHA software but the driver is pretty stable +and has been used for some months now. There are just some +features missing (PCI/multiple HAs) which I want to implement +in the next time. + +The installation is basically a plug and play installation. +Just "cd" to your linux directory and extract the files from the +TAR archive. Then you will find the diffile eata_patch. +Apply that with "patch -p1 >> Edit configuration parameters in ./include/linux/tpqic02.h!' - -else - -comment '>>> Setting runtime QIC-02 configuration is done with qic02conf' -comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/' - -fi -fi - -bool 'QIC-117 tape support' CONFIG_FTAPE y -if [ "$CONFIG_FTAPE" = "y" ]; then -int ' number of ftape buffers' NR_FTAPE_BUFFERS 3 -fi - -comment 'Sound' - -bool 'Sound card support' CONFIG_SOUND n - -comment 'Kernel hacking' - -#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC n -bool 'Kernel profiling support' CONFIG_PROFILE n -if [ "$CONFIG_SCSI" = "y" ]; then -bool 'Verbose scsi error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS n -fi diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/Makefile linux/drivers/block/Makefile --- linux-1.1.55+new_quota/drivers/block/Makefile Sat Sep 17 21:44:49 1994 +++ linux/drivers/block/Makefile Thu Oct 20 18:15:33 1994 @@ -64,7 +64,16 @@ SRCS := $(SRCS) xd.c endif -all: block.a +ifdef CONFIG_CDU535 +OBJS := $(OBJS) sonycd535.o +SRCS := $(SRCS) sonycd535.c +endif + +ifdef CONFIG_NEC260 +OBJS := $(OBJS) nec260.o +SRCS := $(SRCS) nec260.c +CFLAGS := $(CFLAGS) -DLINKED_IN_KERNEL -DNO_IRQACTION +endif block.a: $(OBJS) rm -f block.a diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/blk.h linux/drivers/block/blk.h --- linux-1.1.55+new_quota/drivers/block/blk.h Thu Oct 13 21:31:06 1994 +++ linux/drivers/block/blk.h Thu Oct 20 18:15:33 1994 @@ -40,6 +40,9 @@ extern unsigned long hd_init(unsigned long mem_start, unsigned long mem_end); extern unsigned long cdu31a_init(unsigned long mem_start, unsigned long mem_end); extern unsigned long mcd_init(unsigned long mem_start, unsigned long mem_end); +#ifdef CONFIG_CDU535 +extern unsigned long init_sony535(unsigned long mem_start, unsigned long mem_end); +#endif #ifdef CONFIG_SBPCD extern unsigned long sbpcd_init(unsigned long, unsigned long); #endif CONFIG_SBPCD @@ -135,6 +138,14 @@ #define DEVICE_NAME "CDU31A" #define DEVICE_REQUEST do_cdu31a_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#elif (MAJOR_NR == CDU535_CDROM_MAJOR) +#define DEVICE_NAME "SONY-CDU535" +#define DEVICE_INTR do_cdu535 +#define DEVICE_REQUEST do_cdu535_request #define DEVICE_NR(device) (MINOR(device)) #define DEVICE_ON(device) #define DEVICE_OFF(device) diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/hd.c linux/drivers/block/hd.c --- linux-1.1.55+new_quota/drivers/block/hd.c Thu Oct 20 15:14:27 1994 +++ linux/drivers/block/hd.c Thu Oct 20 18:18:47 1994 @@ -43,6 +43,10 @@ #define MAJOR_NR HD_MAJOR #include "blk.h" +#ifdef CONFIG_NEC260 +int the_nec260_major = 0; +#endif + #define HD_IRQ 14 static int revalidate_hddisk(int, int); @@ -659,6 +663,9 @@ reset = 1; dev = DEVICE_NR(CURRENT->dev); printk("hd%c: timeout\n", dev+'a'); +#ifdef CONFIG_NEC260 + if (CURRENT->dev < 0) return; +#endif if (++CURRENT->errors >= MAX_ERRORS) { #ifdef DEBUG printk("hd%c: too many errors\n", dev+'a'); @@ -717,6 +724,14 @@ return; repeat: timer_active &= ~(1< 0 && + blk_dev[the_nec260_major].current_request) { + cli (); + (blk_dev[the_nec260_major].request_fn) (); + } + if (CURRENT && CURRENT->dev < 0) return; +#endif sti(); INIT_REQUEST; if (reset) { diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- linux-1.1.55+new_quota/drivers/block/ll_rw_blk.c Thu Oct 20 17:01:16 1994 +++ linux/drivers/block/ll_rw_blk.c Thu Oct 20 18:15:34 1994 @@ -34,6 +34,10 @@ int read_ahead[MAX_BLKDEV] = {0, }; +#if defined(CONFIG_CDU535) && defined(CONFIG_CDU31A) + int sonycd_save_mem_start; +#endif + /* blk_dev_struct is: * do_request-address * next-request @@ -504,15 +508,36 @@ #ifdef CONFIG_BLK_DEV_XD mem_start = xd_init(mem_start,mem_end); #endif +#if defined(CONFIG_CDU535) && defined(CONFIG_CDU31A) + { /* since controllers for 535 and 31A can be at same location + * we have to be careful. + */ + sonycd_save_mem_start = mem_start; + mem_start = cdu31a_init(mem_start,mem_end); + if ( mem_start == sonycd_save_mem_start ) { /* CDU31A not found */ + mem_start = init_sony535(mem_start,mem_end); + } + } +#else #ifdef CONFIG_CDU31A mem_start = cdu31a_init(mem_start,mem_end); #endif +#ifdef CONFIG_CDU535 + mem_start = init_sony535(mem_start,mem_end); +#endif +#endif /* CONFIG_CDU31A && CONFIG_CDU535 */ #ifdef CONFIG_MCD mem_start = mcd_init(mem_start,mem_end); #endif #ifdef CONFIG_SBPCD mem_start = sbpcd_init(mem_start, mem_end); #endif CONFIG_SBPCD +#ifdef CONFIG_NEC260 + { + extern unsigned long nec260_init (unsigned long, unsigned long); + mem_start = nec260_init(mem_start, mem_end); + } +#endif if (ramdisk_size) mem_start += rd_init(mem_start, ramdisk_size*1024); return mem_start; diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/mcd.c linux/drivers/block/mcd.c --- linux-1.1.55+new_quota/drivers/block/mcd.c Sat Sep 17 21:48:13 1994 +++ linux/drivers/block/mcd.c Thu Oct 20 18:15:34 1994 @@ -1182,7 +1182,6 @@ mcd_invalidate_buffers(); mcdPresent = 1; - printk("\n"); return mem_start; } diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/nec260.c linux/drivers/block/nec260.c --- linux-1.1.55+new_quota/drivers/block/nec260.c Wed Dec 31 18:00:00 1969 +++ linux/drivers/block/nec260.c Thu Oct 20 18:15:34 1994 @@ -0,0 +1,852 @@ +/* + * nec260.c - NEC CDR-260 driver + * some pieces derived from mcd.c. + * + * scott snyder + * + * 0.1 May 31, 1994 Initial alpha release. + * 0.2 Sep 4, 1994 Converted into a loadable kernel module. + * Made more flexible wrt. hardware configurations. + * Sense disk changes properly. + * 0.3 Sep. 11, 1994 Add LINKED_IN_KERNEL #ifdef's. + * + * Copyright (c) 1994 scott snyder + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define REALLY_SLOW_IO +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef LINKED_IN_KERNEL +# define MAYBE_STATIC static +#else +# include +# define MAYBE_STATIC +#endif + +#include "version.h" + +/***************************************************************************/ + +/* Configuration options which may be set when loading the driver. */ + +/* Major device number. */ +MAYBE_STATIC int nec260_major = 26; + +/* IRQ level used. Only matters if nec260_no_hd is turned on. */ +MAYBE_STATIC int nec260_irq = 14; + +/* Base I/O address for the IDE adapter registers. */ +MAYBE_STATIC int nec260_base = HD_DATA; + +/* Set this to 1 if the device is configured as the master on the bus. */ +MAYBE_STATIC int nec260_master = 0; + +/* Set this to 1 if the cdrom drive is on the bus by itself. */ +MAYBE_STATIC int nec260_no_hd = 0; + +/***************************************************************************/ + +#define HD_DATA_OFFSET (HD_DATA - HD_DATA) +#define HD_STATUS_OFFSET (HD_STATUS - HD_DATA) +#define HD_ERROR_OFFSET (HD_ERROR - HD_DATA) +#define HD_LCYL_OFFSET (HD_LCYL - HD_DATA) +#define HD_HCYL_OFFSET (HD_HCYL - HD_DATA) +#define HD_CURRENT_OFFSET (HD_CURRENT - HD_DATA) +#define HD_COMMAND_OFFSET (HD_COMMAND - HD_DATA) + +#define MAJOR_NR nec260_major +#define DEVICE_NAME "NEC IDE CD-ROM" +#define DEVICE_REQUEST do_nec260_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) +#define DEVICE_INTR do_nec260 + +#include "blk.h" + +/* The call interface for binding interrupts changed in 1.1.43 :-( */ + +#if (KERNEL_MAJOR >= 1) && \ + (KERNEL_MINOR >= 1) && \ + (KERNEL_PATCH >= 43) +# define NO_IRQACTION +#endif + +/***************************************************************************/ + +/* special command codes for strategy routine. */ +#define CHECK_STATUS 4314 + +#define CD_BLOCK_SIZE 2048 + +static void nec260_timeout (unsigned long x); +static void do_read_callback (void); +static void do_read_callback_2 (void); +static int test_read (unsigned int dev, unsigned int block, + unsigned int nsect); + + +/* Values to write to the controller registers to start a transaction. */ + +static char regimage[7] = { + 0, /* HD_PRECOMP */ + 0, /* HD_NSECTOR */ + 0, /* HD_SECTOR */ + (CD_BLOCK_SIZE & 0xff), /* HD_LCYL # of bytes to read (lo) */ + (CD_BLOCK_SIZE >> 8), /* HD_HCYL # of bytes to read (hi) */ + 0xf0, /* HD_CURRENT (device select) (slave) */ + 0xa0 /* HD_COMMAND */ +}; + + +/* Image of command to be sent to the device for a read. + Portions of this will be overwritten to set the sector number. + If i actually had documentation for this device, i could probably + give these values some meaningful names... */ + +static char cmdimage[12] = {0x28, 0, 0, 0, 0x0f, 0x54, 0, 0, 1, 0, 0, 0}; + +static int nec260_request_in_progress = 0; +static int nec260_waiting_for_int = 0; +static int nec260_open_count = 0; +static int nec260_media_changed = 0; +static void (**nec260_handler_ptr)(void); +static int the_hd_major; +static int retrying = 0; + +static struct timer_list nec260_timer = {NULL, NULL, 0, 0, nec260_timeout}; + +/* The transfer buffer. */ +static char nec260_buf[CD_BLOCK_SIZE]; +static int nec260_bn = -1; + +/* The dummy request which we use to plug up the HD queue. */ +static struct request plug; + +/* Symbols from hd.c. */ +extern int the_nec260_major; +extern void (*do_hd)(void); +#ifdef CONFIG_BLK_DEV_HD1 +extern void (*do_hd1)(void); +#endif + + +/***************************************************************************/ + +/* + * Wait until the controller is idle. + * Returns the status field; if it has the bit BUSY_STAT set, + * we timed out. + */ +static int controller_busy (void) +{ + int retries = 100000; + unsigned char status; + + do + { + status = inb_p (nec260_base + HD_STATUS_OFFSET); + } while ((status & BUSY_STAT) && --retries); +#ifdef SHY_DEVICE + { + static doneonce = 0; + if (status == 0xff && !doneonce) + status = 0; + doneonce = 1; + } +#endif + return status; +} + + +/* + * Wait for the controller to be ready to receive a command. + * Returns 0 if successful, -1 if we timed out. + */ +static inline int wait_DRQ (void) +{ + int retries = 100000; + + while (--retries > 0) + if (inb_p (nec260_base + HD_STATUS_OFFSET) & DRQ_STAT) + return 0; + return -1; +} + + +/* + * Stick a dummy request at the head of the HD request queue + * to prevent any HD activity while we're using the controller. + * The HD queue must be empty. + */ +static void plug_hd (void) +{ + if (nec260_no_hd) + printk ("nec260: spurious call to plug_hd\n"); + else + { + cli (); /* safety */ + if (blk_dev[the_hd_major].current_request != NULL) + { + printk ("nec260 (plug_hd): hd already active!\n"); + return; + } + blk_dev[the_hd_major].current_request = &plug; + plug.dev = -1; + plug.next = NULL; + + /* exits with ints clear */ + } +} + + +/* + * Remove the dummy request from the start of the HD queue. + */ +static void unplug_hd (void) +{ + if (nec260_no_hd) + printk ("nec260: spurious call to unplug_hd\n"); + else + { + cli (); /* safety */ + if (blk_dev[the_hd_major].current_request != &plug) + { + printk ("nec260 (unplug_hd): hd not plugged!\n"); + return; + } + blk_dev[the_hd_major].current_request = plug.next; + (blk_dev[the_hd_major].request_fn) (); + } +} + + +/* + * Unplug the HD queue and end a idecd request. + */ +static void end_read_request (int flag) +{ + if (! nec260_no_hd) + unplug_hd (); + end_request (flag); + nec260_request_in_progress = 0; +} + + +/* + * Transfer as much data as we can from NEC260_BUF to the output buffer. + */ +static void +nec260_transfer(void) +{ + long offs; + + while (CURRENT -> nr_sectors > 0 && nec260_bn == CURRENT -> sector / 4) + { + offs = (CURRENT -> sector & 3) * 512; + memcpy (CURRENT -> buffer, nec260_buf + offs, 512); + CURRENT -> nr_sectors--; + CURRENT -> sector++; + CURRENT -> buffer += 512; + } +} + + +/* + * Invalidate any data saved in our internal buffer + */ +static void +nec260_invalidate_buffers (void) +{ + nec260_bn = -1; +} + + +/* + * Complete a read request with status STAT, and call the request routine + * to start off the next one. + */ +static void complete_read_request (int stat) +{ + del_timer (&nec260_timer); /* Cancel the timeout */ + end_read_request (stat); + cli (); + do_nec260_request (); +} + + +/* + * Called when our timer goes off. + */ +static void nec260_timeout (unsigned long x) +{ + /* Ignore if we're not waiting for an interrupt. */ + if (! nec260_waiting_for_int) return; + + /* Complete the request with error status. */ + nec260_waiting_for_int = 0; + printk ("nec260: request timed out\n"); + if (*nec260_handler_ptr == do_read_callback || + *nec260_handler_ptr == do_read_callback_2) + *nec260_handler_ptr = NULL; + else + printk ("nec260: funny value for interrupt handler\n"); + complete_read_request (0); +} + +/* + * Interrupt routine to swallow the extra interrupt from the device. + */ +static void do_read_callback_2 (void) +{ + int stat; + + *nec260_handler_ptr = NULL; + + if (! nec260_waiting_for_int) + { + printk ("nec260 (do_read_callback_2): spurious call?\n"); + return; + } + + nec260_waiting_for_int = 0; + + /* Check error flag and complete the I/O. */ + stat = inb (nec260_base + HD_ERROR_OFFSET); + stat = ((stat & ERR_STAT) == 0); + complete_read_request (stat); +} + + +/* + * Interrupt routine. Called when a read request has completed. + */ +static void do_read_callback (void) +{ + int stat, len, thislen; + + if (! nec260_waiting_for_int) + { + printk ("nec260 (do_read_callback): spurious call?\n"); + return; + } + + nec260_waiting_for_int = 0; + + /* Check for errors. */ + stat = inb (nec260_base + HD_ERROR_OFFSET); + + if (stat & ERR_STAT) + stat = 0; + else + { + if (CURRENT->cmd != CHECK_STATUS) + { + /* Error bit not set. + Read the device registers to see how much data is waiting. */ + len = inb_p (nec260_base + HD_LCYL_OFFSET) + + 256 * inb_p (nec260_base + HD_HCYL_OFFSET); + + /* Read the data into our buffer. */ + thislen = len; + if (thislen > sizeof (nec260_buf)) thislen = sizeof (nec260_buf); + insw (nec260_base + HD_DATA_OFFSET, nec260_buf, thislen/2); + len -= thislen; + + /* Warn if the size of the data from the device is + larger than the buffer. */ + if (len > 0) + { + printk ("nec260: discarding %x bytes\n", len); + while (len > 0) + { + (void) inw_p (nec260_base + HD_DATA_OFFSET); + len -= 2; + } + } + } + + /* Check for tray open/disk changed. + For some reason, these don't set the `error' bit. */ + if (stat == 0x24) + { + /* Tray open. The request fails. */ + printk ("nec260: tray open\n"); + nec260_invalidate_buffers (); + nec260_media_changed = 1; + stat = 0; + } + else if (stat == 0x64) + { + /* Media changed. Try the operation again. */ + printk ("nec260: media change\n"); + nec260_invalidate_buffers (); + nec260_media_changed = 1; + if (CURRENT->cmd != CHECK_STATUS && !retrying) + { + del_timer (&nec260_timer); + retrying = 1; + test_read (MINOR (CURRENT->dev), + CURRENT->sector, CURRENT->nr_sectors); + return; + } + stat = 0; + } + else if (CURRENT->cmd == CHECK_STATUS) + stat = 1; + else + { + /* Copy as much as we can into the output buffer. */ + nec260_bn = CURRENT->sector / 4; + nec260_transfer (); + + stat = 1; + } + } + + /* If there was an error, complete the request now with an error. + But if the read is successful, the device is going to be sending + us another interrupt. It likes long goodbyes, i guess. Anyway, + wait until we see this extra interrupt before ending the request. */ + if (stat && CURRENT->cmd != CHECK_STATUS) + { + *nec260_handler_ptr = do_read_callback_2; + nec260_waiting_for_int = 1; + } + else + complete_read_request (stat); +} + + +/* + * Start a read request from the CD-ROM. + * Returns 0 if the request was started successfully, + * -1 if there was an error. + * Note: The NSECT arg is presently ignored; we always read exactly + * one block. + */ +static int do_read (unsigned int dev, unsigned int block, unsigned int nsect) +{ + int i; + char *the_regimage; + char check_status_regimage[sizeof (regimage)]; + char *the_cmdimage; + char check_status_cmdimage[sizeof (cmdimage)]; + + /* Wait for the controller to be idle. */ + if (controller_busy () & BUSY_STAT) return -1; + + if (CURRENT->cmd == CHECK_STATUS) + { + the_regimage = check_status_regimage; + for (i=0; icmd == CHECK_STATUS) + { + the_cmdimage = check_status_cmdimage; + for (i=0; isector / 4; + cmdimage[2] = conv.b.b3; + cmdimage[3] = conv.b.b2; + cmdimage[4] = conv.b.b1; + cmdimage[5] = conv.b.b0; + } + the_cmdimage = cmdimage; + } + + /* Send the command to the device. */ + outsw (nec260_base + HD_DATA_OFFSET, the_cmdimage, sizeof (cmdimage)/2); + + /* Set up our interrupt handler and return. */ + *nec260_handler_ptr = do_read_callback; + nec260_waiting_for_int = 1; + + /* Set up a timeout. */ + nec260_timer.expires = 500; + add_timer (&nec260_timer); + + return 0; +} + + +/* + * Start a read request. + * If there is an error starting it, terminate the current request + * immediately with an error. + */ +static int test_read (unsigned int dev, unsigned int block, + unsigned int nsect) +{ + int stat; + stat = do_read (dev, block, nsect); + + if (stat) + end_read_request (0); + return 1; +} + + +/* + * I/O request routine called from kernel. + */ +static void do_nec260_request (void) +{ + unsigned int block,dev; + unsigned int nsect; + + /* Don't do anything if we're waiting for a request to complete. + (Can this actually happen?) */ + if (nec260_request_in_progress) return; + + repeat: + cli (); /* safety */ + nec260_request_in_progress = 0; + + /* Return if our queue is plugged. */ + if (!(CURRENT) || CURRENT->dev < 0) return; + + /* Get the next request on the queue. */ + INIT_REQUEST; + dev = MINOR (CURRENT->dev); + block = CURRENT->sector; + nsect = CURRENT->nr_sectors; + + /* Return if there wasn't one. */ + if (CURRENT == NULL || CURRENT -> sector == -1) + return; + + /* We can only read. */ + if (CURRENT -> cmd != READ && CURRENT->cmd != CHECK_STATUS) + { + printk ("nec260: bad cmd %d\n", CURRENT -> cmd); + end_request (0); + goto repeat; + } + + if (CURRENT->cmd != CHECK_STATUS) + { + /* Try to satisfy the request from the buffer. */ + nec260_transfer (); + + /* If we got the entire request, we're done. */ + if (CURRENT->nr_sectors == 0) + { + end_request (1); + goto repeat; + } + } + + /* If the HD is currently active, return - we must wait for it to finish. + If it is idle (no requests in the queue), plug up the queue with a dummy + request until we're done using the controller. */ + if (! nec260_no_hd) + { + if (blk_dev[the_hd_major].current_request) return; + plug_hd (); + } + nec260_request_in_progress = 1; + + retrying = 0; + if (!test_read (dev, block, nsect)) + goto repeat; + + sti (); + return; +} + + +/* + * Open the device. + */ + +static int +nec260_open (struct inode *ip, struct file *fp) +{ + /* should check that h/w is available... */ + + /* no write access */ + if (fp->f_mode & 2) return -EROFS; + + if (!nec260_open_count) + { + nec260_invalidate_buffers (); + + /* should check that there's a disk in the drive? */ + } + + /* Opened ok. Count this access and return success. */ + ++nec260_open_count; +#ifndef LINKED_IN_KERNEL + MOD_INC_USE_COUNT; +#endif + return 0; +} + + +/* + * Close down the device. Invalidate all cached blocks. + */ + +static void +nec260_release (struct inode *inode, struct file *file) +{ + --nec260_open_count; + if (nec260_open_count < 0) + { + printk ("nec260: inconsistent open count %d\n", nec260_open_count); + nec260_open_count = 0; + } + + if (nec260_open_count == 0) + { + nec260_invalidate_buffers (); + sync_dev (inode->i_rdev); + invalidate_buffers (inode->i_rdev); + } +#ifndef LINKED_IN_KERNEL + MOD_DEC_USE_COUNT; +#endif +} + + +static +void nec260_do_check_status (dev_t full_dev) +{ + struct request check_status_req; + struct semaphore sem = MUTEX_LOCKED; + + check_status_req.dev = full_dev; + check_status_req.cmd = CHECK_STATUS; + check_status_req.errors = 0; + check_status_req.sector = 0; + check_status_req.nr_sectors = 0; + check_status_req.current_nr_sectors = 0; + check_status_req.buffer = NULL; + check_status_req.sem = &sem; + check_status_req.bh = NULL; + check_status_req.bhtail = NULL; + check_status_req.next = NULL; + + cli (); + if (CURRENT == NULL) + { + CURRENT = &check_status_req; + do_nec260_request (); + } + else + { + check_status_req.next = CURRENT; + CURRENT->next = &check_status_req; + } + sti (); + + down (&sem); +} + + +static +int nec260_disk_change (dev_t full_dev) +{ + int retval, target; + + target = MINOR (full_dev); + + if (target > 0) + { + printk ("nec260 (nec260_disk_change): invalid device %d\n", target); + return 0; + } + + if (nec260_media_changed == 0) + nec260_do_check_status (full_dev); + + retval = nec260_media_changed; + nec260_media_changed = 0; + + printk ("nec260: disk change %d\n", retval); + + return retval; +} + + +static void nec260_interrupt (int unused) +{ + void (*handler)(void) = DEVICE_INTR; + + DEVICE_INTR = NULL; + if (!handler) + printk ("nec260: unexpected interrupt\n"); + else + handler(); + sti(); +} + + +#ifndef NO_IRQACTION +/* + * This is the cdrom IRQ description. The SA_INTERRUPT in sa_flags + * means we run the IRQ-handler with interrupts disabled. + */ +static struct sigaction nec260_sigaction = { + nec260_interrupt, + 0, + SA_INTERRUPT, + NULL +}; +#endif /* ! NO_IRQACTION */ + + + +static struct file_operations nec260_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + NULL, /* ioctl */ + NULL, /* mmap */ + nec260_open, /* open */ + nec260_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + nec260_disk_change, /* check_media_change */ + NULL, /* revalidate */ +}; + + +MAYBE_STATIC int init_module (void) +{ + /* Find the major numbe of the HD on our IDE bus. */ + the_hd_major = HD_MAJOR; +#ifdef CONFIG_BLK_DEV_HD1 + if (nec260_base == HD1_DATA) + the_hd_major = HD1_MAJOR; +#endif + + /* Register ourselves with the kernel. */ + if (register_blkdev (nec260_major, "nec260", &nec260_fops) != 0) + { + printk("nec260: Unable to get major %d for NEC CDR-260\n", nec260_major); + return -EIO; + } + + printk ("nec260: registered with major #%d\n", nec260_major); + + /* Install our device request routine. */ + blk_dev[nec260_major].request_fn = DEVICE_REQUEST; + read_ahead[nec260_major] = 4; + + /* Set the proper device select code. */ + if (nec260_master) + regimage[5] = 0xe0; + else + regimage[5] = 0xf0; + + if (nec260_no_hd) + { + /* Set up the interrupt. */ + do_nec260 = NULL; + nec260_handler_ptr = &do_nec260; +#ifdef NO_IRQACTION + if (request_irq (nec260_irq, nec260_interrupt, SA_INTERRUPT, "nec260")) +#else + if (irqaction (nec260_irq, &nec260_sigaction)) +#endif + { + printk ("Unable to get IRQ%d for NEC CDR-260 CD-ROM\n", nec260_irq); + unregister_blkdev (nec260_major, "nec260"); + return -EIO; + } + } + else + { + /* tell hd.c our major number */ + the_nec260_major = nec260_major; + + /* Set up nec260_handler_ptr to point to the HD interrupt vector. */ + nec260_handler_ptr = &do_hd; +#ifdef CONFIG_BLK_DEV_HD1 + if (nec260_base == HD1_DATA) + the_hd_major = &do_hd1; +#endif + } + + return 0; +} + +#ifdef LINKED_IN_KERNEL + +unsigned long nec260_init (unsigned long mem_start, unsigned long mem_end) +{ + init_module (); + return mem_start; +} + +#else + +void cleanup_module (void) +{ + if (MOD_IN_USE) + printk("nec260: device busy, remove delayed\n"); + + if (unregister_blkdev (nec260_major, "nec260") != 0) + printk ("nec260: cleanup_module failed\n"); + else + { + printk ("nec260: cleanup_module succeeded\n"); + if (nec260_no_hd) + free_irq (nec260_irq); + else + the_nec260_major = 0; + } +} + +#endif diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/sonycd535.c linux/drivers/block/sonycd535.c --- linux-1.1.55+new_quota/drivers/block/sonycd535.c Wed Dec 31 18:00:00 1969 +++ linux/drivers/block/sonycd535.c Thu Oct 20 18:15:34 1994 @@ -0,0 +1,1537 @@ +/* + * Sony CDU-535 interface device driver + * + * This is a modified version of the CDU-31A device driver (see below). + * Changes were made using documentation for the CDU-531 (which Sony + * assures me is very similar to the 535) and partial disassembly of the + * DOS driver. I used Minyard's driver and replaced the the CDU-31A + * commands with the CDU-531 commands. This was complicated by a different + * interface protocol with the drive. The driver is still polled. + * + * Data transfer rate is about 110 Kb/sec, theoretical maximum is 150 Kb/sec. + * I tried polling without the sony_sleep during the data transfers but + * it did not speed things up any. + * + * 5/23/93 (rgj) changed the major number to 21 to get rid of conflict + * with CDU-31A driver. This is the also the number from the Linux + * Device Driver Registry for the Sony Drive. Hope nobody else is using it. + * + * 8/29/93 (rgj) remove the configuring of the interface board address + * from the top level configuration, you have to modify it in this file. + * + * Things to do: + * - handle errors and status better, put everything into a single word + * - use interrupts, DMA + * + * Known Bugs: + * - + * + * Ron Jeppesen (ronj.an@site007.saic.com) + * + * + *------------------------------------------------------------------------ + * Sony CDROM interface device driver. + * + * Corey Minyard (minyard@wf-rch.cirr.com) (CDU-535 complaints to ronj above) + * + * Colossians 3:17 + * + * The Sony interface device driver handles Sony interface CDROM + * drives and provides a complete block-level interface as well as an + * ioctl() interface compatible with the Sun (as specified in + * include/linux/cdrom.h). With this interface, CDROMs can be + * accessed and standard audio CDs can be played back normally. + * + * This interface is (unfortunatly) a polled interface. This is + * because most Sony interfaces are set up with DMA and interrupts + * disables. Some (like mine) do not even have the capability to + * handle interrupts or DMA. For this reason you will see a lot of + * the following: + * + * retry_count = jiffies+ SONY_JIFFIES_TIMEOUT; + * while ((retry_count > jiffies) && (! +#ifdef CONFIG_CDU535 + +#include +#include +#include +#include +#include +#include +#include +#include + +#define REALLY_SLOW_IO +#include +#include +#include + +#include +#include + +#define MAJOR_NR CDU535_CDROM_MAJOR +#include "blk.h" + +/* + * this is the base address of the interface card for the Sony CDU535 + * CDROM drive. If your jumpers are set for an address other than + * this one (the default), change the following line to the + * proper address. + */ +#define CDU535_ADDRESS (0x340) + +#define DEBUG + +/* + * SONY535_BUFFER_SIZE determines the size of internal buffer used + * by the drive. It must be at least 2K and the larger the buffer + * the better the transfer rate. It does however take system memory. + * On my system I get the following transfer rates using dd to read + * 10 Mb off /dev/cdrom. + * + * 8K buffer 43 Kb/sec + * 16K buffer 66 Kb/sec + * 32K buffer 91 Kb/sec + * 64K buffer 111 Kb/sec + * 128K buffer 123 Kb/sec + * 512K buffer 123 Kb/sec + */ +#define SONY535_BUFFER_SIZE (64*1024) + +/* + * if LOCK_DOORS is defined then the eject button is disabled while + * the device is open. + */ +#define LOCK_DOORS + +static int read_subcode(void); +static void sony_get_toc(void); +static int cdu_open( struct inode *inode, struct file *filp ); +static inline unsigned int int_to_bcd(unsigned int val); +static unsigned int bcd_to_int(unsigned int bcd); +static int do_sony_cmd( Byte *cmd, int nCmd, Byte status[2], + Byte *response, int nResponse, int ignoreStatusBit7 ); + +/* The base I/O address of the Sony Interface. This is a variable (not a + #define) so it can be easily changed via some future ioctl() */ +static unsigned short sony_cd_base_io = CDU535_ADDRESS; + +/* + * The following are I/O addresses of the various registers for the drive. The + * comment for the base address also applies here. + */ +static unsigned short select_unit_reg; +static unsigned short result_reg; +static unsigned short command_reg; +static unsigned short read_status_reg; +static unsigned short data_reg; + +static int initialized = 0; /* Has the drive been initialized? */ +static int sony_disc_changed = 1; /* Has the disk been changed + since the last check? */ +static int sony_toc_read = 0; /* Has the table of contents been + read? */ +static unsigned int sony_buffer_size; /* Size in bytes of the read-ahead + buffer. */ +static unsigned int sony_buffer_sectors; /* Size (in 2048 byte records) of + the read-ahead buffer. */ +static unsigned int sony_usage = 0; /* How many processes have the + drive open. */ + +static int sony_first_block = -1; /* First OS block (512 byte) in + the read-ahead buffer */ +static int sony_last_block = -1; /* Last OS block (512 byte) in + the read-ahead buffer */ + +static struct s535_sony_toc *sony_toc; /* Points to the table of + contents. */ +static struct s535_sony_subcode *last_sony_subcode; /* Points to the last + subcode address read */ +static unsigned char *sony_buffer; /* Points to the read-ahead + buffer */ +static int sony_inuse = 0; /* is the drive in use? Only one + open at a time allowed */ + +/* + * The audio status uses the values from read subchannel data as specified + * in include/linux/cdrom.h. + */ +static int sony_audio_status = CDROM_AUDIO_NO_STATUS; + +/* + * The following are a hack for pausing and resuming audio play. The drive + * does not work as I would expect it, if you stop it then start it again, + * the drive seeks back to the beginning and starts over. This holds the + * position during a pause so a resume can restart it. It uses the + * audio status variable above to tell if it is paused. + * I just kept the CDU-31A driver behavior rather than using the PAUSE + * command on the CDU-535. + */ +static unsigned char cur_pos_msf[3] = { 0, 0, 0 }; +static unsigned char final_pos_msf[3] = { 0, 0, 0 }; + + +/* + * This routine returns 1 if the disk has been changed since the last + * check or 0 if it hasn't. + */ +static int +cdu535_check_media_change(dev_t full_dev) +{ + int retval, target; + + target = MINOR(full_dev); + + if (target > 0) { + printk("Sony CD-ROM request error: invalid device.\n"); + return 0; + } + + /* if driver is not initialized, always return 0 */ + retval = ( initialized ? sony_disc_changed : 0 ); + sony_disc_changed = 0; + return retval; +} + + +/* + * Wait a little while (used for polling the drive). If in initialization, + * setting a timeout doesn't work, so just loop for a while (we trust + * that the sony_sleep() call is protected by a test for proper jiffies count). + */ +static inline void +sony_sleep(void) +{ + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies; + schedule(); +} + +/*------------------start of SONY CDU535 very specific ---------------------*/ + +/**************************************************************************** + * void select_unit( int unit_no ) + * + * Select the specified unit (0-3) so that subsequent commands reference it + ****************************************************************************/ +static void +select_unit( int unit_no ) +{ + unsigned int select_mask = ~(1 << unit_no); + outb( select_mask, select_unit_reg ); +} /* select_unit() */ + +/*************************************************************************** + * int read_result_reg( unsigned char *data_ptr ) + * + * Read a result byte from the Sony CDU controller, store in location pointed + * to by data_ptr. Return zero on success, TIME_OUT if we did not receive + * data. + ***************************************************************************/ +static int +read_result_reg( unsigned char *data_ptr ) +{ + int retry_count; + int read_status; + + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ( retry_count > jiffies ) + { + if ( ((read_status=inb(read_status_reg)) & SONY535_RESULT_NOT_READY_BIT) == 0 ) + { +#if 0 + printk("read_result_reg(): readStatReg = 0x%x\n", read_status ); +#endif + *data_ptr = inb(result_reg); + return( 0 ); + } + else + { + sony_sleep(); + } + } + printk(" Sony CDROM read_result_reg: TIME OUT!\n"); + return( TIME_OUT ); +} /* read_result_reg() */ + +/**************************************************************************** + * int read_exec_status( Byte status[2] ) + * + * Read the execution status of the last command and put into status. + * Handles reading second status word if available. Returns 0 on success, + * TIME_OUT on failure. + ****************************************************************************/ +static int +read_exec_status( Byte status[2] ) +{ + status[1] = 0; + if ( read_result_reg( &(status[0]) ) != 0 ) return( TIME_OUT ); + if ( (status[0] & 0x80) != 0 ) /* byte two follows */ + { + if ( read_result_reg( &(status[1]) ) != 0 ) return( TIME_OUT ); + } +#if 0 + printk("read_exec_status: read 0x%x\n", status[0] ); + if (status[0] & 0x80) printk(" and 0x%x\n", status[1] ); + printk("\n"); +#endif + return( 0 ); +} /* read_exec_status() */ + +/**************************************************************************** + * int check_drive_status( void ) + * + * Check the current drive status. Using this before executing a command + * takes care of the problem of unsolicited drive status-2 messages. + * Add a check of the audio status if we think the disk is playing. + ****************************************************************************/ +static int +check_drive_status( void ) +{ + Byte status, e_status[2]; + int CDD, ATN; + unsigned char cmd; + + select_unit(0); + if ( sony_audio_status == CDROM_AUDIO_PLAY ) /* check status */ + { + outb( SONY535_REQUEST_AUDIO_STATUS, command_reg ); + if ( read_result_reg( &status ) == 0 ) + { + switch( status ) + { + case 0x0: break; /* play in progress */ + case 0x1: break; /* paused */ + case 0x3: /* audio play completed */ + case 0x5: /* play not requested */ + sony_audio_status = CDROM_AUDIO_COMPLETED; + read_subcode(); + break; + case 0x4: /* error during play */ + sony_audio_status = CDROM_AUDIO_ERROR; + break; + } + } + } + /* now check drive status */ + outb( SONY535_REQUEST_DRIVE_STATUS_2, command_reg ); + if ( read_result_reg( &status ) != 0 ) + return( TIME_OUT ); + +#if 0 + printk("--check_drive_status() got 0x%x\n", status ); +#endif + + if ( status == 0 ) return( 0 ); + + ATN = status & 0xf; + CDD = (status >> 4) & 0xf; + + switch ( ATN ) + { + case 0x0: + break; /* go on to CDD stuff */ + case SONY535_ATN_BUSY: + if ( initialized ) + printk("Sony CDROM error, drive busy\n"); + return( CD_BUSY); + case SONY535_ATN_EJECT_IN_PROGRESS: + printk("Sony CDROM error, eject in progress\n"); + sony_audio_status = CDROM_AUDIO_INVALID; + return( CD_BUSY); + case SONY535_ATN_RESET_OCCURRED: + case SONY535_ATN_DISC_CHANGED: + case SONY535_ATN_RESET_AND_DISC_CHANGED: +#ifdef DEBUG + printk("Sony CDROM, reset occurred or disc changed\n"); +#endif + sony_disc_changed = 1; + sony_toc_read = 0; + sony_audio_status = CDROM_AUDIO_NO_STATUS; + sony_first_block = -1; + sony_last_block = -1; + if (initialized) + { + cmd = SONY535_SPIN_UP; + do_sony_cmd( &cmd,1, e_status, NULL,0, 0 ); + sony_get_toc(); + } + return( 0 ); + default: + printk("Sony CDROM error, drive busy (ATN=0x%x)\n", ATN ); + return( CD_BUSY ); + } + switch ( CDD ) + { /* the 531 docs are not helpful in decoding this */ + case 0x0: /* just use the values from the DOS driver */ + case 0x2: + case 0xa: + break; /* no error */ + case 0xc: + printk("check_drive_status(): CDD = 0xc! Not properly handled!\n"); + return( CD_BUSY ); /* ? */ + default: + return( CD_BUSY ); + } + return( 0 ); +} /* check_drive_status() */ + +/***************************************************************************** + * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2], + * Byte *response, int n_response, int ignore_status_bit7 ) + * + * Generic routine for executing commands. The command and its parameters + * should be placed in the cmd[] array, number of bytes in the command is + * stored in nCmd. The response from the command will be stored in the + * response array. The number of bytes you expect back (excluding status) + * should be passed in nReponse. Finally, some + * commands set bit 7 of the return status even when there is no second + * status byte, on these commands set ignoreStatusBit7 TRUE. + * If the command was sent and data recieved back, then we return 0, + * else we return TIME_OUT. You still have to check the status yourself. + * You should call check_drive_status() before calling this routine + * so that you do not lose notifications of disk changes, etc. + ****************************************************************************/ +static int +do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2], + Byte *response, int n_response, int ignore_status_bit7 ) +{ + int i; + + /* write out the command */ + for ( i=0; i < n_cmd; i++ ) + outb( cmd[i], command_reg ); + + /* read back the status */ + if ( read_result_reg( status ) != 0 ) + return( TIME_OUT ); + if ( !ignore_status_bit7 && ((status[0] & 0x80) != 0) ) /* get second status byte */ + { + if ( read_result_reg( status+1 ) != 0 ) + return( TIME_OUT ); + } + else + { + status[1] = 0; + } + + /* do not know about when I should read set of data and when not to */ + if ( (status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0 ) + return( 0 ); + + /* else, read in rest of data */ + for ( i=0; n_response > 0; n_response--, i++ ) + if ( read_result_reg( response+i ) != 0 ) return(TIME_OUT); + return( 0 ); +} /* do_sony_cmd() */ + +/************************************************************************** + * int set_drive_mode( int mode, Byte status[2] ) + * + * Set the drive mode to the specified value (mode=0 is audio, mode=e0 + * is mode-1 CDROM + **************************************************************************/ +static int +set_drive_mode( int mode, Byte status[2] ) +{ + Byte cmd_buff[2], ret_buff[1]; + + cmd_buff[0] = SONY535_SET_DRIVE_MODE; + cmd_buff[1] = mode; + return( do_sony_cmd( cmd_buff,2, status, ret_buff,1, 1 ) ); +} /* set_drive_mode() */ + +/*************************************************************************** + * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2], + * Byte *data_buff, int buff_size ) + * + * Read n_blocks of data from the CDROM starting at position params[0:2], + * number of blocks in stored in params[3:5] -- both these are already + * int bcd format. + * Transfer the data into the buffer pointed at by data_buff. buff_size + * gives the number of bytes available in the buffer. + * The routine returns number of bytes read in if successful, otherwise + * it returns one of the standard error returns. + ***************************************************************************/ +static int +seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2], + Byte *data_buff, int buf_size ) +{ + int i; + const int block_size = 2048; + Byte cmd_buff[7]; + int read_status; + Byte *start_pos = data_buff; + int retry_count; + + if ( ((long)block_size)*n_blocks > buf_size ) + return( NO_ROOM ); + + set_drive_mode( SONY535_CDROM_DRIVE_MODE, status ); + + /* send command to read the data */ + cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1; + for ( i=0; i < 6; i++ ) + cmd_buff[i+1] = params[i]; + for ( i=0; i < 7; i++ ) + outb( cmd_buff[i], command_reg ); + + /* read back the data one block at a time */ + while ( n_blocks-- > 0 ) + { + /* wait for data to be ready */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ( retry_count > jiffies ) + { + read_status = inb(read_status_reg); + if ( (read_status & SONY535_RESULT_NOT_READY_BIT) == 0 ) + { + read_exec_status( status ); + return( BAD_STATUS ); + } + if ( (read_status & SONY535_DATA_NOT_READY_BIT) == 0 ) + { + /* data is ready, read it */ + for ( i=0; i < block_size; i++ ) + *data_buff++ = inb( data_reg ); /* unrolling this loop does not seem to help */ + break; /* exit the timeout loop */ + } + sony_sleep(); /* data not ready, sleep a while */ + } + if ( retry_count <= jiffies ) + return( TIME_OUT ); /* if we reach this stage */ + } + + /* read all the data, now read the status */ + if ( (i=read_exec_status( status )) != 0 ) + return( i ); + return( data_buff - start_pos ); +} /* seek_and_read_N_blocks() */ + +/**************************************************************************** + * int request_toc_data( Byte status[2], struct s535_sony_toc *toc ) + * + * Read in the table of contents data. Converts all the bcd data + * into integers in the toc structure. + ****************************************************************************/ +static int +request_toc_data( Byte status[2], struct s535_sony_toc *toc ) +{ + int to_status; + int i, j, n_tracks, track_no; + Byte cmd_no = 0xb2; + Byte track_address_buffer[5]; + int first_track_num, last_track_num; + + /* read the fixed portion of the table of contents */ + if ((to_status=do_sony_cmd( &cmd_no,1, status, (Byte *)toc,15, 1 )) != 0 ) + return( to_status ); + + /* convert the data into integers so we can use them */ + first_track_num = bcd_to_int(toc->first_track_num); + last_track_num = bcd_to_int(toc->last_track_num); + n_tracks = last_track_num - first_track_num + 1; + + /* read each of the track address descriptors */ + for ( i=0; i < n_tracks; i++ ) + { + /* read the descriptor into a temporary buffer */ + for ( j=0; j < 5; j++ ) + { + if ( read_result_reg( track_address_buffer+j ) != 0 ) + return( TIME_OUT ); + if ( j == 1 ) /* need to convert from bcd */ + track_no = bcd_to_int(track_address_buffer[j]); + } + /* copy the descriptor to proper location - sonycd.c just fills */ + memcpy( toc->tracks+i, track_address_buffer, 5 ); + } + return( 0 ); +} /* request_toc_data() */ + +/*************************************************************************** + * int spin_up_drive( Byte status[2] ) + * + * Spin up the drive (unless it is already spinning). + ***************************************************************************/ +static int +spin_up_drive( Byte status[2] ) +{ + Byte cmd_buff[1]; + + /* first see if the drive is already spinning */ + cmd_buff[0] = SONY535_REQUEST_DRIVE_STATUS_1; + if ( do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ) != 0 ) + return(TIME_OUT ); + if ( (status[0] & SONY535_STATUS1_NOT_SPINNING) == 0 ) + return( 0 ); /* its already spinning */ + + /* else, give the spin-up command */ + cmd_buff[0] = SONY535_SPIN_UP; + return( do_sony_cmd( cmd_buff, 1, status, NULL,0, 0 ) ); +} /* spin_up_drive() */ + +/*--------------------end of SONY CDU535 very specific ---------------------*/ + +/* Convert from an integer 0-99 to BCD */ +static inline unsigned int +int_to_bcd(unsigned int val) +{ + int retval; + + + retval = (val / 10) << 4; + retval = retval | val % 10; + return(retval); +} + + +/* Convert from BCD to an integer from 0-99 */ +static unsigned int +bcd_to_int(unsigned int bcd) +{ + return((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f)); +} + + +/* + * Convert a logical sector value (like the OS would want to use for + * a block device) to an MSF format. + */ +static void +log_to_msf(unsigned int log, unsigned char *msf) +{ + log = log + LOG_START_OFFSET; + msf[0] = int_to_bcd(log / 4500); + log = log % 4500; + msf[1] = int_to_bcd(log / 75); + msf[2] = int_to_bcd(log % 75); +} + + +/* + * Convert an MSF format to a logical sector. + */ +static unsigned int +msf_to_log(unsigned char *msf) +{ + unsigned int log; + + + log = bcd_to_int(msf[2]); + log += bcd_to_int(msf[1]) * 75; + log += bcd_to_int(msf[0]) * 4500; + log = log - LOG_START_OFFSET; + + return log; +} + + +/* + * Take in integer size value and put it into a buffer like + * the drive would want to see a number-of-sector value. + */ +static void +size_to_buf(unsigned int size, + unsigned char *buf) +{ + buf[0] = size / 65536; + size = size % 65536; + buf[1] = size / 256; + buf[2] = size % 256; +} + + +/* + * The OS calls this to perform a read or write operation to the drive. + * Write obviously fail. Reads to a read ahead of sony_buffer_size + * bytes to help speed operations. This especially helps since the OS + * uses 1024 byte blocks and the drive uses 2048 byte blocks. Since most + * data access on a CD is done sequentially, this saves a lot of operations. + */ +static void +do_cdu535_request(void) +{ + int block; + unsigned int dev; + int nsect; + unsigned char params[10]; + int copyoff; + int spin_up_retry; + unsigned int read_size; + unsigned char status[2], cmd[2]; + + + if ( !sony_inuse ) + { + cdu_open( NULL, NULL ); + } + + while (1) + { + /* + * The beginning here is stolen from the hard disk driver. I hope + * its right. + */ + if (!(CURRENT) || CURRENT->dev < 0) + { + return; + } + + INIT_REQUEST; + dev = MINOR(CURRENT->dev); + block = CURRENT->sector; + nsect = CURRENT->nr_sectors; + if (dev != 0) + { + end_request(0); + continue; + } + + switch(CURRENT->cmd) + { + case READ: + /* + * If the block address is invalid or the request goes beyond the end of + * the media, return an error. + */ + if ((block / 4) >= sony_toc->lead_out_start_lba) + { + end_request(0); + return; + } + if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba) + { + end_request(0); + return; + } + + while (nsect > 0) + { + /* + * If the requested sector is not currently in the read-ahead buffer, + * it must be read in. + */ + if ((block < sony_first_block) || (block > sony_last_block)) + { + sony_first_block = (block / 4) * 4; + log_to_msf(block/4, params); + + /* + * If the full read-ahead would go beyond the end of the media, trim + * it back to read just till the end of the media. + */ + if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba) + { + sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1; + read_size = sony_toc->lead_out_start_lba - (block / 4); + } + else + { + sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1; + read_size = sony_buffer_sectors; + } + size_to_buf(read_size, ¶ms[3]); + + /* + * Read the data. If the drive was not spinning, spin it up and try + * once more. I know, the goto is ugly, but I am too lazy to fix it. + */ + spin_up_retry = 0; +try_read_again: +#if 0 + if ( check_drive_status() != 0 ) { /* drive not ready */ + sony_first_block = -1; + sony_last_block = -1; + end_request(0); + return; + } +#endif + if ( seek_and_read_N_blocks( params, read_size, status, sony_buffer, + (read_size * 2048) ) < 0 ) { + if ((status[0] & SONY535_STATUS1_NOT_SPINNING) && (!spin_up_retry)) + { + printk(" Sony535 Debug -- calling spin up when reading data!\n"); + cmd[0] = SONY535_SPIN_UP; + do_sony_cmd( cmd,1, status, NULL,0, 0); + spin_up_retry = 1; + goto try_read_again; + } + + printk("Sony CDROM Read error: 0x%.2x\n", status[0]); + sony_first_block = -1; + sony_last_block = -1; + end_request(0); + return; + } + } + + /* + * The data is in memory now, copy it to the buffer and advance to the + * next block to read. + */ + copyoff = (block - sony_first_block) * 512; + memcpy(CURRENT->buffer, sony_buffer+copyoff, 512); + + block += 1; + nsect -= 1; + CURRENT->buffer += 512; + } + + end_request(1); + break; + + case WRITE: + end_request(0); + break; + + default: + panic("Unkown SONY CD cmd"); + } + } +} + + +/* + * Read the table of contents from the drive and set sony_toc_read if + * successful. + */ +static void +sony_get_toc(void) +{ + unsigned char status[2]; + + if ( !sony_toc_read ) + { + /* do not call check_drive_status() from here since it can call this routine */ + if ( request_toc_data( status, sony_toc ) < 0 ) + return; + sony_toc->lead_out_start_lba = msf_to_log(sony_toc->lead_out_start_msf); + sony_toc_read = 1; + } +} + + +/* + * Search for a specific track in the table of contents. track is + * passed in bcd format + */ +static int +find_track(int track) +{ + int i; + int num_tracks; + + + num_tracks = bcd_to_int(sony_toc->last_track_num) - + bcd_to_int(sony_toc->first_track_num) + 1; + for (i = 0; i < num_tracks; i++) + { + if (sony_toc->tracks[i].track == track) + { + return i; + } + } + + return -1; +} + +/* + * Read the subcode and put it int last_sony_subcode for future use. + */ +static int +read_subcode(void) +{ + Byte cmd = SONY535_REQUEST_SUB_Q_DATA, status[2]; + int dsc_status; + + if ( check_drive_status() != 0 ) + return( -EIO ); + + if ( (dsc_status=do_sony_cmd( &cmd,1, status, (Byte *)last_sony_subcode, + sizeof(struct s535_sony_subcode), 1 )) != 0 ) + { + printk("Sony CDROM error 0x%.2x, %d (read_subcode)\n", status[0], + dsc_status ); + return( -EIO ); + } + return(0); +} + + +/* + * Get the subchannel info like the CDROMSUBCHNL command wants to see it. If + * the drive is playing, the subchannel needs to be read (since it would be + * changing). If the drive is paused or completed, the subcode information has + * already been stored, just use that. The ioctl call wants things in decimal + * (not BCD), so all the conversions are done. + */ +static int +sony_get_subchnl_info(long arg) +{ + struct cdrom_subchnl schi; + + + /* Get attention stuff */ + if ( check_drive_status() != 0 ) + return( -EIO ); + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + verify_area(VERIFY_WRITE /* and read */, (char *) arg, sizeof(schi)); + + memcpy_fromfs(&schi, (char *) arg, sizeof(schi)); + + switch (sony_audio_status) + { + case CDROM_AUDIO_PLAY: + if (read_subcode() < 0) + { + return -EIO; + } + break; + + case CDROM_AUDIO_PAUSED: + case CDROM_AUDIO_COMPLETED: + break; + + case CDROM_AUDIO_NO_STATUS: + schi.cdsc_audiostatus = sony_audio_status; + memcpy_tofs((char *) arg, &schi, sizeof(schi)); + return 0; + break; + + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_ERROR: + default: + return -EIO; + } + + schi.cdsc_audiostatus = sony_audio_status; + schi.cdsc_adr = last_sony_subcode->address; + schi.cdsc_ctrl = last_sony_subcode->control; + schi.cdsc_trk = bcd_to_int(last_sony_subcode->track_num); + schi.cdsc_ind = bcd_to_int(last_sony_subcode->index_num); + if (schi.cdsc_format == CDROM_MSF) + { + schi.cdsc_absaddr.msf.minute = bcd_to_int(last_sony_subcode->abs_msf[0]); + schi.cdsc_absaddr.msf.second = bcd_to_int(last_sony_subcode->abs_msf[1]); + schi.cdsc_absaddr.msf.frame = bcd_to_int(last_sony_subcode->abs_msf[2]); + + schi.cdsc_reladdr.msf.minute = bcd_to_int(last_sony_subcode->rel_msf[0]); + schi.cdsc_reladdr.msf.second = bcd_to_int(last_sony_subcode->rel_msf[1]); + schi.cdsc_reladdr.msf.frame = bcd_to_int(last_sony_subcode->rel_msf[2]); + } + else if (schi.cdsc_format == CDROM_LBA) + { + schi.cdsc_absaddr.lba = msf_to_log(last_sony_subcode->abs_msf); + schi.cdsc_reladdr.lba = msf_to_log(last_sony_subcode->rel_msf); + } + + memcpy_tofs((char *) arg, &schi, sizeof(schi)); + return 0; +} + + +/* + * The big ugly ioctl handler. + */ +static int +cdu_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned int dev; + unsigned char status[2]; + unsigned char cmd_buff[10], params[10]; + int i, dsc_status; + + + if (!inode) + { + return -EINVAL; + } + dev = MINOR(inode->i_rdev) >> 6; + if (dev != 0) + { + return -EINVAL; + } + + if ( check_drive_status() != 0 ) + return( -EIO ); + + switch (cmd) + { + case CDROMSTART: /* Spin up the drive */ + if ( spin_up_drive( status ) < 0 ) + { + printk("Sony CDROM error 0x%.2x (CDROMSTART)\n", status[0]); + return -EIO; + } + return 0; + break; + + case CDROMSTOP: /* Spin down the drive */ + cmd_buff[0] = SONY535_HOLD; + do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ); + + /* + * Spin the drive down, ignoring the error if the disk was + * already not spinning. + */ + sony_audio_status = CDROM_AUDIO_NO_STATUS; + cmd_buff[0] = SONY535_SPIN_DOWN; + dsc_status = do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ); + if ( (( dsc_status < 0 ) && (dsc_status != BAD_STATUS)) || + ( (status[0] & ~(SONY535_STATUS1_NOT_SPINNING)) != 0) ) + { + printk("Sony CDROM error 0x%.2x (CDROMSTOP)\n", status[0]); + return -EIO; + } + + return 0; + break; + + case CDROMPAUSE: /* Pause the drive */ + cmd_buff[0] = SONY535_HOLD; /* CDU-31 driver uses AUDIO_STOP, not pause */ + if ( do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ) != 0 ) + { + printk("Sony CDROM error 0x%.2x (CDROMPAUSE)\n", status[0]); + return -EIO; + } + + /* Get the current position and save it for resuming */ + if (read_subcode() < 0) + { + return -EIO; + } + cur_pos_msf[0] = last_sony_subcode->abs_msf[0]; + cur_pos_msf[1] = last_sony_subcode->abs_msf[1]; + cur_pos_msf[2] = last_sony_subcode->abs_msf[2]; + sony_audio_status = CDROM_AUDIO_PAUSED; + return 0; + break; + + case CDROMRESUME: /* Start the drive after being paused */ + set_drive_mode( SONY535_AUDIO_DRIVE_MODE, status ); + + if (sony_audio_status != CDROM_AUDIO_PAUSED) + { + return -EINVAL; + } + + spin_up_drive( status ); + + /* Start the drive at the saved position. */ + cmd_buff[0] = SONY535_PLAY_AUDIO; + cmd_buff[1] = 0; /* play back starting at this address */ + cmd_buff[2] = cur_pos_msf[0]; + cmd_buff[3] = cur_pos_msf[1]; + cmd_buff[4] = cur_pos_msf[2]; + cmd_buff[5] = SONY535_PLAY_AUDIO; + cmd_buff[6] = 2; /* set ending address */ + cmd_buff[7] = final_pos_msf[0]; + cmd_buff[8] = final_pos_msf[1]; + cmd_buff[9] = final_pos_msf[2]; + if ( (do_sony_cmd( cmd_buff,5, status, NULL,0, 0 ) != 0 ) || + (do_sony_cmd( cmd_buff+5,5, status, NULL,0, 0 ) != 0 ) ) + { + printk("Sony CDROM error 0x%.2x (CDROMRESUME)\n", status[0]); + return -EIO; + } + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + break; + + case CDROMPLAYMSF: /* Play starting at the given MSF address. */ + verify_area(VERIFY_READ, (char *) arg, 6); + spin_up_drive( status ); + set_drive_mode( SONY535_AUDIO_DRIVE_MODE, status ); + memcpy_fromfs(params, (void *) arg, 6); + + /* The parameters are given in int, must be converted */ + for (i=0; i<3; i++) + { + cmd_buff[2+i] = int_to_bcd(params[i]); + cmd_buff[7+i] = int_to_bcd(params[i+3]); + } + cmd_buff[0] = SONY535_PLAY_AUDIO; + cmd_buff[1] = 0; /* play back starting at this address */ + /* cmd_buff[2-4] are filled in for loop above */ + cmd_buff[5] = SONY535_PLAY_AUDIO; + cmd_buff[6] = 2; /* set ending address */ + /* cmd_buff[7-9] are filled in for loop above */ + if ( (do_sony_cmd( cmd_buff,5, status, NULL,0, 0 ) != 0 ) || + (do_sony_cmd( cmd_buff+5,5, status, NULL,0, 0 ) != 0 ) ) + { + printk("Sony CDROM error 0x%.2x (CDROMPLAYMSF)\n", status[0]); + return -EIO; + } + + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = cmd_buff[7]; + final_pos_msf[1] = cmd_buff[8]; + final_pos_msf[2] = cmd_buff[9]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + break; + + case CDROMREADTOCHDR: /* Read the table of contents header */ + { + struct cdrom_tochdr *hdr; + struct cdrom_tochdr loc_hdr; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + hdr = (struct cdrom_tochdr *) arg; + verify_area(VERIFY_WRITE, hdr, sizeof(*hdr)); + loc_hdr.cdth_trk0 = bcd_to_int(sony_toc->first_track_num); + loc_hdr.cdth_trk1 = bcd_to_int(sony_toc->last_track_num); + memcpy_tofs(hdr, &loc_hdr, sizeof(*hdr)); + } + return 0; + break; + + case CDROMREADTOCENTRY: /* Read a given table of contents entry */ + { + struct cdrom_tocentry *entry; + struct cdrom_tocentry loc_entry; + int track_idx; + unsigned char *msf_val = NULL; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + entry = (struct cdrom_tocentry *) arg; + verify_area(VERIFY_WRITE /* and read */, entry, sizeof(*entry)); + + memcpy_fromfs(&loc_entry, entry, sizeof(loc_entry)); + + /* Lead out is handled separately since it is special. */ + if (loc_entry.cdte_track == CDROM_LEADOUT) + { + loc_entry.cdte_adr = 0 /*sony_toc->address2*/; + loc_entry.cdte_ctrl = sony_toc->control2; + msf_val = sony_toc->lead_out_start_msf; + } + else + { + track_idx = find_track(int_to_bcd(loc_entry.cdte_track)); + if (track_idx < 0) + { + return -EINVAL; + } + + loc_entry.cdte_adr = 0 /*sony_toc->tracks[track_idx].address*/; + loc_entry.cdte_ctrl = sony_toc->tracks[track_idx].control; + msf_val = sony_toc->tracks[track_idx].track_start_msf; + } + + /* Logical buffer address or MSF format requested? */ + if (loc_entry.cdte_format == CDROM_LBA) + { + loc_entry.cdte_addr.lba = msf_to_log(msf_val); + } + else if (loc_entry.cdte_format == CDROM_MSF) + { + loc_entry.cdte_addr.msf.minute = bcd_to_int(*msf_val); + loc_entry.cdte_addr.msf.second = bcd_to_int(*(msf_val+1)); + loc_entry.cdte_addr.msf.frame = bcd_to_int(*(msf_val+2)); + } + memcpy_tofs(entry, &loc_entry, sizeof(*entry)); + } + return 0; + break; + + case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ + { + struct cdrom_ti ti; + int track_idx; + + sony_get_toc(); + if (!sony_toc_read) + { + return -EIO; + } + + verify_area( VERIFY_READ, (char *) arg, sizeof(ti)); + + memcpy_fromfs(&ti, (char *) arg, sizeof(ti)); + if ( (ti.cdti_trk0 < sony_toc->first_track_num) + || (ti.cdti_trk0 > sony_toc->last_track_num) + || (ti.cdti_trk1 < ti.cdti_trk0)) + { + return -EINVAL; + } + + track_idx = find_track(int_to_bcd(ti.cdti_trk0)); + if (track_idx < 0) + { + return -EINVAL; + } + params[1] = sony_toc->tracks[track_idx].track_start_msf[0]; + params[2] = sony_toc->tracks[track_idx].track_start_msf[1]; + params[3] = sony_toc->tracks[track_idx].track_start_msf[2]; + /* + * If we want to stop after the last track, use the lead-out + * MSF to do that. + */ + if (ti.cdti_trk1 >= bcd_to_int(sony_toc->last_track_num)) + { + log_to_msf(msf_to_log(sony_toc->lead_out_start_msf)-1, + &(params[4])); + } + else + { + track_idx = find_track(int_to_bcd(ti.cdti_trk1+1)); + if (track_idx < 0) + { + return -EINVAL; + } + log_to_msf(msf_to_log(sony_toc->tracks[track_idx].track_start_msf)-1, + &(params[4])); + } + params[0] = 0x03; + + spin_up_drive( status ); + + set_drive_mode( SONY535_AUDIO_DRIVE_MODE, status ); + + /* Start the drive at the saved position. */ + cmd_buff[0] = SONY535_PLAY_AUDIO; + cmd_buff[1] = 0; /* play back starting at this address */ + cmd_buff[2] = params[1]; + cmd_buff[3] = params[2]; + cmd_buff[4] = params[3]; + cmd_buff[5] = SONY535_PLAY_AUDIO; + cmd_buff[6] = 2; /* set ending address */ + cmd_buff[7] = params[4]; + cmd_buff[8] = params[5]; + cmd_buff[9] = params[6]; + if ( (do_sony_cmd( cmd_buff,5, status, NULL,0, 0 ) != 0 ) || + (do_sony_cmd( cmd_buff+5,5, status, NULL,0, 0 ) != 0 ) ) + { + printk("Params: %x %x %x %x %x %x %x\n", params[0], params[1], + params[2], params[3], params[4], params[5], params[6]); + printk("Sony CDROM error 0x%.2x (CDROMPLAYTRKIND)\n", status[0]); + return -EIO; + } + + /* Save the final position for pauses and resumes */ + final_pos_msf[0] = params[4]; + final_pos_msf[1] = params[5]; + final_pos_msf[2] = params[6]; + sony_audio_status = CDROM_AUDIO_PLAY; + return 0; + } + + case CDROMSUBCHNL: /* Get subchannel info */ + return sony_get_subchnl_info(arg); + + case CDROMVOLCTRL: /* Volume control. What volume does this change, anyway? */ + { + struct cdrom_volctrl volctrl; + + verify_area(VERIFY_READ, (char *) arg, sizeof(volctrl)); + + memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl)); + cmd_buff[0] = SONY535_SET_VOLUME; + cmd_buff[1] = volctrl.channel0; + cmd_buff[2] = volctrl.channel1; + if ( do_sony_cmd( cmd_buff,3, status, NULL,0, 0 ) != 0 ) + { + printk("Sony CDROM error 0x%.2x (CDROMVOLCTRL)\n", status[0]); + return -EIO; + } + } + return 0; + + case CDROMEJECT: /* Eject the drive */ + cmd_buff[0] = SONY535_STOP; + do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ); + cmd_buff[0] = SONY535_SPIN_DOWN; + do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ); + + sony_audio_status = CDROM_AUDIO_INVALID; + cmd_buff[0] = SONY535_EJECT_CADDY; + if ( do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ) != 0 ) + { + printk("Sony CDROM error 0x%.2x (CDROMEJECT)\n", status[0]); + return -EIO; + } + return 0; + break; + + default: + return -EINVAL; + } +} + + +/* + * Open the drive for operations. Spin the drive up and read the table of + * contents if these have not already been done. + */ +static int +cdu_open(struct inode *inode, + struct file *filp) +{ + unsigned char status[2], cmd_buff[2]; + + + if ( sony_inuse ) + { + return( -EBUSY ); + } + + if ( check_drive_status() != 0 ) + { + return( -EIO ); + } + + sony_inuse = 1; + + if ( spin_up_drive( status ) != 0 ) + { + printk("Sony CDROM error 0x%.2x (cdu_open, spin up)\n", status[0]); + sony_inuse = 0; + return -EIO; + } + +#if 0 /* what is this doing - CDU-535 does not have separate READ and REQ_TOC */ + do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size); + if ((res_size < 2) || (res_reg[0] != 0)) + { + /* If the drive is already playing, its ok. */ + if (res_reg[1] == SONY_AUDIO_PLAYING_ERR) + { + goto drive_spinning; + } + + printk("Sony CDROM error 0x%.2x (cdu_open, read toc)\n", res_reg[1]); + do_sony_cd_cmd(SONY_SPIN_DOWN_CMD, NULL, 0, res_reg, &res_size); + + sony_inuse = 0; + return -EIO; + } +#endif + + sony_get_toc(); + if (!sony_toc_read) + { + cmd_buff[0] = SONY535_SPIN_DOWN; + do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ); + sony_inuse = 0; + return -EIO; + } + +#if 0 /* not used without stuff read toc above */ +drive_spinning: +#endif + + if ( inode ) + { + check_disk_change(inode->i_rdev); + } + + sony_usage++; + +#ifdef LOCK_DOORS + /* disable the eject button while mounted */ + cmd_buff[0] = SONY535_DISABLE_EJECT_BUTTON; + do_sony_cmd( cmd_buff,1, status, NULL,0, 0 ); +#endif + + return 0; +} + + +/* + * Close the drive. Spin it down if no task is using it. The spin + * down will fail if playing audio, so audio play is OK. + */ +static void +cdu_release(struct inode *inode, + struct file *filp) +{ + unsigned char status[2], cmd_no; + + sony_inuse = 0; + + if (sony_usage > 0) + { + sony_usage--; + } + if (sony_usage == 0) + { + sync_dev(inode->i_rdev); + check_drive_status(); + + if ( sony_audio_status != CDROM_AUDIO_PLAY ) { + cmd_no = SONY535_SPIN_DOWN; + do_sony_cmd( &cmd_no,1, status, NULL,0, 0 ); + } +#ifdef LOCK_DOORS + /* enable the eject button after umount */ + cmd_no = SONY535_ENABLE_EJECT_BUTTON; + do_sony_cmd( &cmd_no,1, status, NULL,0, 0 ); +#endif + } +} + + +static struct file_operations cdu_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + cdu_ioctl, /* ioctl */ + NULL, /* mmap */ + cdu_open, /* open */ + cdu_release, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + cdu535_check_media_change, /* check_media_change */ + NULL /* revalidate */ +}; + + +/* + * Initialize the driver. + */ +unsigned long +init_sony535(unsigned long mem_start, unsigned long mem_end) +{ + struct s535_sony_drive_config drive_config; + unsigned char cmd_buff[3], ret_buff[2]; + unsigned char status[2]; + int retry_count; + + + /* Set up all the register locations */ + result_reg = sony_cd_base_io; + command_reg = sony_cd_base_io; + data_reg = sony_cd_base_io + 1; + read_status_reg = sony_cd_base_io + 2; + select_unit_reg = sony_cd_base_io + 3; + + /* look for the CD-ROM, follows the procedure in the DOS driver */ + inb( select_unit_reg ); + retry_count = jiffies + 2*HZ; + while ( retry_count > jiffies ) + sony_sleep(); /* wait for 40 18 Hz ticks (from DOS driver) */ + inb( result_reg ); + + outb( 0, read_status_reg ); /* does a reset? */ + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ( retry_count > jiffies ) + { + select_unit(0); + if ( inb( result_reg ) != 0xff ) + break; + sony_sleep(); /* about 1-2 ms on my machine */ + } + + if ( (retry_count > jiffies) && (check_drive_status() != TIME_OUT) ) + { + /* CD-ROM drive responded -- get the drive configuration */ + cmd_buff[0] = SONY535_INQUIRY; + if ( do_sony_cmd( cmd_buff,1, status, (Byte *)&drive_config, 28, 1) == 0 ) + { + /* was able to get the configuration, set drive mode as rest of init */ + if ( (status[0] & 0x7f) != 0 ) + printk("Inquiry command returned status = 0x%x\n",status[0]); + cmd_buff[0] = SONY535_SET_DRIVE_MODE; + cmd_buff[1] = 0x0; /* default audio */ + if (do_sony_cmd( cmd_buff,2, status, ret_buff,1, 1 ) == 0 ) + { + /* set the drive mode successful, we are set! */ + sony_buffer_size = SONY535_BUFFER_SIZE; + sony_buffer_sectors = sony_buffer_size / 2048; + + printk("Sony I/F CDROM : %8.8s %16.16s %4.4s", + drive_config.vendor_id, + drive_config.product_id, + drive_config.product_rev_level ); + printk(" using %d byte buffer\n", sony_buffer_size); + + if (register_blkdev(MAJOR_NR,"cdu",&cdu_fops)) { + printk("Unable to get major %d for sony CDU-535 cd\n",MAJOR_NR); + return( mem_start ); + } + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ + + sony_toc = (struct s535_sony_toc *) mem_start; + mem_start += sizeof(*sony_toc); + last_sony_subcode = (struct s535_sony_subcode *) mem_start; + mem_start += sizeof(*last_sony_subcode); + sony_buffer = (unsigned char *) mem_start; + mem_start += sony_buffer_size; + + initialized = 1; + } + } + } + + if ( !initialized ) + printk( "Did not find a Sony CDU-535 drive\n"); + + return mem_start; +} + +#endif /* CONFIG_CDU535 */ diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/block/version.h linux/drivers/block/version.h --- linux-1.1.55+new_quota/drivers/block/version.h Wed Dec 31 18:00:00 1969 +++ linux/drivers/block/version.h Thu Oct 20 18:15:34 1994 @@ -0,0 +1,7 @@ +#define UTS_RELEASE "1.1.53" +#define UTS_VERSION "#8 Wed Oct 12 21:48:39 CDT 1994" +#define LINUX_COMPILE_TIME "21:48:39" +#define LINUX_COMPILE_BY "root" +#define LINUX_COMPILE_HOST "fuzzy" +#define LINUX_COMPILE_DOMAIN "is.a.good.cat" +#define LINUX_COMPILER "gcc version 2.5.8" diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/53c8xx_d.h linux/drivers/scsi/53c8xx_d.h --- linux-1.1.55+new_quota/drivers/scsi/53c8xx_d.h Sat Sep 17 21:48:35 1994 +++ linux/drivers/scsi/53c8xx_d.h Thu Oct 20 18:15:34 1994 @@ -296,7 +296,7 @@ ABSOLUTE reselected_tag = 0 ; Request sense command pointer, its a 6 byte command, should -; be constant for all commands since we allays want 16 bytes of +; be constant for all commands since we always want 16 bytes of ; sense and we don't need to change any fields as we did under ; SCSI-I when we actually cared about the LUN field. ;EXTERNAL NCR53c7xx_sense ; Request sense command @@ -314,7 +314,7 @@ ; ; MODIFIES : SCRATCH, reconnect_dsa_head ; -; EXITS : allays passes control to schedule +; EXITS : always passes control to schedule ENTRY dsa_schedule dsa_schedule: @@ -1378,7 +1378,7 @@ ; XXX the ALU is only eight bits wide, and the assembler ; wont do the dirt work for us. As long as dsa_check_reselect ; is negative, we need to sign extend with 1 bits to the full - ; 32 bit width of the address. + ; 32 bit width os the address. ; ; A potential work around would be to have a known alignment ; of the DSA structure such that the base address plus diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/Makefile linux/drivers/scsi/Makefile --- linux-1.1.55+new_quota/drivers/scsi/Makefile Sat Sep 17 21:47:29 1994 +++ linux/drivers/scsi/Makefile Thu Oct 20 18:15:35 1994 @@ -62,6 +62,11 @@ SCSI_SRCS := $(SCSI_SRCS) aha1740.c endif +ifdef CONFIG_SCSI_AHA274X +SCSI_OBJS := $(SCSI_OBJS) aha274x.o +SCSI_SRCS := $(SCSI_SRCS) aha274x.c +endif + ifdef CONFIG_SCSI_BUSLOGIC SCSI_OBJS := $(SCSI_OBJS) buslogic.o SCSI_SRCS := $(SCSI_SRCS) buslogic.c @@ -70,6 +75,11 @@ ifdef CONFIG_SCSI_DEBUG SCSI_OBJS := $(SCSI_OBJS) scsi_debug.o SCSI_SRCS := $(SCSI_SRCS) scsi_debug.c +endif + +ifdef CONFIG_SCSI_EATA +SCSI_OBJS := $(SCSI_OBJS) eata.o +SCSI_SRCS := $(SCSI_SRCS) eata.c endif ifdef CONFIG_SCSI_FUTURE_DOMAIN diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x.c linux/drivers/scsi/aha274x.c --- linux-1.1.55+new_quota/drivers/scsi/aha274x.c Wed Dec 31 18:00:00 1969 +++ linux/drivers/scsi/aha274x.c Thu Oct 20 18:15:36 1994 @@ -0,0 +1,1449 @@ +/* + * @(#)aha274x.c 1.28 94/10/04 jda + * + * Adaptec 274x device driver for Linux. + * Copyright (c) 1994 The University of Calgary Department of Computer Science. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Sources include the Adaptec 1740 driver (aha1740.c), the + * Ultrastor 24F driver (ultrastor.c), various Linux kernel + * source, the Adaptec EISA config file (!adp7771.cfg), the + * Adaptec AHA-2740A Series User's Guide, the Linux Kernel + * Hacker's Guide, Writing a SCSI Device Driver for Linux, + * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA + * overlay file (adp7770.ovl), the Adaptec AHA-2740 Series + * Technical Reference Manual, the Adaptec AIC-7770 Data + * Book, the ANSI SCSI specification, the ANSI SCSI-2 + * specification (draft 10c), ... + * + * On a twin-bus adapter card, channel B is ignored. Rationale: + * it would greatly complicate the sequencer and host driver code, + * and both busses are multiplexed on to the EISA bus anyway. So + * I don't really see any technical advantage to supporting both. + * + * As well, multiple adapter card using the same IRQ level are + * not supported. It doesn't make sense to configure the cards + * this way from a performance standpoint. Not to mention that + * the kernel would have to support two devices per registered IRQ. + */ + +#include +#include +#include +#include +#include +#include + +#include "../block/blk.h" +#include "sd.h" +#include "scsi.h" +#include "hosts.h" +#include "aha274x.h" + +/* + * There should be a specific return value for this in scsi.h, but + * it seems that most drivers ignore it. + */ +#define DID_UNDERFLOW DID_ERROR + +/* EISA stuff */ + +#define MINEISA 1 +#define MAXEISA 15 +#define SLOTBASE(x) ((x) << 12) + +#define MAXIRQ 15 + +/* AIC-7770 offset definitions */ + +#define O_MINREG(x) ((x) + 0xc00) /* i/o range to reserve */ +#define O_MAXREG(x) ((x) + 0xcbf) + +#define O_SCSISEQ(x) ((x) + 0xc00) /* scsi sequence control */ +#define O_SCSISIGI(x) ((x) + 0xc03) /* scsi control signal read */ +#define O_SCSISIGO(x) ((x) + 0xc03) /* scsi control signal write */ +#define O_SCSIID(x) ((x) + 0xc05) /* scsi id */ +#define O_SSTAT0(x) ((x) + 0xc0b) /* scsi status register 0 */ +#define O_CLRSINT1(x) ((x) + 0xc0c) /* clear scsi interrupt 1 */ +#define O_SSTAT1(x) ((x) + 0xc0c) /* scsi status register 1 */ +#define O_SELID(x) ((x) + 0xc19) /* [re]selection id */ +#define O_SBLKCTL(x) ((x) + 0xc1f) /* scsi block control */ +#define O_SEQCTL(x) ((x) + 0xc60) /* sequencer control */ +#define O_SEQRAM(x) ((x) + 0xc61) /* sequencer ram data */ +#define O_SEQADDR(x) ((x) + 0xc62) /* sequencer address (W) */ +#define O_BIDx(x) ((x) + 0xc80) /* board id */ +#define O_BCTL(x) ((x) + 0xc84) /* board control */ +#define O_HCNTRL(x) ((x) + 0xc87) /* host control */ +#define O_SCBPTR(x) ((x) + 0xc90) /* scb pointer */ +#define O_INTSTAT(x) ((x) + 0xc91) /* interrupt status */ +#define O_ERROR(x) ((x) + 0xc92) /* hard error */ +#define O_CLRINT(x) ((x) + 0xc92) /* clear interrupt status */ +#define O_SCBCNT(x) ((x) + 0xc9a) /* scb auto increment */ +#define O_QINFIFO(x) ((x) + 0xc9b) /* queue in fifo */ +#define O_QINCNT(x) ((x) + 0xc9c) /* queue in count */ +#define O_QOUTFIFO(x) ((x) + 0xc9d) /* queue out fifo */ +#define O_QOUTCNT(x) ((x) + 0xc9e) /* queue out count */ +#define O_SCBARRAY(x) ((x) + 0xca0) /* scb array start */ + +/* host adapter offset definitions */ + +#define HA_REJBYTE(x) ((x) + 0xc31) /* 1st message in byte */ +#define HA_MSG_FLAGS(x) ((x) + 0xc35) /* outgoing message flag */ +#define HA_MSG_LEN(x) ((x) + 0xc36) /* outgoing message length */ +#define HA_MSG_START(x) ((x) + 0xc37) /* outgoing message body */ +#define HA_ARG_1(x) ((x) + 0xc4c) /* sdtr <-> rate parameters */ +#define HA_ARG_2(x) ((x) + 0xc4d) +#define HA_RETURN_1(x) ((x) + 0xc4c) +#define HA_RETURN_2(x) ((x) + 0xc4d) +#define HA_SIGSTATE(x) ((x) + 0xc4e) /* value in SCSISIGO */ +#define HA_NEEDSDTR(x) ((x) + 0xc4f) /* synchronous negotiation? */ + +#define HA_SCSICONF(x) ((x) + 0xc5a) /* SCSI config register */ +#define HA_INTDEF(x) ((x) + 0xc5c) /* interrupt def'n register */ +#define HA_HOSTCONF(x) ((x) + 0xc5d) /* host config def'n register */ + +/* debugging code */ + +#define AHA274X_DEBUG + +/* + * If a parity error occurs during a data transfer phase, run the + * command to completion - it's easier that way - making a note + * of the error condition in this location. This then will modify + * a DID_OK status into a DID_PARITY one for the higher-level SCSI + * code. + */ +#define aha274x_parity(cmd) ((cmd)->SCp.Status) + +/* + * Since the sequencer code DMAs the scatter-gather structures + * directly from memory, we use this macro to assert that the + * kernel structure hasn't changed. + */ +#define SG_STRUCT_CHECK(sg) \ + ((char *)&(sg).address - (char *)&(sg) != 0 || \ + (char *)&(sg).length - (char *)&(sg) != 8 || \ + sizeof((sg).address) != 4 || \ + sizeof((sg).length) != 4 || \ + sizeof(sg) != 12) + +/* + * "Static" structures. Note that these are NOT initialized + * to zero inside the kernel - we have to initialize them all + * explicitly. + * + * We support a maximum of one adapter card per IRQ level (see the + * rationale for this above). On an interrupt, use the IRQ as an + * index into aha274x_boards[] to locate the card information. + */ +static struct Scsi_Host *aha274x_boards[MAXIRQ + 1]; + +struct aha274x_host { + int base; /* card base address */ + int startup; /* intr type check */ + volatile int unpause; /* value for HCNTRL */ + volatile Scsi_Cmnd *SCB_array[AHA274X_MAXSCB]; /* active commands */ +}; + +struct aha274x_scb { + unsigned char control; + unsigned char target_channel_lun; /* 4/1/3 bits */ + unsigned char SG_segment_count; + unsigned char SG_list_pointer[4]; + unsigned char SCSI_cmd_pointer[4]; + unsigned char SCSI_cmd_length; + unsigned char RESERVED[2]; /* must be zero */ + unsigned char target_status; + unsigned char residual_data_count[3]; + unsigned char residual_SG_segment_count; + unsigned char data_pointer[4]; + unsigned char data_count[3]; +#if 0 + /* + * No real point in transferring this to the + * SCB registers. + */ + unsigned char RESERVED[6]; +#endif +}; + +/* + * NB. This table MUST be ordered shortest period first. + */ +static struct { + short period; + short rate; + char *english; +} aha274x_synctab[] = { + 100, 0, "10.0", + 125, 1, "8.0", + 150, 2, "6.67", + 175, 3, "5.7", + 200, 4, "5.0", + 225, 5, "4.4", + 250, 6, "4.0", + 275, 7, "3.6" +}; + +static int aha274x_synctab_max = + sizeof(aha274x_synctab) / sizeof(aha274x_synctab[0]); + +enum aha_type { + T_NONE, + T_274X, + T_284X, + T_MAX +}; + +#ifdef AHA274X_DEBUG + + extern int vsprintf(char *, const char *, va_list); + + static + void debug(const char *fmt, ...) + { + va_list ap; + char buf[256]; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + printk(buf); + va_end(ap); + } + + static + void debug_config(enum aha_type type, int base) + { + int ioport2, ioport3, ioport4; + + static char *BRT[T_MAX][16] = { + { }, /* T_NONE */ + { + "2", "???", "???", "12", /* T_274X */ + "???", "???", "???", "28", + "???", "???", "???", "44", + "???", "???", "???", "60" + }, + { + "2", "4", "8", "12", /* T_284X */ + "16", "20", "24", "28", + "32", "36", "40", "44", + "48", "52", "56", "60" + } + }; + static int DFT[4] = { + 0, 50, 75, 100 + }; + static int SST[4] = { + 256, 128, 64, 32 + }; + + ioport2 = inb(HA_HOSTCONF(base)); + ioport3 = inb(HA_SCSICONF(base)); + ioport4 = inb(HA_INTDEF(base)); + + if (type == T_284X) + printk("AHA284X AT SLOT %d:\n", base >> 12); + else + printk("AHA274X AT EISA SLOT %d:\n", base >> 12); + + printk(" irq %d\n" + " bus release time %s bclks\n" + " data fifo threshold %d%%\n", + ioport4 & 0xf, + BRT[type][(ioport2 >> 2) & 0xf], + DFT[(ioport2 >> 6) & 0x3]); + + printk(" SCSI CHANNEL A:\n" + " scsi id %d\n" + " scsi bus parity check %sabled\n" + " scsi selection timeout %d ms\n" + " scsi bus reset at power-on %sabled\n", + ioport3 & 0x7, + (ioport3 & 0x20) ? "en" : "dis", + SST[(ioport3 >> 3) & 0x3], + (ioport3 & 0x40) ? "en" : "dis"); + + if (type == T_274X) { + printk(" scsi bus termination %sabled\n", + (ioport3 & 0x80) ? "en" : "dis"); + } + } + + static + void debug_rate(int base, int rate) + { + int target = inb(O_SCSIID(base)) >> 4; + + if (rate) { + printk("aha274x: target %d now synchronous at %sMb/s\n", + target, + aha274x_synctab[(rate >> 4) & 0x7].english); + } else { + printk("aha274x: target %d using asynchronous mode\n", + target); + } + } + +#else + +# define debug(fmt, args...) +# define debug_config(x) +# define debug_rate(x,y) + +#endif AHA274X_DEBUG + +static +void aha274x_getscb(int base, struct aha274x_scb *scb) +{ + /* + * This is almost identical to aha274x_putscb(). + */ + outb(0x80, O_SCBCNT(base)); /* SCBAUTO */ + + asm volatile("cld\n\t" + "rep\n\t" + "insb" + : /* no output */ + :"D" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base)) + :"di", "cx", "dx"); + + outb(0, O_SCBCNT(base)); +} + +/* + * How much data should be transferred for this SCSI command? Stop + * at segment sg_last if it's a scatter-gather command so we can + * compute underflow easily. + */ +static +unsigned aha274x_length(Scsi_Cmnd *cmd, int sg_last) +{ + int i, segments; + unsigned length; + struct scatterlist *sg; + + segments = cmd->use_sg - sg_last; + sg = (struct scatterlist *)cmd->buffer; + + if (cmd->use_sg) { + for (i = length = 0; + i < cmd->use_sg && i < segments; + i++) + { + length += sg[i].length; + } + } else + length = cmd->request_bufflen; + + return(length); +} + +static +void aha274x_sg_check(Scsi_Cmnd *cmd) +{ + int i; + struct scatterlist *sg = (struct scatterlist *)cmd->buffer; + + if (cmd->use_sg) { + for (i = 0; i < cmd->use_sg; i++) + if ((unsigned)sg[i].length > 0xffff) + panic("aha274x_sg_check: s/g segment > 64k\n"); + } +} + +static +void aha274x_to_scsirate(unsigned char *rate, + unsigned char transfer, + unsigned char offset) +{ + int i; + + transfer *= 4; + + for (i = 0; i < aha274x_synctab_max-1; i++) { + + if (transfer == aha274x_synctab[i].period) { + *rate = (aha274x_synctab[i].rate << 4) | (offset & 0xf); + return; + } + + if (transfer > aha274x_synctab[i].period && + transfer < aha274x_synctab[i+1].period) + { + *rate = (aha274x_synctab[i+1].rate << 4) | + (offset & 0xf); + return; + } + } + *rate = 0; +} + +/* + * Pause the sequencer and wait for it to actually stop - this + * is important since the sequencer can disable pausing for critical + * sections. + */ +#define PAUSE_SEQUENCER(p) \ + do { \ + outb(0xe, O_HCNTRL(p->base)); /* IRQMS|PAUSE|INTEN */ \ + \ + while ((inb(O_HCNTRL(p->base)) & 0x4) == 0) \ + ; \ + } while (0) + +/* + * Unpause the sequencer. Unremarkable, yet done often enough to + * warrant an easy way to do it. + */ +#define UNPAUSE_SEQUENCER(p) \ + outb(p->unpause, O_HCNTRL(p->base)) /* IRQMS|INTEN */ + +/* + * See comments in aha274x_loadram() wrt this. + */ +#define RESTART_SEQUENCER(p) \ + do { \ + do { \ + outb(0x2, O_SEQCTL(p->base)); \ + } while (inw(O_SEQADDR(p->base)) != 0); \ + \ + UNPAUSE_SEQUENCER(p); \ + } while (0) + +/* + * Since we declared this using SA_INTERRUPT, interrupts should + * be disabled all through this function unless we say otherwise. + */ +static +void aha274x_isr(int irq) +{ + int base, intstat; + struct aha274x_host *p; + + p = (struct aha274x_host *)aha274x_boards[irq]->hostdata; + base = p->base; + + /* + * Check the startup flag - if no commands have been queued, + * we probably have the interrupt type set wrong. Reverse + * the stored value and the active one in the host control + * register. + */ + if (p->startup) { + p->unpause ^= 0x8; + outb(inb(O_HCNTRL(p->base)) ^ 0x8, O_HCNTRL(p->base)); + return; + } + + /* + * Handle all the interrupt sources - especially for SCSI + * interrupts, we won't get a second chance at them. + */ + intstat = inb(O_INTSTAT(base)); + + if (intstat & 0x8) { /* BRKADRINT */ + + panic("aha274x_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n", + inb(O_ERROR(base)), inw(O_SEQADDR(base))); + } + + if (intstat & 0x4) { /* SCSIINT */ + + int scbptr = inb(O_SCBPTR(base)); + int status = inb(O_SSTAT1(base)); + Scsi_Cmnd *cmd; + + cmd = (Scsi_Cmnd *)p->SCB_array[scbptr]; + if (!cmd) { + printk("aha274x_isr: no command for scb (scsiint)\n"); + /* + * Turn off the interrupt and set status + * to zero, so that it falls through the + * reset of the SCSIINT code. + */ + outb(status, O_CLRSINT1(base)); + UNPAUSE_SEQUENCER(p); + outb(0x4, O_CLRINT(base)); /* undocumented */ + status = 0; + } + p->SCB_array[scbptr] = NULL; + + /* + * Only the SCSI Status 1 register has information + * about exceptional conditions that we'd have a + * SCSIINT about; anything in SSTAT0 will be handled + * by the sequencer. Note that there can be multiple + * bits set. + */ + if (status & 0x80) { /* SELTO */ + /* + * Hardware selection timer has expired. Turn + * off SCSI selection sequence. + */ + outb(0, O_SCSISEQ(base)); + cmd->result = DID_TIME_OUT << 16; + + /* + * If there's an active message, it belongs to the + * command that is getting punted - remove it. + */ + outb(0, HA_MSG_FLAGS(base)); + + /* + * Shut off the offending interrupt sources, reset + * the sequencer address to zero and unpause it, + * then call the high-level SCSI completion routine. + * + * WARNING! This is a magic sequence! After many + * hours of guesswork, turning off the SCSI interrupts + * in CLRSINT? does NOT clear the SCSIINT bit in + * INTSTAT. By writing to the (undocumented, unused + * according to the AIC-7770 manual) third bit of + * CLRINT, you can clear INTSTAT. But, if you do it + * while the sequencer is paused, you get a BRKADRINT + * with an Illegal Host Address status, so the + * sequencer has to be restarted first. + */ + outb(0x80, O_CLRSINT1(base)); /* CLRSELTIMO */ + RESTART_SEQUENCER(p); + + outb(0x4, O_CLRINT(base)); /* undocumented */ + cmd->scsi_done(cmd); + } + + if (status & 0x4) { /* SCSIPERR */ + /* + * A parity error has occurred during a data + * transfer phase. Flag it and continue. + */ + printk("aha274x: parity error on target %d, lun %d\n", + cmd->target, + cmd->lun); + aha274x_parity(cmd) = DID_PARITY; + + /* + * Clear interrupt and resume as above. + */ + outb(0x4, O_CLRSINT1(base)); /* CLRSCSIPERR */ + UNPAUSE_SEQUENCER(p); + + outb(0x4, O_CLRINT(base)); /* undocumented */ + } + + if ((status & (0x8|0x4)) == 0 && status) { + /* + * We don't know what's going on. Turn off the + * interrupt source and try to continue. + */ + printk("aha274x_isr: sstat1 = 0x%x\n", status); + outb(status, O_CLRSINT1(base)); + UNPAUSE_SEQUENCER(p); + outb(0x4, O_CLRINT(base)); /* undocumented */ + } + } + + if (intstat & 0x2) { /* CMDCMPLT */ + + int complete, old_scbptr; + struct aha274x_scb scb; + unsigned actual; + Scsi_Cmnd *cmd; + + /* + * The sequencer will continue running when it + * issues this interrupt. There may be >1 commands + * finished, so loop until we've processed them all. + */ + do { + complete = inb(O_QOUTFIFO(base)); + + cmd = (Scsi_Cmnd *)p->SCB_array[complete]; + if (!cmd) { + printk("aha274x warning: " + "no command for scb (cmdcmplt)\n"); + continue; + } + p->SCB_array[complete] = NULL; + + PAUSE_SEQUENCER(p); + + /* + * After pausing the sequencer (and waiting + * for it to stop), save its SCB pointer, then + * write in our completed one and read the SCB + * registers. Afterwards, restore the saved + * pointer, unpause the sequencer and call the + * higher-level completion function - unpause + * first since we have no idea how long done() + * will take. + */ + old_scbptr = inb(O_SCBPTR(base)); + outb(complete, O_SCBPTR(base)); + + aha274x_getscb(base, &scb); + outb(old_scbptr, O_SCBPTR(base)); + + UNPAUSE_SEQUENCER(p); + + cmd->result = scb.target_status | + (aha274x_parity(cmd) << 16); + + /* + * Did we underflow? At this time, there's only + * one other driver that bothers to check for this, + * and cmd->underflow seems to be set rather half- + * heartedly in the higher-level SCSI code. + */ + actual = aha274x_length(cmd, + scb.residual_SG_segment_count); + + actual -= ((scb.residual_data_count[2] << 16) | + (scb.residual_data_count[1] << 8) | + (scb.residual_data_count[0])); + + if (actual < cmd->underflow) { + printk("aha274x: target %d underflow - " + "wanted (at least) %u, got %u\n", + cmd->target, cmd->underflow, actual); + + cmd->result = scb.target_status | + (DID_UNDERFLOW << 16); + } + + cmd->scsi_done(cmd); + + /* + * Clear interrupt status before checking + * the output queue again. This eliminates + * a race condition whereby a command could + * complete between the queue poll and the + * interrupt clearing, so notification of the + * command being complete never made it back + * up to the kernel. + */ + outb(0x2, O_CLRINT(base)); /* CLRCMDINT */ + + } while (inb(O_QOUTCNT(base))); + } + + if (intstat & 0x1) { /* SEQINT */ + + unsigned char transfer, offset, rate; + + /* + * Although the sequencer is paused immediately on + * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT + * condition will have unpaused the sequencer before + * this point. + */ + PAUSE_SEQUENCER(p); + + switch (intstat & 0xf0) { + case 0x00: + panic("aha274x_isr: unknown scsi bus phase\n"); + case 0x10: + debug("aha274x_isr warning: " + "issuing message reject, 1st byte 0x%x\n", + inb(HA_REJBYTE(base))); + break; + case 0x20: + panic("aha274x_isr: reconnecting target %d " + "didn't issue IDENTIFY message\n", + (inb(O_SELID(base)) >> 4) & 0xf); + case 0x30: + debug("aha274x_isr: sequencer couldn't find match " + "for reconnecting target %d - issuing ABORT\n", + (inb(O_SELID(base)) >> 4) & 0xf); + break; + case 0x40: + transfer = inb(HA_ARG_1(base)); + offset = inb(HA_ARG_2(base)); + aha274x_to_scsirate(&rate, transfer, offset); + outb(rate, HA_RETURN_1(base)); + debug_rate(base, rate); + break; + default: + debug("aha274x_isr: seqint, " + "intstat = 0x%x, scsisigi = 0x%x\n", + intstat, inb(O_SCSISIGI(base))); + break; + } + + outb(0x1, O_CLRINT(base)); /* CLRSEQINT */ + UNPAUSE_SEQUENCER(p); + } +} + +/* + * Probing for EISA boards: it looks like the first two bytes + * are a manufacturer code - three characters, five bits each: + * + * BYTE 0 BYTE 1 BYTE 2 BYTE 3 + * ?1111122 22233333 PPPPPPPP RRRRRRRR + * + * The characters are baselined off ASCII '@', so add that value + * to each to get the real ASCII code for it. The next two bytes + * appear to be a product and revision number, probably vendor- + * specific. This is what is being searched for at each port, + * and what should probably correspond to the ID= field in the + * ECU's .cfg file for the card - if your card is not detected, + * make sure your signature is listed in the array. + * + * The fourth byte's lowest bit seems to be an enabled/disabled + * flag (rest of the bits are reserved?). + */ + +static +enum aha_type aha274x_probe(int slot, int s_base) +{ + int i; + unsigned char buf[4]; + + static struct { + int n; + unsigned char signature[sizeof(buf)]; + enum aha_type type; + } S[] = { + 4, { 0x04, 0x90, 0x77, 0x71 }, T_274X, /* host adapter 274x */ + 4, { 0x04, 0x90, 0x77, 0x70 }, T_274X, /* motherboard 274x */ + 4, { 0x04, 0x90, 0x77, 0x56 }, T_284X, /* 284x, BIOS enabled */ + }; + + for (i = 0; i < sizeof(buf); i++) { + /* + * The VL-bus cards need to be primed by + * writing before a signature check. + */ + outb(0x80 + i, s_base); + buf[i] = inb(s_base + i); + } + + for (i = 0; i < sizeof(S)/sizeof(S[0]); i++) { + if (!memcmp(buf, S[i].signature, S[i].n)) { + /* + * Signature match on enabled card? + */ + if (inb(s_base + 4) & 1) + return(S[i].type); + printk("aha274x disabled at slot %d, ignored\n", slot); + } + } + return(T_NONE); +} + +/* + * Return ' ' for plain 274x, 'T' for twin-channel, 'W' for + * wide channel, '?' for anything else. + */ + +static +char aha274x_type(int base) +{ + /* + * The AIC-7770 can be wired so that, on chip reset, + * the SCSI Block Control register indicates how many + * busses the chip is configured for. + */ + switch (inb(O_SBLKCTL(base))) { + case 0: + return(' '); + case 2: + return('W'); + case 8: + return('T'); + default: + printk("aha274x has unknown bus configuration\n"); + return('?'); + } +} + +static +void aha274x_loadram(int base) +{ + static unsigned char seqprog[] = { + /* + * Each sequencer instruction is 29 bits + * long (fill in the excess with zeroes) + * and has to be loaded from least -> most + * significant byte, so this table has the + * byte ordering reversed. + */ +# include "aha274x_seq.h" + }; + + /* + * When the AIC-7770 is paused (as on chip reset), the + * sequencer address can be altered and a sequencer + * program can be loaded by writing it, byte by byte, to + * the sequencer RAM port - the Adaptec documentation + * recommends using REP OUTSB to do this, hence the inline + * assembly. Since the address autoincrements as we load + * the program, reset it back to zero afterward. Disable + * sequencer RAM parity error detection while loading, and + * make sure the LOADRAM bit is enabled for loading. + */ + outb(0x83, O_SEQCTL(base)); /* PERRORDIS|SEQRESET|LOADRAM */ + + asm volatile("cld\n\t" + "rep\n\t" + "outsb" + : /* no output */ + :"S" (seqprog), "c" (sizeof(seqprog)), "d" (O_SEQRAM(base)) + :"si", "cx", "dx"); + + /* + * WARNING! This is a magic sequence! After extensive + * experimentation, it seems that you MUST turn off the + * LOADRAM bit before you play with SEQADDR again, else + * you will end up with parity errors being flagged on + * your sequencer program. (You would also think that + * turning off LOADRAM and setting SEQRESET to reset the + * address to zero would work, but you need to do it twice + * for it to take effect on the address. Timing problem?) + */ + outb(0, O_SEQCTL(base)); + do { + /* + * Actually, reset it until + * the address shows up as + * zero just to be safe.. + */ + outb(0x2, O_SEQCTL(base)); /* SEQRESET */ + + } while (inw(O_SEQADDR(base)) != 0); +} + +static +int aha274x_register(Scsi_Host_Template *template, + enum aha_type type, + int base) +{ + int i, irq, scsi_id; + struct Scsi_Host *host; + struct aha274x_host *p; + + /* + * Give the AIC-7770 a reset - reading the 274x's registers + * returns zeroes unless you do. This forces a pause of the + * Sequencer. + */ + outb(1, O_HCNTRL(base)); /* CHIPRST */ + + /* + * The IRQ level in i/o port 4 maps directly onto the real + * IRQ number. If it's ok, register it with the kernel. + * + * NB. the Adaptec documentation says the IRQ number is only + * in the lower four bits; the ECU information shows the + * high bit being used as well. Which is correct? + */ + irq = inb(HA_INTDEF(base)) & 0xf; + if (irq < 9 || irq > 15) { + printk("aha274x uses unsupported IRQ level, ignoring\n"); + return(0); + } + + /* + * Lock out other contenders for our i/o space. + */ + snarf_region(O_MINREG(base), O_MAXREG(base)-O_MINREG(base)); + + /* + * Any card-type-specific adjustments before we register + * the scsi host(s). + */ + + scsi_id = inb(HA_SCSICONF(base)) & 0x7; + + switch (aha274x_type(base)) { + case 'T': + printk("aha274x warning: ignoring channel B of 274x-twin\n"); + break; + case ' ': + break; + default: + printk("aha274x is an unsupported type, ignoring\n"); + return(0); + } + + /* + * Before registry, make sure that the offsets of the + * struct scatterlist are what the sequencer will expect, + * otherwise disable scatter-gather altogether until someone + * can fix it. This is important since the sequencer will + * DMA elements of the SG array in while executing commands. + */ + if (template->sg_tablesize != SG_NONE) { + struct scatterlist sg; + + if (SG_STRUCT_CHECK(sg)) { + printk("aha274x warning: kernel scatter-gather " + "structures changed, disabling it\n"); + template->sg_tablesize = SG_NONE; + } + } + + /* + * Register each "host" and fill in the returned Scsi_Host + * structure as best we can. Some of the parameters aren't + * really relevant for EISA, and none of the high-level SCSI + * code looks at it anyway.. why are the fields there? Also + * save the pointer so that we can find the information when + * an IRQ is triggered. + */ + host = scsi_register(template, sizeof(struct aha274x_host)); + host->this_id = scsi_id; + host->irq = irq; + + aha274x_boards[irq] = host; + + p = (struct aha274x_host *)host->hostdata; + for (i = 0; i < AHA274X_MAXSCB; i++) + p->SCB_array[i] = NULL; + p->base = base; + + /* + * The interrupt trigger is different depending + * on whether the card is EISA or VL-bus - sometimes. + * The startup variable will be cleared once the first + * command is queued, and is checked in the isr to + * try and detect when the interrupt type is set + * incorrectly, triggering an interrupt immediately. + */ + p->unpause = (type != T_274X ? 0x2 : 0xa); + p->startup = !0; + + /* + * Register IRQ with the kernel _after_ the host information + * is set up, in case we take an interrupt right away, due to + * the interrupt type being set wrong. + */ + if (request_irq(irq, aha274x_isr, SA_INTERRUPT, "AHA274x/284x")) { + printk("aha274x couldn't register irq %d, ignoring\n", irq); + return(0); + } + + /* + * Print out debugging information before re-enabling + * the card - a lot of registers on it can't be read + * when the sequencer is active. + */ + debug_config(type, base); + + /* + * Load the sequencer program, then re-enable the board - + * resetting the AIC-7770 disables it, leaving the lights + * on with nobody home. + */ + aha274x_loadram(base); + outb(1, O_BCTL(base)); /* ENABLE */ + + /* + * Set the host adapter registers to indicate that synchronous + * negotiation should be attempted the first time the targets + * are communicated with. Also initialize the active message + * flag to indicate that there is no message. + */ + outb(0xff, HA_NEEDSDTR(base)); + outb(0, HA_MSG_FLAGS(base)); + + /* + * Unpause the sequencer before returning and enable + * interrupts - we shouldn't get any until the first + * command is sent to us by the high-level SCSI code. + */ + UNPAUSE_SEQUENCER(p); + return(1); +} + +int aha274x_detect(Scsi_Host_Template *template) +{ + enum aha_type type; + int found = 0, slot, base; + + for (slot = MINEISA; slot <= MAXEISA; slot++) { + + base = SLOTBASE(slot); + + if (check_region(O_MINREG(base), + O_MAXREG(base)-O_MINREG(base))) + { + /* + * Some other driver has staked a + * claim to this i/o region already. + */ + continue; + } + + type = aha274x_probe(slot, O_BIDx(base)); + + if (type != T_NONE) { + /* + * We "find" a 274x if we locate the card + * signature and we can set it up and register + * it with the kernel without incident. + */ + found += aha274x_register(template, type, base); + } + } + template->name = (char *)aha274x_info(); + return(found); +} + +const char *aha274x_info(void) +{ + return("Adaptec AHA274x/284x (EISA/VL-bus -> Fast SCSI) " + AHA274X_SEQ_VERSION "/" + AHA274X_H_VERSION "/" + "1.28"); +} + +int aha274x_command(Scsi_Cmnd *cmd) +{ + /* + * This is a relic of non-interrupt-driven SCSI + * drivers. With the can_queue variable set, this + * should never be called. + */ + panic("aha274x_command was called\n"); +} + +static +void aha274x_buildscb(struct aha274x_host *p, + Scsi_Cmnd *cmd, + struct aha274x_scb *scb) +{ + void *addr; + unsigned length; + + memset(scb, 0, sizeof(*scb)); + + /* + * NB. channel selection (bit 3) is always zero. + */ + scb->target_channel_lun = ((cmd->target << 4) & 0xf0) | + (cmd->lun & 0x7); + + /* + * The interpretation of request_buffer and request_bufflen + * changes depending on whether or not use_sg is zero; a + * non-zero use_sg indicates the number of elements in the + * scatter-gather array. + * + * The AIC-7770 can't support transfers of any sort larger + * than 2^24 (three-byte count) without backflips. For what + * the kernel is doing, this shouldn't occur. I hope. + */ + length = aha274x_length(cmd, 0); + + /* + * The sequencer code cannot yet handle scatter-gather segments + * larger than 64k (two-byte length). The 1.1.x kernels, however, + * have a four-byte length field in the struct scatterlist, so + * make sure we don't exceed 64k on these kernels for now. + */ + aha274x_sg_check(cmd); + + if (length > 0xffffff) { + panic("aha274x_buildscb: can't transfer > 2^24 - 1 bytes\n"); + } + + /* + * XXX - this relies on the host data being stored in a + * little-endian format. + */ + addr = cmd->cmnd; + scb->SCSI_cmd_length = COMMAND_SIZE(cmd->cmnd[0]); + memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer)); + + if (cmd->use_sg) { +#if 0 + debug("aha274x_buildscb: SG used, %d segments, length %u\n", + cmd->use_sg, + length); +#endif + scb->SG_segment_count = cmd->use_sg; + memcpy(scb->SG_list_pointer, + &cmd->request_buffer, + sizeof(scb->SG_list_pointer)); + } else { + scb->SG_segment_count = 0; + memcpy(scb->data_pointer, + &cmd->request_buffer, + sizeof(scb->data_pointer)); + memcpy(scb->data_count, + &cmd->request_bufflen, + sizeof(scb->data_count)); + } +} + +static +void aha274x_putscb(int base, struct aha274x_scb *scb) +{ + /* + * By turning on the SCB auto increment, any reference + * to the SCB I/O space postincrements the SCB address + * we're looking at. So turn this on and dump the relevant + * portion of the SCB to the card. + */ + outb(0x80, O_SCBCNT(base)); /* SCBAUTO */ + + asm volatile("cld\n\t" + "rep\n\t" + "outsb" + : /* no output */ + :"S" (scb), "c" (sizeof(*scb)), "d" (O_SCBARRAY(base)) + :"si", "cx", "dx"); + + outb(0, O_SCBCNT(base)); +} + +int aha274x_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) +{ + long flags; + int empty, old_scbptr; + struct aha274x_host *p; + struct aha274x_scb scb; + +#if 0 + debug("aha274x_queue: cmd 0x%x (size %u), target %d, lun %d\n", + cmd->cmnd[0], + COMMAND_SIZE(cmd->cmnd[0]), + cmd->target, + cmd->lun); +#endif + + p = (struct aha274x_host *)cmd->host->hostdata; + + /* + * Construct the SCB beforehand, so the sequencer is + * paused a minimal amount of time. + */ + aha274x_buildscb(p, cmd, &scb); + + /* + * Clear the startup flag - we can now legitimately + * expect interrupts. + */ + p->startup = 0; + + /* + * This is a critical section, since we don't want the + * interrupt routine mucking with the host data or the + * card. Since the kernel documentation is vague on + * whether or not we are in a cli/sti pair already, save + * the flags to be on the safe side. + */ + save_flags(flags); + cli(); + + /* + * Find a free slot in the SCB array to load this command + * into. Since can_queue is set to AHA274X_MAXSCB, we + * should always find one. + */ + for (empty = 0; empty < AHA274X_MAXSCB; empty++) + if (!p->SCB_array[empty]) + break; + if (empty == AHA274X_MAXSCB) + panic("aha274x_queue: couldn't find a free scb\n"); + + /* + * Pause the sequencer so we can play with its registers - + * wait for it to acknowledge the pause. + * + * XXX - should the interrupts be left on while doing this? + */ + PAUSE_SEQUENCER(p); + + /* + * Save the SCB pointer and put our own pointer in - this + * selects one of the four banks of SCB registers. Load + * the SCB, then write its pointer into the queue in FIFO + * and restore the saved SCB pointer. + */ + old_scbptr = inb(O_SCBPTR(p->base)); + outb(empty, O_SCBPTR(p->base)); + + aha274x_putscb(p->base, &scb); + + outb(empty, O_QINFIFO(p->base)); + outb(old_scbptr, O_SCBPTR(p->base)); + + /* + * Make sure the Scsi_Cmnd pointer is saved, the struct it + * points to is set up properly, and the parity error flag + * is reset, then unpause the sequencer and watch the fun + * begin. + */ + cmd->scsi_done = fn; + p->SCB_array[empty] = cmd; + aha274x_parity(cmd) = DID_OK; + + UNPAUSE_SEQUENCER(p); + + restore_flags(flags); + return(0); +} + +/* return values from aha274x_kill */ + +enum k_state { + k_ok, /* scb found and message sent */ + k_busy, /* message already present */ + k_absent, /* couldn't locate scb */ + k_disconnect, /* scb found, but disconnected */ +}; + +/* + * This must be called with interrupts disabled - it's going to + * be messing around with the host data, and an interrupt being + * fielded in the middle could get ugly. + * + * Since so much of the abort and reset code is shared, this + * function performs more magic than it really should. If the + * command completes ok, then it will call scsi_done with the + * result code passed in. The unpause parameter controls whether + * or not the sequencer gets unpaused - the reset function, for + * instance, may want to do something more aggressive. + * + * Note that the command is checked for in our SCB_array first + * before the sequencer is paused, so if k_absent is returned, + * then the sequencer is NOT paused. + */ + +static +enum k_state aha274x_kill(Scsi_Cmnd *cmd, unsigned char message, + unsigned int result, int unpause) +{ + struct aha274x_host *p; + int i, scb, found, queued; + unsigned char scbsave[AHA274X_MAXSCB]; + + p = (struct aha274x_host *)cmd->host->hostdata; + + /* + * If we can't find the command, assume it just completed + * and shrug it away. + */ + for (scb = 0; scb < AHA274X_MAXSCB; scb++) + if (p->SCB_array[scb] == cmd) + break; + + if (scb == AHA274X_MAXSCB) + return(k_absent); + + PAUSE_SEQUENCER(p); + + /* + * This is the best case, really. Check to see if the + * command is still in the sequencer's input queue. If + * so, simply remove it. Reload the queue afterward. + */ + queued = inb(O_QINCNT(p->base)); + + for (i = found = 0; i < queued; i++) { + scbsave[i] = inb(O_QINFIFO(p->base)); + + if (scbsave[i] == scb) { + found = 1; + i -= 1; + } + } + + queued -= found; + for (i = 0; i < queued; i++) + outb(scbsave[i], O_QINFIFO(p->base)); + + if (found) + goto complete; + + /* + * Check the current SCB bank. If it's not the one belonging + * to the command we want to kill, assume that the command + * is disconnected. It's rather a pain to force a reconnect + * and send a message to the target, so we abdicate responsibility + * in this case. + */ + if (inb(O_SCBPTR(p->base)) != scb) { + if (unpause) + UNPAUSE_SEQUENCER(p); + return(k_disconnect); + } + + /* + * Presumably at this point our target command is active. Check + * to see if there's a message already in effect. If not, place + * our message in and assert ATN so the target goes into MESSAGE + * OUT phase. + */ + if (inb(HA_MSG_FLAGS(p->base)) & 0x80) { + if (unpause) + UNPAUSE_SEQUENCER(p); + return(k_busy); + } + + outb(0x80, HA_MSG_FLAGS(p->base)); /* active message */ + outb(1, HA_MSG_LEN(p->base)); /* length = 1 */ + outb(message, HA_MSG_START(p->base)); /* message body */ + + /* + * Assert ATN. Use the value of SCSISIGO saved by the + * sequencer code so we don't alter its contents radically + * in the middle of something critical. + */ + outb(inb(HA_SIGSTATE(p->base)) | 0x10, O_SCSISIGO(p->base)); + + /* + * The command has been killed. Do the bookkeeping, unpause + * the sequencer, and notify the higher-level SCSI code. + */ +complete: + p->SCB_array[scb] = NULL; + if (unpause) + UNPAUSE_SEQUENCER(p); + + cmd->result = result << 16; + cmd->scsi_done(cmd); + return(k_ok); +} + +int aha274x_abort(Scsi_Cmnd *cmd) +{ + int rv; + long flags; + + save_flags(flags); + cli(); + + switch (aha274x_kill(cmd, ABORT, DID_ABORT, !0)) { + case k_ok: rv = SCSI_ABORT_SUCCESS; break; + case k_busy: rv = SCSI_ABORT_BUSY; break; + case k_absent: rv = SCSI_ABORT_NOT_RUNNING; break; + case k_disconnect: rv = SCSI_ABORT_SNOOZE; break; + default: + panic("aha274x_do_abort: internal error\n"); + } + + restore_flags(flags); + return(rv); +} + +/* + * Resetting the bus always succeeds - is has to, otherwise the + * kernel will panic! Try a surgical technique - sending a BUS + * DEVICE RESET message - on the offending target before pulling + * the SCSI bus reset line. + */ + +int aha274x_reset(Scsi_Cmnd *cmd) +{ + int i; + long flags; + Scsi_Cmnd *reset; + struct aha274x_host *p; + + p = (struct aha274x_host *)cmd->host->hostdata; + save_flags(flags); + cli(); + + switch (aha274x_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0)) { + + case k_ok: + /* + * The RESET message was sent to the target + * with no problems. Flag that target as + * needing a SDTR negotiation on the next + * connection and restart the sequencer. + */ + outb((1 << cmd->target), HA_NEEDSDTR(p->base)); + UNPAUSE_SEQUENCER(p); + break; + + case k_absent: + /* + * The sequencer will not be paused if aha274x_kill() + * couldn't find the command. + */ + PAUSE_SEQUENCER(p); + /* falls through */ + + case k_busy: + case k_disconnect: + /* + * Do a hard reset of the SCSI bus. According to the + * SCSI-2 draft specification, reset has to be asserted + * for at least 25us. I'm invoking the kernel delay + * function for 30us since I'm not totally trusting of + * the busy loop timing. + * + * XXX - I'm not convinced this works. I tried resetting + * the bus before, trying to get the devices on the + * bus to revert to asynchronous transfer, and it + * never seemed to work. + */ + debug("aha274x: attempting to reset scsi bus and card\n"); + + outb(1, O_SCSISEQ(p->base)); /* SCSIRSTO */ + udelay(30); + outb(0, O_SCSISEQ(p->base)); /* !SCSIRSTO */ + + outb(0xff, HA_NEEDSDTR(p->base)); + UNPAUSE_SEQUENCER(p); + + /* + * Locate the command and return a "reset" status + * for it. This is not completely correct and will + * probably return to haunt me later. + */ + for (i = 0; i < AHA274X_MAXSCB; i++) { + if (cmd == p->SCB_array[i]) { + reset = (Scsi_Cmnd *)p->SCB_array[i]; + p->SCB_array[i] = NULL; + reset->result = DID_RESET << 16; + reset->scsi_done(reset); + break; + } + } + break; + + default: + panic("aha274x_reset: internal error\n"); + } + + restore_flags(flags); + return(SCSI_RESET_SUCCESS); +} + +int aha274x_biosparam(Disk *disk, int devno, int geom[]) +{ + /* + * XXX - when I find the EISA configuration information, + * this should change to handle the "extended translation + * for drives >1G" option, which uses 255 heads and + * 63 sectors/track for drives >1G. Right now, just + * assume it's turned off. + */ + debug("aha274x_biosparam warning: don't know translation config\n"); + + geom[0] = 64; + geom[1] = 32; + geom[2] = disk->capacity / (64 * 32); + + return(0); +} + diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x.h linux/drivers/scsi/aha274x.h --- linux-1.1.55+new_quota/drivers/scsi/aha274x.h Wed Dec 31 18:00:00 1969 +++ linux/drivers/scsi/aha274x.h Thu Oct 20 18:15:36 1994 @@ -0,0 +1,62 @@ +/* @(#)aha274x.h 1.11 94/09/06 jda */ + +/* + * Adaptec 274x device driver for Linux. + * Copyright (c) 1994 The University of Calgary Department of Computer Science. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef aha274x_h +#define aha274x_h + +#define AHA274X_MAXSCB 4 +#define AHA274X_H_VERSION "1.11" + +/* + * Scsi_Host_Template (see hosts.h) for 274x - some fields + * to do with card config are filled in after the card is + * detected. + */ +#define AHA274X { \ + NULL, \ + "", \ + aha274x_detect, \ + NULL, \ + aha274x_info, \ + aha274x_command, \ + aha274x_queue, \ + aha274x_abort, \ + aha274x_reset, \ + NULL, \ + aha274x_biosparam, \ + AHA274X_MAXSCB, /* max simultaneous cmds */\ + -1, /* scsi id of host adapter */\ + SG_ALL, /* max scatter-gather cmds */\ + 1, /* cmds per lun (linked cmds) */\ + 0, /* number of 274x's present */\ + 0, /* no memory DMA restrictions */\ + DISABLE_CLUSTERING \ +} + +extern int aha274x_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); +extern int aha274x_biosparam(Disk *, int, int[]); +extern int aha274x_detect(Scsi_Host_Template *); +extern int aha274x_command(Scsi_Cmnd *); +extern int aha274x_abort(Scsi_Cmnd *); +extern int aha274x_reset(Scsi_Cmnd *); +extern const char *aha274x_info(void); + +#endif diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x.seq linux/drivers/scsi/aha274x.seq --- linux-1.1.55+new_quota/drivers/scsi/aha274x.seq Wed Dec 31 18:00:00 1969 +++ linux/drivers/scsi/aha274x.seq Thu Oct 20 18:15:36 1994 @@ -0,0 +1,1021 @@ +# @(#)aha274x.seq 1.28 94/10/04 jda +# +# Adaptec 274x device driver for Linux. +# Copyright (c) 1994 The University of Calgary Department of Computer Science. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +VERSION AHA274X_SEQ_VERSION 1.28 + +MAXSCB = 4 + +SCSISEQ = 0x00 +SXFRCTL0 = 0x01 +SXFRCTL1 = 0x02 +SCSISIGI = 0x03 +SCSISIGO = 0x03 +SCSIRATE = 0x04 +SCSIID = 0x05 +SCSIDATL = 0x06 +STCNT = 0x08 +STCNT+0 = 0x08 +STCNT+1 = 0x09 +STCNT+2 = 0x0a +SSTAT0 = 0x0b +CLRSINT1 = 0x0c +SSTAT1 = 0x0c +SIMODE1 = 0x11 +SCSIBUSL = 0x12 +SHADDR = 0x14 +SELID = 0x19 +SBLKCTL = 0x1f +SEQCTL = 0x60 +A = 0x64 # == ACCUM +SINDEX = 0x65 +DINDEX = 0x66 +ALLZEROS = 0x6a +NONE = 0x6a +SINDIR = 0x6c +DINDIR = 0x6d +FUNCTION1 = 0x6e +HADDR = 0x88 +HCNT = 0x8c +HCNT+0 = 0x8c +HCNT+1 = 0x8d +HCNT+2 = 0x8e +SCBPTR = 0x90 +INTSTAT = 0x91 +DFCNTRL = 0x93 +DFSTATUS = 0x94 +DFDAT = 0x99 +QINFIFO = 0x9b +QINCNT = 0x9c +QOUTFIFO = 0x9d + +SCSICONF = 0x5a + +# The two reserved bytes at SCBARRAY+1[23] are expected to be set to +# zero, and the reserved bit in SCBARRAY+0 is used as an internal flag +# to indicate whether or not to reload scatter-gather parameters after +# a disconnect. +# +SCBARRAY+0 = 0xa0 +SCBARRAY+1 = 0xa1 +SCBARRAY+2 = 0xa2 +SCBARRAY+3 = 0xa3 +SCBARRAY+7 = 0xa7 +SCBARRAY+11 = 0xab +SCBARRAY+14 = 0xae +SCBARRAY+15 = 0xaf +SCBARRAY+16 = 0xb0 +SCBARRAY+17 = 0xb1 +SCBARRAY+18 = 0xb2 +SCBARRAY+19 = 0xb3 +SCBARRAY+20 = 0xb4 +SCBARRAY+21 = 0xb5 +SCBARRAY+22 = 0xb6 +SCBARRAY+23 = 0xb7 +SCBARRAY+24 = 0xb8 +SCBARRAY+25 = 0xb9 + +SIGNAL_0 = 0x01 # unknown scsi bus phase +SIGNAL_1 = 0x11 # message reject +SIGNAL_2 = 0x21 # no IDENTIFY after reconnect +SIGNAL_3 = 0x31 # no cmd match for reconnect +SIGNAL_4 = 0x41 # SDTR -> SCSIRATE conversion + +# The host adapter card (at least the BIOS) uses 20-2f for SCSI +# device information, 32-33 and 5a-5f as well. Since we don't support +# wide or twin-bus SCSI, 28-2f can be reclaimed. As it turns out, the +# BIOS trashes 20-27 anyway, writing the synchronous negotiation results +# on top of the BIOS values, so we re-use those for our per-target +# scratchspace (actually a value that can be copied directly into +# SCSIRATE). This implies, since we can't get the BIOS config values, +# that all targets will be negotiated with for synchronous transfer. +# NEEDSDTR has one bit per target indicating if an SDTR message is +# needed for that device - this will be set initially, as well as +# after a bus reset condition. +# +# The high bit of DROPATN is set if ATN should be dropped before the ACK +# when outb is called. REJBYTE contains the first byte of a MESSAGE IN +# message, so the driver can report an intelligible error if a message is +# rejected. +# +# RESELECT's high bit is true if we are currently handling a reselect; +# its next-highest bit is true ONLY IF we've seen an IDENTIFY message +# from the reselecting target. If we haven't had IDENTIFY, then we have +# no idea what the lun is, and we can't select the right SCB register +# bank, so force a kernel panic if the target attempts a data in/out or +# command phase instead of corrupting something. +# +# Note that SG_NEXT occupies four bytes. +# +SYNCNEG = 0x20 +DISC_DSB_A = 0x32 + +DROPATN = 0x30 +REJBYTE = 0x31 +RESELECT = 0x34 + +MSG_FLAGS = 0x35 +MSG_LEN = 0x36 +MSG_START+0 = 0x37 +MSG_START+1 = 0x38 +MSG_START+2 = 0x39 +MSG_START+3 = 0x3a +MSG_START+4 = 0x3b +MSG_START+5 = 0x3c +-MSG_START+0 = 0xc9 # 2's complement of MSG_START+0 + +ARG_1 = 0x4c # sdtr conversion args & return +ARG_2 = 0x4d +RETURN_1 = 0x4c + +SIGSTATE = 0x4e # value written to SCSISIGO +NEEDSDTR = 0x4f # send SDTR message, 1 bit/trgt + +SG_SIZEOF = 12 # sizeof(struct scatterlist) +SG_NOLOAD = 0x50 # load SG pointer/length? +SG_COUNT = 0x51 # working value of SG count +SG_NEXT = 0x52 # working value of SG pointer +SG_NEXT+0 = 0x52 +SG_NEXT+1 = 0x53 +SG_NEXT+2 = 0x54 +SG_NEXT+3 = 0x55 + +# Poll QINCNT for work - the lower three bits contain +# the number of entries in the Queue In FIFO. +# +start: + test SCSISIGI,0x4 jnz reselect # BSYI + test QINCNT,0x7 jz start + +# We have at least one queued SCB now. Set the SCB pointer +# from the FIFO so we see the right bank of SCB registers, +# then set SCSI options and set the initiator and target +# SCSI IDs. +# + mov SCBPTR,QINFIFO + mov SCBARRAY+1 call initialize + clr SG_NOLOAD + clr RESELECT + +# As soon as we get a successful selection, the target should go +# into the message out phase since we have ATN asserted. Prepare +# the message to send, locking out the device driver. If the device +# driver hasn't beaten us with an ABORT or RESET message, then tack +# on a SDTR negotation if required. +# +# Messages are stored in scratch RAM starting with a flag byte (high bit +# set means active message), one length byte, and then the message itself. +# + mov SCBARRAY+1 call disconnect # disconnect ok? + + and SINDEX,0x7,SCBARRAY+1 # lun + or SINDEX,A # return value from disconnect + or SINDEX,0x80 call mk_mesg # IDENTIFY message + + mov A,SINDEX + cmp MSG_START+0,A jne !message # did driver beat us? + mvi MSG_START+1 call mk_sdtr # build SDTR message if needed + +!message: + +# Enable selection phase as an initiator, and do automatic ATN +# after the selection. +# + mvi SCSISEQ,0x48 # ENSELO|ENAUTOATNO + +# Wait for successful arbitration. The AIC-7770 documentation says +# that SELINGO indicates successful arbitration, and that it should +# be used to look for SELDO. However, if the sequencer is paused at +# just the right time - a parallel fsck(8) on two drives did it for +# me - then SELINGO can flip back to false before we've seen it. This +# makes the sequencer sit in the arbitration loop forever. This is +# Not Good. +# +# Therefore, I've added a check in the arbitration loop for SELDO +# too. This could arguably be made a critical section by disabling +# pauses, but I don't want to make a potentially infinite loop a CS. +# I suppose you could fold it into the select loop, too, but since +# I've been hunting this bug for four days it's kinda like a trophy. +# +arbitrate: + test SSTAT0,0x40 jnz *select # SELDO + test SSTAT0,0x10 jz arbitrate # SELINGO + +# Wait for a successful selection. If the hardware selection +# timer goes off, then the driver gets the interrupt, so we don't +# need to worry about it. +# +select: + test SSTAT0,0x40 jz select # SELDO + jmp *select + +# Reselection is being initiated by a target - we've seen the BSY +# line driven active, and we didn't do it! Enable the reselection +# hardware, and wait for it to finish. Make a note that we've been +# reselected, but haven't seen an IDENTIFY message from the target +# yet. +# +reselect: + mvi SCSISEQ,0x10 # ENRSELI + +reselect1: + test SSTAT0,0x20 jz reselect1 # SELDI + mov SELID call initialize + + mvi RESELECT,0x80 # reselected, no IDENTIFY + +# After the [re]selection, make sure that the [re]selection enable +# bit is off. This chip is flaky enough without extra things +# turned on. Also clear the BUSFREE bit in SSTAT1 since we'll be +# using it shortly. +# +*select: + clr SCSISEQ + mvi CLRSINT1,0x8 # CLRBUSFREE + +# Main loop for information transfer phases. If BSY is false, then +# we have a bus free condition, expected or not. Otherwise, wait +# for the target to assert REQ before checking MSG, C/D and I/O +# for the bus phase. +# +# We can't simply look at the values of SCSISIGI here (if we want +# to do synchronous data transfer), because the target won't assert +# REQ if it's already sent us some data that we haven't acknowledged +# yet. +# +ITloop: + test SSTAT1,0x8 jnz p_busfree # BUSFREE + test SSTAT1,0x1 jz ITloop # REQINIT + + and A,0xe0,SCSISIGI # CDI|IOI|MSGI + + cmp ALLZEROS,A je p_dataout + cmp A,0x40 je p_datain + cmp A,0x80 je p_command + cmp A,0xc0 je p_status + cmp A,0xa0 je p_mesgout + cmp A,0xe0 je p_mesgin + + mvi INTSTAT,SIGNAL_0 # unknown - signal driver + +p_dataout: + mvi 0 call scsisig # !CDO|!IOO|!MSGO + call assert + call sg_load + + mvi A,3 + mvi DINDEX,HCNT + mvi SCBARRAY+23 call bcopy + + mvi A,3 + mvi DINDEX,STCNT + mvi SCBARRAY+23 call bcopy + + mvi A,4 + mvi DINDEX,HADDR + mvi SCBARRAY+19 call bcopy + + mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| + # DIRECTION|FIFORESET + +# After a DMA finishes, save the final transfer pointer and count +# back into the SCB, in case a device disconnects in the middle of +# a transfer. Use SHADDR and STCNT instead of HADDR and HCNT, since +# it's a reflection of how many bytes were transferred on the SCSI +# (as opposed to the host) bus. +# + mvi A,3 + mvi DINDEX,SCBARRAY+23 + mvi STCNT call bcopy + + mvi A,4 + mvi DINDEX,SCBARRAY+19 + mvi SHADDR call bcopy + + call sg_advance + mov SCBARRAY+18,SG_COUNT # residual S/G count + + jmp ITloop + +p_datain: + mvi 0x40 call scsisig # !CDO|IOO|!MSGO + call assert + call sg_load + + mvi A,3 + mvi DINDEX,HCNT + mvi SCBARRAY+23 call bcopy + + mvi A,3 + mvi DINDEX,STCNT + mvi SCBARRAY+23 call bcopy + + mvi A,4 + mvi DINDEX,HADDR + mvi SCBARRAY+19 call bcopy + + mvi 0x39 call dma # SCSIEN|SDMAEN|HDMAEN| + # !DIRECTION|FIFORESET + mvi A,3 + mvi DINDEX,SCBARRAY+23 + mvi STCNT call bcopy + + mvi A,4 + mvi DINDEX,SCBARRAY+19 + mvi SHADDR call bcopy + + call sg_advance + mov SCBARRAY+18,SG_COUNT # residual S/G count + + jmp ITloop + +# Command phase. Set up the DMA registers and let 'er rip - the +# two bytes after the SCB SCSI_cmd_length are zeroed by the driver, +# so we can copy those three bytes directly into HCNT. +# +p_command: + mvi 0x80 call scsisig # CDO|!IOO|!MSGO + call assert + + mvi A,3 + mvi DINDEX,HCNT + mvi SCBARRAY+11 call bcopy + + mvi A,3 + mvi DINDEX,STCNT + mvi SCBARRAY+11 call bcopy + + mvi A,4 + mvi DINDEX,HADDR + mvi SCBARRAY+7 call bcopy + + mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN| + # DIRECTION|FIFORESET + jmp ITloop + +# Status phase. Wait for the data byte to appear, then read it +# and store it into the SCB. +# +p_status: + mvi 0xc0 call scsisig # CDO|IOO|!MSGO + + mvi SCBARRAY+14 call inb + jmp ITloop + +# Message out phase. If there is no active message, but the target +# took us into this phase anyway, build a no-op message and send it. +# +p_mesgout: + mvi 0xa0 call scsisig # CDO|!IOO|MSGO + mvi 0x8 call mk_mesg # build NOP message + +# Set up automatic PIO transfer from MSG_START. Bit 3 in +# SXFRCTL0 (SPIOEN) is already on. +# + mvi SINDEX,MSG_START+0 + mov DINDEX,MSG_LEN + clr A + +# When target asks for a byte, drop ATN if it's the last one in +# the message. Otherwise, keep going until the message is exhausted. +# (We can't use outb for this since it wants the input in SINDEX.) +# +# Keep an eye out for a phase change, in case the target issues +# a MESSAGE REJECT. +# +p_mesgout2: + test SSTAT0,0x2 jz p_mesgout2 # SPIORDY + test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS + + cmp DINDEX,1 jne p_mesgout3 # last byte? + mvi CLRSINT1,0x40 # CLRATNO - drop ATN + +# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically +# send ACKs in automatic PIO or DMA mode unless you make sure that the +# "expected" bus phase in SCSISIGO matches the actual bus phase. This +# behaviour is completely undocumented and caused me several days of +# grief. +# +# After plugging in different drives to test with and using a longer +# SCSI cable, I found that I/O in Automatic PIO mode ceased to function, +# especially when transferring >1 byte. It seems to be much more stable +# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is +# polled for transfer completion - for both output _and_ input. The +# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL +# is accessed (like the documentation says it does), and that on a longer +# cable run, the sequencer code was fast enough to loop back and see +# an SPIORDY that hadn't dropped yet. +# +p_mesgout3: + call one_stcnt + mov SCSIDATL,SINDIR + +p_mesgout4: + test SSTAT0,0x4 jz p_mesgout4 # SDONE + dec DINDEX + inc A + cmp MSG_LEN,A jne p_mesgout2 + +# If the next bus phase after ATN drops is a message out, it means +# that the target is requesting that the last message(s) be resent. +# +p_mesgout5: + test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE + test SSTAT1,0x1 jz p_mesgout5 # REQINIT + + and A,0xe0,SCSISIGI # CDI|IOI|MSGI + cmp A,0xa0 jne p_mesgout6 + mvi 0x10 call scsisig # ATNO - re-assert ATN + + jmp ITloop + +p_mesgout6: + mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS + clr MSG_FLAGS # no active msg + jmp ITloop + +# Message in phase. Bytes are read using Automatic PIO mode, but not +# using inb. This alleviates a race condition, namely that if ATN had +# to be asserted under Automatic PIO mode, it had to beat the SCSI +# circuitry sending an ACK to the target. This showed up under heavy +# loads and really confused things, since ABORT commands wouldn't be +# seen by the drive after an IDENTIFY message in until it had changed +# to a data I/O phase. +# +p_mesgin: + mvi 0xe0 call scsisig # CDO|IOO|MSGO + mvi A call inb_first # read the 1st message byte + mvi REJBYTE,A # save it for the driver + + cmp ALLZEROS,A jne p_mesgin1 + +# We got a "command complete" message, so put the SCB pointer +# into the Queue Out, and trigger a completion interrupt. +# + mov QOUTFIFO,SCBPTR + mvi INTSTAT,0x2 # CMDCMPLT + jmp p_mesgin_done + +# Is it an extended message? We only support the synchronous data +# transfer request message, which will probably be in response to +# an SDTR message out from us. If it's not an SDTR, reject it - +# apparently this can be done after any message in byte, according +# to the SCSI-2 spec. +# +# XXX - we should really reject this if we didn't initiate the SDTR +# negotiation; this may cause problems with unusual devices. +# +p_mesgin1: + cmp A,1 jne p_mesgin2 # extended message code? + + mvi A call inb_next + cmp A,3 jne p_mesginN # extended mesg length = 3 + mvi A call inb_next + cmp A,1 jne p_mesginN # SDTR code + + mvi ARG_1 call inb_next # xfer period + mvi ARG_2 call inb_next # REQ/ACK offset + mvi INTSTAT,SIGNAL_4 # call driver to convert + + call ndx_sdtr # index sync config for target + mov DINDEX,SINDEX + mov DINDIR,RETURN_1 # save returned value + + not A # turn off "need sdtr" flag + and NEEDSDTR,A + +# Even though the SCSI-2 specification says that a device responding +# to our SDTR message should honor our parameters for transmitting +# to us, it doesn't seem to work too well in real life. In particular, +# a lot of CD-ROM and tape units don't function: try using the SDTR +# parameters the device sent us for both transmitting and receiving. +# + mov SCSIRATE,RETURN_1 + jmp p_mesgin_done + +# Is it a disconnect message? Set a flag in the SCB to remind us +# and await the bus going free. +# +p_mesgin2: + cmp A,4 jne p_mesgin3 # disconnect code? + + or SCBARRAY+0,0x4 # set "disconnected" bit + jmp p_mesgin_done + +# Save data pointers message? Copy working values into the SCB, +# usually in preparation for a disconnect. +# +p_mesgin3: + cmp A,2 jne p_mesgin4 # save data pointers code? + + call sg_ram2scb + jmp p_mesgin_done + +# Restore pointers message? Data pointers are recopied from the +# SCB anyway at the start of any DMA operation, so the only thing +# to copy is the scatter-gather values. +# +p_mesgin4: + cmp A,3 jne p_mesgin5 # restore pointers code? + + call sg_scb2ram + jmp p_mesgin_done + +# Identify message? For a reconnecting target, this tells us the lun +# that the reconnection is for - find the correct SCB and switch to it, +# clearing the "disconnected" bit so we don't "find" it by accident later. +# +p_mesgin5: + test A,0x80 jz p_mesgin6 # identify message? + + test A,0x78 jnz p_mesginN # !DiscPriv|!LUNTAR|!Reserved + + mov A call findSCB # switch to correct SCB + +# If a active message is present after calling findSCB, then either it +# or the driver is trying to abort the command. Either way, something +# untoward has happened and we should just leave it alone. +# + test MSG_FLAGS,0x80 jnz p_mesgin_done + + xor SCBARRAY+0,0x4 # clear disconnect bit in SCB + mvi RESELECT,0xc0 # make note of IDENTIFY + + call sg_scb2ram # implied restore pointers + # required on reselect + jmp p_mesgin_done + +# Message reject? If we have an outstanding SDTR negotiation, assume +# that it's a response from the target selecting asynchronous transfer, +# otherwise just ignore it since we have no clue what it pertains to. +# +# XXX - I don't have a device that responds this way. Does this code +# actually work? +# +p_mesgin6: + cmp A,7 jne p_mesgin7 # message reject code? + + and FUNCTION1,0x70,SCSIID # outstanding SDTR message? + mov A,FUNCTION1 + test NEEDSDTR,A jz p_mesgin_done # no - ignore rejection + + call ndx_sdtr # note use of asynch xfer + mov DINDEX,SINDEX + clr DINDIR + + not A # turn off "active sdtr" flag + and NEEDSDTR,A + + clr SCSIRATE # select asynch xfer + jmp p_mesgin_done + +# [ ADD MORE MESSAGE HANDLING HERE ] +# +p_mesgin7: + +# We have no idea what this message in is, and there's no way +# to pass it up to the kernel, so we issue a message reject and +# hope for the best. Since we're now using manual PIO mode to +# read in the message, there should no longer be a race condition +# present when we assert ATN. In any case, rejection should be a +# rare occurrence - signal the driver when it happens. +# +p_mesginN: + or SINDEX,0x10,SIGSTATE # turn on ATNO + call scsisig + mvi INTSTAT,SIGNAL_1 # let driver know + + mvi 0x7 call mk_mesg # MESSAGE REJECT message + +p_mesgin_done: + call inb_last # ack & turn auto PIO back on + jmp ITloop + +# Bus free phase. It might be useful to interrupt the device +# driver if we aren't expecting this. For now, make sure that +# ATN isn't being asserted and look for a new command. +# +p_busfree: + mvi CLRSINT1,0x40 # CLRATNO + clr SIGSTATE + jmp start + +# Bcopy: number of bytes to transfer should be in A, DINDEX should +# contain the destination address, and SINDEX should contain the +# source address. All input parameters are trashed on return. +# +bcopy: + mov DINDIR,SINDIR + dec A + cmp ALLZEROS,A jne bcopy + ret + +# Locking the driver out, build a one-byte message passed in SINDEX +# if there is no active message already. SINDEX is returned intact. +# +mk_mesg: + mvi SEQCTL,0x40 # PAUSEDIS + test MSG_FLAGS,0x80 jnz mk_mesg1 # active message? + + mvi MSG_FLAGS,0x80 # if not, there is now + mvi MSG_LEN,1 # length = 1 + mov MSG_START+0,SINDEX # 1-byte message + +mk_mesg1: + clr SEQCTL # !PAUSEDIS + ret + +# Input byte in Automatic PIO mode. The address to store the byte +# in should be in SINDEX. DINDEX will be used by this routine. +# +inb: + test SSTAT0,0x2 jz inb # SPIORDY + mov DINDEX,SINDEX + call one_stcnt # xfer one byte + mov DINDIR,SCSIDATL +inb1: + test SSTAT0,0x4 jz inb1 # SDONE - wait to "finish" + ret + +# Carefully read data in Automatic PIO mode. I first tried this using +# Manual PIO mode, but it gave me continual underrun errors, probably +# indicating that I did something wrong, but I feel more secure leaving +# Automatic PIO on all the time. +# +# According to Adaptec's documentation, an ACK is not sent on input from +# the target until SCSIDATL is read from. So we wait until SCSIDATL is +# latched (the usual way), then read the data byte directly off the bus +# using SCSIBUSL. When we have pulled the ATN line, or we just want to +# acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI +# spec guarantees that the target will hold the data byte on the bus until +# we send our ACK. +# +# The assumption here is that these are called in a particular sequence, +# and that REQ is already set when inb_first is called. inb_{first,next} +# use the same calling convention as inb. +# +inb_first: + mov DINDEX,SINDEX + mov DINDIR,SCSIBUSL ret # read byte directly from bus + +inb_next: + mov DINDEX,SINDEX # save SINDEX + + call one_stcnt # xfer one byte + mov NONE,SCSIDATL # dummy read from latch to ACK +inb_next1: + test SSTAT0,0x4 jz inb_next1 # SDONE +inb_next2: + test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte + mov DINDIR,SCSIBUSL ret # read byte directly from bus + +inb_last: + call one_stcnt # ACK with dummy read + mov NONE,SCSIDATL +inb_last1: + test SSTAT0,0x4 jz inb_last1 # wait for completion + ret + +# Output byte in Automatic PIO mode. The byte to output should be +# in SINDEX. If DROPATN's high bit is set, then ATN will be dropped +# before the byte is output. +# +outb: + test SSTAT0,0x2 jz outb # SPIORDY + call one_stcnt # xfer one byte + + test DROPATN,0x80 jz outb1 + mvi CLRSINT1,0x40 # CLRATNO + clr DROPATN +outb1: + mov SCSIDATL,SINDEX +outb2: + test SSTAT0,0x4 jz outb2 # SDONE + ret + +# Write the value "1" into the STCNT registers, for Automatic PIO +# transfers. +# +one_stcnt: + clr STCNT+2 + clr STCNT+1 + mvi STCNT+0,1 ret + +# DMA data transfer. HADDR and HCNT must be loaded first, and +# SINDEX should contain the value to load DFCNTRL with - 0x3d for +# host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared +# during initialization. +# +dma: + mov DFCNTRL,SINDEX +dma1: +dma2: + test SSTAT0,0x1 jnz dma3 # DMADONE + test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun + +# We will be "done" DMAing when the transfer count goes to zero, or +# the target changes the phase (in light of this, it makes sense that +# the DMA circuitry doesn't ACK when PHASEMIS is active). If we are +# doing a SCSI->Host transfer, flush the data FIFO. +# +dma3: + test SINDEX,0x4 jnz dma5 # DIRECTION + and SINDEX,0xfe # mask out FIFORESET + or DFCNTRL,0x2,SINDEX # FIFOFLUSH +dma4: + test DFCNTRL,0x2 jnz dma4 # FIFOFLUSHACK + +# Now shut the DMA enables off, and copy STCNT (ie. the underrun +# amount, if any) to the SCB registers; SG_COUNT will get copied to +# the SCB's residual S/G count field after sg_advance is called. Make +# sure that the DMA enables are actually off first lest we get an ILLSADDR. +# +dma5: + clr DFCNTRL # disable DMA +dma6: + test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK + + mvi A,3 + mvi DINDEX,SCBARRAY+15 + mvi STCNT call bcopy + + ret + +# Common SCSI initialization for selection and reselection. Expects +# the target SCSI ID to be in the upper four bits of SINDEX, and A's +# contents are stomped on return. +# +initialize: + clr SBLKCTL # channel A, !wide + and SCSIID,0xf0,SINDEX # target ID + and A,0x7,SCSICONF # SCSI_ID_A[210] + or SCSIID,A + +# Esundry initialization. +# + clr DROPATN + clr SIGSTATE + +# Turn on Automatic PIO mode now, before we expect to see an REQ +# from the target. It shouldn't hurt anything to leave it on. Set +# CLRCHN here before the target has entered a data transfer mode - +# with synchronous SCSI, if you do it later, you blow away some +# data in the SCSI FIFO that the target has already sent to you. +# + mvi SXFRCTL0,0xa # SPIOEN|CLRCHN + +# Set SCSI bus parity checking and the selection timeout value, +# and enable the hardware selection timer. Set the SELTO interrupt +# to signal the driver. +# + and A,0x38,SCSICONF # PARITY_ENB_A|SEL_TIM_A[10] + or SXFRCTL1,0x4,A # ENSTIMER + mvi SIMODE1,0x84 # ENSELTIMO|ENSCSIPERR + +# Initialize scatter-gather pointers by setting up the working copy +# in scratch RAM. +# + call sg_scb2ram + +# Initialize SCSIRATE with the appropriate value for this target. +# + call ndx_sdtr + mov SCSIRATE,SINDIR + ret + +# Assert that if we've been reselected, then we've seen an IDENTIFY +# message. +# +assert: + test RESELECT,0x80 jz assert1 # reselected? + test RESELECT,0x40 jnz assert1 # seen IDENTIFY? + + mvi INTSTAT,SIGNAL_2 # no - cause a kernel panic + +assert1: + ret + +# Find out if disconnection is ok from the information the BIOS has left +# us. The target ID should be in the upper four bits of SINDEX; A will +# contain either 0x40 (disconnection ok) or 0x00 (diconnection not ok) +# on exit. +# +# This is the only place the target ID is limited to three bits, so we +# can use the FUNCTION1 register. +# +disconnect: + and FUNCTION1,0x70,SINDEX # strip off extra just in case + mov A,FUNCTION1 + test DISC_DSB_A,A jz disconnect1 # bit nonzero if DISabled + + clr A ret +disconnect1: + mvi A,0x40 ret + +# Locate the SCB matching the target ID in SELID and the lun in the lower +# three bits of SINDEX, and switch the SCB to it. Have the kernel print +# a warning message if it can't be found - this seems to happen occasionally +# under high loads. Also, if not found, generate an ABORT message to the +# target. +# +findSCB: + and A,0x7,SINDEX # lun in lower three bits + or A,A,SELID # can I do this? + and A,0xf7 # only channel A implemented + + clr SINDEX + +findSCB1: + mov SCBPTR,SINDEX # switch to new SCB + cmp SCBARRAY+1,A jne findSCB2 # target ID/channel/lun match? + test SCBARRAY+0,0x4 jz findSCB2 # should be disconnected + + ret + +findSCB2: + inc SINDEX + cmp SINDEX,MAXSCB jne findSCB1 + + mvi INTSTAT,SIGNAL_3 # not found - signal kernel + mvi 0x6 call mk_mesg # ABORT message + + or SINDEX,0x10,SIGSTATE # assert ATNO + call scsisig + ret + +# Make a working copy of the scatter-gather parameters in the SCB. +# +sg_scb2ram: + mov SG_COUNT,SCBARRAY+2 + + mvi A,4 + mvi DINDEX,SG_NEXT + mvi SCBARRAY+3 call bcopy + + mvi SG_NOLOAD,0x80 + test SCBARRAY+0,0x10 jnz sg_scb2ram1 # don't reload s/g? + clr SG_NOLOAD + +sg_scb2ram1: + ret + +# Copying RAM values back to SCB, for Save Data Pointers message. +# +sg_ram2scb: + mov SCBARRAY+2,SG_COUNT + + mvi A,4 + mvi DINDEX,SCBARRAY+3 + mvi SG_NEXT call bcopy + + and SCBARRAY+0,0xef,SCBARRAY+0 + test SG_NOLOAD,0x80 jz sg_ram2scb1 # reload s/g? + or SCBARRAY+0,0x10 + +sg_ram2scb1: + ret + +# Load a struct scatter if needed and set up the data address and +# length. If the working value of the SG count is nonzero, then +# we need to load a new set of values. +# +# This, like the above DMA, assumes a little-endian host data storage. +# +sg_load: + test SG_COUNT,0xff jz sg_load3 # SG being used? + test SG_NOLOAD,0x80 jnz sg_load3 # don't reload s/g? + + clr HCNT+2 + clr HCNT+1 + mvi HCNT+0,SG_SIZEOF + + mvi A,4 + mvi DINDEX,HADDR + mvi SG_NEXT call bcopy + + mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET + +# Wait for DMA from host memory to data FIFO to complete, then disable +# DMA and wait for it to acknowledge that it's off. +# +sg_load1: + test DFSTATUS,0x8 jz sg_load1 # HDONE + + clr DFCNTRL # disable DMA +sg_load2: + test DFCNTRL,0x8 jnz sg_load2 # HDMAENACK + +# Copy data from FIFO into SCB data pointer and data count. This assumes +# that the struct scatterlist has this structure (this and sizeof(struct +# scatterlist) == 12 are asserted in aha274x.c): +# +# struct scatterlist { +# char *address; /* four bytes, little-endian order */ +# ... /* four bytes, ignored */ +# unsigned short length; /* two bytes, little-endian order */ +# } +# + mov SCBARRAY+19,DFDAT # new data address + mov SCBARRAY+20,DFDAT + mov SCBARRAY+21,DFDAT + mov SCBARRAY+22,DFDAT + + mov NONE,DFDAT # throw away four bytes + mov NONE,DFDAT + mov NONE,DFDAT + mov NONE,DFDAT + + mov SCBARRAY+23,DFDAT + mov SCBARRAY+24,DFDAT + clr SCBARRAY+25 + +sg_load3: + ret + +# Advance the scatter-gather pointers only IF NEEDED. If SG is enabled, +# and the SCSI transfer count is zero (note that this should be called +# right after a DMA finishes), then move the working copies of the SG +# pointer/length along. If the SCSI transfer count is not zero, then +# presumably the target is disconnecting - do not reload the SG values +# next time. +# +sg_advance: + test SG_COUNT,0xff jz sg_advance2 # s/g enabled? + + test STCNT+0,0xff jnz sg_advance1 # SCSI transfer count nonzero? + test STCNT+1,0xff jnz sg_advance1 + test STCNT+2,0xff jnz sg_advance1 + + clr SG_NOLOAD # reload s/g next time + dec SG_COUNT # one less segment to go + + clr A # add sizeof(struct scatter) + add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0 + adc SG_NEXT+1,A,SG_NEXT+1 + adc SG_NEXT+2,A,SG_NEXT+2 + adc SG_NEXT+3,A,SG_NEXT+3 + + ret + +sg_advance1: + mvi SG_NOLOAD,0x80 # don't reload s/g next time +sg_advance2: + ret + +# Add the array base SYNCNEG to the target offset (the target address +# is in SCSIID), and return the result in SINDEX. The accumulator +# contains the 3->8 decoding of the target ID on return. +# +ndx_sdtr: + shr A,SCSIID,4 + and A,0x7 + add SINDEX,SYNCNEG,A + + and FUNCTION1,0x70,SCSIID # 3-bit target address decode + mov A,FUNCTION1 ret + +# If we need to negotiate transfer parameters, build the SDTR message +# starting at the address passed in SINDEX. DINDEX is modified on return. +# +mk_sdtr: + mov DINDEX,SINDEX # save SINDEX + + call ndx_sdtr + test NEEDSDTR,A jnz mk_sdtr1 # do we need negotiation? + ret + +mk_sdtr1: + mvi DINDIR,1 # extended message + mvi DINDIR,3 # extended message length = 3 + mvi DINDIR,1 # SDTR code + mvi DINDIR,25 # REQ/ACK transfer period + mvi DINDIR,15 # REQ/ACK offset + + add MSG_LEN,-MSG_START+0,DINDEX # update message length + ret + +# Set SCSI bus control signal state. This also saves the last-written +# value into a location where the higher-level driver can read it - if +# it has to send an ABORT or RESET message, then it needs to know this +# so it can assert ATN without upsetting SCSISIGO. The new value is +# expected in SINDEX. Change the actual state last to avoid contention +# from the driver. +# +scsisig: + mov SIGSTATE,SINDEX + mov SCSISIGO,SINDEX ret diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/aha274x_seq.h linux/drivers/scsi/aha274x_seq.h --- linux-1.1.55+new_quota/drivers/scsi/aha274x_seq.h Wed Dec 31 18:00:00 1969 +++ linux/drivers/scsi/aha274x_seq.h Thu Oct 20 18:15:36 1994 @@ -0,0 +1,336 @@ +#define AHA274X_SEQ_VERSION "1.26" + 0x04, 0x03, 0x12, 0x1a, + 0x07, 0x9c, 0x00, 0x1e, + 0xff, 0x9b, 0x90, 0x02, + 0x00, 0xa1, 0xe1, 0x16, + 0xff, 0x6a, 0x54, 0x02, + 0xff, 0x6a, 0x34, 0x02, + 0x00, 0xa1, 0xf3, 0x16, + 0x07, 0xa1, 0x65, 0x02, + 0x00, 0x65, 0x65, 0x00, + 0x80, 0x65, 0xad, 0x16, + 0xff, 0x65, 0x64, 0x02, + 0x00, 0x37, 0x0d, 0x18, + 0x38, 0x6a, 0x42, 0x17, + 0x48, 0x6a, 0x00, 0x00, + 0x40, 0x0b, 0x16, 0x1a, + 0x10, 0x0b, 0x0e, 0x1e, + 0x40, 0x0b, 0x10, 0x1e, + 0x00, 0x65, 0x16, 0x10, + 0x10, 0x6a, 0x00, 0x00, + 0x20, 0x0b, 0x13, 0x1e, + 0x00, 0x19, 0xe1, 0x16, + 0x80, 0x6a, 0x34, 0x00, + 0xff, 0x6a, 0x00, 0x02, + 0x08, 0x6a, 0x0c, 0x00, + 0x08, 0x0c, 0xa6, 0x1a, + 0x01, 0x0c, 0x18, 0x1e, + 0xe0, 0x03, 0x64, 0x02, + 0x00, 0x6a, 0x22, 0x1c, + 0x40, 0x64, 0x32, 0x1c, + 0x80, 0x64, 0x42, 0x1c, + 0xc0, 0x64, 0x4f, 0x1c, + 0xa0, 0x64, 0x52, 0x1c, + 0xe0, 0x64, 0x68, 0x1c, + 0x01, 0x6a, 0x91, 0x00, + 0x00, 0x6a, 0x4d, 0x17, + 0x00, 0x65, 0xef, 0x16, + 0x00, 0x65, 0x17, 0x17, + 0x03, 0x6a, 0x64, 0x00, + 0x8c, 0x6a, 0x66, 0x00, + 0xb7, 0x6a, 0xa9, 0x16, + 0x03, 0x6a, 0x64, 0x00, + 0x08, 0x6a, 0x66, 0x00, + 0xb7, 0x6a, 0xa9, 0x16, + 0x04, 0x6a, 0x64, 0x00, + 0x88, 0x6a, 0x66, 0x00, + 0xb3, 0x6a, 0xa9, 0x16, + 0x3d, 0x6a, 0xd1, 0x16, + 0x00, 0x65, 0x2f, 0x17, + 0xff, 0x55, 0xb2, 0x02, + 0x00, 0x65, 0x18, 0x10, + 0x40, 0x6a, 0x4d, 0x17, + 0x00, 0x65, 0xef, 0x16, + 0x00, 0x65, 0x17, 0x17, + 0x03, 0x6a, 0x64, 0x00, + 0x8c, 0x6a, 0x66, 0x00, + 0xb7, 0x6a, 0xa9, 0x16, + 0x03, 0x6a, 0x64, 0x00, + 0x08, 0x6a, 0x66, 0x00, + 0xb7, 0x6a, 0xa9, 0x16, + 0x04, 0x6a, 0x64, 0x00, + 0x88, 0x6a, 0x66, 0x00, + 0xb3, 0x6a, 0xa9, 0x16, + 0x39, 0x6a, 0xd1, 0x16, + 0x00, 0x65, 0x2f, 0x17, + 0xff, 0x55, 0xb2, 0x02, + 0x00, 0x65, 0x18, 0x10, + 0x80, 0x6a, 0x4d, 0x17, + 0x00, 0x65, 0xef, 0x16, + 0x03, 0x6a, 0x64, 0x00, + 0x8c, 0x6a, 0x66, 0x00, + 0xab, 0x6a, 0xa9, 0x16, + 0x03, 0x6a, 0x64, 0x00, + 0x08, 0x6a, 0x66, 0x00, + 0xab, 0x6a, 0xa9, 0x16, + 0x04, 0x6a, 0x64, 0x00, + 0x88, 0x6a, 0x66, 0x00, + 0xa7, 0x6a, 0xa9, 0x16, + 0x3d, 0x6a, 0xd1, 0x16, + 0x00, 0x65, 0x18, 0x10, + 0xc0, 0x6a, 0x4d, 0x17, + 0xae, 0x6a, 0xb4, 0x16, + 0x00, 0x65, 0x18, 0x10, + 0xa0, 0x6a, 0x4d, 0x17, + 0x08, 0x6a, 0xad, 0x16, + 0x37, 0x6a, 0x65, 0x00, + 0xff, 0x36, 0x66, 0x02, + 0xff, 0x6a, 0x64, 0x02, + 0x02, 0x0b, 0x57, 0x1e, + 0x01, 0x66, 0x5a, 0x18, + 0x40, 0x6a, 0x0c, 0x00, + 0x00, 0x65, 0xce, 0x16, + 0xff, 0x6c, 0x06, 0x02, + 0x04, 0x0b, 0x5c, 0x1e, + 0xff, 0x66, 0x66, 0x06, + 0x01, 0x64, 0x64, 0x06, + 0x00, 0x36, 0x57, 0x18, + 0x08, 0x0c, 0x66, 0x1a, + 0x01, 0x0c, 0x60, 0x1e, + 0xe0, 0x03, 0x64, 0x02, + 0xa0, 0x64, 0x66, 0x18, + 0x10, 0x6a, 0x4d, 0x17, + 0x00, 0x65, 0x18, 0x10, + 0xff, 0x6a, 0x35, 0x02, + 0x00, 0x65, 0x18, 0x10, + 0xe0, 0x6a, 0x4d, 0x17, + 0x64, 0x6a, 0xba, 0x16, + 0x00, 0x6a, 0x31, 0x00, + 0x00, 0x6a, 0x6f, 0x18, + 0xff, 0x90, 0x9d, 0x02, + 0x02, 0x6a, 0x91, 0x00, + 0x00, 0x65, 0xa4, 0x10, + 0x01, 0x64, 0x7e, 0x18, + 0x64, 0x6a, 0xbc, 0x16, + 0x03, 0x64, 0xa0, 0x18, + 0x64, 0x6a, 0xbc, 0x16, + 0x01, 0x64, 0xa0, 0x18, + 0x4c, 0x6a, 0xbc, 0x16, + 0x4d, 0x6a, 0xbc, 0x16, + 0x41, 0x6a, 0x91, 0x00, + 0x00, 0x65, 0x3d, 0x17, + 0xff, 0x65, 0x66, 0x02, + 0xff, 0x4c, 0x6d, 0x02, + 0xff, 0x64, 0x64, 0x04, + 0x00, 0x4f, 0x4f, 0x02, + 0xff, 0x4c, 0x04, 0x02, + 0x00, 0x65, 0xa4, 0x10, + 0x04, 0x64, 0x81, 0x18, + 0x04, 0xa0, 0xa0, 0x00, + 0x00, 0x65, 0xa4, 0x10, + 0x02, 0x64, 0x8a, 0x18, + 0x04, 0x6a, 0x64, 0x00, + 0xb3, 0x6a, 0x66, 0x00, + 0x50, 0x6a, 0xa9, 0x16, + 0x03, 0x6a, 0x64, 0x00, + 0xb7, 0x6a, 0x66, 0x00, + 0xaf, 0x6a, 0xa9, 0x16, + 0x00, 0x65, 0x0f, 0x17, + 0x00, 0x65, 0xa4, 0x10, + 0x03, 0x64, 0x8d, 0x18, + 0x00, 0x65, 0x07, 0x17, + 0x00, 0x65, 0xa4, 0x10, + 0x80, 0x64, 0x95, 0x1e, + 0x78, 0x64, 0xa0, 0x1a, + 0x00, 0x64, 0xf8, 0x16, + 0x80, 0x35, 0xa4, 0x1a, + 0x04, 0xa0, 0xa0, 0x04, + 0xc0, 0x6a, 0x34, 0x00, + 0x00, 0x65, 0x07, 0x17, + 0x00, 0x65, 0xa4, 0x10, + 0x07, 0x64, 0xa0, 0x18, + 0x70, 0x05, 0x6e, 0x02, + 0xff, 0x6e, 0x64, 0x02, + 0x00, 0x4f, 0xa4, 0x1e, + 0x00, 0x65, 0x3d, 0x17, + 0xff, 0x65, 0x66, 0x02, + 0xff, 0x6a, 0x6d, 0x02, + 0xff, 0x64, 0x64, 0x04, + 0x00, 0x4f, 0x4f, 0x02, + 0xff, 0x6a, 0x04, 0x02, + 0x00, 0x65, 0xa4, 0x10, + 0x10, 0x4e, 0x65, 0x00, + 0x00, 0x65, 0x4d, 0x17, + 0x11, 0x6a, 0x91, 0x00, + 0x07, 0x6a, 0xad, 0x16, + 0x00, 0x65, 0xc2, 0x16, + 0x00, 0x65, 0x18, 0x10, + 0x40, 0x6a, 0x0c, 0x00, + 0xff, 0x6a, 0x4e, 0x02, + 0x00, 0x65, 0x00, 0x10, + 0xff, 0x6c, 0x6d, 0x02, + 0xff, 0x64, 0x64, 0x06, + 0x00, 0x6a, 0xa9, 0x18, + 0xff, 0x6a, 0x6a, 0x03, + 0x40, 0x6a, 0x60, 0x00, + 0x80, 0x35, 0xb2, 0x1a, + 0x80, 0x6a, 0x35, 0x00, + 0x01, 0x6a, 0x36, 0x00, + 0xff, 0x65, 0x37, 0x02, + 0xff, 0x6a, 0x60, 0x02, + 0xff, 0x6a, 0x6a, 0x03, + 0x02, 0x0b, 0xb4, 0x1e, + 0xff, 0x65, 0x66, 0x02, + 0x00, 0x65, 0xce, 0x16, + 0xff, 0x06, 0x6d, 0x02, + 0x04, 0x0b, 0xb8, 0x1e, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0x65, 0x66, 0x02, + 0xff, 0x12, 0x6d, 0x03, + 0xff, 0x65, 0x66, 0x02, + 0x00, 0x65, 0xce, 0x16, + 0xff, 0x06, 0x6a, 0x02, + 0x04, 0x0b, 0xbf, 0x1e, + 0x02, 0x0b, 0xc0, 0x1e, + 0xff, 0x12, 0x6d, 0x03, + 0x00, 0x65, 0xce, 0x16, + 0xff, 0x06, 0x6a, 0x02, + 0x04, 0x0b, 0xc4, 0x1e, + 0xff, 0x6a, 0x6a, 0x03, + 0x02, 0x0b, 0xc6, 0x1e, + 0x00, 0x65, 0xce, 0x16, + 0x80, 0x30, 0xcb, 0x1e, + 0x40, 0x6a, 0x0c, 0x00, + 0xff, 0x6a, 0x30, 0x02, + 0xff, 0x65, 0x06, 0x02, + 0x04, 0x0b, 0xcc, 0x1e, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0x6a, 0x0a, 0x02, + 0xff, 0x6a, 0x09, 0x02, + 0x01, 0x6a, 0x08, 0x01, + 0xff, 0x65, 0x93, 0x02, + 0x01, 0x0b, 0xd4, 0x1a, + 0x10, 0x0c, 0xd2, 0x1e, + 0x04, 0x65, 0xd8, 0x1a, + 0xfe, 0x65, 0x65, 0x02, + 0x02, 0x65, 0x93, 0x00, + 0x02, 0x93, 0xd7, 0x1a, + 0xff, 0x6a, 0x93, 0x02, + 0x38, 0x93, 0xd9, 0x1a, + 0x04, 0x6a, 0x64, 0x00, + 0x50, 0x6a, 0x66, 0x00, + 0x14, 0x6a, 0xa9, 0x16, + 0x03, 0x6a, 0x64, 0x00, + 0xaf, 0x6a, 0x66, 0x00, + 0x08, 0x6a, 0xa9, 0x16, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0x6a, 0x1f, 0x02, + 0xf0, 0x65, 0x05, 0x02, + 0x07, 0x5a, 0x64, 0x02, + 0x00, 0x05, 0x05, 0x00, + 0xff, 0x6a, 0x30, 0x02, + 0xff, 0x6a, 0x4e, 0x02, + 0x0a, 0x6a, 0x01, 0x00, + 0x38, 0x5a, 0x64, 0x02, + 0x04, 0x64, 0x02, 0x00, + 0x84, 0x6a, 0x11, 0x00, + 0x00, 0x65, 0x07, 0x17, + 0x00, 0x65, 0x3d, 0x17, + 0xff, 0x6c, 0x04, 0x02, + 0xff, 0x6a, 0x6a, 0x03, + 0x80, 0x34, 0xf2, 0x1e, + 0x40, 0x34, 0xf2, 0x1a, + 0x21, 0x6a, 0x91, 0x00, + 0xff, 0x6a, 0x6a, 0x03, + 0x70, 0x65, 0x6e, 0x02, + 0xff, 0x6e, 0x64, 0x02, + 0x00, 0x32, 0xf7, 0x1e, + 0xff, 0x6a, 0x64, 0x03, + 0x40, 0x6a, 0x64, 0x01, + 0x07, 0x65, 0x64, 0x02, + 0x00, 0x19, 0x64, 0x00, + 0xf7, 0x64, 0x64, 0x02, + 0xff, 0x6a, 0x65, 0x02, + 0xff, 0x65, 0x90, 0x02, + 0x00, 0xa1, 0x00, 0x19, + 0x04, 0xa0, 0x00, 0x1f, + 0xff, 0x6a, 0x6a, 0x03, + 0x01, 0x65, 0x65, 0x06, + 0x04, 0x65, 0xfc, 0x18, + 0x31, 0x6a, 0x91, 0x00, + 0x06, 0x6a, 0xad, 0x16, + 0x10, 0x4e, 0x65, 0x00, + 0x00, 0x65, 0x4d, 0x17, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0xa2, 0x55, 0x02, + 0x04, 0x6a, 0x64, 0x00, + 0x56, 0x6a, 0x66, 0x00, + 0xa3, 0x6a, 0xa9, 0x16, + 0x80, 0x6a, 0x54, 0x00, + 0x10, 0xa0, 0x0e, 0x1b, + 0xff, 0x6a, 0x54, 0x02, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0x55, 0xa2, 0x02, + 0x04, 0x6a, 0x64, 0x00, + 0xa3, 0x6a, 0x66, 0x00, + 0x56, 0x6a, 0xa9, 0x16, + 0xef, 0xa0, 0xa0, 0x02, + 0x80, 0x54, 0x16, 0x1f, + 0x10, 0xa0, 0xa0, 0x00, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0x55, 0x2e, 0x1f, + 0x80, 0x54, 0x2e, 0x1b, + 0xff, 0x6a, 0x8e, 0x02, + 0xff, 0x6a, 0x8d, 0x02, + 0x0c, 0x6a, 0x8c, 0x00, + 0x04, 0x6a, 0x64, 0x00, + 0x88, 0x6a, 0x66, 0x00, + 0x56, 0x6a, 0xa9, 0x16, + 0x0d, 0x6a, 0x93, 0x00, + 0x08, 0x94, 0x20, 0x1f, + 0xff, 0x6a, 0x93, 0x02, + 0x08, 0x93, 0x22, 0x1b, + 0xff, 0x99, 0xb3, 0x02, + 0xff, 0x99, 0xb4, 0x02, + 0xff, 0x99, 0xb5, 0x02, + 0xff, 0x99, 0xb6, 0x02, + 0xff, 0x99, 0x6a, 0x02, + 0xff, 0x99, 0x6a, 0x02, + 0xff, 0x99, 0x6a, 0x02, + 0xff, 0x99, 0x6a, 0x02, + 0xff, 0x99, 0xb7, 0x02, + 0xff, 0x99, 0xb8, 0x02, + 0xff, 0x6a, 0xb9, 0x02, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0x55, 0x3c, 0x1f, + 0xff, 0x08, 0x3b, 0x1b, + 0xff, 0x09, 0x3b, 0x1b, + 0xff, 0x0a, 0x3b, 0x1b, + 0xff, 0x6a, 0x54, 0x02, + 0xff, 0x55, 0x55, 0x06, + 0xff, 0x6a, 0x64, 0x02, + 0x0c, 0x56, 0x56, 0x06, + 0x00, 0x57, 0x57, 0x08, + 0x00, 0x58, 0x58, 0x08, + 0x00, 0x59, 0x59, 0x08, + 0xff, 0x6a, 0x6a, 0x03, + 0x80, 0x6a, 0x54, 0x00, + 0xff, 0x6a, 0x6a, 0x03, + 0x4c, 0x05, 0x64, 0x0a, + 0x07, 0x64, 0x64, 0x02, + 0x20, 0x64, 0x65, 0x06, + 0x70, 0x05, 0x6e, 0x02, + 0xff, 0x6e, 0x64, 0x03, + 0xff, 0x65, 0x66, 0x02, + 0x00, 0x65, 0x3d, 0x17, + 0x00, 0x4f, 0x46, 0x1b, + 0xff, 0x6a, 0x6a, 0x03, + 0x01, 0x6a, 0x6d, 0x00, + 0x03, 0x6a, 0x6d, 0x00, + 0x01, 0x6a, 0x6d, 0x00, + 0x19, 0x6a, 0x6d, 0x00, + 0x0f, 0x6a, 0x6d, 0x00, + 0xc9, 0x66, 0x36, 0x06, + 0xff, 0x6a, 0x6a, 0x03, + 0xff, 0x65, 0x4e, 0x02, + 0xff, 0x65, 0x03, 0x03, diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/eata.c linux/drivers/scsi/eata.c --- linux-1.1.55+new_quota/drivers/scsi/eata.c Wed Dec 31 18:00:00 1969 +++ linux/drivers/scsi/eata.c Thu Oct 20 18:15:36 1994 @@ -0,0 +1,621 @@ +/************************************************************ + * * + * Linux EATA SCSI driver * + * * + * based on the EATA proposal rev 2.0b, DPT DOS driver kit * + * some proprietary source (DPT Unix driver), several * + * other linux scsi drivers and kernel documentation * + * * + * The driver currently: * + * -has ISA detection routines * + * -has EISA detection routines (new) * + * -doesn't support more than one HA and one channel * + * -gets sometimes timeouts when accessing more than * + * one device "simultaniously" * + * * + * ECS_emulation_sense() doesn't work and I don't know why * + * Until this is working, the drive geometry has to be * + * hardcoded into the driver :-( (Any ideas ?) * + * * + * (c)1993,94 Michael Neuffer * + * Michael_Neuffer@wi2.maus.de (mails <16KB!) * + * neuffer@goofy.zdv.uni-mainz.de * + * * + * This program is free software; you can redistribute it * + * and/or modify it under the terms of the GNU General * + * Public License as published by the Free Software * + * Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the * + * implied warranty of MERCHANTABILITY or FITNESS FOR A * + * PARTICULAR PURPOSE. See the GNU General Public License * + * for more details. * + * * + * You should have received a copy of the GNU General * + * Public License along with this kernel; if not, write to * + * the Free Software Foundation, Inc., 675 Mass Ave, * + * Cambridge, MA 02139, USA. * + * * + * * + * I have to thank DPT for their excellent support. I took * + * me almost a year and a stopover at their HQ on my first * + * trip to the USA to get it, but now they are very helpful * + * and try to give me all the infos and support I need.... * + * * + ************************************************************ + * last change: 02.10.94 * + ************************************************************/ + +/* Look in eata.h for configuration information */ + +#include +#include +#include +#include +#include +#include +#include "eata.h" +#include "scsi.h" +#include "sd.h" + +#define MAXISA 4 +#define MAXEISA 16 + +static unsigned int ISAbases[]={0x1F0,0x170,0x330,0x230}; +static unsigned int EISAbases[]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static struct eata_register *EATA_base; + +static unsigned char dma_channel; +static struct geom_emul geometry; /* Drive 1 & 2 geometry */ + +static int eata_int_happened, eata_status, expecting_ints; + +struct eata_cmd_queue outstanding[64]; /* We should allocate this dynamicly */ + +int cont_errs[]= +{ + DID_OK, /* No error */ + DID_NO_CONNECT, /* Selection Timeout */ + DID_TIME_OUT, /* Command Timeout */ + DID_RESET, /* SCSI Bus Reset Received */ + DID_ERROR, /* Initial Controller Power up */ + DID_ERROR, /* Unexpected Bus Phase */ + DID_ERROR, /* Unexpected Bus Free */ + DID_PARITY, /* Bus Parity Error */ + DID_ERROR, /* SCSI Hung */ + DID_ERROR, /* Unexpected Message Rejected */ + DID_ERROR, /* SCSI Bus Reset STUCK */ + DID_ERROR, /* Auto Request-Sense Failed */ + DID_PARITY, /* Controller Ram Parity Error */ +}; + +inline void end_of_command(void) +{ + Scsi_Cmnd *cmd; + struct eata_ccb *cp; + struct eata_sp *sp; + int i, result; + /* Find out which CP caused the interrupt */ + for(i=0; i<64; i++) + { + if(outstanding[i].used && outstanding[i].sp.cont_stat & 0x80) + break; + } + if(i==64) + { + printk("Got interrupt but no finished packet found!\n"); + return; + } + DBG(DBG_AHMON,outb(i, 0x80)); /* Ahmon's special */ + cmd=outstanding[i].cmd; + cp=&outstanding[i].cp; + sp=&outstanding[i].sp; + outstanding[i].used=0; + if(cmd->use_sg) scsi_free(cmd->host_scribble, 512); + result=sp->scsi_stat; + + DBG(DBG_INTR,printk("end_of_command: queuenr: %d message bytes: %x,%x,%x\n", + i,sp->msg[0],sp->msg[1],sp->msg[2])); + + result|=COMMAND_COMPLETE<<8; + result|=cont_errs[sp->cont_stat&0x7f] << 16; /* Fixed the error array */ + cmd->result=result; + + cmd->scsi_done(cmd); +} + +void eata_int_handler(int irq) +{ + eata_status=inb(((int)EATA_base)+HA_RSTATUS); /* Acknowledge interrupt */ + DBG(DBG_INTR, printk(" Interrupt %d received, expected: %d,Status: %x\n",irq, + expecting_ints,eata_status)); + if(expecting_ints==EXP_NOTHING) + { + printk("eata_int_handler: Unexpected interrupt!\n"); + return; + } + if(expecting_ints==EXP_NORMAL) + { + eata_int_happened=1; + return; + } + end_of_command(); +} + +inline void eata_set_dma_address(int add) +{ + outb(add & 0x000000ff, ((int)EATA_base)+HA_WDMAADDR); + outb((add & 0x0000ff00) >> 8, ((int)EATA_base)+HA_WDMAADDR+1); + outb((add & 0x00ff0000) >> 16, ((int)EATA_base)+HA_WDMAADDR+2); + outb((add & 0xff000000) >> 24, ((int)EATA_base)+HA_WDMAADDR+3); +} + +const char *eata_info(void) +{ + static char *information="EATA SCSI Controller Driver\n"; + return information; +} + +inline int eata_reverse(int arg) +{ + int result; + result=(arg & 0xff000000) >> 24; + result|=((arg & 0x00ff0000) >> 8); + result|=((arg & 0x0000ff00) << 8); + result|=((arg & 0x000000ff) << 24); + return result; +} + +int eata_queue(Scsi_Cmnd *cmd, void *(done)(struct scsi_cmnd *)) +{ + int i=64,status; + struct eata_sp *sp; + struct eata_ccb *cp; + struct eata_sg_list *sglist; + struct scatterlist *sl; + + /* Search for an open command slot. If i==64, then + there must be none free. If this is the case, keep + trying because an interrupt will eventually happen + that will free up a slot. + + This is assuming that interrupts are enabled when this + routine is called. Is that true? + */ + + i=64; + while(i==64) for(i=0; i<64 && outstanding[i].used; i++); + cli(); + outstanding[i].used=1; /* Claim it */ + cmd->scsi_done=done; + sp=&outstanding[i].sp; + cp=&outstanding[i].cp; + sp->cont_stat=0; /* Clear this out because the interrupt handler + checks it. Note that I clear it BEFORE I set the + pointer in outstanding[] */ + outstanding[i].cmd=cmd; + sti(); + if(cmd->cmnd[0]==WRITE_6 || cmd->cmnd[0]==WRITE_10) + cp->cp_option.Byte=HA_DATA_OUT; /* Output mode */ + else + cp->cp_option.Byte=HA_DATA_IN; /* Input mode */ + if(cmd->use_sg) { + cp->cp_option.Byte|=HA_SC_GA; /* SG mode */ + sglist=(struct eata_sg_list *)scsi_malloc(512); /* Max needed 64 */ + if(!sglist) + { + panic("eata_queue: scsi_malloc failed. No memory for SG list\n"); + } + cmd->host_scribble=(unsigned char *)sglist; + sl=(struct scatterlist *)cmd->request_buffer; + for(i=0; iuse_sg; i++, sglist++, sl++) + { + sglist->data=(void *)eata_reverse((int )sl->address); + sglist->len=eata_reverse((int )sl->length); + } + sglist=(struct eata_sg_list *)cmd->host_scribble; + } + cp->reqlen=38; + cp->cp_option2.Byte=0x00; + cp->cp_id=cmd->target; + cp->cp_msg0=cmd->lun+HA_IDENTIFY_MSG+HA_G_DISCO_RECO; /*Those bits should always be set*/ + cp->cp_msg1=cp->cp_msg2=cp->cp_msg3=0; + memset(cp->cp_cdb, 0, 12); + memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd)); + if(cmd->use_sg) + { + *((int *)cp->cp_dataDMA)=eata_reverse((int )sglist); + *((int *)cp->cp_datalen)=eata_reverse(cmd->use_sg*8); + } + else + { + *((int *)cp->cp_datalen)=eata_reverse(cmd->request_bufflen); + *((int *)cp->cp_dataDMA)=eata_reverse((int )cmd->request_buffer); + } + *((int *)cp->cp_statDMA)=eata_reverse((int )sp); + *((int *)cp->cp_viraddr)=i; + DBG(DBG_QUEUE,printk("EATA_QUEUE pos: %d, com: %x, SG: %d\n",i,cmd->cmnd[0], + cmd->use_sg)); + status=inb(((int)EATA_base)+HA_RSTATUS); + while(status & HA_SBSY) status=inb(((int)EATA_base)+HA_RSTATUS); + expecting_ints=EXP_RETURN; + eata_set_dma_address((int )cp); + outb(EATA_CMD_DMA_SEND_CP, ((int)EATA_base)+HA_WCOMMAND); /* Let 'er rip */ + return(0); +} + +static volatile int internal_done_flag = 0; +static volatile int internal_done_errcode = 0; + +static void internal_done(Scsi_Cmnd * SCpnt) +{ + internal_done_errcode = SCpnt->result; + ++internal_done_flag; +} + +int eata_command(Scsi_Cmnd *SCpnt) +{ + + DBG(DBG_FUNC,printk("eata_command: calling eata_queue\n")); + + eata_queue(SCpnt, internal_done); + + while (!internal_done_flag); + internal_done_flag = 0; + return internal_done_errcode; +} + +int eata_abort(Scsi_Cmnd *cmd) +{ + int i; + + /* Just act like we did something */ + DBG(DBG_ABNORM,printk("eata_abort()\n")); + DELAY(200); + /* + eata_reset(cmd); + */ + + for(i=0; i<64; i++) + if((outstanding[i].cmd==cmd)&& outstanding[i].used) + return(SCSI_ABORT_BUSY); + return(SCSI_ABORT_NOT_RUNNING); /*SNOOZE*/ +} + +int eata_reset(Scsi_Cmnd *cmd) +{ + int i; + long time; + DBG(DBG_ABNORM,printk("eata_reset()\n")); + DELAY(200); + outb(EATA_CMD_RESET, ((int)EATA_base)+HA_WCOMMAND); +/* if(cmd) + cmd->flags|=NEEDS_JUMPSTART; +*/ + time=jiffies+75; + while(time>jiffies); + for(i=0; i<64; i++) outstanding[i].used=0; + return(SCSI_RESET_WAKEUP); +} + +int eata_biosparam(Scsi_Disk * disk, int dev, int geo[]) +{ + int id; + int size = disk->capacity; + + id=MINOR(dev); + if (id) id>>=4; + + DBG(DBG_PROBE,printk("\nSize: %d, Device: %x, id: %x\n\n",size,dev,id)); + + if(geometry.drv[0].id==id){ + geo[0]=geometry.drv[0].heads; + geo[1]=geometry.drv[0].sectors; + geo[2]=geometry.drv[0].cylinder; + }else if(geometry.drv[1].id==id){ + geo[0]=geometry.drv[1].heads; + geo[1]=geometry.drv[1].sectors; + geo[2]=geometry.drv[1].cylinder; + }else{ + if(size<0x200000){ + geo[0]=64; + geo[1]=32; + }else if(size<0x400000){ + geo[0]=65; + geo[1]=63; + }else if(size<0x800000){ + geo[0]=128; + geo[1]=63; + }else{ + geo[0]=255; + geo[1]=63; + } + geo[2]=size/(geo[0]*geo[1]); + } + return(0); +} + +int get_conf_DMA(struct eata_register *base,struct get_conf *buf) +{ + long i; + + inb(((int)base)+HA_RSTATUS); /* Clear any pending conditions */ + + i=jiffies; + expecting_ints=EXP_NORMAL; + eata_int_happened=0; + + outb((unchar)((long)(buf)>>24),(int)base+HA_WDMAADDR+3);/*get config of */ + outb((unchar)((long)(buf)>>16),(int)base+HA_WDMAADDR+2);/*poss. controllers*/ + outb((unchar)((long)(buf)>>8), (int)base+HA_WDMAADDR+1); + outb((unchar)((long)(buf)), (int)base+HA_WDMAADDR); + outb(EATA_CMD_DMA_READ_CONFIG, (int)base+HA_WCOMMAND); /* set opcode */ + + while(!eata_int_happened && jiffiesgco_sig[0], + (char)buf->gco_sig[1],(char)buf->gco_sig[2],(char)buf->gco_sig[3])); + if((buf->gco_sig[0]=='E')&&(buf->gco_sig[1]== 'A') + &&(buf->gco_sig[2]== 'T')&&(buf->gco_sig[3]== 'A')) { + DBG(DBG_PROBE,printk("EATA Controller found at %x EATA Level: %x\n", + (unsigned int) base,(unsigned int)(buf->gco_version>>4))); + return(1); + } + return(0); +} + +long find_EISA(int flag, struct get_conf *buf) +{ +/* + Is there anyone using more than 2 controllers + at the same time ? ie. more than one secondary ? + I don't think so. +*/ + struct eata_register *base; + int i; + unsigned char pal1,pal2,pal3,*p; + + for(i=0; ibit2.SECOND)&&(buf->bit2.IRQ)&&(flag==-1)){ + /* We just found a primary EISA */ + ISAbases[0]=0; /* so there is no prim. ISA cont. */ + EISAbases[i]=0; /* and we don't need to look for */ + return((long)base); /* a sec. EISA controller here */ + } + else if((buf->bit2.SECOND)&&(buf->bit2.IRQ)&&(!flag)){ + /* We've found a EISA secondary */ + ISAbases[1]=0; /* so there is no sec. ISA cont. */ + EISAbases[i]=0; + return((long)base); + } + else EISAbases[i]=0; + } + } + } + return(0l); /* Nothing found :-( */ +} + +long find_ISA(struct eata_register *base, struct get_conf *buf) +{ + int i,l; + long ret; + + ret=0; + if(!base){ + for (l=1;l<=MAXISA;l++){ + if(ISAbases[l]){ + i=get_conf_DMA(base,buf); + if (!i) ISAbases[l]=0; + else{ + ret=(int)base; + break; + } + } + } + }else{ + i=get_conf_DMA(base, buf); + if((i==1)&&((((long)base==0x1F0l)&&!buf->bit2.SECOND) + ||(((long)base!=0x1F0l)&&buf->bit2.SECOND))) + ret=(long)base; + else ret=0; + } + return(ret); +} + +int ECS_emulation_sense(long base,int drive) +{ + /* + I'm not sure what's going wrong here, because now I have + documentation of the ECS command set but still I don't + get it to work, I don't get a reponse from the + controller (Ready + Seek complete...) :-( + */ + int x,ret; + struct emul_sense *p; + char buff[256]; + /* long time;*/ + + ret=0; + + while(inb_p(base+HA_RSTATUS)&HA_SBSY); /* Waiting to get not busy */ + + expecting_ints=EXP_NORMAL; + eata_int_happened=0; + + x=drive<<5; + outb(base+1,0); + outb(base+2,0); + outb(base+3,255); + outb(base+4,0); + outb(base+5,0); + outb(base+HA_WCOMMAND-1,x); + outb(base+HA_WCOMMAND,ECS_EMULATE_SENSE); + + DBG(DBG_WINTR,printk("ECS: Waiting for interrupt\n")); + +/* while(!eata_int_happened); */ /* wait for interrupt */ + + expecting_ints=EXP_NOTHING; + while(!((x=inb_p(base+HA_RSTATUS))&HA_SDRQ)){ + DBG(DBG_WINTR,printk("Controller status %x\n",x)); + DELAY(50); + } + insw(base+HA_RDATA,buff,256); /* Get the Data and */ + while(inb(base+HA_RSTATUS)&HA_SDRQ) /* throw away the rest */ + inb(base+HA_RDATA); + p=(struct emul_sense *)buff; + geometry.drv[drive].heads=p->Heads; + geometry.drv[drive].sectors=p->Sectors; + geometry.drv[drive].cylinder=(p->Cyls[0]<<8)+p->Cyls[1]; + if(p->lunmap[drive]&0x80) + geometry.drv[drive].trans=0; + if(drive==0){ + if(p->lunmap[2]&0x80) ret=0; + else ret=1; + geometry.drv[drive].id=p->lunmap[0]; + geometry.drv[drive].lun=p->lunmap[1]; + } else if(drive==1){ + ret=1; + geometry.drv[drive].id=p->lunmap[2]; + geometry.drv[drive].lun=p->lunmap[3]; + } + return(ret); +} + +void get_geo_trans(long base) +{ + outb(0x70,0x12); + if(inb(0x71)){ + geometry.drv[0].trans=1; + if(ECS_emulation_sense(base,0)){ + outb(0x70,0x12); + if(inb(0x71)&0xF){ + geometry.drv[1].trans=1; + ECS_emulation_sense(base,1); + } + } + } +} + +/* int eata_detect(int index) */ +int eata_detect(Scsi_Host_Template * tpnt) +{ + struct get_conf gc; + int i; + int err; + long base; + long time; + unsigned int irqlist[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + geometry.drv[0].trans=geometry.drv[1].trans=0; + base=0; + err=0; + + + for(i=0;i<16;i++) /* Here we grab every available IRQ */ + if(!request_irq(i,eata_int_handler,SA_INTERRUPT,"EATA"))/* until we know which one we */ + irqlist[i]=1; /* will need */ + + + base=find_EISA(-1,&gc); /* Check for primary EISA HA */ + if(base) ISAbases[0]=0; /* If found, there can't be an ISA prim. here */ + else base=find_ISA((void *)0x1F0,&gc); /* Check for primary ISA HA */ + if (!base) { /* no primary found */ + base=find_EISA(0,&gc); /* Check for secondary EISA */ + if (!base) base=find_ISA((void *)0,&gc); + if (!base) { /* No controller fould :-( */ + for(i=0;i<16;i++) /* release all interrupts */ + if(irqlist[i]){ + free_irq(i); + irqlist[i]=0; + } + return(0); + } + } +/* else get_geo_trans(base); */ /* set emulation parameters */ + + else if(IDE_EMULATION){ /* Unfortionally,we have to */ + geometry.drv[0].heads=HEADS0; /* hardcode the drive geometry */ + geometry.drv[0].sectors=SECTORS0; /* into the driver for now */ + geometry.drv[0].cylinder=CYLINDER0; + geometry.drv[0].id=ID0; + geometry.drv[1].heads=HEADS1; + geometry.drv[1].sectors=SECTORS1; + geometry.drv[1].cylinder=CYLINDER1; + geometry.drv[1].id=ID1; + } else { + geometry.drv[0].id=-1; + geometry.drv[1].id=-1; + } + + dma_channel=(8-gc.bit2.DRQX)&7; /* Which DMA channel ? */ + if (!gc.bit1.DRQ_valid) { + DBG(DBG_PROBE,printk("EISA EATA controller found.\n")); + err=0; + }else if (request_dma(dma_channel,"DPT_EATA")) { + printk("Unable to allocate DMA channel %d for EATA controller.\n", + dma_channel); + err=1; + } + + for(i=0;i<16;i++) /* Now we can release all */ + if(irqlist[i] && (i!=gc.bit2.IRQ)){ /* unused interrupts again */ + free_irq(i); + irqlist[i]=0; + } + if (err) { + free_irq((unsigned int)gc.bit2.IRQ); + return(0); + } + if(!irqlist[(unsigned int) gc.bit2.IRQ]){ + printk("eata_detect: Couldn't alloc. IRQ%d!\n",(unsigned int)gc.bit2.IRQ); + return(0); + } + + tpnt->can_queue=((int)(gc.gco_queuesiz[0])<<8) + ((int)(gc.gco_queuesiz[1])); + DBG(DBG_PROBE, printk("Can queue %d commands \n",tpnt->can_queue)); + if(tpnt->can_queue>64) /* This is only temporarily in here */ + tpnt->can_queue=64; /* We'll allocate the needed space */ + + tpnt->this_id=(int)(xscsi2int(gc.gco_HAaddress)); + tpnt->sg_tablesize=((short unsigned int)(gc.gco_SGsiz[0])<<8) + +((short unsigned int)(gc.gco_SGsiz[1])); + tpnt->cmd_per_lun=1; + + if (!gc.bit1.DRQ_valid) tpnt->unchecked_isa_dma=0; /* This is an EISA contr.*/ + else tpnt->unchecked_isa_dma=1; /* Here we have only ISA */ + + snarf_region(base, 9); + scsi_register(tpnt,0); + printk("EATA - DMA driver version: %d.%d%s\n",VER_MAJOR,VER_MINOR,VER_SUB); + printk("EATA compliant controller detected. EATA Level %x, SCSI ID is %d \n", + (unsigned int)(gc.gco_version>>4),tpnt->this_id); + printk("Using IRQ %d, DMA channel %d, at %lx\n",(unsigned int)gc.bit2.IRQ, + dma_channel,base); + time=jiffies; + while(jiffies 0 problems, but that is not host specific. Now 6/6/94. + */ +/* Changes for 1.1.28 kernel made 7/19/94, code not affected. (WDE) + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "../block/blk.h" +#include "scsi.h" +#include "hosts.h" + +#include "in2000.h" + +/*#define FAST_FIFO_IO*/ + +/*#define DEBUG*/ +#ifdef DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + +/* These functions are based on include/asm/io.h */ +#ifndef inw +inline static unsigned short inw( unsigned short port ) +{ + unsigned short _v; + + __asm__ volatile ("inw %1,%0" + :"=a" (_v):"d" ((unsigned short) port)); + return _v; +} +#endif + +#ifndef outw +inline static void outw( unsigned short value, unsigned short port ) +{ + __asm__ volatile ("outw %0,%1" + : /* no outputs */ + :"a" ((unsigned short) value), + "d" ((unsigned short) port)); +} +#endif + +/* These functions are lifted from drivers/block/hd.c */ + +#define port_read(port,buf,nr) \ +__asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di") + +#define port_write(port,buf,nr) \ +__asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si") + +static unsigned int base; +static unsigned int ficmsk; +static unsigned char irq_level; +static int in2000_datalen; +static unsigned int in2000_nsegment; +static unsigned int in2000_current_segment; +static unsigned short *in2000_dataptr; +static char in2000_datawrite; +struct scatterlist * in2000_scatter; +static Scsi_Cmnd *in2000_SCptr = 0; + +void (*in2000_done)(Scsi_Cmnd *); + +int in2000_test_port(int index) +{ + static const int *bios_tab[] = { + (int *) 0xc8000, (int *) 0xd0000, (int *) 0xd8000 }; + int i; + char tmp; + + tmp = inb(INFLED); + /* First, see if the DIP switch values are valid */ + /* The test of B7 may fail on some early boards, mine works. */ + if (((~tmp & 0x3) != index ) || (tmp & 0x80) || !(tmp & 0x4) ) + return 0; + printk("IN-2000 probe got dip setting of %02X\n", tmp); + tmp = inb(INVERS); +/* Add some extra sanity checks here */ + for(i=0; i < 3; i++) + if(*(bios_tab[i]+0x04) == 0x41564f4e) { + printk("IN-2000 probe found hdw. vers. %02x, BIOS at %06x\n", + tmp, (unsigned int)bios_tab[i]); + return 1; + } + printk("in2000 BIOS not found.\n"); + return 0; +} + + +/* + * retreive the current transaction counter from the WD + */ + +unsigned in2000_txcnt(void) +{ + unsigned total=0; + + if(inb(INSTAT) & 0x20) return 0xffffff; /* not readable now */ + outb(TXCNTH,INSTAT); /* then autoincrement */ + total = (inb(INDATA) & 0xff) << 16; + outb(TXCNTM,INSTAT); + total += (inb(INDATA) & 0xff) << 8; + outb(TXCNTL,INSTAT); + total += (inb(INDATA) & 0xff); + return total; +} + +/* + * Note: the FIFO is screwy, and has a counter granularity of 16 bytes, so + * we have to reconcile the FIFO counter, the transaction byte count from the + * WD chip, and of course, our desired transaction size. It may look strange, + * and could probably use improvement, but it works, for now. + */ + +void in2000_fifo_out(void) /* uses FIFOCNTR */ +{ + unsigned count, infcnt, txcnt; + + infcnt = inb(INFCNT)& 0xfe; /* FIFO counter */ + do { + txcnt = in2000_txcnt(); +/*DEB(printk("FIw:%d %02x %d\n", in2000_datalen, infcnt, txcnt));*/ + count = (infcnt << 3) - 32; /* dont fill completely */ + if ( count > in2000_datalen ) + count = in2000_datalen; /* limit to actual data on hand */ + count >>= 1; /* Words, not bytes */ +#ifdef FAST_FIFO_IO + if ( count ) { + port_write(INFIFO, in2000_dataptr, count); + in2000_datalen -= (count<<1); + } +#else + while ( count-- ) + { + outw(*in2000_dataptr++, INFIFO); + in2000_datalen -= 2; + } +#endif + } while((in2000_datalen > 0) && ((infcnt = (inb(INFCNT)) & 0xfe) >= 0x20) ); + /* If scatter-gather, go on to next segment */ + if( !in2000_datalen && in2000_current_segment < in2000_nsegment) + { + in2000_scatter++; + in2000_current_segment++; + in2000_datalen = in2000_scatter->length; + in2000_dataptr = (unsigned short*)in2000_scatter->address; + } + if ( in2000_datalen <= 0 ) + { + ficmsk = 0; + count = 32; /* Always says to use this much flush */ + while ( count-- ) + outw(0, INFIFO); + outb(2, ININTR); /* Mask FIFO Interrupts when done */ + } +} + +void in2000_fifo_in(void) /* uses FIFOCNTR */ +{ + unsigned fic, count, count2; + + count = inb(INFCNT) & 0xe1; + do{ + count2 = count; + count = (fic = inb(INFCNT)) & 0xe1; + } while ( count != count2 ); +DEB(printk("FIir:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr)); + do { + count2 = in2000_txcnt(); /* bytes yet to come over SCSI bus */ +DEB(printk("FIr:%d %02x %08x %08x\n", in2000_datalen,fic,count2,(unsigned int)in2000_dataptr)); + if(count2 > 65536) count2 = 0; + if(fic > 128) count = 1024; + else if(fic > 64) count = 512; + else if (fic > 32) count = 256; + else if ( count2 < in2000_datalen ) /* if drive has < what we want */ + count = in2000_datalen - count2; /* FIFO has the rest */ + if ( count > in2000_datalen ) /* count2 is lesser of FIFO & rqst */ + count2 = in2000_datalen >> 1; /* converted to word count */ + else + count2 = count >> 1; + count >>= 1; /* also to words */ + count -= count2; /* extra left over in FIFO */ +#ifdef FAST_FIFO_IO + if ( count2 ) { + port_read(INFIFO, in2000_dataptr, count2); + in2000_datalen -= (count2<<1); + } +#else + while ( count2-- ) + { + *in2000_dataptr++ = inw(INFIFO); + in2000_datalen -=2; + } +#endif + } while((in2000_datalen > 0) && (fic = inb(INFCNT)) ); +DEB(printk("FIer:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr)); +/* while ( count-- ) + inw(INFIFO);*/ /* Throw away some extra stuff */ + if( !in2000_datalen && in2000_current_segment < in2000_nsegment) + { + in2000_scatter++; + in2000_current_segment++; + in2000_datalen = in2000_scatter->length; + in2000_dataptr = (unsigned short*)in2000_scatter->address; + } + if ( ! in2000_datalen ){ + outb(2, ININTR); /* Mask FIFO Interrupts when done */ + ficmsk = 0;} +} + +const char *in2000_info(void) +{ + static char buffer[] = ""; + return buffer; +} + +void in2000_intr_handle(int foo) +{ + int result=0; + unsigned int count,auxstatus,scsistatus,cmdphase,scsibyte; + int action=0; + Scsi_Cmnd *SCptr; + + DEB(printk("INT:%d %02x %08x\n", in2000_datalen, inb(INFCNT),(unsigned int)in2000_dataptr)); + + if (( (ficmsk & (count = inb(INFCNT))) == 0xfe ) || + ( (inb(INSTAT) & 0x8c) == 0x80)) + { /* FIFO interrupt or WD interrupt */ + auxstatus = inb(INSTAT); /* need to save now */ + outb(SCSIST,INSTAT); + scsistatus = inb(INDATA); /* This clears the WD intrpt bit */ + outb(TARGETU,INSTAT); /* then autoincrement */ + scsibyte = inb(INDATA); /* Get the scsi status byte */ + outb(CMDPHAS,INSTAT); + cmdphase = inb(INDATA); + DEB(printk("(int2000:%02x %02x %02x %02x %02x)\n",count,auxstatus, + scsistatus,cmdphase,scsibyte)); + + /* Why do we assume that we need to send more data here??? ERY */ + if ( in2000_datalen && in2000_dataptr ) /* data xfer pending */ + { + if ( in2000_datawrite ) + in2000_fifo_out(); + else + in2000_fifo_in(); + } else ficmsk = 0; + if ( (auxstatus & 0x8c) == 0x80 ) + { /* There is a WD Chip interrupt & register read good */ + outb(2,ININTR); /* Disable fifo interrupts */ + ficmsk = 0; + result = DID_OK << 16; + /* 16=Select & transfer complete, 85=got disconnect */ + if ((scsistatus != 0x16) && (scsistatus != 0x85) + && (scsistatus != 0x42)){ +/* printk("(WDi2000:%02x %02x %02x %02x %02x)\n",count,auxstatus, + scsistatus,cmdphase,scsibyte);*/ +/* printk("QDAT:%d %08x %02x\n", + in2000_datalen,(unsigned int)in2000_dataptr,ficmsk);*/ + ; + } + switch ( scsistatus & 0xf0 ) + { + case 0x00: /* Card Reset Completed */ + action = 3; + break; + case 0x10: /* Successful Command Completion */ + if ( scsistatus & 0x8 ) + action = 1; + break; + case 0x20: /* Command Paused or Aborted */ + if ( (scsistatus & 0x8) ) + action = 1; + else if ( (scsistatus & 7) < 2 ) + action = 2; + else + result = DID_ABORT << 16; + break; + case 0x40: /* Terminated early */ + if ( scsistatus & 0x8 ) + action = 1; + else if ( (scsistatus & 7) > 2 ) + action = 2; + else + result = DID_TIME_OUT << 16; + break; + case 0x80: /* Service Required from SCSI bus */ + if ( scsistatus & 0x8 ) + action = 1; + else + action = 2; + break; + } /* end switch(scsistatus) */ + outb(0,INFLED); + switch ( action ) + { + case 0x02: /* Issue an abort */ + outb(COMMAND,INSTAT); + outb(1,INDATA); /* ABORT COMMAND */ + result = DID_ABORT << 16; + case 0x00: /* Basically all done */ + if ( ! in2000_SCptr ) + return; + in2000_SCptr->result = result | scsibyte; + SCptr = in2000_SCptr; + in2000_SCptr = 0; + if ( in2000_done ) + (*in2000_done)(SCptr); + break; + case 0x01: /* We need to reissue a command */ + outb(CMDPHAS,INSTAT); + switch ( scsistatus & 7 ) + { + case 0: /* Data out phase */ + case 1: /* Data in phase */ + case 4: /* Unspec info out phase */ + case 5: /* Unspec info in phase */ + case 6: /* Message in phase */ + case 7: /* Message in phase */ + outb(0x41,INDATA); /* rdy to disconn */ + break; + case 2: /* command phase */ + outb(0x30,INDATA); /* rdy to send cmd bytes */ + break; + case 3: /* status phase */ + outb(0x45,INDATA); /* To go to status phase,*/ + outb(TXCNTH,INSTAT); /* elim. data, autoinc */ + outb(0,INDATA); + outb(0,INDATA); + outb(0,INDATA); + in2000_datalen = 0; + in2000_dataptr = 0; + break; + } /* end switch(scsistatus) */ + outb(COMMAND,INSTAT); + outb(8,INDATA); /* RESTART THE COMMAND */ + break; + case 0x03: /* Finish up a Card Reset */ + outb(TIMEOUT,INSTAT); /* I got these values */ + /* by reverse Engineering */ + outb(IN2000_TMOUT,INDATA); /* the Always' bios. */ + outb(CONTROL,INSTAT); + outb(0,INDATA); + outb(SYNCTXR,INSTAT); + outb(0x40,INDATA); /* async, 4 cyc xfer per. */ + break; + } /* end switch(action) */ + } /* end if auxstatus for WD int */ + } /* end while intrpt active */ +} + +int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) +{ + unchar direction; + unchar *cmd = (unchar *) SCpnt->cmnd; + unchar target = SCpnt->target; + void *buff = SCpnt->request_buffer; + int bufflen = SCpnt->request_bufflen; + int timeout, size, loop; + int i; + + /* + * This SCSI command has no data phase, but unfortunately the mid-level + * SCSI drivers ask for 256 bytes of data xfer. Our card hangs if you + * do this, so we protect against it here. It would be nice if the mid- + * level could be changed, but who knows if that would break other host + * adapter drivers. + */ + if ( *cmd == TEST_UNIT_READY ) + bufflen = 0; + + /* + * What it looks like. Boy did I get tired of reading it's output. + */ + if (*cmd == READ_10 || *cmd == WRITE_10) { + i = xscsi2int((cmd+1)); + } else if (*cmd == READ_6 || *cmd == WRITE_6) { + i = scsi2int((cmd+1)); + } else { + i = -1; + } +#ifdef DEBUG + printk("in2000qcmd: pos %d len %d ", i, bufflen); + printk("scsi cmd:"); + for (i = 0; i < (COMMAND_SIZE(*cmd)); i++) printk("%02x ", cmd[i]); + printk("\n"); +#endif + direction = 1; /* assume for most commands */ + if (*cmd == WRITE_10 || *cmd == WRITE_6) + direction = 0; + size = COMMAND_SIZE(*cmd); /* CDB length */ + /* + * Setup our current pointers + * This is where you would allocate a control structure in a queue, + * If you were going to upgrade this to do multiple issue. + * Note that datalen and dataptr exist because we can change the + * values during the course of the operation, while managing the + * FIFO. + * Note the nasty little first clause. In theory, the mid-level + * drivers should never hand us more than one command at a time, + * but just in case someone gets cute in configuring the driver, + * we'll protect them, although not very politely. + */ + if ( in2000_SCptr ) + { + printk("in2000_queue_command waiting for free command block!\n"); + while ( in2000_SCptr ); + } + for ( timeout = jiffies + 5; timeout > jiffies; ) + { + if ( ! ( inb(INSTAT) & 0xb0 ) ) + { + timeout = 0; + break; + } + else + { + inb(INSTAT); + outb(SCSIST,INSTAT); + inb(INDATA); + outb(TARGETU,INSTAT); /* then autoinc */ + inb(INDATA); + inb(INDATA); + } + } + if ( timeout ) + { + printk("in2000_queue_command timeout!\n"); + SCpnt->result = DID_TIME_OUT << 16; + (*done)(SCpnt); + return 1; + } + /* Added for scatter-gather support */ + in2000_nsegment = SCpnt->use_sg; + in2000_current_segment = 0; + if(SCpnt->use_sg){ + in2000_scatter = (struct scatterlist *) buff; + in2000_datalen = in2000_scatter->length; + in2000_dataptr = (unsigned short*)in2000_scatter->address; + } else { + in2000_scatter = NULL; + in2000_datalen = bufflen; + in2000_dataptr = (unsigned short*) buff; + }; + in2000_done = done; + in2000_SCptr = SCpnt; + /* + * Write the CDB to the card, then the LUN, the length, and the target. + */ + outb(TOTSECT, INSTAT); /* start here then autoincrement */ + for ( loop=0; loop < size; loop++ ) + outb(cmd[loop],INDATA); + outb(TARGETU,INSTAT); + outb(SCpnt->lun & 7,INDATA); + SCpnt->host_scribble = NULL; + outb(TXCNTH,INSTAT); /* then autoincrement */ + outb(bufflen>>16,INDATA); + outb(bufflen>>8,INDATA); + outb(bufflen,INDATA); + outb(target&7,INDATA); + /* + * Set up the FIFO + */ + cli(); /* so FIFO init waits till WD set */ + outb(0,INFRST); + if ( direction == 1 ) + { + in2000_datawrite = 0; + outb(0,INFWRT); + } + else + { + in2000_datawrite = 1; + for ( loop=16; --loop; ) /* preload the outgoing fifo */ + { + outw(*in2000_dataptr++,INFIFO); + if(in2000_datalen > 0) in2000_datalen-=2; + } + } + ficmsk = 0xff; + /* + * Start it up + */ + outb(CONTROL,INSTAT); /* WD BUS Mode */ + outb(0x4C,INDATA); + if ( in2000_datalen ) /* if data xfer cmd */ + outb(0,ININTR); /* Enable FIFO intrpt some boards? */ + outb(COMMAND,INSTAT); + outb(0,INNLED); + outb(8,INDATA); /* Select w/ATN & Transfer */ + sti(); /* let the intrpt rip */ + return 0; +} + +static volatile int internal_done_flag = 0; +static volatile int internal_done_errcode = 0; + +static void internal_done(Scsi_Cmnd * SCpnt) +{ + internal_done_errcode = SCpnt->result; + ++internal_done_flag; +} + +int in2000_command(Scsi_Cmnd * SCpnt) +{ + in2000_queuecommand(SCpnt, internal_done); + + while (!internal_done_flag); + internal_done_flag = 0; + return internal_done_errcode; +} + +int in2000_detect(Scsi_Host_Template * tpnt) +{ +/* Order chosen to reduce conflicts with some multi-port serial boards */ + int base_tab[] = { 0x220,0x200,0x110,0x100 }; + int int_tab[] = { 15,14,11,10 }; + int loop, tmp; + + DEB(printk("in2000_detect: \n")); + + for ( loop=0; loop < 4; loop++ ) + { + base = base_tab[loop]; + if ( in2000_test_port(loop)) break; + } + if ( loop == 4 ) + return 0; + + /* Read the dip switch values again for miscellaneous checking and + informative messages */ + tmp = inb(INFLED); + + /* Bit 2 tells us if interrupts are disabled */ + if ( (tmp & 0x4) == 0 ) { + printk("The IN-2000 is not configured for interrupt operation\n"); + printk("Change the DIP switch settings to enable interrupt operation\n"); + } + + /* Bit 6 tells us about floppy controller */ + printk("IN-2000 probe found floppy controller on IN-2000 "); + if ( (tmp & 0x40) == 0) + printk("enabled\n"); + else + printk("disabled\n"); + + /* Bit 5 tells us about synch/asynch mode */ + printk("IN-2000 probe found IN-2000 in "); + if ( (tmp & 0x20) == 0) + printk("synchronous mode\n"); + else + printk("asynchronous mode\n"); + + irq_level = int_tab [ ((~inb(INFLED)>>3)&0x3) ]; + + printk("Configuring IN2000 at IO:%x, IRQ %d" +#ifdef FAST_FIFO_IO + " (using fast FIFO I/O code)" +#endif + "\n",base, irq_level); + + outb(2,ININTR); /* Shut off the FIFO first, so it won't ask for data.*/ + if (request_irq(irq_level, in2000_intr_handle, SA_INTERRUPT, "IN2000")) + { + printk("in2000_detect: Unable to allocate IRQ.\n"); + return 0; + } + outb(0,INFWRT); /* read mode so WD can intrpt */ + outb(SCSIST,INSTAT); + inb(INDATA); /* free status reg, clear WD intrpt */ + outb(OWNID,INSTAT); + outb(0x7,INDATA); /* we use addr 7 */ + outb(COMMAND,INSTAT); + outb(0,INDATA); /* do chip reset */ + return 1; +} + +int in2000_abort(Scsi_Cmnd * SCpnt, int i) +{ + DEB(printk("in2000_abort\n")); + /* + * Ask no stupid questions, just order the abort. + */ + outb(COMMAND,INSTAT); + outb(1,INDATA); /* Abort Command */ + return 0; +} + +static inline void delay( unsigned how_long ) +{ + unsigned long time = jiffies + how_long; + while (jiffies < time) ; +} + +int in2000_reset(Scsi_Cmnd * SCpnt) +{ + DEB(printk("in2000_reset called\n")); + /* + * Note: this is finished off by an incoming interrupt + */ + outb(0,INFWRT); /* read mode so WD can intrpt */ + outb(SCSIST,INSTAT); + inb(INDATA); + outb(OWNID,INSTAT); + outb(0x7,INDATA); /* ID=7,noadv, no parity, clk div=2 (8-10Mhz clk) */ + outb(COMMAND,INSTAT); + outb(0,INDATA); /* reset WD chip */ + delay(2); +#ifdef SCSI_RESET_PENDING + return SCSI_RESET_PENDING; +#else + if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART; + return 0; +#endif +} + +int in2000_biosparam(int size, int dev, int* iinfo) + { + DEB(printk("in2000_biosparam\n")); + iinfo[0] = 64; + iinfo[1] = 32; + iinfo[2] = size >> 11; + return 0; + } + + diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/scsi/in2000.h linux/drivers/scsi/in2000.h --- linux-1.1.55+new_quota/drivers/scsi/in2000.h Wed Dec 31 18:00:00 1969 +++ linux/drivers/scsi/in2000.h Thu Oct 20 18:15:37 1994 @@ -0,0 +1,124 @@ +#ifndef _IN2000_H + +/* $Id: in2000.h,v 1.1 1994/03/14 06:27:38 root Exp root $ + * + * Header file for the Always IN 2000 driver for Linux + * + */ + +#include + +/* The IN-2000 is based on a WD33C93 */ + +#define INSTAT (base + 0x0) /* R: Auxiliary Status; W: register select */ +#define INDATA (base + 0x1) /* R/W: Data port */ +#define INFIFO (base + 0x2) /* R/W FIFO, Word access only */ +#define INREST (base + 0x3) /* W: Reset everything */ +#define INFCNT (base + 0x4) /* R: FIFO byte count */ +#define INFRST (base + 0x5) /* W: Reset Fifo count and to write */ +#define INFWRT (base + 0x7) /* W: Set FIFO to read */ +#define INFLED (base + 0x8) /* W: Set LED; R: Dip Switch settings */ +#define INNLED (base + 0x9) /* W: reset LED */ +#define INVERS (base + 0xa) /* R: Read hw version, end-reset */ +#define ININTR (base + 0xc) /* W: Interrupt Mask Port */ +#define G2CNTRL_HRDY 0x20 /* Sets HOST ready */ + +/* WD33C93 defines */ +#define OWNID 0 +#define CONTROL 1 +#define TIMEOUT 2 +#define TOTSECT 3 +#define TOTHEAD 4 +#define TOTCYLH 5 +#define TOTCYLL 6 +#define LADRSHH 7 +#define LADRSHL 8 +#define LADRSLH 9 +#define LADRSLL 10 +#define SECTNUM 11 +#define HEADNUM 12 +#define CYLNUMH 13 +#define CYLNUML 14 +#define TARGETU 15 +#define CMDPHAS 16 +#define SYNCTXR 17 +#define TXCNTH 18 +#define TXCNTM 19 +#define TXCNTL 20 +#define DESTID 21 +#define SRCID 22 +#define SCSIST 23 +#define COMMAND 24 +#define WDDATA 25 +#define AUXSTAT 31 + +/* OWNID Register Bits */ +#define OWN_EAF 0x08 +#define OWN_EHP 0x10 +#define OWN_FS0 0x40 +#define OWN_FS1 0x80 +/* AUX Register Bits */ +#define AUX_DBR 0 +#define AUX_PE 1 +#define AUX_CIP 0x10 +#define AUX_BSY 0x20 +#define AUX_LCI 0x40 +#define AUX_INT 0x80 + +/* Select timeout const, 1 count = 8ms */ +#define IN2000_TMOUT 0x1f + +#if 0 +/* This is used with scatter-gather */ +struct in2000_chain { + ulong dataptr; /* Location of data */ + ulong datalen; /* Size of this part of chain */ +}; +#endif + +/* These belong in scsi.h also */ +#define any2scsi(up, p) \ +(up)[0] = (((unsigned long)(p)) >> 16); \ +(up)[1] = (((unsigned long)(p)) >> 8); \ +(up)[2] = ((unsigned long)(p)); + +#define scsi2int(up) ( ((((long)*(up))&0x1f) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) ) + +#define xany2scsi(up, p) \ +(up)[0] = ((long)(p)) >> 24; \ +(up)[1] = ((long)(p)) >> 16; \ +(up)[2] = ((long)(p)) >> 8; \ +(up)[3] = ((long)(p)); + +#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \ + + (((long)(up)[2]) << 8) + ((long)(up)[3]) ) + +#define MAX_CDB 12 +#define MAX_SENSE 14 +#define MAX_STATUS 32 + +int in2000_detect(Scsi_Host_Template *); +int in2000_command(Scsi_Cmnd *); +int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +int in2000_abort(Scsi_Cmnd *, int); +const char *in2000_info(void); +int in2000_reset(Scsi_Cmnd *); +int in2000_biosparam(int, int, int*); + + +#ifndef NULL + #define NULL 0 +#endif + +/* next may be "SG_NONE" or "SG_ALL" or nr. of (1k) blocks per R/W Cmd. */ +#define IN2000_SG SG_ALL +#define IN2000 {NULL, "Always IN2000", in2000_detect, NULL, \ + in2000_info, in2000_command, \ + in2000_queuecommand, \ + in2000_abort, \ + in2000_reset, \ + NULL, \ + in2000_biosparam, \ + 1, 7, IN2000_SG, 1, 0, 0} + +#endif diff -u --recursive --new-file linux-1.1.55+new_quota/drivers/sound/local.h linux/drivers/sound/local.h --- linux-1.1.55+new_quota/drivers/sound/local.h Wed Dec 31 18:00:00 1969 +++ linux/drivers/sound/local.h Thu Oct 20 18:15:37 1994 @@ -0,0 +1,9 @@ +/* Generated by configure. Don't edit!!!! */ + +#undef CONFIGURE_SOUNDCARD +#undef KERNEL_SOUNDCARD +#define SOUND_VERSION_STRING "2.90-2" +#define SOUND_CONFIG_DATE "Wed Oct 12 19:29:11 CDT 1994" +#define SOUND_CONFIG_BY "root" +#define SOUND_CONFIG_HOST "fuzzy" +#define SOUND_CONFIG_DOMAIN "" diff -u --recursive --new-file linux-1.1.55+new_quota/eata.lsm linux/eata.lsm --- linux-1.1.55+new_quota/eata.lsm Wed Dec 31 18:00:00 1969 +++ linux/eata.lsm Thu Oct 20 18:15:37 1994 @@ -0,0 +1,29 @@ +Begin2 +Title = EATA SCSI driver (DPT/NEC/AT&T) +Version = 0.3a +Desc1 = Lowlevel device driver for all EATA compliant ISA and EISA +Desc2 = SCSI controllers +Desc3 = Supported controllers are DPT PM2011, PM2012A, PM2012B +Desc4 = PM2021, PM2022, PM2122, PM2322 and some OEMs from NEC and AT&T +Author = Michael Neuffer +AuthorEmail = Michael_Neuffer@wi2.maus.de, neuffer@goofy.zdv.uni-mainz.de +Maintainer = Michael Neuffer +MaintEmail = Michael_Neuffer@wi2.maus.de, neuffer@goofy.zdv.uni-mainz.de +Site1 = tsx-11.mit.edu +Path1 = /pub/linux/ALPHA/scsi/ +File1 = dpt_eata3a.tgz +Site2 = sunsite.unc.edu +Path2 = /pub/Linux/ALPHA/scsi +File2 = dpt_eata03a.tgz +Site3 = ftp.uni-mainz.de +Path3 = /pub/Linux/Driver/SCSI +File3 = dpt_eata03a.tgz +Required1 = EATA compilant controller, Linux 1.1.51 +CopyPolicy1 = Code copyrighted be Michael Neuffer +CopyPolicy2 = GNU Copyleft +Keywords = SCSI EATA DPT PM2011 PM2012A PM2012B PM2021 PM2022 PM2122 PM2322 NEC AT&T +RelFiles1 = Slackboot_eata.gz Linux 1.1.51 Slackware bootdisk, kernel with many enabled features +Entered = 15AUG94 +EnteredBy = Michael Neuffer +End + diff -u --recursive --new-file linux-1.1.55+new_quota/include/linux/sonycd535.h linux/include/linux/sonycd535.h --- linux-1.1.55+new_quota/include/linux/sonycd535.h Wed Dec 31 18:00:00 1969 +++ linux/include/linux/sonycd535.h Thu Oct 20 18:15:38 1994 @@ -0,0 +1,183 @@ +#ifndef SONYCD535_H +#define SONYCD535_H + +/* + * define all the commands recognized by the CDU-531/5 + */ +#define SONY535_REQUEST_DRIVE_STATUS_1 (0x80) +#define SONY535_REQUEST_SENSE (0x82) +#define SONY535_REQUEST_DRIVE_STATUS_2 (0x84) +#define SONY535_REQUEST_ERROR_STATUS (0x86) +#define SONY535_REQUEST_AUDIO_STATUS (0x88) +#define SONY535_INQUIRY (0x8a) + +#define SONY535_SET_INACTIVITY_TIME (0x90) + +#define SONY535_SEEK_AND_READ_N_BLOCKS_1 (0xa0) +#define SONY535_SEEK_AND_READ_N_BLOCKS_2 (0xa4) +#define SONY535_PLAY_AUDIO (0xa6) + +#define SONY535_REQUEST_DISC_CAPACITY (0xb0) +#define SONY535_REQUEST_TOC_DATA (0xb2) +#define SONY535_REQUEST_SUB_Q_DATA (0xb4) +#define SONY535_REQUEST_ISRC (0xb6) +#define SONY535_REQUEST_UPC_EAN (0xb8) + +#define SONY535_SET_DRIVE_MODE (0xc0) +#define SONY535_REQUEST_DRIVE_MODE (0xc2) +#define SONY535_SET_RETRY_COUNT (0xc4) + +#define SONY535_DIAGNOSTIC_1 (0xc6) +#define SONY535_DIAGNOSTIC_4 (0xcc) +#define SONY535_DIAGNOSTIC_5 (0xce) + +#define SONY535_EJECT_CADDY (0xd0) +#define SONY535_DISABLE_EJECT_BUTTON (0xd2) +#define SONY535_ENABLE_EJECT_BUTTON (0xd4) + +#define SONY535_HOLD (0xe0) +#define SONY535_AUDIO_PAUSE_ON_OFF (0xe2) +#define SONY535_SET_VOLUME (0xe8) + +#define SONY535_STOP (0xf0) +#define SONY535_SPIN_UP (0xf2) +#define SONY535_SPIN_DOWN (0xf4) + +#define SONY535_CLEAR_PARAMETERS (0xf6) +#define SONY535_CLEAR_ENDING_ADDRESS (0xf8) + +/* + * define some masks + */ +#define SONY535_DATA_NOT_READY_BIT (0x1) +#define SONY535_RESULT_NOT_READY_BIT (0x2) + +/* + * drive status 1 + */ +#define SONY535_STATUS1_COMMAND_ERROR (0x1) +#define SONY535_STATUS1_DATA_ERROR (0x2) +#define SONY535_STATUS1_SEEK_ERROR (0x4) +#define SONY535_STATUS1_DISC_TYPE_ERROR (0x8) +#define SONY535_STATUS1_NOT_SPINNING (0x10) +#define SONY535_STATUS1_EJECT_BUTTON_PRESSED (0x20) +#define SONY535_STATUS1_CADDY_NOT_INSERTED (0x40) +#define SONY535_STATUS1_BYTE_TWO_FOLLOWS (0x80) + +/* + * drive status 2 + */ +#define SONY535_CDD_LOADING_ERROR (0x7) +#define SONY535_CDD_NO_DISC (0x8) +#define SONY535_CDD_UNLOADING_ERROR (0x9) +#define SONY535_CDD_CADDY_NOT_INSERTED (0xd) +#define SONY535_ATN_RESET_OCCURRED (0x2) +#define SONY535_ATN_DISC_CHANGED (0x4) +#define SONY535_ATN_RESET_AND_DISC_CHANGED (0x6) +#define SONY535_ATN_EJECT_IN_PROGRESS (0xe) +#define SONY535_ATN_BUSY (0xf) + +/* + * define some parameters + */ +#define SONY535_AUDIO_DRIVE_MODE (0) +#define SONY535_CDROM_DRIVE_MODE (0xe0) + +#define SONY535_PLAY_OP_PLAYBACK (0) +#define SONY535_PLAY_OP_ENTER_HOLD (1) +#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR (2) +#define SONY535_PLAY_OP_SCAN_FORWARD (3) +#define SONY535_PLAY_OP_SCAN_BACKWARD (4) + +/* + * convert from msf format to block number + */ +#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f)) +#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2]) + +/* + * error return values from the doSonyCmd() routines + */ +#define TIME_OUT (-1) +#define NO_CDROM (-2) +#define BAD_STATUS (-3) +#define CD_BUSY (-4) +#define NOT_DATA_CD (-5) +#define NO_ROOM (-6) + +#define LOG_START_OFFSET 150 /* Offset of first logical sector */ + +#define SONY_JIFFIES_TIMEOUT 500 /* Maximum number of jiffies (10ms) + the drive will wait/try for an + operation */ +#define SONY_READY_RETRIES (50000) /* How many times to retry a + spin waiting for a register + to come ready */ +#define SONY535_FAST_POLLS (10000) /* how many times recheck + status waiting for a data + to become ready */ + +typedef unsigned char Byte; + +/* + * This is the complete status returned from the drive configuration request + * command. + */ +struct s535_sony_drive_config +{ + char vendor_id[8]; + char product_id[16]; + char product_rev_level[4]; +}; + +/* The following is returned from the request sub-q data command */ +struct s535_sony_subcode +{ + unsigned char address :4; + unsigned char control :4; + unsigned char track_num; + unsigned char index_num; + unsigned char rel_msf[3]; + unsigned char abs_msf[3]; +}; + +struct s535_sony_disc_capacity +{ + Byte mFirstTrack, sFirstTrack, fFirstTrack; + Byte mLeadOut, sLeadOut, fLeadOut; +}; + +/* + * The following is returned from the request TOC (Table Of Contents) command. + * (last_track_num-first_track_num+1) values are valid in tracks. + */ +struct s535_sony_toc +{ + unsigned char reserved0 :4; + unsigned char control0 :4; + unsigned char point0; + unsigned char first_track_num; + unsigned char reserved0a; + unsigned char reserved0b; + unsigned char reserved1 :4; + unsigned char control1 :4; + unsigned char point1; + unsigned char last_track_num; + unsigned char dummy1; + unsigned char dummy2; + unsigned char reserved2 :4; + unsigned char control2 :4; + unsigned char point2; + unsigned char lead_out_start_msf[3]; + struct + { + unsigned char reserved :4; + unsigned char control :4; + unsigned char track; + unsigned char track_start_msf[3]; + } tracks[100]; + + unsigned int lead_out_start_lba; +}; + +#endif /* SONYCD535_H */ diff -u --recursive --new-file linux-1.1.55+new_quota/kernel/ksyms.c linux/kernel/ksyms.c --- linux-1.1.55+new_quota/kernel/ksyms.c Thu Oct 20 15:14:28 1994 +++ linux/kernel/ksyms.c Thu Oct 20 18:15:38 1994 @@ -52,6 +52,15 @@ extern void (* iABI_hook)(struct pt_regs * regs); +#ifdef CONFIG_NEC260 +#include +extern int the_nec260_major; +extern void (*do_hd) (void); +#ifdef CONFIG_BLK_DEV_HD1 +extern void (*do_hd1) (void); +#endif +#endif + struct symbol_table symbol_table = { 0, 0, 0, /* for stacked module support */ { /* stackable module support */ @@ -187,6 +196,18 @@ X(dev_rint), X(dev_tint), X(irq2dev_map), +#endif +#ifdef CONFIG_NEC260 + /* for the nec cdr-260 cdrom drive */ + X (the_nec260_major), + X (do_hd), + X (read_ahead), + X (sync_dev), + X (invalidate_buffers), + X (__down), +#ifdef CONFIG_BLK_DEV_HD1 + X (do_hd1), +#endif #endif /******************************************************** diff -u --recursive --new-file linux-1.1.55+new_quota/patch.eata linux/patch.eata --- linux-1.1.55+new_quota/patch.eata Wed Dec 31 18:00:00 1969 +++ linux/patch.eata Thu Oct 20 18:15:38 1994 @@ -0,0 +1,50 @@ +diff -u --recursive --new-file linux50/config.in linux/config.in +--- config.old Tue Sep 27 17:54:13 1994 ++++ config.in Tue Sep 27 17:53:40 1994 +@@ -51,6 +51,7 @@ + bool 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 n + bool 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 n + bool 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC n ++bool 'EATA-DMA (rev. 2.0b) (DPT,NEC,AT&T) support' CONFIG_SCSI_EATA y + bool 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN n + bool 'Generic NCR5380 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 n + bool 'NCR53c7,8xx SCSI support' CONFIG_SCSI_NCR53C7xx n +diff -u --recursive --new-file linux50/drivers/scsi/Makefile linux/drivers/scsi/Makefile +--- linux50/drivers/scsi/Makefile Tue Sep 27 18:02:02 1994 ++++ linux/drivers/scsi/Makefile Thu Sep 8 23:23:19 1994 +@@ -72,6 +72,11 @@ + SCSI_SRCS := $(SCSI_SRCS) scsi_debug.c + endif + ++ifdef CONFIG_SCSI_EATA ++SCSI_OBJS := $(SCSI_OBJS) eata.o ++SCSI_SRCS := $(SCSI_SRCS) eata.c ++endif ++ + ifdef CONFIG_SCSI_FUTURE_DOMAIN + SCSI_OBJS := $(SCSI_OBJS) fdomain.o + SCSI_SRCS := $(SCSI_SRCS) fdomain.c +diff -u --recursive --new-file linux50/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c +--- linux50/drivers/scsi/hosts.c Tue Sep 27 18:03:15 1994 ++++ linux/drivers/scsi/hosts.c Thu Sep 8 23:23:19 1994 +@@ -43,6 +43,10 @@ + #include "buslogic.h" + #endif + ++#ifdef CONFIG_SCSI_EATA ++#include "eata.h" ++#endif ++ + #ifdef CONFIG_SCSI_FUTURE_DOMAIN + #include "fdomain.h" + #endif +@@ -131,6 +135,9 @@ + #ifdef CONFIG_SCSI_AHA1740 + AHA1740, + #endif ++#ifdef CONFIG_SCSI_EATA ++ EATA, ++#endif + #ifdef CONFIG_SCSI_FUTURE_DOMAIN + FDOMAIN_16X0, + #endif diff -u --recursive --new-file linux-1.1.55+new_quota/tools/version.h linux/tools/version.h --- linux-1.1.55+new_quota/tools/version.h Wed Dec 31 18:00:00 1969 +++ linux/tools/version.h Thu Oct 20 18:15:38 1994 @@ -0,0 +1,7 @@ +#define UTS_RELEASE "1.1.53" +#define UTS_VERSION "#8 Wed Oct 12 21:48:39 CDT 1994" +#define LINUX_COMPILE_TIME "21:48:39" +#define LINUX_COMPILE_BY "root" +#define LINUX_COMPILE_HOST "fuzzy" +#define LINUX_COMPILE_DOMAIN "is.a.good.cat" +#define LINUX_COMPILER "gcc version 2.5.8"