diff -u --recursive --new-file v1.1.11/linux/CREDITS linux/CREDITS --- v1.1.11/linux/CREDITS Sat May 7 14:54:01 1994 +++ linux/CREDITS Fri May 6 08:50:12 1994 @@ -34,7 +34,7 @@ S: Germany N: Donald Becker -E: becker@super.org +E: becker@cesdis.gsfc.nasa.gov D: General low-level networking hacker D: Most of the ethercard drivers D: Original author of the NFS server @@ -107,6 +107,7 @@ S: USA N: Alan Cox +E: A.Cox@swansea.ac.uk E: iiitac@pyr.swan.ac.uk E: gw4pts@gw4pts.ampr.org E: GW4PTS@GB7SWN (packet radio) @@ -418,6 +419,14 @@ S: FIN-00330 Helsingfors S: Finland +N: David C. Niemi +E: David.Niemi@oasis.gtegsc.com +D: FSSTND, The XFree86 Project +D: DMA memory support and future floppy driver +S: 2364 Old Trail Drive +S: Reston, VA 22091 +S: USA + N: Kai Petzke E: wpp@marie.physik.tu-berlin.de D: Driver for Laser Magnetic Storage CD-ROM @@ -450,6 +459,13 @@ N: Robert Sanders E: gt8134b@prism.gatech.edu D: Dosemu + +N: Hannu Savolainen +E: hannu@voxware.pp.fi +D: Kernel sound drivers +S: Pallaksentie 4 A 2 +S: 00970 Helsinki +S: Finland N: Peter De Schrijver E: stud11@cc4.kuleuven.ac.be diff -u --recursive --new-file v1.1.11/linux/Makefile linux/Makefile --- v1.1.11/linux/Makefile Sat May 7 14:54:01 1994 +++ linux/Makefile Fri May 6 08:30:11 1994 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 11 +SUBLEVEL = 12 all: Version zImage diff -u --recursive --new-file v1.1.11/linux/config.in linux/config.in --- v1.1.11/linux/config.in Tue Apr 19 10:53:40 1994 +++ linux/config.in Sat May 7 14:10:07 1994 @@ -13,6 +13,19 @@ bool 'System V IPC' CONFIG_SYSVIPC y bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y +if [ "$CONFIG_INET" = "y" ]; then + +comment 'Networking options' +comment '(it is safe to leave these untouched)' + +comment 'IP (required for now) y' +bool 'Reverse ARP' CONFIG_INET_RARP y +bool 'Assume subnets are local' CONFIG_INET_SNARL y +bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n +#bool 'Novell IPX protocol' CONFIG_IPX n +#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n +fi + comment 'Program binary formats' bool 'Elf executables' CONFIG_BINFMT_ELF y diff -u --recursive --new-file v1.1.11/linux/drivers/net/slip.c linux/drivers/net/slip.c --- v1.1.11/linux/drivers/net/slip.c Tue Apr 19 22:20:34 1994 +++ linux/drivers/net/slip.c Sat May 7 14:13:00 1994 @@ -21,6 +21,7 @@ * Michael Riepe : Automatic CSLIP recognition added * Charles Hedrick : CSLIP header length problem fix. * Alan Cox : Corrected non-IP cases of the above. + * Alan Cox : Now uses hardware type as per FvK. * * * FIXME: This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without @@ -56,6 +57,7 @@ #include "tcp.h" #endif #include +#include #include "sock.h" #include "slip.h" #ifdef CONFIG_INET @@ -169,6 +171,7 @@ dev->rmem_start = (unsigned long) NULL; dev->mem_end = (unsigned long) NULL; dev->mem_start = (unsigned long) NULL; + dev->type = ARPHRD_SLIP + sl->mode; } @@ -1072,15 +1075,14 @@ { sl->dev->addr_len=7; /* sizeof an AX.25 addr */ sl->dev->hard_header_len=17; /* We don't do digipeaters */ - sl->dev->type=3; /* AF_AX25 not an AF_INET device */ } else { sl->dev->addr_len=0; /* No mac addr in slip mode */ sl->dev->hard_header_len=0; - sl->dev->type=0; } #endif + sl->dev->type=ARPHRD_SLIP+sl->mode; return(0); case SIOCSIFHWADDR: #ifdef CONFIG_AX25 diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/README.st linux/drivers/scsi/README.st --- v1.1.11/linux/drivers/scsi/README.st +++ linux/drivers/scsi/README.st Fri May 6 08:28:58 1994 @@ -0,0 +1,141 @@ +This file contains brief information about the SCSI tape driver. +Last modified: Thu May 5 00:13:24 1994 by root@kai.home + +BASICS + +The driver is generic. The state of a drive is not modified when the +driver is initalized or a device is opened. The mode parameters of the +drive can be modified with ioctls. The driver supports fixed and +variable block size (within buffer limits). Both the auto-rewind +(minor equals device number) and non-rewind devices (minor is 128 + +device number) are implemented. + + +BUFFERING + +The driver uses a buffer allocated at system initialization. The size +of the buffer is selectable at compile and/or boot time. The buffer is +used to store the data being transferred to/from the SCSI adapter. The +following buffering options are selectable at compile time and/or at run +time (via ioctl): + +Buffering of data to be written across write calls for fixed block +mode (define ST_BUFFER_WRITES). This should be disabled if reliable +detection of end of media (EOM) for fixed block mode is desired. + +Asynchronous writing. Writing the buffer contents to the tape is +started and the write call returns immediately. The status is checked +at the next tape operation. Should not used if reliable EOM detection +is desired. + +Read ahead for fixed block mode (ST_READ_AHEAD). Filling the buffer is +attempted even if the user does not want to get all of the data at +this read command. Should be disabled for those drives that don't like +a filemark to truncate a read request or that don't like backspacing. + +The buffer size is defined (in 1024 byte units) by ST_BUFFER_BLOCKS or +at boot time. The maximum number of buffers allocated is defined by +ST_MAX_BUFFERS. One buffer is allocated for each detected drive up to +the maximum. The threshold for triggering asynchronous write is +defined by ST_WRITE_THRESHOLD. + + +BOOT TIME CONFIGURATION + +The buffer size, write threshold, and the maximum number of allocated buffers +are configurable at boot time using, e.g., the LILO command line. The option +syntax is the following: + + st=aa[,bb[,cc]] + +where + aa is the buffer size in 1024 byte units + bb is the write threshold in 1024 byte units + cc is the maximum number of tape buffers to allocate (the number of + buffers is bounded also by the number of drives detected) + + +IOCTLS + +The tape is positioned and the drive parameters are set with ioctls +defined in mtio.h The tape control program 'mt' uses these ioctls. Try +to find an mt that supports all of the Linux SCSI tape ioctls and +opens the device for writing if the tape contents will be modified +(look for a package mt-st* from the Linux ftp sites; the GNU mt does +not open for writing for, e.g., erase). + +The supported ioctls are: + +The following use the structure mtop: + +MTFSF Space forward over count filemarks. Tape positioned after filemark. +MTFSFM As above but tape positioned before filemark. +MTBSF Space backward over count filemarks. Tape positioned before + filemark. +MTBSFM As above but ape positioned after filemark. +MTFSR Space forward over count records. +MTBSR Space backward over count records. +MTWEOF Write count filemarks. +MTREW Rewind tape. +MTOFFL Set device off line (often rewind plus eject). +MTNOP Do nothing. +MTRETEN Retension tape. +MTEOM Space to end of recorded data. +MTERASE Erase tape. +MTSEEK Seek to tape block count. Uses Tandberg-compatible seek (QFA) + for SCSI-1 drives and SCSI-2 seek for SCSI-2 drives. +MTSETBLK Set the drive block size. Setting to zero sets the drive into + variable block mode (if applicable). +MTSETDENSITY Sets the drive density code to arg. See drive + documentation for available codes. +MTSETDRVBUFFER + Is used for several things. The command is obtained from count + with mask MT_SET_OPTIONS, the low order bits are used as argument. + The subcommands are: + 0 + The drive buffer option is set to the argument. Zero means + no buffering. + MT_ST_BOOLEANS + Sets the buffering options. The bits are the new states + (enabled/disabled) of the write buffering (MT_ST_BUFFER_WRITES), + asynchronous writes (MT_ST_ASYNC_WRITES), read ahead + (MT_ST_READ_AHEAD) and debugging (MT_ST_DEBUGGING; + (debugging must be compiled into the driver). + MT_ST_WRITE_THRESHOLD + Sets the write threshold for this device to kilobytes + specified by the lowest bits. + +The following ioctl uses the structure mtpos: +MTIOCPOS Reads the current position from the drive. Uses + Tandberg-compatible QFA for SCSI-1 drives and the SCSI-2 + command for the SCSI-2 drives. + +The following ioctl uses the structure mtget to return the status: +MTIOCGET Returns some status information. + The file number and block number within file are returned. The + block is -1 when it can't be determined (e.g., after MTBSF). + The drive type is either MTISSCSI1 or MTISSCSI2. + The number of recovered errors since the previous status call + is stored in the lower word of the field mt_erreg. + The current block size and the density code are stored in the field + mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and + MT_ST_DENSITY_SHIFT). + The other fields are empty. + + +MISCELLANEOUS COMPILE OPTIONS + +The recovered write errors are considered fatal if ST_RECOVERED_WRITE_FATAL +is defined. + +Immediate return from tape positioning SCSI commands can be enabled by +defining ST_NOWAIT. + +When using read ahead or buffered writes the position within the file +may not be correct after the file is closed (correct position may +require backspacing over more than one record). The correct position +within file can be obtained if ST_IN_FILE_POS is defined. (The +driver always backs over a filemark crossed by read ahead if the user +does not request data that far.) + +Kai M{kisara diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/st.c linux/drivers/scsi/st.c --- v1.1.11/linux/drivers/scsi/st.c Wed Mar 16 19:12:09 1994 +++ linux/drivers/scsi/st.c Fri May 6 08:28:59 1994 @@ -1,42 +1,17 @@ /* - SCSI Tape Driver for Linux + SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying + file README.st for more information. - Version 0.02 for Linux 0.98.4 and Eric Youngdale's new scsi driver - History: Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara. - Contribution and ideas from several people including Eric Youngdale and - Wolfgang Denk. - - Features: - - support for different block sizes and internal buffering - - support for fixed and variable block size (within buffer limit; - blocksize set to zero) - - *nix-style ioctl with codes from mtio.h from the QIC-02 driver by - Hennus Bergman (command MTSETBLK added) - - character device - - rewind and non-rewind devices - - capability to handle several tape drives simultaneously - - one buffer if one drive, two buffers if more than one drive (limits the - number of simultaneously open drives to two) - - write behind - - seek and tell (Tandberg compatible and SCSI-2) - - Devices: - Autorewind devices have minor numbers equal to the tape numbers (0 > ). - Nonrewind device has the minor number equal to tape number + 128. - - Problems: - The end of media detection works correctly in writing only if the drive - writes the buffer contents after the early-warning mark. If you want to - be sure that EOM is reported correctly, you should uncomment the line - defining ST_NO_DELAYED_WRITES. Note that when delayed writes are disabled - each write byte count must be an integral number of blocks. + Contribution and ideas from several people including (in alphabetical + order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and + Eric Youngdale. - Copyright 1992, 1993 Kai Makisara + Copyright 1992, 1993, 1994 Kai Makisara email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi - Last modified: Thu Nov 25 21:49:02 1993 by root@kai.home + Last modified: Wed May 4 20:29:42 1994 by root@kai.home */ #include @@ -57,44 +32,40 @@ #include "st.h" #include "constants.h" -/* Uncomment the following if you want the rewind, etc. commands return - before command completion. */ +/* #define DEBUG */ + /* #define ST_NOWAIT */ -/* Uncomment the following if you want the tape to be positioned correctly - within file after close (the tape is positioned correctly with respect - to the filemarks even wihout ST_IN_FILE_POS defined */ /* #define ST_IN_FILE_POS */ -/* Uncomment the following if you want recovered write errors to be - fatal. */ /* #define ST_RECOVERED_WRITE_FATAL */ -/* Uncomment the following if you want all data from a write command to - be written to tape before the command returns. Disables write-behind. */ -/* #define ST_NO_DELAYED_WRITES */ - -/* Number of ST_BLOCK_SIZE blocks in the buffers */ -#define ST_BUFFER_BLOCKS 64 -/* Write-behind can be disabled by setting ST_WRITE_THRESHOLD_BLOCKS equal - to or larger than ST_BUFFER_BLOCKS */ -#define ST_WRITE_THRESHOLD_BLOCKS 60 -#define ST_BLOCK_SIZE 512 +#define ST_BUFFER_WRITES 1 + +#define ST_ASYNC_WRITES 1 + +#define ST_READ_AHEAD 1 + +#define ST_BLOCK_SIZE 1024 + +#define ST_MAX_BUFFERS 2 + +#define ST_BUFFER_BLOCKS 32 + +#define ST_WRITE_THRESHOLD_BLOCKS 30 + #define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE) #define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE) -#ifdef ST_NO_DELAYED_WRITES -#undef ST_WRITE_THRESHOLD_BLOCKS -#define ST_WRITE_THRESHOLD_BLOCKS ST_BUFFER_BLOCKS -#endif - -/* The buffer size should fit into the 24 bits reserved for length in the +/* The buffer size should fit into the 24 bits for length in the 6-byte SCSI read and write commands. */ #if ST_BUFFER_SIZE >= (2 << 24 - 1) #error "Buffer size should not exceed (2 << 24 - 1) bytes!" #endif -/* #define DEBUG */ +#ifdef DEBUG +static int debugging = 1; +#endif #define MAX_RETRIES 0 #define MAX_READY_RETRIES 5 @@ -104,7 +75,10 @@ #define ST_LONG_TIMEOUT 200000 static int st_nbr_buffers; -static ST_buffer *st_buffers[2]; +static ST_buffer **st_buffers; +static int st_buffer_size = ST_BUFFER_SIZE; +static int st_write_threshold = ST_WRITE_THRESHOLD; +static int st_max_buffers = ST_MAX_BUFFERS; static Scsi_Tape * scsi_tapes; int NR_ST=0; @@ -128,8 +102,14 @@ if (!result && SCpnt->sense_buffer[0] == 0) return 0; #ifdef DEBUG - printk("st%d: Error: %x\n", dev, result); - print_sense("st", SCpnt); + if (debugging) { + printk("st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result, + SCpnt->cmnd[0], SCpnt->cmnd[1], SCpnt->cmnd[2], + SCpnt->cmnd[3], SCpnt->cmnd[4], SCpnt->cmnd[5], + SCpnt->request_bufflen); + if (driver_byte(result) & DRIVER_SENSE) + print_sense("st", SCpnt); + } #endif /* if ((sense[0] & 0x70) == 0x70 && ((sense[2] & 0x80) )) @@ -142,6 +122,7 @@ #endif ) { scsi_tapes[dev].recover_count++; + scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT); if (SCpnt->cmnd[0] == READ_6) stp = "read"; else if (SCpnt->cmnd[0] == WRITE_6) @@ -192,13 +173,12 @@ wake_up( &(STp->waiting) ); } #ifdef DEBUG - else + else if (debugging) printk("st?: Illegal interrupt device %x\n", st_nbr); #endif } -#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS /* Handle the write-behind checking */ static void write_behind_check(int dev) @@ -222,12 +202,45 @@ STbuffer->b_data + STbuffer->writing, STbuffer->buffer_bytes - STbuffer->writing); STbuffer->buffer_bytes -= STbuffer->writing; + if (STp->drv_block >= 0) { + if (STp->block_size == 0) + STp->drv_block++; + else + STp->drv_block += STbuffer->writing / STp->block_size; + } STbuffer->writing = 0; return; } -#endif + + +/* Back over EOF if it has been inadvertently crossed (ioctl not used because + it messes up the block number). */ + static int +back_over_eof(int dev) +{ + Scsi_Cmnd *SCpnt; + Scsi_Tape *STp = &(scsi_tapes[dev]); + unsigned char cmd[10]; + + cmd[0] = SPACE; + cmd[1] = 0x01; /* Space FileMarks */ + cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */ + cmd[5] = 0; + + SCpnt = allocate_device(NULL, (STp->device)->index, 1); + SCpnt->sense_buffer[0] = 0; + SCpnt->request.dev = dev; + scsi_do_cmd(SCpnt, + (void *) cmd, (void *) (STp->buffer)->b_data, 0, + st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + + if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + SCpnt->request.dev = -1; + return (STp->buffer)->last_result_fatal; +} + /* Flush the write buffer (never need to write if variable blocksize). */ static int @@ -239,20 +252,19 @@ Scsi_Cmnd *SCpnt; Scsi_Tape *STp = &(scsi_tapes[dev]); -#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS if ((STp->buffer)->writing) { write_behind_check(dev); if ((STp->buffer)->last_result_fatal) { #ifdef DEBUG - printk("st%d: Async write error %x.\n", dev, - (STp->buffer)->last_result); + if (debugging) + printk("st%d: Async write error (flush) %x.\n", dev, + (STp->buffer)->last_result); #endif if ((STp->buffer)->last_result == INT_MAX) return (-ENOSPC); return (-EIO); } } -#endif result = 0; if (STp->dirty == 1) { @@ -262,7 +274,8 @@ transfer = ((offset + STp->block_size - 1) / STp->block_size) * STp->block_size; #ifdef DEBUG - printk("st%d: Flushing %d bytes.\n", dev, transfer); + if (debugging) + printk("st%d: Flushing %d bytes.\n", dev, transfer); #endif memset((STp->buffer)->b_data + offset, 0, transfer - offset); @@ -292,8 +305,11 @@ } else result = (-EIO); + STp->drv_block = (-1); } else { + if (STp->drv_block >= 0) + STp->drv_block += blks; STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; } @@ -332,7 +348,7 @@ result = 0; if (!seek_next) { if ((STp->eof == ST_FM) && !STp->eof_hit) { - result = st_int_ioctl(inode, filp, MTBSF, 1); /* Back over the EOF hit */ + result = back_over_eof(dev); /* Back over the EOF hit */ if (!result) { STp->eof = ST_NOEOF; STp->eof_hit = 0; @@ -400,31 +416,36 @@ SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, + 0, st_sleep_done, ST_LONG_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ + (STp->mt_status)->mt_fileno = 0 ; SCpnt->sense_buffer[0]=0; memset ((void *) &cmd[0], 0, 10); cmd[0] = TEST_UNIT_READY; SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT, + 0, st_sleep_done, ST_LONG_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); + (STp->mt_status)->mt_fileno = STp->drv_block = 0; } if ((STp->buffer)->last_result_fatal != 0) { if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && - (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) + (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) { + (STp->mt_status)->mt_fileno = STp->drv_block = 0 ; printk("st%d: No tape.\n", dev); - else + } else { printk("st%d: Error %x.\n", dev, SCpnt->result); + (STp->mt_status)->mt_fileno = STp->drv_block = (-1); + } (STp->buffer)->in_use = 0; STp->in_use = 0; SCpnt->request.dev = -1; /* Mark as not busy */ @@ -437,7 +458,7 @@ SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); + 6, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); @@ -447,14 +468,16 @@ STp->min_block = ((STp->buffer)->b_data[4] << 8) | (STp->buffer)->b_data[5]; #ifdef DEBUG - printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block, - STp->max_block); + if (debugging) + printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block, + STp->max_block); #endif } else { STp->min_block = STp->max_block = (-1); #ifdef DEBUG - printk("st%d: Can't read block limits.\n", dev); + if (debugging) + printk("st%d: Can't read block limits.\n", dev); #endif } @@ -465,13 +488,14 @@ SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); + 12, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((STp->buffer)->last_result_fatal != 0) { #ifdef DEBUG - printk("st%d: No Mode Sense.\n", dev); + if (debugging) + printk("st%d: No Mode Sense.\n", dev); #endif (STp->buffer)->b_data[2] = (STp->buffer)->b_data[3] = 0; @@ -479,9 +503,10 @@ SCpnt->request.dev = -1; /* Mark as not busy */ #ifdef DEBUG - printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev, - (STp->buffer)->b_data[0], (STp->buffer)->b_data[1], - (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]); + if (debugging) + printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev, + (STp->buffer)->b_data[0], (STp->buffer)->b_data[1], + (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]); #endif if ((STp->buffer)->b_data[3] >= 8) { @@ -490,13 +515,13 @@ STp->block_size = (STp->buffer)->b_data[9] * 65536 + (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11]; #ifdef DEBUG - printk( - "st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n", - dev, STp->density, (STp->buffer)->b_data[5] * 65536 + - (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7], - STp->block_size, STp->drv_buffer); + if (debugging) + printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n", + dev, STp->density, (STp->buffer)->b_data[5] * 65536 + + (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7], + STp->block_size, STp->drv_buffer); #endif - if (STp->block_size > ST_BUFFER_SIZE) { + if (STp->block_size > st_buffer_size) { printk("st%d: Blocksize %d too large for buffer.\n", dev, STp->block_size); (STp->buffer)->in_use = 0; @@ -506,29 +531,31 @@ } else - STp->block_size = ST_BLOCK_SIZE; + STp->block_size = 512; /* "Educated Guess" (?) */ if (STp->block_size > 0) { - (STp->buffer)->buffer_blocks = ST_BUFFER_SIZE / STp->block_size; + (STp->buffer)->buffer_blocks = st_buffer_size / STp->block_size; (STp->buffer)->buffer_size = (STp->buffer)->buffer_blocks * STp->block_size; } else { (STp->buffer)->buffer_blocks = 1; - (STp->buffer)->buffer_size = ST_BUFFER_SIZE; + (STp->buffer)->buffer_size = st_buffer_size; } (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0; #ifdef DEBUG - printk("st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev, - STp->block_size, (STp->buffer)->buffer_size, - (STp->buffer)->buffer_blocks); + if (debugging) + printk("st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev, + STp->block_size, (STp->buffer)->buffer_size, + (STp->buffer)->buffer_blocks); #endif if ((STp->buffer)->b_data[2] & 0x80) { STp->write_prot = 1; #ifdef DEBUG - printk( "st%d: Write protected\n", dev); + if (debugging) + printk( "st%d: Write protected\n", dev); #endif } @@ -557,7 +584,8 @@ result = flush_write_buffer(dev); #ifdef DEBUG - printk("st%d: File length %d bytes.\n", dev, filp->f_pos); + if (debugging) + printk("st%d: File length %ld bytes.\n", dev, filp->f_pos); #endif if (result == 0 || result == (-ENOSPC)) { @@ -570,24 +598,29 @@ SCpnt->request.dev = dev; scsi_do_cmd( SCpnt, (void *) cmd, (void *) (STp->buffer)->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); + 0, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((STp->buffer)->last_result_fatal != 0) printk("st%d: Error on write filemark.\n", dev); + else { + (STp->mt_status)->mt_fileno++ ; + STp->drv_block = 0; + } SCpnt->request.dev = -1; /* Mark as not busy */ } #ifdef DEBUG - printk("st%d: Buffer flushed, EOF written\n", dev); + if (debugging) + printk("st%d: Buffer flushed, EOF written\n", dev); #endif } else if (!rewind) { #ifndef ST_IN_FILE_POS if ((STp->eof == ST_FM) && !STp->eof_hit) - st_int_ioctl(inode, filp, MTBSF, 1); /* Back over the EOF hit */ + back_over_eof(dev); #else flush_buffer(inode, filp, 0); #endif @@ -627,7 +660,7 @@ if (STp->write_prot) return (-EACCES); - if (STp->block_size == 0 && count > ST_BUFFER_SIZE) + if (STp->block_size == 0 && count > st_buffer_size) return (-EOVERFLOW); if (STp->rw == ST_READING) { @@ -637,13 +670,13 @@ STp->rw = ST_WRITING; } -#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS if ((STp->buffer)->writing) { write_behind_check(dev); if ((STp->buffer)->last_result_fatal) { #ifdef DEBUG - printk("st%d: Async write error %x.\n", dev, - (STp->buffer)->last_result); + if (debugging) + printk("st%d: Async write error (write) %x.\n", dev, + (STp->buffer)->last_result); #endif if ((STp->buffer)->last_result == INT_MAX) { retval = (-ENOSPC); /* All has been written */ @@ -654,20 +687,21 @@ return retval; } } -#endif if (STp->eof == ST_EOM_OK) return (-ENOSPC); else if (STp->eof == ST_EOM_ERROR) return (-EIO); -#ifdef ST_NO_DELAYED_WRITES - if (STp->block_size != 0 && (count % STp->block_size) != 0) - return (-EIO); /* Write must be integral number of blocks */ - write_threshold = 1; -#else - write_threshold = (STp->buffer)->buffer_size; -#endif + if (!STp->do_buffer_writes) { + if (STp->block_size != 0 && (count % STp->block_size) != 0) + return (-EIO); /* Write must be integral number of blocks */ + write_threshold = 1; + } + else + write_threshold = (STp->buffer)->buffer_size; + if (!STp->do_async_writes) + write_threshold--; SCpnt = allocate_device(NULL, (STp->device)->index, 1); @@ -680,16 +714,9 @@ STp->rw = ST_WRITING; b_point = buf; - while( -#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS - STp->block_size != 0 && - ((STp->buffer)->buffer_bytes + count) > - write_threshold) -#else - (STp->block_size == 0 && count > 0) || - ((STp->buffer)->buffer_bytes + count) >= - write_threshold) -#endif + while((STp->block_size == 0 && !STp->do_async_writes && count > 0) || + (STp->block_size != 0 && + (STp->buffer)->buffer_bytes + count > write_threshold)) { if (STp->block_size == 0) do_count = count; @@ -702,25 +729,27 @@ (STp->buffer)->buffer_bytes, b_point, do_count); if (STp->block_size == 0) - blks = do_count; - else + blks = transfer = do_count; + else { blks = ((STp->buffer)->buffer_bytes + do_count) / STp->block_size; + transfer = blks * STp->block_size; + } cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; SCpnt->sense_buffer[0] = 0; SCpnt->request.dev = dev; scsi_do_cmd (SCpnt, - (void *) cmd, (STp->buffer)->b_data, - (STp->buffer)->buffer_size, + (void *) cmd, (STp->buffer)->b_data, transfer, st_sleep_done, ST_TIMEOUT, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((STp->buffer)->last_result_fatal != 0) { #ifdef DEBUG - printk("st%d: Error on write:\n", dev); + if (debugging) + printk("st%d: Error on write:\n", dev); #endif if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40)) { @@ -738,23 +767,34 @@ if (transfer <= do_count) { filp->f_pos += do_count - transfer; count -= do_count - transfer; + if (STp->drv_block >= 0) { + if (STp->block_size == 0 && transfer < do_count) + STp->drv_block++; + else if (STp->block_size != 0) + STp->drv_block += (do_count - transfer) / STp->block_size; + } STp->eof = ST_EOM_OK; retval = (-ENOSPC); /* EOM within current request */ #ifdef DEBUG - printk("st%d: EOM with %d bytes unwritten.\n", - dev, transfer); + if (debugging) + printk("st%d: EOM with %d bytes unwritten.\n", + dev, transfer); #endif } else { STp->eof = ST_EOM_ERROR; + STp->drv_block = (-1); /* Too cautious? */ retval = (-EIO); /* EOM for old data */ #ifdef DEBUG - printk("st%d: EOM with lost data.\n", dev); + if (debugging) + printk("st%d: EOM with lost data.\n", dev); #endif } } - else + else { + STp->drv_block = (-1); /* Too cautious? */ retval = (-EIO); + } SCpnt->request.dev = -1; /* Mark as not busy */ (STp->buffer)->buffer_bytes = 0; @@ -767,6 +807,12 @@ filp->f_pos += do_count; b_point += do_count; count -= do_count; + if (STp->drv_block >= 0) { + if (STp->block_size == 0) + STp->drv_block++; + else + STp->drv_block += blks; + } (STp->buffer)->buffer_bytes = 0; STp->dirty = 0; } @@ -784,9 +830,9 @@ return (STp->buffer)->last_result_fatal; } -#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS - if ((STp->buffer)->buffer_bytes >= ST_WRITE_THRESHOLD || - STp->block_size == 0) { + if (STp->do_async_writes && + ((STp->buffer)->buffer_bytes >= STp->write_threshold || + STp->block_size == 0) ) { /* Schedule an asynchronous write */ if (STp->block_size == 0) (STp->buffer)->writing = (STp->buffer)->buffer_bytes; @@ -811,7 +857,6 @@ st_sleep_done, ST_TIMEOUT, MAX_RETRIES); } else -#endif SCpnt->request.dev = -1; /* Mark as not busy */ return( total); @@ -838,9 +883,13 @@ } #endif - if (STp->block_size == 0 && count > ST_BUFFER_SIZE) + if (STp->block_size == 0 && count > st_buffer_size) return (-EOVERFLOW); + if (!(STp->do_read_ahead) && STp->block_size != 0 && + (count % STp->block_size) != 0) + return (-EIO); /* Read must be integral number of blocks */ + if (STp->rw == ST_WRITING) { transfer = flush_buffer(inode, filp, 0); if (transfer) @@ -849,7 +898,7 @@ } #ifdef DEBUG - if (STp->eof != ST_NOEOF) + if (debugging && STp->eof != ST_NOEOF) printk("st%d: EOF flag up. Bytes %d\n", dev, (STp->buffer)->buffer_bytes); #endif @@ -872,8 +921,17 @@ if (STp->block_size == 0) blks = bytes = count; else { - blks = (STp->buffer)->buffer_blocks; - bytes = blks * STp->block_size; + if (STp->do_read_ahead) { + blks = (STp->buffer)->buffer_blocks; + bytes = blks * STp->block_size; + } + else { + bytes = count; + if (bytes > st_buffer_size) + bytes = st_buffer_size; + blks = bytes / STp->block_size; + bytes = blks * STp->block_size; + } } cmd[2] = blks >> 16; cmd[3] = blks >> 8; @@ -893,11 +951,12 @@ if ((STp->buffer)->last_result_fatal) { #ifdef DEBUG - printk("st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev, - SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], - SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], - SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], - SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]); + if (debugging) + printk("st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev, + SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], + SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], + SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], + SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]); #endif if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ @@ -917,7 +976,7 @@ if (STp->block_size == 0) { if (transfer <= 0) transfer = 0; - (STp->buffer)->buffer_bytes = count - transfer; + (STp->buffer)->buffer_bytes = bytes - transfer; } else { printk("st%d: Incorrect block size.\n", dev); @@ -928,14 +987,14 @@ else if (SCpnt->sense_buffer[2] & 0x40) { STp->eof = ST_EOM_OK; if (STp->block_size == 0) - (STp->buffer)->buffer_bytes = count - transfer; + (STp->buffer)->buffer_bytes = bytes - transfer; else (STp->buffer)->buffer_bytes = - ((STp->buffer)->buffer_blocks - transfer) * - STp->block_size; + bytes - transfer * STp->block_size; #ifdef DEBUG - printk("st%d: EOM detected (%d bytes read).\n", dev, - (STp->buffer)->buffer_bytes); + if (debugging) + printk("st%d: EOM detected (%d bytes read).\n", dev, + (STp->buffer)->buffer_bytes); #endif } else if (SCpnt->sense_buffer[2] & 0x80) { @@ -944,20 +1003,22 @@ (STp->buffer)->buffer_bytes = 0; else (STp->buffer)->buffer_bytes = - ((STp->buffer)->buffer_blocks - transfer) * - STp->block_size; + bytes - transfer * STp->block_size; #ifdef DEBUG - printk( - "st%d: EOF detected (%d bytes read, transferred %d bytes).\n", - dev, (STp->buffer)->buffer_bytes, total); + if (debugging) + printk( + "st%d: EOF detected (%d bytes read, transferred %d bytes).\n", + dev, (STp->buffer)->buffer_bytes, total); #endif } /* end of EOF, EOM, ILI test */ } else { /* nonzero sense key */ #ifdef DEBUG - printk("st%d: Tape error while reading.\n", dev); + if (debugging) + printk("st%d: Tape error while reading.\n", dev); #endif SCpnt->request.dev = -1; + STp->drv_block = (-1); if (total) return total; else @@ -973,12 +1034,19 @@ else /* Read successful */ (STp->buffer)->buffer_bytes = bytes; + if (STp->drv_block >= 0) { + if (STp->block_size == 0) + STp->drv_block++; + else + STp->drv_block += (STp->buffer)->buffer_bytes / STp->block_size; + } + } /* if ((STp->buffer)->buffer_bytes == 0 && STp->eof == ST_NOEOF) */ if ((STp->buffer)->buffer_bytes > 0) { #ifdef DEBUG - if (STp->eof != ST_NOEOF) + if (debugging && STp->eof != ST_NOEOF) printk("st%d: EOF up. Left %d, needed %d.\n", dev, (STp->buffer)->buffer_bytes, count - total); #endif @@ -995,8 +1063,11 @@ else if (STp->eof != ST_NOEOF) { STp->eof_hit = 1; SCpnt->request.dev = -1; /* Mark as not busy */ - if (total == 0 && STp->eof == ST_FM) + if (total == 0 && STp->eof == ST_FM) { STp->eof = 0; + STp->drv_block = 0; + (STp->mt_status)->mt_fileno++; + } if (total == 0 && STp->eof == ST_EOM_OK) return (-EIO); /* ST_EOM_ERROR not used in read */ return total; @@ -1012,6 +1083,49 @@ return total; } + + +/* Set the driver options */ + static int +st_set_options(struct inode * inode, long options) +{ + int dev, value; + Scsi_Tape *STp; + + dev = MINOR(inode->i_rdev) & 127; + STp = &(scsi_tapes[dev]); + if ((options & MT_ST_OPTIONS) == MT_ST_BOOLEANS) { + STp->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0; + STp->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0; + STp->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0; +#ifdef DEBUG + debugging = (options & MT_ST_DEBUGGING) != 0; + printk( +"st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n", + dev, STp->do_buffer_writes, STp->do_async_writes, + STp->do_read_ahead); + printk(" debugging: %d\n", debugging); +#endif + } + else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) { + value = (options & ~MT_ST_OPTIONS) * ST_BLOCK_SIZE; + if (value < 1 || value > st_buffer_size) { + printk("st: Write threshold %d too small or too large.\n", + value); + return (-EIO); + } + STp->write_threshold = value; +#ifdef DEBUG + printk("st%d: Write threshold set to %d bytes.\n", dev, + STp->write_threshold); +#endif + } + else + return (-EIO); + + return 0; +} + /* Internal ioctl function */ static int @@ -1025,11 +1139,15 @@ unsigned char cmd[10]; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; + int fileno, blkno, undone, datalen; dev = dev & 127; STp = &(scsi_tapes[dev]); + fileno = (STp->mt_status)->mt_fileno ; + blkno = STp->drv_block; memset(cmd, 0, 10); + datalen = 0; switch (cmd_in) { case MTFSF: case MTFSFM: @@ -1039,9 +1157,12 @@ cmd[3] = (arg >> 8); cmd[4] = arg; #ifdef DEBUG - printk("st%d: Spacing tape forward over %d filemarks.\n", dev, - cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); + if (debugging) + printk("st%d: Spacing tape forward over %d filemarks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); #endif + fileno += arg; + blkno = 0; break; case MTBSF: case MTBSFM: @@ -1052,11 +1173,15 @@ cmd[3] = (ltmp >> 8); cmd[4] = ltmp; #ifdef DEBUG - if (cmd[2] & 0x80) - ltmp = 0xff000000; - ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; - printk("st%d: Spacing tape backward over %d filemarks.\n", dev, (-ltmp)); + if (debugging) { + if (cmd[2] & 0x80) + ltmp = 0xff000000; + ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; + printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp)); + } #endif + fileno -= arg; + blkno = (-1); /* We can't know the block number */ break; case MTFSR: cmd[0] = SPACE; @@ -1065,9 +1190,12 @@ cmd[3] = (arg >> 8); cmd[4] = arg; #ifdef DEBUG - printk("st%d: Spacing tape forward %d blocks.\n", dev, - cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); + if (debugging) + printk("st%d: Spacing tape forward %d blocks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); #endif + if (blkno >= 0) + blkno += arg; break; case MTBSR: cmd[0] = SPACE; @@ -1077,11 +1205,15 @@ cmd[3] = (ltmp >> 8); cmd[4] = ltmp; #ifdef DEBUG - if (cmd[2] & 0x80) - ltmp = 0xff000000; - ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; - printk("st%d: Spacing tape backward %d blocks.\n", dev, (-ltmp)); + if (debugging) { + if (cmd[2] & 0x80) + ltmp = 0xff000000; + ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; + printk("st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp)); + } #endif + if (blkno >= 0) + blkno -= arg; break; case MTWEOF: if (STp->write_prot) @@ -1092,9 +1224,12 @@ cmd[4] = arg; timeout = ST_TIMEOUT; #ifdef DEBUG - printk("st%d: Writing %d filemarks.\n", dev, - cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); + if (debugging) + printk("st%d: Writing %d filemarks.\n", dev, + cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); #endif + fileno += arg; + blkno = 0; break; case MTREW: cmd[0] = REZERO_UNIT; @@ -1103,8 +1238,10 @@ timeout = ST_TIMEOUT; #endif #ifdef DEBUG - printk("st%d: Rewinding tape.\n", dev); + if (debugging) + printk("st%d: Rewinding tape.\n", dev); #endif + fileno = blkno = 0 ; break; case MTOFFL: cmd[0] = START_STOP; @@ -1113,12 +1250,15 @@ timeout = ST_TIMEOUT; #endif #ifdef DEBUG - printk("st%d: Unloading tape.\n", dev); + if (debugging) + printk("st%d: Unloading tape.\n", dev); #endif + fileno = blkno = 0 ; break; case MTNOP: #ifdef DEBUG - printk("st%d: No op on tape.\n", dev); + if (debugging) + printk("st%d: No op on tape.\n", dev); #endif return 0; /* Should do something ? */ break; @@ -1130,15 +1270,26 @@ #endif cmd[4] = 3; #ifdef DEBUG - printk("st%d: Retensioning tape.\n", dev); + if (debugging) + printk("st%d: Retensioning tape.\n", dev); #endif + fileno = blkno = 0 ; break; case MTEOM: + /* space to the end of tape */ + ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff); + fileno = (STp->mt_status)->mt_fileno ; + /* The next lines would hide the number of spaced FileMarks + That's why I inserted the previous lines. I had no luck + with detecting EOM with FSF, so we go now to EOM. + Joerg Weule */ cmd[0] = SPACE; cmd[1] = 3; #ifdef DEBUG - printk("st%d: Spacing to end of recorded medium.\n", dev); + if (debugging) + printk("st%d: Spacing to end of recorded medium.\n", dev); #endif + blkno = (-1); break; case MTERASE: if (STp->write_prot) @@ -1146,8 +1297,10 @@ cmd[0] = ERASE; cmd[1] = 1; /* To the end of tape */ #ifdef DEBUG - printk("st%d: Erasing tape.\n", dev); + if (debugging) + printk("st%d: Erasing tape.\n", dev); #endif + fileno = blkno = 0 ; break; case MTSEEK: if ((STp->device)->scsi_level < SCSI_2) { @@ -1170,8 +1323,10 @@ timeout = ST_TIMEOUT; #endif #ifdef DEBUG - printk("st%d: Seeking tape to block %d.\n", dev, arg); + if (debugging) + printk("st%d: Seeking tape to block %ld.\n", dev, arg); #endif + fileno = blkno = (-1); break; case MTSETBLK: /* Set block length */ case MTSETDENSITY: /* Set tape density */ @@ -1181,12 +1336,12 @@ if (cmd_in == MTSETBLK && arg != 0 && (arg < STp->min_block || arg > STp->max_block || - arg > ST_BUFFER_SIZE)) { + arg > st_buffer_size)) { printk("st%d: Illegal block size.\n", dev); return (-EINVAL); } cmd[0] = MODE_SELECT; - cmd[4] = 12; + cmd[4] = datalen = 12; memset((STp->buffer)->b_data, 0, 12); if (cmd_in == MTSETDRVBUFFER) @@ -1208,17 +1363,19 @@ (STp->buffer)->b_data[11] = ltmp; timeout = ST_TIMEOUT; #ifdef DEBUG - if (cmd_in == MTSETBLK) - printk("st%d: Setting block size to %d bytes.\n", dev, - (STp->buffer)->b_data[9] * 65536 + - (STp->buffer)->b_data[10] * 256 + - (STp->buffer)->b_data[11]); - else if (cmd_in == MTSETDENSITY) - printk("st%d: Setting density code to %x.\n", dev, - (STp->buffer)->b_data[4]); - else - printk("st%d: Setting drive buffer code to %d.\n", dev, - ((STp->buffer)->b_data[2] >> 4) & 7); + if (debugging) { + if (cmd_in == MTSETBLK) + printk("st%d: Setting block size to %d bytes.\n", dev, + (STp->buffer)->b_data[9] * 65536 + + (STp->buffer)->b_data[10] * 256 + + (STp->buffer)->b_data[11]); + else if (cmd_in == MTSETDENSITY) + printk("st%d: Setting density code to %x.\n", dev, + (STp->buffer)->b_data[4]); + else + printk("st%d: Setting drive buffer code to %d.\n", dev, + ((STp->buffer)->b_data[2] >> 4) & 7); + } #endif break; default: @@ -1230,7 +1387,7 @@ SCpnt->sense_buffer[0] = 0; SCpnt->request.dev = dev; scsi_do_cmd(SCpnt, - (void *) cmd, (void *) (STp->buffer)->b_data, ST_BLOCK_SIZE, + (void *) cmd, (void *) (STp->buffer)->b_data, datalen, st_sleep_done, timeout, MAX_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); @@ -1240,6 +1397,8 @@ SCpnt->request.dev = -1; /* Mark as not busy */ if (!ioctl_result) { + STp->drv_block = blkno; + (STp->mt_status)->mt_fileno = fileno; if (cmd_in == MTBSFM) ioctl_result = st_int_ioctl(inode, file, MTFSF, 1); else if (cmd_in == MTFSFM) @@ -1248,13 +1407,13 @@ STp->block_size = arg; if (arg != 0) { (STp->buffer)->buffer_blocks = - ST_BUFFER_SIZE / STp->block_size; + st_buffer_size / STp->block_size; (STp->buffer)->buffer_size = (STp->buffer)->buffer_blocks * STp->block_size; } else { (STp->buffer)->buffer_blocks = 1; - (STp->buffer)->buffer_size = ST_BUFFER_SIZE; + (STp->buffer)->buffer_size = st_buffer_size; } (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0; @@ -1263,7 +1422,7 @@ STp->drv_buffer = arg; else if (cmd_in == MTSETDENSITY) STp->density = arg; - if (cmd_in == MTEOM || cmd_in == MTWEOF) { + else if (cmd_in == MTEOM || cmd_in == MTWEOF) { STp->eof = ST_EOM_OK; STp->eof_hit = 0; } @@ -1271,6 +1430,33 @@ STp->eof = ST_NOEOF; STp->eof_hit = 0; } + } else { + if (SCpnt->sense_buffer[2] & 0x40) { + STp->eof = ST_EOM_OK; + STp->eof_hit = 0; + STp->drv_block = 0; + } + undone = ( + (SCpnt->sense_buffer[3] << 24) + + (SCpnt->sense_buffer[4] << 16) + + (SCpnt->sense_buffer[5] << 8) + + SCpnt->sense_buffer[6] ); + if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) + (STp->mt_status)->mt_fileno = fileno - undone ; + else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) + (STp->mt_status)->mt_fileno = fileno + undone ; + else if (cmd_in == MTFSR) { + if (blkno >= undone) + STp->drv_block = blkno - undone; + else + STp->drv_block = (-1); + } + else if (cmd_in == MTBSR && blkno >= 0) { + if (blkno >= 0) + STp->drv_block = blkno + undone; + else + STp->drv_block = (-1); + } } return ioctl_result ; @@ -1294,7 +1480,7 @@ dev = dev & 127; STp = &(scsi_tapes[dev]); #ifdef DEBUG - if (!STp->in_use) { + if (debugging && !STp->in_use) { printk("st%d: Incorrect device.\n", dev); return (-EIO); } @@ -1318,7 +1504,11 @@ if (i < 0) return i; - return st_int_ioctl(inode, file, mtc.mt_op, mtc.mt_count); + if (mtc.mt_op == MTSETDRVBUFFER && + (mtc.mt_count & MT_ST_OPTIONS) != 0) + return st_set_options(inode, mtc.mt_count); + else + return st_int_ioctl(inode, file, mtc.mt_op, mtc.mt_count); } else if (cmd == (MTIOCGET & IOCCMD_MASK)) { @@ -1328,13 +1518,29 @@ if (i) return i; - memcpy_tofs((char *)arg, (char *)(STp->buffer)->mt_status, + (STp->mt_status)->mt_dsreg = + ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) | + ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK); + (STp->mt_status)->mt_blkno = STp->drv_block; + if (STp->block_size != 0) { + if (STp->rw == ST_WRITING) + (STp->mt_status)->mt_blkno += + (STp->buffer)->buffer_bytes / STp->block_size; + else if (STp->rw == ST_READING) + (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes + + STp->block_size - 1) / STp->block_size; + } + + memcpy_tofs((char *)arg, (char *)(STp->mt_status), sizeof(struct mtget)); + + (STp->mt_status)->mt_erreg = 0; /* Clear after read */ return 0; } else if (cmd == (MTIOCPOS & IOCCMD_MASK)) { #ifdef DEBUG - printk("st%d: get tape position.\n", dev); + if (debugging) + printk("st%d: get tape position.\n", dev); #endif if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) return (-EINVAL); @@ -1363,14 +1569,15 @@ SCpnt->sense_buffer[0] = 0; scsi_do_cmd(SCpnt, (void *) scmd, (void *) (STp->buffer)->b_data, - ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); + 20, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if ((STp->buffer)->last_result_fatal != 0) { mt_pos.mt_blkno = (-1); #ifdef DEBUG - printk("st%d: Can't read tape position.\n", dev); + if (debugging) + printk("st%d: Can't read tape position.\n", dev); #endif result = (-EIO); } @@ -1397,8 +1604,25 @@ return scsi_ioctl(STp->device, cmd_in, (void *) arg); } - +/* Set the boot options. Syntax: st=xxx,yyy + where xxx is buffer size in 512 byte blocks and yyy is write threshold + in 512 byte blocks. */ + void +st_setup(char *str, int *ints) +{ + if (ints[0] > 0 && ints[1] > 0) + st_buffer_size = ints[1] * ST_BLOCK_SIZE; + if (ints[0] > 1 && ints[2] > 0) { + st_write_threshold = ints[2] * ST_BLOCK_SIZE; + if (st_write_threshold > st_buffer_size) + st_write_threshold = st_buffer_size; + } + if (ints[0] > 2 && ints[3] > 0) + st_max_buffers = ints[3]; +} + + static struct file_operations st_fops = { NULL, /* lseek - default */ st_read, /* read - general block-dev read */ @@ -1426,7 +1650,8 @@ /* Driver initialization */ unsigned long st_init(unsigned long mem_start, unsigned long mem_end) { - int i; + int i, dev_nbr; + Scsi_Tape * STp; if (register_chrdev(MAJOR_NR,"st",&st_fops)) { printk("Unable to get major %d for SCSI tapes\n",MAJOR_NR); @@ -1435,40 +1660,56 @@ if (NR_ST == 0) return mem_start; #ifdef DEBUG - printk("st: Init tape.\n"); + printk("st: Buffer size %d bytes, write threshold %d bytes.\n", + st_buffer_size, st_write_threshold); #endif - for (i=0; i < NR_ST; ++i) { - scsi_tapes[i].capacity = 0xfffff; - scsi_tapes[i].dirty = 0; - scsi_tapes[i].rw = ST_IDLE; - scsi_tapes[i].eof = ST_NOEOF; - scsi_tapes[i].waiting = NULL; - scsi_tapes[i].in_use = 0; - scsi_tapes[i].drv_buffer = 1; /* Try buffering if no mode sense */ - scsi_tapes[i].density = 0; + for (i=0, dev_nbr=(-1); i < NR_ST; ++i) { + STp = &(scsi_tapes[i]); + STp->capacity = 0xfffff; + STp->dirty = 0; + STp->rw = ST_IDLE; + STp->eof = ST_NOEOF; + STp->waiting = NULL; + STp->in_use = 0; + STp->drv_buffer = 1; /* Try buffering if no mode sense */ + STp->density = 0; + STp->do_buffer_writes = ST_BUFFER_WRITES; + STp->do_async_writes = ST_ASYNC_WRITES; + STp->do_read_ahead = ST_READ_AHEAD; + STp->write_threshold = st_write_threshold; + STp->drv_block = 0; + STp->mt_status = (struct mtget *) mem_start; + mem_start += sizeof(struct mtget); + /* Initialize status */ + memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget)); + for (dev_nbr++; dev_nbr < NR_SCSI_DEVICES; dev_nbr++) + if (scsi_devices[dev_nbr].type == TYPE_TAPE) + break; + if (dev_nbr >= NR_SCSI_DEVICES) + printk("st%d: ERROR: Not found in scsi chain.\n", i); + else { + if (scsi_devices[dev_nbr].scsi_level <= 2) + STp->mt_status->mt_type = MT_ISSCSI1; + else + STp->mt_status->mt_type = MT_ISSCSI2; + } } - /* Allocate the buffers */ - if (NR_ST == 1) - st_nbr_buffers = 1; - else - st_nbr_buffers = 2; + st_nbr_buffers = NR_ST; + if (st_nbr_buffers > st_max_buffers) + st_nbr_buffers = st_max_buffers; + st_buffers = (ST_buffer **)mem_start; + mem_start += st_nbr_buffers * sizeof(ST_buffer *); for (i=0; i < st_nbr_buffers; i++) { st_buffers[i] = (ST_buffer *) mem_start; #ifdef DEBUG - printk("st: Buffer address: %p\n", st_buffers[i]); +/* printk("st: Buffer address: %p\n", st_buffers[i]); */ #endif - mem_start += sizeof(ST_buffer) - 1 + ST_BUFFER_BLOCKS * ST_BLOCK_SIZE; - st_buffers[i]->mt_status = (struct mtget *) mem_start; - mem_start += sizeof(struct mtget); + mem_start += sizeof(ST_buffer) - 1 + st_buffer_size; st_buffers[i]->in_use = 0; st_buffers[i]->writing = 0; - - /* "generic" status */ - memset((void *) st_buffers[i]->mt_status, 0, sizeof(struct mtget)); - st_buffers[i]->mt_status->mt_type = MT_ISSCSI1; } return mem_start; diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/st.h linux/drivers/scsi/st.h --- v1.1.11/linux/drivers/scsi/st.h Thu Dec 30 11:39:04 1993 +++ linux/drivers/scsi/st.h Fri May 6 08:28:58 1994 @@ -11,7 +11,6 @@ typedef struct { int in_use; - struct mtget * mt_status; int buffer_size; int buffer_blocks; int buffer_bytes; @@ -33,12 +32,18 @@ unsigned in_use:1; unsigned eof_hit:1; unsigned drv_buffer:3; + unsigned do_buffer_writes:1; + unsigned do_async_writes:1; + unsigned do_read_ahead:1; unsigned char density; ST_buffer * buffer; int block_size; int min_block; int max_block; + int write_threshold; int recover_count; + int drv_block; /* The block where the drive head is */ + struct mtget * mt_status; Scsi_Cmnd SCpnt; } Scsi_Tape; diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c --- v1.1.11/linux/drivers/scsi/wd7000.c Thu Mar 31 11:44:57 1994 +++ linux/drivers/scsi/wd7000.c Wed Apr 6 21:49:04 1994 @@ -1,5 +1,5 @@ -/* $Id: wd7000.c,v 1.2 1994/01/15 06:02:32 drew Exp $ - * linux/kernel/wd7000.c +/* $Id: $ + * linux/drivers/scsi/wd7000.c * * Copyright (C) 1992 Thomas Wuensche * closely related to the aha1542 driver from Tommy Thorn @@ -9,6 +9,95 @@ * accomodate Eric Youngdale's modifications to scsi.c. Nov 1992. * * Additional changes to support scatter/gather. Dec. 1992. tw/jb + * + * No longer tries to reset SCSI bus at boot (it wasn't working anyway). + * Rewritten to support multiple host adapters. + * Miscellaneous cleanup. + * So far, still doesn't do reset or abort correctly, since I have no idea + * how to do them with this board (8^(. Jan 1994 jb + * + * This driver now supports both of the two standard configurations (per + * the 3.36 Owner's Manual, my latest reference) by the same method as + * before; namely, by looking for a BIOS signature. Thus, the location of + * the BIOS signature determines the board configuration. Until I have + * time to do something more flexible, users should stick to one of the + * following: + * + * Standard configuration for single-adapter systems: + * - BIOS at CE00h + * - I/O base address 350h + * - IRQ level 15 + * - DMA channel 6 + * Standard configuration for a second adapter in a system: + * - BIOS at C800h + * - I/O base address 330h + * - IRQ level 11 + * - DMA channel 5 + * + * Anyone who can recompile the kernel is welcome to add others as need + * arises, but unpredictable results may occur if there are conflicts. + * In any event, if there are multiple adapters in a system, they MUST + * use different I/O bases, IRQ levels, and DMA channels, since they will be + * indistinguishable (and in direct conflict) otherwise. + * + * As a point of information, the NO_OP command toggles the CMD_RDY bit + * of the status port, and this fact could be used as a test for the I/O + * base address (or more generally, board detection). There is an interrupt + * status port, so IRQ probing could also be done. I suppose the full + * DMA diagnostic could be used to detect the DMA channel being used. I + * haven't done any of this, though, because I think there's too much of + * a chance that such explorations could be destructive, if some other + * board's resources are used inadvertently. So, call me a wimp, but I + * don't want to try it. The only kind of exploration I trust is memory + * exploration, since it's more certain that reading memory won't be + * destructive. + * + * More to my liking would be a LILO boot command line specification, such + * as is used by the aha152x driver (and possibly others). I'll look into + * it, as I have time... + * + * I get mail occasionally from people who either are using or are + * considering using a WD7000 with Linux. There is a variety of + * nomenclature describing WD7000's. To the best of my knowledge, the + * following is a brief summary (from an old WD doc - I don't work for + * them or anything like that): + * + * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS + * installed. Last I heard, the BIOS was actually done by Columbia + * Data Products. The BIOS is only used by this driver (and thus + * by Linux) to identify the board; none of it can be executed under + * Linux. + * + * WD7000-ASC: This is the original adapter board, with or without BIOS. + * The board uses a WD33C93 or WD33C93A SBIC, which in turn is + * controlled by an onboard Z80 processor. The board interface + * visible to the host CPU is defined effectively by the Z80's + * firmware, and it is this firmware's revision level that is + * determined and reported by this driver. (The version of the + * on-board BIOS is of no interest whatsoever.) The host CPU has + * no access to the SBIC; hence the fact that it is a WD33C93 is + * also of no interest to this driver. + * + * WD7000-AX: + * WD7000-MX: + * WD7000-EX: These are newer versions of the WD7000-ASC. The -ASC is + * largely built from discrete components; these boards use more + * integration. The -AX is an ISA bus board (like the -ASC), + * the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an + * EISA bus board. + * + * At the time of my documentation, the -?X boards were "future" products, + * and were not yet available. However, I vaguely recall that Thomas + * Wuensche had an -AX, so I believe at least it is supported by this + * driver. I have no personal knowledge of either -MX or -EX boards. + * + * P.S. Just recently, I've discovered (directly from WD and Future + * Domain) that all but the WD7000-EX have been out of production for + * two years now. FD has production rights to the 7000-EX, and are + * producing it under a new name, and with a new BIOS. If anyone has + * one of the FD boards, it would be nice to come up with a signature + * for it. + * J.B. Jan 1994. */ #include @@ -17,6 +106,7 @@ #include #include #include +#include #include #include #include @@ -26,17 +116,179 @@ #include "scsi.h" #include "hosts.h" -/* #define DEBUG */ +#define ANY2SCSI_INLINE /* undef this to use old macros */ +#undef DEBUG #include "wd7000.h" -#ifdef DEBUG -#define DEB(x) x -#else -#define DEB(x) -#endif +/* + * Mailbox structure sizes. + * I prefer to keep the number of ICMBs much larger than the number of + * OGMBs. OGMBs are used very quickly by the driver to start one or + * more commands, while ICMBs are used by the host adapter per command. + */ +#define OGMB_CNT 16 +#define ICMB_CNT 32 + +/* + * Scb's are shared by all active adapters. So, if they all become busy, + * callers may be made to wait in alloc_scbs for them to free. That can + * be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q. If you'd + * rather conserve memory, use a smaller number (> 0, of course) - things + * will should still work OK. + */ +#define MAX_SCBS 32 + +/* + * WD7000-specific mailbox structure + * + */ +typedef volatile struct mailbox{ + unchar status; + unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */ +} Mailbox; + +/* + * This structure should contain all per-adapter global data. I.e., any + * new global per-adapter data should put in here. + * + */ +typedef struct adapter { + int num; /* Index into Scsi_hosts array */ + struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */ + int iobase; /* This adapter's I/O base address */ + int irq; /* This adapter's IRQ level */ + int dma; /* This adapter's DMA channel */ + struct { /* This adapter's mailboxes */ + Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */ + Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */ + } mb; + int next_ogmb; /* to reduce contention at mailboxes */ + unchar control; /* shadows CONTROL port value */ + unchar rev1, rev2; /* filled in by wd7000_revision */ +} Adapter; +/* + * The following is set up by wd7000_detect, and used thereafter by + * wd7000_intr_handle to map the irq level to the corresponding Adapter. + * Note that if request_irq instead of irqaction to allocate the IRQ, + * or if SA_INTERRUPT is not used, wd7000_intr_handle must be changed + * to pick up the IRQ level correctly. + */ +Adapter *irq2host[16] = {NULL}; /* Possible IRQs are 0-15 */ + +/* + * Standard Adapter Configurations - used by wd7000_detect + */ +typedef struct { + const void *bios; /* (linear) base address for ROM BIOS */ + int iobase; /* I/O ports base address */ + int irq; /* IRQ level */ + int dma; /* DMA channel */ +} Config; + +static const Config configs[] = { + {(void *) 0xce000, 0x350, 15, 6}, /* defaults for single adapter */ + {(void *) 0xc8000, 0x330, 11, 5}, /* defaults for second adapter */ + {(void *) 0xd8000, 0x350, 15, 6}, /* Arghhh.... who added this ? */ +}; +#define NUM_CONFIGS (sizeof(configs)/sizeof(Config)) + +/* + * The following list defines strings to look for in the BIOS that identify + * it as the WD7000-FASST2 SST BIOS. I suspect that something should be + * added for the Future Domain version. + */ +typedef struct signature { + void *sig; /* String to look for */ + unsigned ofs; /* offset from BIOS base address */ + unsigned len; /* length of string */ +} Signature; + +static const Signature signatures[] = { + {"SSTBIOS",0x0000d,7} /* "SSTBIOS" @ offset 0x0000d */ +}; +#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature)) + + +/* + * I/O Port Offsets and Bit Definitions + * 4 addresses are used. Those not defined here are reserved. + */ +#define ASC_STAT 0 /* Status, Read */ +#define ASC_COMMAND 0 /* Command, Write */ +#define ASC_INTR_STAT 1 /* Interrupt Status, Read */ +#define ASC_INTR_ACK 1 /* Acknowledge, Write */ +#define ASC_CONTROL 2 /* Control, Write */ + +/* ASC Status Port + */ +#define INT_IM 0x80 /* Interrupt Image Flag */ +#define CMD_RDY 0x40 /* Command Port Ready */ +#define CMD_REJ 0x20 /* Command Port Byte Rejected */ +#define ASC_INIT 0x10 /* ASC Initialized Flag */ +#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */ + +/* COMMAND opcodes + * + * Unfortunately, I have no idea how to properly use some of these commands, + * as the OEM manual does not make it clear. I have not been able to use + * enable/disable unsolicited interrupts or the reset commands with any + * discernable effect whatsoever. I think they may be related to certain + * ICB commands, but again, the OEM manual doesn't make that clear. + */ +#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */ +#define INITIALIZATION 1 /* initialization (10 bytes) */ +#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ +#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ +#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ +#define SOFT_RESET 5 /* SCSI bus soft reset */ +#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */ +#define START_OGMB 0x80 /* start command in OGMB (n) */ +#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ + /* where (n) = lower 6 bits */ +/* For INITIALIZATION: + */ +typedef struct initCmd { + unchar op; /* command opcode (= 1) */ + unchar ID; /* Adapter's SCSI ID */ + unchar bus_on; /* Bus on time, x 125ns (see below) */ + unchar bus_off; /* Bus off time, "" "" */ + unchar rsvd; /* Reserved */ + unchar mailboxes[3]; /* Address of Mailboxes, MSB first */ + unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */ + unchar icmbs; /* Number of incoming MBs, "" "" */ +} InitCmd; + +#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */ +#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */ + +/* Interrupt Status Port - also returns diagnostic codes at ASC reset + * + * if msb is zero, the lower bits are diagnostic status + * Diagnostics: + * 01 No diagnostic error occurred + * 02 RAM failure + * 03 FIFO R/W failed + * 04 SBIC register read/write failed + * 05 Initialization D-FF failed + * 06 Host IRQ D-FF failed + * 07 ROM checksum error + * Interrupt status (bitwise): + * 10NNNNNN outgoing mailbox NNNNNN is free + * 11NNNNNN incoming mailbox NNNNNN needs service + */ +#define MB_INTR 0xC0 /* Mailbox Service possible/required */ +#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */ +#define MB_MASK 0x3f /* mask for mailbox number */ + +/* CONTROL port bits + */ +#define INT_EN 0x08 /* Interrupt Enable */ +#define DMA_EN 0x04 /* DMA Enable */ +#define SCSI_RES 0x02 /* SCSI Reset */ +#define ASC_RES 0x01 /* ASC Reset */ /* Driver data structures: @@ -63,103 +315,327 @@ indices need not be involved. */ -static struct { - struct wd_mailbox ogmb[OGMB_CNT]; - struct wd_mailbox icmb[ICMB_CNT]; -} mb; -static int next_ogmb = 0; /* to reduce contention at mailboxes */ +/* + * WD7000-specific scatter/gather element structure + */ +typedef struct sgb { + unchar len[3]; + unchar ptr[3]; /* Also SCSI-style - MSB first */ +} Sgb; + +typedef struct scb { /* Command Control Block 5.4.1 */ + unchar op; /* Command Control Block Operation Code */ + unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ + /* Outbound data transfer, length is checked*/ + /* Inbound data transfer, length is checked */ + /* Logical Unit Number */ + unchar cdb[12]; /* SCSI Command Block */ + volatile unchar status; /* SCSI Return Status */ + volatile unchar vue; /* Vendor Unique Error Code */ + unchar maxlen[3]; /* Maximum Data Transfer Length */ + unchar dataptr[3]; /* SCSI Data Block Pointer */ + unchar linkptr[3]; /* Next Command Link Pointer */ + unchar direc; /* Transfer Direction */ + unchar reserved2[6]; /* SCSI Command Descriptor Block */ + /* end of hardware SCB */ + Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */ + Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */ + Adapter *host; /* host adapter */ + struct scb *next; /* for lists of scbs */ +} Scb; + +/* + * This driver is written to allow host-only commands to be executed. + * These use a 16-byte block called an ICB. The format is extended by the + * driver to 18 bytes, to support the status returned in the ICMB and + * an execution phase code. + * + * There are other formats besides these; these are the ones I've tried + * to use. Formats for some of the defined ICB opcodes are not defined + * (notably, get/set unsolicited interrupt status) in my copy of the OEM + * manual, and others are ambiguous/hard to follow. + */ +#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */ +#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */ +#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */ +#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */ +#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */ +#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */ +#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */ + /* 0x87 is reserved */ +#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */ +#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */ +#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */ +#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */ +#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */ +#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */ +#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */ +#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */ + +typedef struct icbRecvCmd { + unchar op; + unchar IDlun; /* Initiator SCSI ID/lun */ + unchar len[3]; /* command buffer length */ + unchar ptr[3]; /* command buffer address */ + unchar rsvd[7]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbRecvCmd; + +typedef struct icbSendStat { + unchar op; + unchar IDlun; /* Target SCSI ID/lun */ + unchar stat; /* (outgoing) completion status byte 1 */ + unchar rsvd[12]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbSendStat; + +typedef struct icbRevLvl { + unchar op; + volatile unchar primary; /* primary revision level (returned) */ + volatile unchar secondary; /* secondary revision level (returned) */ + unchar rsvd[12]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbRevLvl; + +typedef struct icbUnsMask { /* I'm totally guessing here */ + unchar op; + volatile unchar mask[14]; /* mask bits */ +#ifdef 0 + unchar rsvd[12]; /* reserved */ +#endif + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbUnsMask; + +typedef struct icbDiag { + unchar op; + unchar type; /* diagnostics type code (0-3) */ + unchar len[3]; /* buffer length */ + unchar ptr[3]; /* buffer address */ + unchar rsvd[7]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbDiag; + +#define ICB_DIAG_POWERUP 0 /* Power-up diags only */ +#define ICB_DIAG_WALKING 1 /* walking 1's pattern */ +#define ICB_DIAG_DMA 2 /* DMA - system memory diags */ +#define ICB_DIAG_FULL 3 /* do both 1 & 2 */ + +typedef struct icbParms { + unchar op; + unchar rsvd1; /* reserved */ + unchar len[3]; /* parms buffer length */ + unchar ptr[3]; /* parms buffer address */ + unchar idx[2]; /* index (MSB-LSB) */ + unchar rsvd2[5]; /* reserved */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbParms; + +typedef struct icbAny { + unchar op; + unchar data[14]; /* format-specific data */ + volatile unchar vue; /* vendor-unique error code */ + volatile unchar status; /* returned (icmb) status */ + volatile unchar phase; /* used by interrupt handler */ +} IcbAny; + +typedef union icb { + unchar op; /* ICB opcode */ + IcbRecvCmd recv_cmd; /* format for receive command */ + IcbSendStat send_stat; /* format for send status */ + IcbRevLvl rev_lvl; /* format for get revision level */ + IcbDiag diag; /* format for execute diagnostics */ + IcbParms eparms; /* format for get/set exec parms */ + IcbAny icb; /* generic format */ + unchar data[18]; +} Icb; + +/* + * Driver SCB structure pool. + * + * The SCBs declared here are shared by all host adapters; hence, this + * structure is not part of the Adapter structure. + */ static Scb scbs[MAX_SCBS]; -static Scb *scbfree = NULL; +static Scb *scbfree = NULL; /* free list */ +static int freescbs = MAX_SCBS; /* free list counter */ + +/* + * END of data/declarations - code follows. + */ + + +#ifdef ANY2SCSI_INLINE +/* + Since they're used a lot, I've redone the following from the macros + formerly in wd7000.h, hopefully to speed them up by getting rid of + all the shifting (it may not matter; GCC might have done as well anyway). + + xany2scsi and xscsi2int were not being used, and are no longer defined. + (They were simply 4-byte versions of these routines). +*/ + +typedef union { /* let's cheat... */ + int i; + unchar u[sizeof(int)]; /* the sizeof(int) makes it more portable */ +} i_u; -static int wd7000_host = 0; -static unchar controlstat = 0; -static unchar rev_1 = 0, rev_2 = 0; /* filled in by wd7000_revision */ +static inline void any2scsi( unchar *scsi, int any ) +{ + *scsi++ = ((i_u) any).u[2]; + *scsi++ = ((i_u) any).u[1]; + *scsi++ = ((i_u) any).u[0]; +} -#define wd7000_intr_ack() outb(0,INTR_ACK) -#define WAITnexttimeout 3000000 +static inline int scsi2int( unchar *scsi ) +{ + i_u result; + result.i = 0; /* clears unused bytes */ + *(result.u+2) = *scsi++; + *(result.u+1) = *scsi++; + *(result.u) = *scsi++; + return result.i; +} +#else +/* + These are the old ones - I've just moved them here... +*/ +#undef any2scsi +#define any2scsi(up, p) \ +(up)[0] = (((unsigned long)(p)) >> 16); \ +(up)[1] = ((unsigned long)(p)) >> 8; \ +(up)[2] = ((unsigned long)(p)); + +#undef scsi2int +#define scsi2int(up) ( (((unsigned long)*(up)) << 16) + \ + (((unsigned long)(up)[1]) << 8) + ((unsigned long)(up)[2]) ) +#endif -static inline void wd7000_enable_intr(void) + +static inline void wd7000_enable_intr(Adapter *host) { - controlstat |= INT_EN; - outb(controlstat,CONTROL); + host->control |= INT_EN; + outb(host->control, host->iobase+ASC_CONTROL); } -static inline void wd7000_enable_dma(void) +static inline void wd7000_enable_dma(Adapter *host) { - controlstat |= DMA_EN; - outb(controlstat,CONTROL); - set_dma_mode(DMA_CH, DMA_MODE_CASCADE); - enable_dma(DMA_CH); + host->control |= DMA_EN; + outb(host->control,host->iobase+ASC_CONTROL); + set_dma_mode(host->dma, DMA_MODE_CASCADE); + enable_dma(host->dma); } +#define WAITnexttimeout 200 /* 2 seconds */ + #define WAIT(port, mask, allof, noneof) \ - { register WAITbits; \ - register WAITtimeout = WAITnexttimeout; \ + { register volatile unsigned WAITbits; \ + register unsigned long WAITtimeout = jiffies + WAITnexttimeout; \ while (1) { \ WAITbits = inb(port) & (mask); \ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \ break; \ - if (--WAITtimeout == 0) goto fail; \ + if (jiffies > WAITtimeout) goto fail; \ } \ } static inline void delay( unsigned how_long ) { - unsigned long time = jiffies + how_long; + register unsigned long time = jiffies + how_long; while (jiffies < time); } -static inline int command_out(unchar *cmdp, int len) +static inline int command_out(Adapter *host, unchar *cmd, int len) { - if(len == 1) { - while(1==1){ - WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); - cli(); - if(!(inb(ASC_STAT) & CMD_RDY)) {sti(); continue;} - outb(*cmdp, COMMAND); - sti(); - return 1; - } - } else { - cli(); + WAIT(host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0); while (len--) { - WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); - outb(*cmdp++, COMMAND); + do { + outb(*cmd, host->iobase+ASC_COMMAND); + WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0); + } while (inb(host->iobase+ASC_STAT) & CMD_REJ); + cmd++; } - sti(); - } return 1; fail: - sti(); - printk("wd7000_out WAIT failed(%d): ", len+1); + printk("wd7000 command_out: WAIT failed(%d)\n", len+1); return 0; } -static inline Scb *alloc_scb(void) + +/* + * This version of alloc_scbs is in preparation for supporting multiple + * commands per lun and command chaining, by queueing pending commands. + * We will need to allocate Scbs in blocks since they will wait to be + * executed so there is the possibility of deadlock otherwise. + * Also, to keep larger requests from being starved by smaller requests, + * we limit access to this routine with an internal busy flag, so that + * the satisfiability of a request is not dependent on the size of the + * request. + */ +static inline Scb *alloc_scbs(int needed) { - Scb *scb; - unsigned long flags; + register Scb *scb, *p; + register unsigned long flags; + register unsigned long timeout = jiffies + WAITnexttimeout; + register unsigned long now; + static int busy = 0; + int i; + if (needed <= 0) return NULL; /* sanity check */ + save_flags(flags); cli(); + while (busy) { /* someone else is allocating */ + sti(); + now = jiffies; while (jiffies == now) /* wait a jiffy */; + cli(); + } + busy = 1; /* not busy now; it's our turn */ - if (scbfree == NULL) { - panic("wd7000: can't allocate free SCB.\n"); - restore_flags(flags); - return NULL; + while (freescbs < needed) { + timeout = jiffies + WAITnexttimeout; + do { + sti(); + now = jiffies; while (jiffies == now) /* wait a jiffy */; + cli(); + } while (freescbs < needed && jiffies <= timeout); + /* + * If we get here with enough free Scbs, we can take them. + * Otherwise, we timed out and didn't get enough. + */ + if (freescbs < needed) { + busy = 0; + panic("wd7000: can't get enough free SCBs.\n"); + restore_flags(flags); + return NULL; + } } - scb = scbfree; scbfree = scb->next; - memset(scb, 0, sizeof(Scb)); scb->next = NULL; + scb = scbfree; freescbs -= needed; + for (i = 0; i < needed; i++) { p = scbfree; scbfree = p->next; } + p->next = NULL; + + busy = 0; /* we're done */ restore_flags(flags); @@ -169,13 +645,14 @@ static inline void free_scb( Scb *scb ) { - unsigned long flags; + register unsigned long flags; save_flags(flags); cli(); memset(scb, 0, sizeof(Scb)); scb->next = scbfree; scbfree = scb; + freescbs++; restore_flags(flags); } @@ -190,62 +667,74 @@ cli(); scbfree = &(scbs[0]); - for (i = 0; i < MAX_SCBS-1; i++) scbs[i].next = &(scbs[i+1]); + memset(scbs, 0, sizeof(scbs)); + for (i = 0; i < MAX_SCBS-1; i++) { + scbs[i].next = &(scbs[i+1]); scbs[i].SCpnt = NULL; + } scbs[MAX_SCBS-1].next = NULL; + scbs[MAX_SCBS-1].SCpnt = NULL; restore_flags(flags); } -static int mail_out( Scb *scbptr ) +static int mail_out( Adapter *host, Scb *scbptr ) /* * Note: this can also be used for ICBs; just cast to the parm type. */ { - int i, ogmb; - unsigned char start_cmd; - unsigned long flags; - - DEB(printk("wd7000_scb_out: %06x");) - + register int i, ogmb; + register unsigned long flags; + unchar start_ogmb; + Mailbox *ogmbs = host->mb.ogmb; + int *next_ogmb = &(host->next_ogmb); +#ifdef DEBUG + printk("wd7000 mail_out: %06x",(unsigned int) scbptr); +#endif /* We first look for a free outgoing mailbox */ save_flags(flags); cli(); - ogmb = next_ogmb; + ogmb = *next_ogmb; for (i = 0; i < OGMB_CNT; i++) { - if (mb.ogmb[ogmb].status == 0) { - DEB(printk(" using OGMB %x",ogmb)); - mb.ogmb[ogmb].status = 1; - any2scsi(mb.ogmb[ogmb].scbptr, scbptr); + if (ogmbs[ogmb].status == 0) { +#ifdef DEBUG + printk(" using OGMB %x",ogmb); +#endif + ogmbs[ogmb].status = 1; + any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); - next_ogmb = (ogmb+1) % OGMB_CNT; + *next_ogmb = (ogmb+1) % OGMB_CNT; break; } else ogmb = (++ogmb) % OGMB_CNT; } restore_flags(flags); - DEB(printk(", scb is %x",scbptr);) - +#ifdef DEBUG + printk(", scb is %x",(unsigned int) scbptr); +#endif if (i >= OGMB_CNT) { - DEB(printk(", no free OGMBs.\n");) - /* Alternatively, issue "interrupt on free OGMB", and sleep... */ - return 0; + /* + * Alternatively, we might issue the "interrupt on free OGMB", + * and sleep, but it must be ensured that it isn't the init + * task running. Instead, this version assumes that the caller + * will be persistent, and try again. Since it's the adapter + * that marks OGMB's free, waiting even with interrupts off + * should work, since they are freed very quickly in most cases. + */ + #ifdef DEBUG + printk(", no free OGMBs.\n"); +#endif + return 0; } - start_cmd = START_OGMB | ogmb; - - wd7000_enable_intr(); - do { - command_out(&start_cmd, 1); - WAIT(ASC_STAT,STATMASK,CMD_RDY,0); - } while (inb(ASC_STAT) & CMD_REJ); + wd7000_enable_intr(host); - DEB(printk(", awaiting interrupt.\n");) + start_ogmb = START_OGMB | ogmb; + command_out( host, &start_ogmb, 1 ); +#ifdef DEBUG + printk(", awaiting interrupt.\n"); +#endif return 1; - -fail: - DEB(printk(", WAIT timed out.\n");) - return 0; } @@ -300,135 +789,159 @@ static void wd7000_scsi_done(Scsi_Cmnd * SCpnt) { - DEB(printk("wd7000_scsi_done: %06x\n",SCpnt);) +#ifdef DEBUG + printk("wd7000_scsi_done: %06x\n",(unsigned int) SCpnt); +#endif SCpnt->SCp.phase = 0; } +#define wd7000_intr_ack(host) outb(0,host->iobase+ASC_INTR_ACK) + void wd7000_intr_handle(int irq) { - int flag, icmb, errstatus, icmb_status; - int host_error, scsi_error; - Scb *scb; /* for SCSI commands */ - unchar *icb; /* for host commands */ - Scsi_Cmnd *SCpnt; - - flag = inb(INTR_STAT); - DEB(printk("wd7000_intr_handle: intr stat = %02x",flag);) - - if (!(inb(ASC_STAT)&0x80)){ - DEB(printk("\nwd7000_intr_handle: phantom interrupt...\n");) - wd7000_intr_ack(); +#ifdef 0 + /* + * Use irqp as the parm, and the following declaration, if request_irq + * is used or if SA_INTERRUPT is not used. + */ + register int irq = *(((int *)irqp)-2); +#endif + register int flag, icmb, errstatus, icmb_status; + register int host_error, scsi_error; + register Scb *scb; /* for SCSI commands */ + register IcbAny *icb; /* for host commands */ + register Scsi_Cmnd *SCpnt; + Adapter *host = irq2host[irq]; /* This MUST be set!!! */ + Mailbox *icmbs = host->mb.icmb; + +#ifdef DEBUG + printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host); +#endif + + flag = inb(host->iobase+ASC_INTR_STAT); +#ifdef DEBUG + printk("wd7000_intr_handle: intr stat = %02x\n",flag); +#endif + + if (!(inb(host->iobase+ASC_STAT) & INT_IM)) { + /* NB: these are _very_ possible if IRQ 15 is being used, since + it's the "garbage collector" on the 2nd 8259 PIC. Specifically, + any interrupt signal into the 8259 which can't be identified + comes out as 7 from the 8259, which is 15 to the host. Thus, it + is a good thing the WD7000 has an interrupt status port, so we + can sort these out. Otherwise, electrical noise and other such + problems would be indistinguishable from valid interrupts... + */ +#ifdef DEBUG + printk("wd7000_intr_handle: phantom interrupt...\n"); +#endif + wd7000_intr_ack(host); return; } - /* check for an incoming mailbox */ - if ((flag & 0x40) == 0) { - /* for a free OGMB - need code for this case... */ - DEB(printk("wd7000_intr_handle: free outgoing mailbox\n");) - wd7000_intr_ack(); - return; - } - /* The interrupt is for an incoming mailbox */ - icmb = flag & 0x3f; - scb = (struct scb *) scsi2int(mb.icmb[icmb].scbptr); - icmb_status = mb.icmb[icmb].status; - mb.icmb[icmb].status = 0; - -#ifdef DEBUG - printk(" ICMB %d posted for SCB/ICB %06x, status %02x, vue %02x", - icmb, scb, icmb_status, scb->vue ); -#endif - - if (!(scb->op & 0x80)) { /* an SCB is done */ - SCpnt = scb->SCpnt; - if (--(SCpnt->SCp.phase) <= 0) { /* all scbs for SCpnt are done */ - host_error = scb->vue | (icmb_status << 8); - scsi_error = scb->status; - errstatus = make_code(host_error,scsi_error); - SCpnt->result = errstatus; - - if (SCpnt->host_scribble != NULL) - scsi_free(SCpnt->host_scribble,WD7000_SCRIBBLE); - free_scb(scb); - - SCpnt->scsi_done(SCpnt); - } - } else { /* an ICB is done */ - icb = (unchar *) scb; - icb[ICB_STATUS] = icmb_status; - icb[ICB_PHASE] = 0; + if (flag & MB_INTR) { + /* The interrupt is for a mailbox */ + if (!(flag & IMB_INTR)) { +#ifdef DEBUG + printk("wd7000_intr_handle: free outgoing mailbox"); +#endif + /* + * If sleep_on() and the "interrupt on free OGMB" command are + * used in mail_out(), wake_up() should correspondingly be called + * here. For now, we don't need to do anything special. + */ + wd7000_intr_ack(host); + return; + } else { + /* The interrupt is for an incoming mailbox */ + icmb = flag & MB_MASK; + icmb_status = icmbs[icmb].status; + if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ +#ifdef DEBUG + printk("wd7000_intr_handle: unsolicited interrupt %02xh\n", + icmb_status); +#endif + wd7000_intr_ack(host); + return; + } + scb = (struct scb *) scsi2int((unchar *)icmbs[icmb].scbptr); + icmbs[icmb].status = 0; + if (!(scb->op & ICB_OP_MASK)) { /* an SCB is done */ + SCpnt = scb->SCpnt; + if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */ + host_error = scb->vue | (icmb_status << 8); + scsi_error = scb->status; + errstatus = make_code(host_error,scsi_error); + SCpnt->result = errstatus; + + free_scb(scb); + + SCpnt->scsi_done(SCpnt); + } + } else { /* an ICB is done */ + icb = (IcbAny *) scb; + icb->status = icmb_status; + icb->phase = 0; + } + } /* incoming mailbox */ } - wd7000_intr_ack(); - DEB(printk(".\n");) + wd7000_intr_ack(host); return; } int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) { - Scb *scb; - Sgb *sgb; - unchar *cdb; - unchar idlun; - short cdblen; + register Scb *scb; + register Sgb *sgb; + register unchar *cdb = (unchar *) SCpnt->cmnd; + register unchar idlun; + register short cdblen; + Adapter *host = (Adapter *) SCpnt->host->hostdata; - cdb = (unchar *) SCpnt->cmnd; - cdblen = COMMAND_SIZE(*cdb); + cdblen = COMMAND_SIZE(cdb[0]); idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; - scb = alloc_scb(); + scb = alloc_scbs(1); scb->idlun = idlun; memcpy(scb->cdb, cdb, cdblen); scb->direc = 0x40; /* Disable direction check */ + scb->SCpnt = SCpnt; /* so we can find stuff later */ - SCpnt->host_scribble = NULL; - DEB(printk("request_bufflen is %x, bufflen is %x\n",\ - SCpnt->request_bufflen, SCpnt->bufflen);) + SCpnt->host_scribble = (unchar *) scb; + scb->host = host; if (SCpnt->use_sg) { struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; unsigned i; - if (scsi_hosts[wd7000_host].sg_tablesize <= 0) { + if (SCpnt->host->sg_tablesize == SG_NONE) { panic("wd7000_queuecommand: scatter/gather not supported.\n"); } #ifdef DEBUG printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg); #endif - /* - Allocate memory for a scatter/gather-list in wd7000 format. - Save the pointer at host_scribble. - */ -#ifdef DEBUG - if (SCpnt->use_sg > WD7000_SG) - panic("WD7000: requesting too many scatterblocks\n"); -#endif - SCpnt->host_scribble = (unsigned char *) scsi_malloc(WD7000_SCRIBBLE); - sgb = (Sgb *) SCpnt->host_scribble; - if (sgb == NULL) - panic("wd7000_queuecommand: scsi_malloc() failed.\n"); + sgb = scb->sgb; scb->op = 1; - any2scsi(scb->dataptr, sgb); + any2scsi(scb->dataptr, (int) sgb); any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) ); for (i = 0; i < SCpnt->use_sg; i++) { - any2scsi(sgb->ptr, sg[i].address); - any2scsi(sgb->len, sg[i].length); - sgb++; + any2scsi(sgb[i].ptr, (int) sg[i].address); + any2scsi(sgb[i].len, sg[i].length); } - DEB(printk("Using %d bytes for %d scatter/gather blocks\n",\ - scsi2int(scb->maxlen), SCpnt->use_sg);) } else { scb->op = 0; - any2scsi(scb->dataptr, SCpnt->request_buffer); + any2scsi(scb->dataptr, (int) SCpnt->request_buffer); any2scsi(scb->maxlen, SCpnt->request_bufflen); } + while (!mail_out(host, scb)) /* keep trying */; - return mail_out(scb); + return 1; } @@ -442,192 +955,292 @@ } -int wd7000_init(void) -{ int i; - unchar init_block[] = { - INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT +int wd7000_diagnostics( Adapter *host, int code ) +{ + static IcbDiag icb = {ICB_OP_DIAGNOSTICS}; + static unchar buf[256]; + unsigned long timeout; + + icb.type = code; + any2scsi(icb.len, sizeof(buf)); + any2scsi(icb.ptr, (int) &buf); + icb.phase = 1; + /* + * This routine is only called at init, so there should be OGMBs + * available. I'm assuming so here. If this is going to + * fail, I can just let the timeout catch the failure. + */ + mail_out(host, (struct scb *) &icb); + timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ + while (icb.phase && jiffies < timeout) /* wait for completion */; + + if (icb.phase) { + printk("wd7000_diagnostics: timed out.\n"); + return 0; + } + if (make_code(icb.vue|(icb.status << 8),0)) { + printk("wd7000_diagnostics: failed (%02x,%02x)\n", + icb.vue, icb.status); + return 0; + } + + return 1; +} + + +int wd7000_init( Adapter *host ) +{ + InitCmd init_cmd = { + INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0,0,0, OGMB_CNT, ICMB_CNT }; + struct sigaction sa = {wd7000_intr_handle, 0, SA_INTERRUPT, NULL}; + int diag; - /* Reset the adapter. */ - outb(SCSI_RES|ASC_RES, CONTROL); - delay(1); /* reset pulse: this is 10ms, only need 25us */ - outb(0,CONTROL); controlstat = 0; /* - Wait 2 seconds, then expect Command Port Ready. - - I suspect something else needs to be done here, but I don't know - what. The OEM doc says power-up diagnostics take 2 seconds, and - indeed, SCSI commands submitted before then will time out, but - none of what follows seems deterred by _not_ waiting 2 secs. + Reset the adapter - only. The SCSI bus was initialized at power-up, + and we need to do this just so we control the mailboxes, etc. */ - delay(200); - - WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); - DEB(printk("wd7000_init: Power-on Diagnostics finished\n");) - if (((i=inb(INTR_STAT)) != 1) && (i != 7)) { - panic("wd7000_init: Power-on Diagnostics error\n"); - return 0; + outb(ASC_RES, host->iobase+ASC_CONTROL); + delay(1); /* reset pulse: this is 10ms, only need 25us */ + outb(0,host->iobase+ASC_CONTROL); + host->control = 0; /* this must always shadow ASC_CONTROL */ + WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0); + + if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1) { + printk("wd7000_init: "); + switch (diag) { + case 2: + printk("RAM failure.\n"); + break; + case 3: + printk("FIFO R/W failed\n"); + break; + case 4: + printk("SBIC register R/W failed\n"); + break; + case 5: + printk("Initialization D-FF failed.\n"); + break; + case 6: + printk("Host IRQ D-FF failed.\n"); + break; + case 7: + printk("ROM checksum error.\n"); + break; + default: + printk("diagnostic code %02Xh received.\n", diag); + break; + } + return 0; } /* Clear mailboxes */ - memset(&mb,0,sizeof (mb)); - /* Set up SCB free list */ - init_scbs(); + memset(&(host->mb), 0, sizeof(host->mb)); - /* Set up init block */ - any2scsi(init_block+5,&mb); /* Execute init command */ - if (!command_out(init_block,sizeof(init_block))) { - panic("WD-7000 Initialization failed.\n"); + any2scsi((unchar *) &(init_cmd.mailboxes), (int) &(host->mb)); + if (!command_out(host, (unchar *) &init_cmd, sizeof(init_cmd))) { + printk("wd7000_init: adapter initialization failed.\n"); return 0; } - - /* Wait until init finished */ - WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); - outb(DISABLE_UNS_INTR, COMMAND); - WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); - - /* Enable Interrupt and DMA */ - if (request_irq(IRQ_LVL, wd7000_intr_handle)) { - panic("Unable to allocate IRQ for WD-7000.\n"); - return 0; - }; - if(request_dma(DMA_CH)) { - panic("Unable to allocate DMA channel for WD-7000.\n"); - free_irq(IRQ_LVL); - return 0; - }; - wd7000_enable_dma(); - wd7000_enable_intr(); + WAIT(host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0); + + if (irqaction(host->irq, &sa)) { + printk("wd7000_init: can't get IRQ %d.\n", host->irq); + return 0; + } + if (request_dma(host->dma)) { + printk("wd7000_init: can't get DMA channel %d.\n", host->dma); + free_irq(host->irq); + return 0; + } + wd7000_enable_dma(host); + wd7000_enable_intr(host); + + if (!wd7000_diagnostics(host,ICB_DIAG_FULL)) { + free_dma(host->dma); + free_irq(host->irq); + return 0; + } - printk("WD-7000 initialized.\n"); return 1; + fail: + printk("wd7000_init: WAIT timed out.\n"); return 0; /* 0 = not ok */ } -void wd7000_revision(void) +void wd7000_revision(Adapter *host) { - volatile unchar icb[ICB_LEN] = {0x8c}; /* read firmware revision level */ - - icb[ICB_PHASE] = 1; - mail_out( (struct scb *) icb ); - while (icb[ICB_PHASE]) /* wait for completion */; - rev_1 = icb[1]; - rev_2 = icb[2]; + static IcbRevLvl icb = {ICB_OP_GET_REVISION}; + icb.phase = 1; /* - For boards at rev 7.0 or later, enable scatter/gather. - */ - if (rev_1 >= 7) scsi_hosts[wd7000_host].sg_tablesize = WD7000_SG; + * Like diagnostics, this is only done at init time, in fact, from + * wd7000_detect, so there should be OGMBs available. If it fails, + * the only damage will be that the revision will show up as 0.0, + * which in turn means that scatter/gather will be disabled. + */ + mail_out(host, (struct scb *) &icb); + while (icb.phase) /* wait for completion */; + host->rev1 = icb.primary; + host->rev2 = icb.secondary; } -static const char *wd_bases[] = {(char *)0xce000,(char *)0xd8000}; - -typedef struct { - char * signature; - unsigned offset; - unsigned length; -} Signature; - -static const Signature signatures[] = {{"SSTBIOS",0xd,0x7}}; - -#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature)) - - int wd7000_detect(int hostnum) /* - * return non-zero on detection + * Returns the number of adapters this driver is supporting. + * + * The source for hosts.c says to wait to call scsi_register until 100% + * sure about an adapter. We need to do it a little sooner here; we + * need the storage set up by scsi_register before wd7000_init, and + * changing the location of an Adapter structure is more trouble than + * calling scsi_unregister. + * */ { - int i,j; - char const *base_address = NULL; + int i,j, present = 0; + const Config *cfg; + const Signature *sig; + Adapter *host = NULL; + struct Scsi_Host *sh; + + /* Set up SCB free list, which is shared by all adapters */ + init_scbs(); - if(check_region(IO_BASE, 4)) return 0; /* IO ports in use */ - for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){ - for(j=0;jbios+sig->ofs, sig->sig, sig->len)) { + /* matched this one */ +#ifdef DEBUG + printk("WD-7000 SST BIOS detected at %04X: checking...\n", + (int) cfg->bios); +#endif + /* + * We won't explicitly test the configuration (in this + * version); instead, we'll just see if it works to + * setup the adapter; if it does, we'll use it. + */ + if (check_region(cfg->iobase, 4)) { /* ports in use */ + printk("IO %xh already in use.\n", host->iobase); + continue; + } + /* + * We register here, to get a pointer to the extra space, + * which we'll use as the Adapter structure (host) for + * this adapter. It is located just after the registered + * Scsi_Host structure (sh), and is located by the empty + * array hostdata. + */ + sh = scsi_register( hostnum, sizeof(Adapter) ); + host = (Adapter *) sh->hostdata; +#ifdef DEBUG + printk("wd7000_detect: adapter allocated at %06x\n", + (int)host); +#endif + memset( host, 0, sizeof(Adapter) ); + host->num = hostnum; host->sh = sh; + host->irq = cfg->irq; + host->iobase = cfg->iobase; + host->dma = cfg->dma; + irq2host[host->irq] = host; + + if (!wd7000_init(host)) { /* Initialization failed */ + scsi_unregister( sh, sizeof(Adapter) ); + continue; + } + + /* + * OK from here - we'll use this adapter/configuration. + */ + wd7000_revision(host); /* important for scatter/gather */ + + printk("Western Digital WD-7000 (%d.%d) ", + host->rev1, host->rev2); + printk("using IO %xh IRQ %d DMA %d.\n", + host->iobase, host->irq, host->dma); + + snarf_region(host->iobase, 4); /* Register our ports */ + /* + * For boards before rev 6.0, scatter/gather isn't supported. + */ + if (host->rev1 < 6) sh->sg_tablesize = SG_NONE; + + present++; /* count it */ + break; /* don't try any more sigs */ + } + sig++; /* try next signature with this configuration */ } + cfg++; /* try next configuration */ } - if (base_address == NULL) return 0; - snarf_region(IO_BASE, 4); /* Register our ports */ - /* Store our host number */ - wd7000_host = hostnum; - - wd7000_init(); - wd7000_revision(); /* will set scatter/gather by rev level */ - - return 1; + return present; } - -static void wd7000_append_info( char *info, const char *fmt, ... ) /* - * This is just so I can use vsprintf... + * I have absolutely NO idea how to do an abort with the WD7000... */ -{ - va_list args; - extern int vsprintf(char *buf, const char *fmt, va_list args); - - va_start(args, fmt); - vsprintf(info, fmt, args); - va_end(args); - - return; -} - - -const char *wd7000_info(void) -{ - static char info[80] = "Western Digital WD-7000, Firmware Revision "; - - wd7000_revision(); - wd7000_append_info( info+strlen(info), "%d.%d.\n", rev_1, rev_2 ); - - return info; -} - int wd7000_abort(Scsi_Cmnd * SCpnt, int i) { #ifdef DEBUG - printk("wd7000_abort: Scsi_Cmnd = 0x%08x, code = %d ", SCpnt, i); + printk("wd7000_abort: Scsi_Cmnd = 0x%06x, code = %d ", (int) SCpnt, i); printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun); - { int j; unchar *cdbj = (unchar *) SCpnt->cmnd; - for (j=0; j < COMMAND_SIZE(*cdbj); j++) printk(" %02x", *(cdbj++)); - printk(" result %08x\n", SCpnt->result); + { + int j; unchar *cdbj = (unchar *) SCpnt->cmnd; + for (j=0; j < COMMAND_SIZE(*cdbj); j++) printk(" %02x", *(cdbj++)); + printk(" result %08x\n", SCpnt->result); } #endif return 0; } -/* We do not implement a reset function here, but the upper level code assumes - that it will get some kind of response for the command in SCpnt. We must - oblige, or the command will hang the scsi system */ - +/* + * I also have no idea how to do a reset... + */ int wd7000_reset(Scsi_Cmnd * SCpnt) { #ifdef DEBUG - printk("wd7000_reset\n"); + printk("wd7000_reset: Scsi_Cmnd = 0x%06x ", (int) SCpnt); + if (SCpnt) { + printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun); + { + int j; unchar *cdbj = (unchar *) SCpnt->cmnd; + for (j=0; j < COMMAND_SIZE(*cdbj); j++) + printk(" %02x", *(cdbj++)); + printk(" result %08x", SCpnt->result); + } + } + printk("\n"); #endif if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART; return 0; } -int wd7000_biosparam(int size, int dev, int* ip) +/* + * The info routine in the WD7000 structure isn't per-adapter, so it can't + * really return any useful information about an adapter. Because of this, + * I'm no longer using it to return rev. level. + */ +const char *wd7000_info(void) +{ + static char info[] = "Western Digital WD-7000"; + return info; +} + + /* - * This is borrowed directly from aha1542.c, but my disks are organized - * this way, so I think it will work OK. + * This was borrowed directly from aha1542.c, but my disks are organized + * this way, so I think it will work OK. Someone who is ambitious can + * borrow a newer or more complete version from another driver. */ +int wd7000_biosparam(int size, int dev, int* ip) { ip[0] = 64; ip[1] = 32; @@ -635,4 +1248,3 @@ /* if (ip[2] >= 1024) ip[2] = 1024; */ return 0; } - diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h --- v1.1.11/linux/drivers/scsi/wd7000.h Wed Dec 1 14:44:16 1993 +++ linux/drivers/scsi/wd7000.h Wed Apr 6 21:16:39 1994 @@ -4,169 +4,14 @@ * * Header file for the WD-7000 driver for Linux * - * $Log: $ - * Revision 1.1 1992/07/24 06:27:38 root - * Initial revision + * John Boyd Jan 1994: + * This file has been reduced to only the definitions needed for the + * WD7000 host structure. * - * Revision 1.1 1992/07/05 08:32:32 root - * Initial revision - * - * Revision 1.1 1992/05/15 18:38:05 root - * Initial revision - * - * Revision 1.1 1992/04/02 03:23:13 drew - * Initial revision - * - * Revision 1.3 1992/01/27 14:46:29 tthorn - * *** empty log message *** - * */ #include -#undef STATMASK -#undef CONTROL - -#define IO_BASE 0x350 -#define IRQ_LVL 15 -#define DMA_CH 6 -#define OGMB_CNT 8 -#define ICMB_CNT 16 - -/* I/O Port interface 4.2 */ -/* READ */ -#define ASC_STAT IO_BASE -#define INT_IM 0x80 /* Interrupt Image Flag */ -#define CMD_RDY 0x40 /* Command Port Ready */ -#define CMD_REJ 0x20 /* Command Port Byte Rejected */ -#define ASC_INI 0x10 /* ASC Initialized Flag */ -#define STATMASK 0xf0 /* The lower 4 Bytes are reserved */ - -/* This register serves two purposes - * Diagnostics error code - * Interrupt Status - */ -#define INTR_STAT ASC_STAT+1 -#define ANYINTR 0x80 /* Mailbox Service possible/required */ -#define IMB 0x40 /* 1 Incoming / 0 Outgoing */ -#define MBMASK 0x3f -/* if MSb is zero, the lower bits are diagnostic status * - * Diagnostics: - * 01 No diagnostic error occurred - * 02 RAM failure - * 03 FIFO R/W failed - * 04 SBIC register read/write failed - * 05 Initialization D-FF failed - * 06 Host IRQ D-FF failed - * 07 ROM checksum error - * Interrupt status (bitwise): - * 10NNNNNN outgoing mailbox NNNNNN is free - * 11NNNNNN incoming mailbox NNNNNN needs service - */ - -/* WRITE */ -#define COMMAND ASC_STAT -/* - * COMMAND opcodes - */ -#define NO_OP 0 -#define INITIALIZATION 1 /* initialization after reset (10 bytes) */ -#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */ -#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */ -#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */ -#define SCSI_SOFT_RESET 5 /* SCSI soft reset */ -#define SCSI_HARD_RESET 6 /* SCSI hard reset acknowledge */ -#define START_OGMB 0x80 /* start command in OGMB (n) */ -#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */ - /* where (n) = lower 6 bits */ -/* - * For INITIALIZATION: - */ -#define BUS_ON 48 /* x 125ns, 48 = 6000ns, BIOS uses 8000ns */ -#define BUS_OFF 24 /* x 125ns, 24 = 3000ns, BIOS uses 1875ns */ - -#define INTR_ACK ASC_STAT+1 - - -#define CONTROL ASC_STAT+2 -#define INT_EN 0x08 /* Interrupt Enable */ -#define DMA_EN 0x04 /* DMA Enable */ -#define SCSI_RES 0x02 /* SCSI Reset */ -#define ASC_RES 0x01 /* ASC Reset */ - -/* Mailbox Definition */ - -struct wd_mailbox{ - unchar status; - unchar scbptr[3]; -}; - - -/* These belong in scsi.h also */ -#undef any2scsi -#define any2scsi(up, p) \ -(up)[0] = (((long)(p)) >> 16); \ -(up)[1] = ((long)(p)) >> 8; \ -(up)[2] = ((long)(p)); - -#define scsi2int(up) ( (((long)*(up)) << 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 - -typedef struct scb { /* Command Control Block 5.4.1 */ - unchar op; /* Command Control Block Operation Code */ - unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */ - /* Outbound data transfer, length is checked*/ - /* Inbound data transfer, length is checked */ - /* Logical Unit Number */ - unchar cdb[12]; /* SCSI Command Block */ - unchar status; /* SCSI Return Status */ - unchar vue; /* Vendor Unique Error Code */ - unchar maxlen[3]; /* Maximum Data Transfer Length */ - unchar dataptr[3]; /* SCSI Data Block Pointer */ - unchar linkptr[3]; /* Next Command Link Pointer */ - unchar direc; /* Transfer Direction */ - unchar reserved2[6]; /* SCSI Command Descriptor Block */ - /* end of hardware SCB */ - Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */ - struct scb *next; /* for lists of scbs */ -} Scb; - -/* - * WD7000-specific scatter/gather element structure - */ -typedef struct sgb { - unchar len[3]; - unchar ptr[3]; -} Sgb; - -/* - * Note: MAX_SCBS _must_ be defined large enough to keep ahead of the - * demand for SCBs, which will be at most WD7000_Q * WD7000_SG. 1 is - * added to each because they can be 0. - */ -#define MAX_SCBS ((WD7000_Q+1) * (WD7000_SG+1)) - -/* - * The driver is written to allow host-only commands to be executed. These - * use a 16-byte block called an ICB. - * - * (Currently, only wd7000_info uses this, to get the firmware rev. level.) - */ -#define ICB_STATUS 16 /* set to icmb status by wd7000_intr_handle */ -#define ICB_PHASE 17 /* set to 0 by wd7000_intr_handle */ -#define ICB_LEN 18 /* actually 16; this includes the above */ - int wd7000_detect(int); int wd7000_command(Scsi_Cmnd *); int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); @@ -176,30 +21,32 @@ int wd7000_biosparam(int, int, int*); #ifndef NULL - #define NULL 0 +#define NULL 0L #endif /* - * Define WD7000_SG to be the number of Sgbs that will fit in a block of - * size WD7000_SCRIBBLE. WD7000_SCRIBBLE must be 512, 1024, 2048, or 4096. - * - * The sg_tablesize value will default to SG_NONE for older boards (before - * rev 7.0), but will be changed to WD7000_SG when a newer board is - * detected. + * In this version, sg_tablesize now defaults to WD7000_SG, and will + * be set to SG_NONE for older boards. This is the reverse of the + * previous default, and was changed so that the driver-level + * Scsi_Host_Template would reflect the driver's support for scatter/ + * gather. + * + * Also, it has been reported that boards at Revision 6 support scatter/ + * gather, so the new definition of an "older" board has been changed + * accordingly. */ -#define WD7000_SCRIBBLE 512 - -#define WD7000_Q OGMB_CNT -#define WD7000_SG (WD7000_SCRIBBLE / sizeof(Sgb)) +#define WD7000_Q 16 +#define WD7000_SG 16 #define WD7000 {\ "Western Digital WD-7000", \ wd7000_detect, \ - wd7000_info, wd7000_command, \ + wd7000_info, \ + wd7000_command, \ wd7000_queuecommand, \ wd7000_abort, \ wd7000_reset, \ NULL, \ wd7000_biosparam, \ - WD7000_Q, 7, SG_NONE, 1, 0, 1} + WD7000_Q, 7, WD7000_SG, 1, 0, 1} #endif diff -u --recursive --new-file v1.1.11/linux/fs/buffer.c linux/fs/buffer.c --- v1.1.11/linux/fs/buffer.c Sat May 7 14:54:04 1994 +++ linux/fs/buffer.c Tue May 3 14:19:18 1994 @@ -49,7 +49,7 @@ static char buffersize_index[9] = {-1, 0, 1, -1, 2, -1, -1, -1, 3}; static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096}; -#define BUFSIZE_INDEX(X) (buffersize_index[(X)>>9]) +#define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9]) static int grow_buffers(int pri, int size); static int shrink_specific_buffers(unsigned int priority, int size); diff -u --recursive --new-file v1.1.11/linux/fs/proc/net.c linux/fs/proc/net.c --- v1.1.11/linux/fs/proc/net.c Tue Apr 19 10:53:47 1994 +++ linux/fs/proc/net.c Sat May 7 14:13:00 1994 @@ -44,6 +44,7 @@ extern int udp_get_info(char *, char **, off_t, int); extern int raw_get_info(char *, char **, off_t, int); extern int arp_get_info(char *, char **, off_t, int); +extern int rarp_get_info(char *, char **, off_t, int); extern int dev_get_info(char *, char **, off_t, int); extern int rt_get_info(char *, char **, off_t, int); #endif /* CONFIG_INET */ @@ -96,11 +97,14 @@ { 131,3,"dev" }, { 132,3,"raw" }, { 133,3,"tcp" }, - { 134,3,"udp" } + { 134,3,"udp" }, +#ifdef CONFIG_INET_RARP + { 135,4,"rarp"} +#endif #endif /* CONFIG_INET */ #ifdef CONFIG_IPX - ,{ 135,9,"ipx_route" }, - { 136,3,"ipx" } + ,{ 136,9,"ipx_route" }, + { 137,3,"ipx" } #endif /* CONFIG_IPX */ }; @@ -212,12 +216,15 @@ case 134: length = udp_get_info(page,&start,file->f_pos,thistime); break; + case 135: + length = rarp_get_info(page,&start,file->f_pos,thistime); + break; #endif /* CONFIG_INET */ #ifdef CONFIG_IPX - case 135: + case 136: length = ipx_rt_get_info(page,&start,file->f_pos,thistime); break; - case 136: + case 137: length = ipx_get_info(page,&start,file->f_pos,thistime); break; #endif /* CONFIG_IPX */ diff -u --recursive --new-file v1.1.11/linux/include/linux/if_arp.h linux/include/linux/if_arp.h --- v1.1.11/linux/include/linux/if_arp.h Tue Apr 19 10:53:49 1994 +++ linux/include/linux/if_arp.h Sat May 7 14:12:56 1994 @@ -30,6 +30,13 @@ #define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet- huh? */ #define ARPHRD_ARCNET 7 /* ARCnet */ #define ARPHRD_APPLETLK 8 /* APPLEtalk */ +/* Dummy types for non ARP hardware */ +#define ARPHRD_SLIP 256 +#define ARPHRD_CSLIP 257 +#define ARPHRD_SLIP6 258 +#define ARPHRD_CSLIP6 259 +#define ARPHRD_KISS 260 +#define ARPHRD_ADAPT 264 /* ARP protocol opcodes. */ #define ARPOP_REQUEST 1 /* ARP request */ diff -u --recursive --new-file v1.1.11/linux/include/linux/if_slip.h linux/include/linux/if_slip.h --- v1.1.11/linux/include/linux/if_slip.h +++ linux/include/linux/if_slip.h Sat May 7 14:12:56 1994 @@ -0,0 +1,19 @@ +/* + * Swansea University Computer Society NET3 + * + * This file declares the constants of special use with the SLIP/CSLIP/ + * KISS TNC driver. + */ + +#ifndef __LINUX_SLIP_H +#define __LINUX_SLIP_H + +#define SL_MODE_SLIP 0 +#define SL_MODE_CSLIP 1 +#define SL_MODE_KISS 4 + +#define SL_OPT_SIXBIT 2 +#define SL_OPT_ADAPTIVE 8 + + +#endif diff -u --recursive --new-file v1.1.11/linux/include/linux/ip.h linux/include/linux/ip.h --- v1.1.11/linux/include/linux/ip.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/ip.h Sat May 7 14:12:56 1994 @@ -32,8 +32,17 @@ unsigned char len; unsigned char ptr; union { +#if defined(__i386__) unsigned char flags:4, overflow:4; +#else +#if defined(__mc680x0__) + unsigned char overflow:4, + flags:4; +#else +#error "Adjust this structure to match your CPU" +#endif +#endif unsigned char full_char; } x; unsigned long data[9]; @@ -63,8 +72,17 @@ struct iphdr { +#if defined(__i386__) unsigned char ihl:4, version:4; +#else +#if defined (__mc680x0__) + unsigned char version:4, + ihl:4; +#else +#error "Adjust this structure to match your CPU" +#endif +#endif unsigned char tos; unsigned short tot_len; unsigned short id; diff -u --recursive --new-file v1.1.11/linux/include/linux/mtio.h linux/include/linux/mtio.h --- v1.1.11/linux/include/linux/mtio.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/mtio.h Fri May 6 08:28:58 1994 @@ -88,6 +88,7 @@ #define MT_ISDDS1 0x51 /* DDS device without partitions */ #define MT_ISDDS2 0x52 /* DDS device with partitions */ #define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */ +#define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */ struct mt_tape_info { long t_type; /* device type id (mt_type) */ @@ -106,6 +107,7 @@ {MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \ {MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \ {MT_ISSCSI1, "Generic SCSI-1 tape"}, \ + {MT_ISSCSI2, "Generic SCSI-2 tape"}, \ {0, NULL} \ } @@ -151,6 +153,23 @@ * files, rather than blocks. Not used. Not supported. * I think DDS drives are DAT drives. */ + +/* SCSI-tape specific definitions */ +#define MT_ST_BLKSIZE_SHIFT 0 +#define MT_ST_BLKSIZE_MASK 0xffffff +#define MT_ST_DENSITY_SHIFT 24 +#define MT_ST_DENSITY_MASK 0xff000000 + +#define MT_ST_SOFTERR_SHIFT 0 +#define MT_ST_SOFTERR_MASK 0xffff + +#define MT_ST_OPTIONS 0xf0000000 +#define MT_ST_BOOLEANS 0x10000000 +#define MT_ST_WRITE_THRESHOLD 0x20000000 +#define MT_ST_BUFFER_WRITES 0x1 +#define MT_ST_ASYNC_WRITES 0x2 +#define MT_ST_READ_AHEAD 0x4 +#define MT_ST_DEBUGGING 0x8 #endif /* _LINUX_MTIO_H */ diff -u --recursive --new-file v1.1.11/linux/include/linux/netdevice.h linux/include/linux/netdevice.h --- v1.1.11/linux/include/linux/netdevice.h Tue Apr 19 10:53:49 1994 +++ linux/include/linux/netdevice.h Sat May 7 14:12:56 1994 @@ -11,6 +11,7 @@ * Fred N. van Kempen, * Corey Minyard * Donald J. Becker, + * Alan Cox, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,7 +35,7 @@ #define IS_MYADDR 1 /* address is (one of) our own */ #define IS_LOOPBACK 2 /* address is for LOOPBACK */ #define IS_BROADCAST 3 /* address is a valid broadcast */ -#define IS_INVBCAST 4 /* Wrong netmask bcast not for us */ +#define IS_INVBCAST 4 /* Wrong netmask bcast not for us (unused)*/ /* * The DEVICE structure. @@ -52,7 +53,7 @@ */ char *name; - /* I/O specific fields. These will be moved to DDI soon. */ + /* I/O specific fields. */ unsigned long rmem_end; /* shmem "recv" end */ unsigned long rmem_start; /* shmem "recv" start */ unsigned long mem_end; /* sahared mem end */ @@ -65,12 +66,6 @@ tbusy, /* transmitter busy */ interrupt; /* interrupt arrived */ - /* - * Another mistake. - * This points to the next device in the "dev" chain. It will - * be moved to the "invisible" part of the structure as soon as - * it has been cleaned up. -FvK - */ struct device *next; /* The device initialization function. Called only once. */ @@ -135,6 +130,8 @@ int num_addrs, void *addrs); #define HAVE_SET_MAC_ADDR int (*set_mac_address)(struct device *dev, void *addr); +#define HAVE_PRIVATE_IOCTL + int (*do_ioctl)(struct device *dev, struct ifreq *ifr); }; diff -u --recursive --new-file v1.1.11/linux/include/linux/sockios.h linux/include/linux/sockios.h --- v1.1.11/linux/include/linux/sockios.h Tue Apr 19 10:53:50 1994 +++ linux/include/linux/sockios.h Sat May 7 14:12:57 1994 @@ -67,4 +67,21 @@ #define SIOCGARP 0x8951 /* get ARP table entry */ #define SIOCSARP 0x8952 /* set ARP table entry */ +/* RARP cache control calls. */ +#define SIOCDRARP 0x8960 /* delete RARP table entry */ +#define SIOCGRARP 0x8961 /* get RARP table entry */ +#define SIOCSRARP 0x8962 /* set RARP table entry */ + +/* Device private ioctl calls */ + +/* + * These 16 ioctls are available to devices via the do_ioctl() device + * vector. Each device should include this file and redefine these names + * as their own. Because these are device dependant it is a good idea + * _NOT_ to issue them to random objects and hope. + */ + +#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */ + + #endif /* _LINUX_SOCKIOS_H */ diff -u --recursive --new-file v1.1.11/linux/include/linux/tcp.h linux/include/linux/tcp.h --- v1.1.11/linux/include/linux/tcp.h Wed Dec 1 14:44:15 1993 +++ linux/include/linux/tcp.h Sat May 7 14:12:57 1994 @@ -26,6 +26,7 @@ unsigned short dest; unsigned long seq; unsigned long ack_seq; +#if defined(__i386__) unsigned short res1:4, doff:4, fin:1, @@ -35,6 +36,21 @@ ack:1, urg:1, res2:2; +#else +#if defined(__mc680x0__) + unsigned short res2:2, + urg:1, + ack:1, + psh:1, + rst:1, + syn:1, + fin:1, + doff:4, + res1:4; +#else +#error "Adjust this structure for your cpu alignment rules" +#endif +#endif unsigned short window; unsigned short check; unsigned short urg_ptr; @@ -45,17 +61,14 @@ TCP_ESTABLISHED = 1, TCP_SYN_SENT, TCP_SYN_RECV, -#if 0 - TCP_CLOSING, /* not a valid state, just a seperator so we can use - < tcp_closing or > tcp_closing for checks. */ -#endif TCP_FIN_WAIT1, TCP_FIN_WAIT2, TCP_TIME_WAIT, TCP_CLOSE, TCP_CLOSE_WAIT, TCP_LAST_ACK, - TCP_LISTEN + TCP_LISTEN, + TCP_CLOSING /* now a valid state */ }; #endif /* _LINUX_TCP_H */ diff -u --recursive --new-file v1.1.11/linux/init/main.c linux/init/main.c --- v1.1.11/linux/init/main.c Mon Apr 25 10:04:36 1994 +++ linux/init/main.c Fri May 6 08:28:58 1994 @@ -84,6 +84,7 @@ extern void eth_setup(char *str, int *ints); extern void xd_setup(char *str, int *ints); extern void mcd_setup(char *str, int *ints); +extern void st_setup(char *str, int *ints); extern void st0x_setup(char *str, int *ints); extern void tmc8xx_setup(char *str, int *ints); extern void t128_setup(char *str, int *ints); @@ -175,6 +176,9 @@ #endif #ifdef CONFIG_BLK_DEV_HD { "hd=", hd_setup }, +#endif +#ifdef CONFIG_CHR_DEV_ST + { "st=", st_setup }, #endif #ifdef CONFIG_BUSMOUSE { "bmouse=", bmouse_setup }, diff -u --recursive --new-file v1.1.11/linux/kernel/sys.c linux/kernel/sys.c --- v1.1.11/linux/kernel/sys.c Sat May 7 14:54:16 1994 +++ linux/kernel/sys.c Mon May 2 19:30:12 1994 @@ -227,7 +227,7 @@ } } if (rgid != (gid_t) -1 || - egid != (gid_t) -1 && egid != old_rgid) + (egid != (gid_t) -1 && egid != old_rgid)) current->sgid = current->egid; return 0; } @@ -314,7 +314,7 @@ } } if (ruid != (uid_t) -1 || - euid != (uid_t) -1 && euid != old_ruid) + (euid != (uid_t) -1 && euid != old_ruid)) current->suid = current->euid; return 0; } diff -u --recursive --new-file v1.1.11/linux/kernel/vm86.c linux/kernel/vm86.c --- v1.1.11/linux/kernel/vm86.c Sat May 7 14:54:16 1994 +++ linux/kernel/vm86.c Thu May 5 10:59:35 1994 @@ -31,8 +31,8 @@ /* * 8- and 16-bit register defines.. */ -#define AL(regs) (((unsigned char *) ((regs)->eax))[0]) -#define AH(regs) (((unsigned char *) ((regs)->eax))[1]) +#define AL(regs) (((unsigned char *)&((regs)->eax))[0]) +#define AH(regs) (((unsigned char *)&((regs)->eax))[1]) #define IP(regs) (*(unsigned short *)&((regs)->eip)) #define SP(regs) (*(unsigned short *)&((regs)->esp)) @@ -289,9 +289,8 @@ if (seg == BIOSSEG || regs->cs == BIOSSEG || is_revectored(i, ¤t->vm86_info->int_revectored)) return_to_32bit(regs, VM86_INTx + (i << 8)); - if (i==0x21 && is_revectored(AH(regs),¤t->vm86_info->int21_revectored)) { + if (i==0x21 && is_revectored(AH(regs),¤t->vm86_info->int21_revectored)) return_to_32bit(regs, VM86_INTx + (i << 8)); - } pushw(ssp, sp, get_vflags(regs)); pushw(ssp, sp, regs->cs); pushw(ssp, sp, IP(regs)); @@ -305,7 +304,15 @@ void handle_vm86_debug(struct vm86_regs * regs, long error_code) { - do_int(regs, 3, (unsigned char *) (regs->ss << 4), SP(regs)); +#if 0 + do_int(regs, 1, (unsigned char *) (regs->ss << 4), SP(regs)); +#else + if (current->flags & PF_PTRACED) + current->blocked &= ~(1 << (SIGTRAP-1)); + send_sig(SIGTRAP, current, 1); + current->tss.trap_no = 1; + current->tss.error_code = error_code; +#endif } void handle_vm86_fault(struct vm86_regs * regs, long error_code) diff -u --recursive --new-file v1.1.11/linux/net/inet/arp.c linux/net/inet/arp.c --- v1.1.11/linux/net/inet/arp.c Tue Apr 19 10:54:17 1994 +++ linux/net/inet/arp.c Sat May 7 14:12:57 1994 @@ -25,6 +25,7 @@ * Alan Cox : Allow >4K in /proc * Alan Cox : Make ARP add its own protocol entry * + * Ross Martin : Rewrote arp_rcv() and arp_get_info() */ #include @@ -69,7 +70,7 @@ unsigned long ip; /* ip address of entry */ unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */ unsigned char hlen; /* Length of hardware address */ - unsigned char htype; /* Type of hardware in use */ + unsigned short htype; /* Type of hardware in use */ struct device *dev; /* Device the entry is tied to */ /* @@ -246,8 +247,9 @@ * message. */ -static void arp_send(int type, unsigned long dest_ip, struct device *dev, - unsigned long src_ip, unsigned char *dest_hw, unsigned char *src_hw) +void arp_send(int type, int ptype, unsigned long dest_ip, + struct device *dev, unsigned long src_ip, + unsigned char *dest_hw, unsigned char *src_hw) { struct sk_buff *skb; struct arphdr *arp; @@ -280,7 +282,7 @@ * Fill the device header for the ARP frame */ - dev->hard_header(skb->data,dev,ETH_P_ARP,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb); + dev->hard_header(skb->data,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb); /* Fill out the arp protocol part. */ arp = (struct arphdr *) (skb->data + dev->hard_header_len); @@ -348,8 +350,8 @@ entry->timer.expires = ARP_RES_TIME; add_timer(&entry->timer); restore_flags(flags); - arp_send(ARPOP_REQUEST, ip, dev, dev->pa_addr, NULL, - dev->dev_addr); + arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr, + NULL, dev->dev_addr); return; } @@ -463,166 +465,249 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { - /* - * We shouldn't use this type conversion. Check later. - */ +/* + * We shouldn't use this type conversion. Check later. + */ struct arphdr *arp = (struct arphdr *)skb->h.raw; unsigned char *arp_ptr= (unsigned char *)(arp+1); struct arp_table *entry; struct arp_table *proxy_entry; - int addr_hint; - unsigned long hash; + int addr_hint,hlen,htype; + unsigned long hash,dest_hash; unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */ long sip,tip; unsigned char *sha,*tha; - /* - * If this test doesn't pass, its not IP, or we should ignore it anyway - */ - - if (arp->ar_hln != dev->addr_len || dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP) +/* + * The hardware length of the packet should match the hardware length + * of the device. Similarly, the hardware types should match. The + * device should be ARP-able. Also, if pln is not 4, then the lookup + * is not from an IP number. We can't currently handle this, so toss + * it. + */ + if (arp->ar_hln != dev->addr_len || + dev->type != ntohs(arp->ar_hrd) || + dev->flags & IFF_NOARP || + arp->ar_pln != 4) { kfree_skb(skb, FREE_READ); return 0; } - /* - * For now we will only deal with IP addresses. - */ - if ( +/* + * Another test. + * The logic here is that the protocol being looked up by arp should + * match the protocol the device speaks. If it doesn't, there is a + * problem, so toss the packet. + */ + switch(dev->type) + { #ifdef CONFIG_AX25 - (arp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) || + case ARPHRD_AX25: + if(arp->ar_pro != htons(AX25_P_IP)) + { + kfree_skb(skb, FREE_READ); + return 0; + } + break; #endif - (arp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) - || arp->ar_pln != 4) - { - /* This packet is not for us. Remove it. */ - kfree_skb(skb, FREE_READ); - return 0; + case ARPHRD_ETHER: + if(arp->ar_pro != htons(ETH_P_IP)) + { + kfree_skb(skb, FREE_READ); + return 0; + } + break; + + default: + printk("ARP: dev->type mangled!\n"); + kfree_skb(skb, FREE_READ); + return 0; } - /* - * Extract variable width fields - */ +/* + * Extract fields + */ + + hlen = dev->addr_len; + htype = dev->type; sha=arp_ptr; - arp_ptr+=dev->addr_len; + arp_ptr+=hlen; memcpy(&sip,arp_ptr,4); arp_ptr+=4; tha=arp_ptr; - arp_ptr+=dev->addr_len; + arp_ptr+=hlen; memcpy(&tip,arp_ptr,4); + +/* + * Check for bad requests for 127.0.0.1. If this is one such, delete it. + */ + if(tip == INADDR_LOOPBACK) + { + kfree_skb(skb, FREE_READ); + return 0; + } - /* - * Process entry - */ +/* + * Process entry. The idea here is we want to send a reply if it is a + * request for us or if it is a request for someone else that we hold + * a proxy for. We want to add an entry to our cache if it is a reply + * to us or if it is a request for our address. + * (The assumption for this last is that if someone is requesting our + * address, they are probably intending to talk to us, so it saves time + * if we cache their address. Their address is also probably not in + * our cache, since ours is not in their cache.) + * + * Putting this another way, we only care about replies if they are to + * us, in which case we add them to the cache. For requests, we care + * about those for us and those for our proxies. We reply to both, + * and in the case of requests for us we add the requester to the arp + * cache. + */ addr_hint = ip_chk_addr(tip); - hash = HASH(sip); - proxy_entry = NULL; - if (proxies != 0 && addr_hint != IS_MYADDR) + if(arp->ar_op == htons(ARPOP_REPLY)) { - unsigned long dest_hash = HASH(tip); - cli(); - proxy_entry = arp_tables[dest_hash]; - while (proxy_entry != NULL) + if(addr_hint!=IS_MYADDR) { - if (proxy_entry->ip == tip && proxy_entry->htype==arp->ar_hrd) - break; - proxy_entry = proxy_entry->next; +/* + * Replies to other machines get tossed. + */ + kfree_skb(skb, FREE_READ); + return 0; } - if (proxy_entry && (proxy_entry->flags & ATF_PUBL)) - memcpy(ha, proxy_entry->ha, dev->addr_len); - else - proxy_entry = NULL; +/* + * Fall through to code below that adds sender to cache. + */ } else - cli(); + { +/* + * It is now an arp request + */ + if(addr_hint != IS_MYADDR) + { +/* + * To get in here, it is a request for someone else. We need to + * check if that someone else is one of our proxies. If it isn't, + * we can toss it. + */ + if (proxies == 0) + { + kfree_skb(skb, FREE_READ); + return 0; + } + + dest_hash = HASH(tip); + cli(); + for(proxy_entry=arp_tables[dest_hash]; + proxy_entry; + proxy_entry = proxy_entry->next) + { + if(proxy_entry->ip == tip && proxy_entry->htype==htype) + break; + } + if (proxy_entry && (proxy_entry->flags & ATF_PUBL)) + { + memcpy(ha, proxy_entry->ha, hlen); + sti(); + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha); + kfree_skb(skb, FREE_READ); + return 0; + } + else + { + sti(); + kfree_skb(skb, FREE_READ); + return 0; + } + } + else + { +/* + * To get here, it must be an arp request for us. We need to reply. + */ + arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr); + } + } - for (entry = arp_tables[hash]; entry != NULL; entry = entry->next) - if (entry->ip == sip) + +/* + * Now all replies are handled. Next, anything that falls through to here + * needs to be added to the arp cache, or have its entry updated if it is + * there. + */ + + hash = HASH(sip); + cli(); + for(entry=arp_tables[hash];entry;entry=entry->next) + if(entry->ip==sip && entry->htype==htype) break; - if (entry != NULL) + if(entry) { - int old_flags = entry->flags; - memcpy(entry->ha, sha, arp->ar_hln); - entry->hlen = arp->ar_hln; - /* This seems sensible but not everyone gets it right ! */ - entry->htype = ntohs(arp->ar_hrd); - if(entry->htype==0) - entry->htype = dev->type; /* Not good but we have no choice */ +/* + * Entry found; update it. + */ + memcpy(entry->ha, sha, hlen); + entry->hlen = hlen; entry->last_used = jiffies; if (!(entry->flags & ATF_COM)) { +/* + * This entry was incomplete. Delete the retransmit timer + * and switch to complete status. + */ del_timer(&entry->timer); entry->flags |= ATF_COM; - } - sti(); - if (!(old_flags & ATF_COM)) - { - /* Send out waiting packets. We might have problems, - if someone is manually removing entries right now. - I will fix this one. */ + sti(); +/* + * Send out waiting packets. We might have problems, if someone is + * manually removing entries right now -- entry might become invalid + * underneath us. + */ arp_send_q(entry, sha); } - if (addr_hint != IS_MYADDR && proxy_entry == NULL) + else { - kfree_skb(skb, FREE_READ); - return 0; + sti(); } } else { - if (addr_hint != IS_MYADDR && proxy_entry == NULL) - { - /* We don't do "smart arp" and cache all possible - entries. That just makes us more work. */ - sti(); - kfree_skb(skb, FREE_READ); - return 0; - } - entry = (struct arp_table *)kmalloc(sizeof(struct arp_table), - GFP_ATOMIC); - if (entry == NULL) +/* + * No entry found. Need to add a new entry to the arp table. + */ + entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC); + if(entry == NULL) { sti(); - kfree_skb(skb, FREE_READ); printk("ARP: no memory for new arp entry\n"); + + kfree_skb(skb, FREE_READ); return 0; } + entry->ip = sip; - entry->hlen = arp->ar_hln; - entry->htype = arp->ar_hrd; + entry->hlen = hlen; + entry->htype = htype; entry->flags = ATF_COM; - memcpy(entry->ha, sha, arp->ar_hln); + memcpy(entry->ha, sha, hlen); entry->last_used = jiffies; - entry->next = arp_tables[hash]; - arp_tables[hash] = entry; entry->dev = skb->dev; skb_queue_head_init(&entry->skb); - sti(); - } - - /* From here on, interrupts are enabled. Never touch entry->.. - any more. */ + entry->next = arp_tables[hash]; + arp_tables[hash] = entry; - if (arp->ar_op != htons(ARPOP_REQUEST) - || tip == INADDR_LOOPBACK) - { - /* This wasn't a request, or some bad request for 127.0.0.1 - has made its way to the net, so delete it. */ - kfree_skb(skb, FREE_READ); - return 0; + sti(); } - /* Either we respond with our own hw address, or we do proxy arp for - another machine. */ - arp_send(ARPOP_REPLY, sip, dev, tip, sha, - (addr_hint == IS_MYADDR)? dev->dev_addr : ha); - +/* + * Replies have been sent, and entries have been added. All done. + */ kfree_skb(skb, FREE_READ); return 0; } @@ -726,7 +811,8 @@ * If we didn't find an entry, we will try to send an ARP packet. */ - arp_send(ARPOP_REQUEST, paddr, dev, saddr, NULL, dev->dev_addr); + arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, + dev->dev_addr); return 1; } @@ -734,51 +820,66 @@ /* * Write the contents of the ARP cache to a PROCfs file. - * - * Will change soon to ASCII format */ +#define HBUFFERLEN 30 + int arp_get_info(char *buffer, char **start, off_t offset, int length) { - struct arp_table *entry; - struct arpreq *req = (struct arpreq *) buffer; - int i; - off_t pos=0; - off_t begin=0; int len=0; - + off_t begin=0; + off_t pos=0; + int size; + struct arp_table *entry; + char hbuffer[HBUFFERLEN]; + int i,j,k; + const char hexbuf[] = "0123456789ABCDEF"; + + size = sprintf(buffer,"IP address HW type Flags HW address\n"); + pos+=size; + len+=size; + cli(); - /* Loop over the ARP table and copy structures to the buffer. */ - for (i = 0; i < ARP_TABLE_SIZE; i++) + for(i=0; inext) + for(entry=arp_tables[i]; entry!=NULL; entry=entry->next) { - memset(req, 0, sizeof(struct arpreq)); - req->arp_pa.sa_family = AF_INET; - memcpy(req->arp_pa.sa_data, &entry->ip, 4); - req->arp_ha.sa_family = entry->htype; - memcpy(req->arp_ha.sa_data, &entry->ha, MAX_ADDR_LEN); - req->arp_flags = entry->flags; - req++; - len+=sizeof(struct arpreq); - pos+=sizeof(struct arpreq); +/* + * Convert hardware address to XX:XX:XX:XX ... form. + */ + for(k=0,j=0;khlen;j++) + { + hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ]; + hbuffer[k++]=hexbuf[ entry->ha[j]&15 ]; + hbuffer[k++]=':'; + } + hbuffer[--k]=0; + + size = sprintf(buffer+len, + "%-17s0x%-10x0x%-10x%s\n", + in_ntoa(entry->ip), + (unsigned int)entry->htype, + entry->flags, + hbuffer); + + len+=size; + pos=begin+len; + if(posoffset+length) break; } - if(pos>offset+length) - break; } sti(); - *start=buffer+(offset-begin); - len-=(offset-begin); + + *start=buffer+(offset-begin); /* Start of wanted data */ + len-=(offset-begin); /* Start slop */ if(len>length) - len=length; + len=length; /* Ending slop */ return len; } diff -u --recursive --new-file v1.1.11/linux/net/inet/arp.h linux/net/inet/arp.h --- v1.1.11/linux/net/inet/arp.h Tue Apr 19 10:53:52 1994 +++ linux/net/inet/arp.h Sat May 7 14:12:57 1994 @@ -10,5 +10,8 @@ struct device *dev, unsigned long saddr, struct sk_buff *skb); extern int arp_get_info(char *buffer, char **start, off_t origin, int length); extern int arp_ioctl(unsigned int cmd, void *arg); +extern void arp_send(int type, int ptype, unsigned long dest_ip, + struct device *dev, unsigned long src_ip, + unsigned char *dest_hw, unsigned char *src_hw); #endif /* _ARP_H */ diff -u --recursive --new-file v1.1.11/linux/net/inet/ip.c linux/net/inet/ip.c --- v1.1.11/linux/net/inet/ip.c Tue Apr 19 22:20:35 1994 +++ linux/net/inet/ip.c Sat May 7 14:12:58 1994 @@ -1273,7 +1273,7 @@ * Set up data on packet */ - skb2->arp = skb->arp; + skb2->arp = 0;/*skb->arp;*/ skb2->free = skb->free; skb2->len = len + hlen; skb2->h.raw=(char *) skb2->data; @@ -1549,7 +1549,7 @@ ip_statistics.IpInReceives++; DPRINTF((DBG_IP, "<<\n")); - + /* * Tag the ip header of this packet so we can find it */ diff -u --recursive --new-file v1.1.11/linux/net/inet/rarp.c linux/net/inet/rarp.c --- v1.1.11/linux/net/inet/rarp.c +++ linux/net/inet/rarp.c Sat May 7 14:12:58 1994 @@ -0,0 +1,512 @@ +/* linux/net/inet/rarp.c + * + * Copyright (C) 1994 by Ross Martin + * Based on linux/net/inet/arp.c, Copyright (C) 1994 by Florian La Roche + * + * This module implements the Reverse Address Resolution Protocol + * (RARP, RFC 903), which is used to convert low level addresses such + * as ethernet addresses into high level addresses such as IP addresses. + * The most common use of RARP is as a means for a diskless workstation + * to discover its IP address during a network boot. + * + ** + *** WARNING:::::::::::::::::::::::::::::::::WARNING + **** + ***** SUN machines seem determined to boot solely from the person who + **** answered their RARP query. NEVER add a SUN to your RARP table + *** unless you have all the rest to boot the box from it. + ** + * + * Currently, only ethernet address -> IP address is likely to work. + * (Is RARP ever used for anything else?) + * + * This code 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ip.h" +#include "route.h" +#include "protocol.h" +#include "tcp.h" +#include +#include "sock.h" +#include "arp.h" +#include "rarp.h" +#ifdef CONFIG_AX25 +#include "ax25.h" +#endif + +#ifdef CONFIG_INET_RARP + +/* + * This structure defines the RARP mapping cache. As long as we make + * changes in this structure, we keep interrupts off. + */ + +struct rarp_table +{ + struct rarp_table *next; /* Linked entry list */ + unsigned long ip; /* ip address of entry */ + unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */ + unsigned char hlen; /* Length of hardware address */ + unsigned char htype; /* Type of hardware in use */ + struct device *dev; /* Device the entry is tied to */ +}; + +struct rarp_table *rarp_tables = NULL; + +/* + * This structure defines an ethernet arp header, which is the same header + * that is used for rarp. + */ + +struct arphdr +{ + unsigned short ar_hrd; /* format of hardware address */ + unsigned short ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + unsigned short ar_op; /* ARP opcode (command) */ +#if 0 + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +#endif +}; + +static struct packet_type rarp_packet_type = +{ + 0, /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */ + 0, /* copy */ + rarp_rcv, + NULL, + NULL +}; + +static initflag = 1; + +/* + * Called once when data first added to rarp cache with ioctl. + */ + +static void rarp_init (void) +{ + /* Register the packet type */ + rarp_packet_type.type=htons(ETH_P_RARP); + dev_add_pack(&rarp_packet_type); +} + +/* + * Release the memory for this entry. + */ + +static inline void rarp_release_entry(struct rarp_table *entry) +{ + kfree_s(entry, sizeof(struct rarp_table)); + return; +} + +/* + * Delete a RARP mapping entry in the cache. + */ + +static void rarp_destroy(unsigned long ip_addr) +{ + struct rarp_table *entry; + struct rarp_table **pentry; + + cli(); + pentry = &rarp_tables; + while ((entry = *pentry) != NULL) + { + if (entry->ip == ip_addr) + { + *pentry = entry->next; + sti(); + rarp_release_entry(entry); + return; + } + pentry = &entry->next; + } + sti(); +} + + +/* + * Receive an arp request by the device layer. Maybe it should be + * rewritten to use the incoming packet for the reply. The current + * "overhead" time isn't that high... + */ + +int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +{ +/* + * We shouldn't use this type conversion. Check later. + */ + struct arphdr *rarp = (struct arphdr *)skb->h.raw; + unsigned char *rarp_ptr = (unsigned char *)(rarp+1); + struct rarp_table *entry; + long sip,tip; + unsigned char *sha,*tha; /* s for "source", t for "target" */ + +/* + * If this test doesn't pass, its not IP, or we should ignore it anyway + */ + + if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd) + || dev->flags&IFF_NOARP) + { + kfree_skb(skb, FREE_READ); + return 0; + } + +/* + * If it's not a RARP request, delete it. + */ + if (rarp->ar_op != htons(ARPOP_RREQUEST)) + { + kfree_skb(skb, FREE_READ); + return 0; + } + +/* + * For now we will only deal with IP addresses. + */ + + if ( +#ifdef CONFIG_AX25 + (rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) || +#endif + (rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) + || rarp->ar_pln != 4) + { + /* + * This packet is not for us. Remove it. + */ + kfree_skb(skb, FREE_READ); + return 0; +} + +/* + * Extract variable width fields + */ + + sha=rarp_ptr; + rarp_ptr+=dev->addr_len; + memcpy(&sip,rarp_ptr,4); + rarp_ptr+=4; + tha=rarp_ptr; + rarp_ptr+=dev->addr_len; + memcpy(&tip,rarp_ptr,4); + +/* + * Process entry + */ + + cli(); + for (entry = rarp_tables; entry != NULL; entry = entry->next) + if (!memcmp(entry->ha, sha, rarp->ar_hln)) + break; + + if (entry != NULL) + { + sip=entry->ip; + sti(); + + arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, + dev->dev_addr); + } + else + sti(); + + kfree_skb(skb, FREE_READ); + return 0; +} + + +/* + * Set (create) a RARP cache entry. + */ + +static int rarp_req_set(struct arpreq *req) +{ + struct arpreq r; + struct rarp_table *entry; + struct sockaddr_in *si; + int htype, hlen; + unsigned long ip; + struct rtable *rt; + + memcpy_fromfs(&r, req, sizeof(r)); + + /* + * We only understand about IP addresses... + */ + + if (r.arp_pa.sa_family != AF_INET) + return -EPFNOSUPPORT; + + switch (r.arp_ha.sa_family) + { + case ARPHRD_ETHER: + htype = ARPHRD_ETHER; + hlen = ETH_ALEN; + break; +#ifdef CONFIG_AX25 + case ARPHRD_AX25: + htype = ARPHRD_AX25; + hlen = 7; + break; +#endif + default: + return -EPFNOSUPPORT; + } + + si = (struct sockaddr_in *) &r.arp_pa; + ip = si->sin_addr.s_addr; + if (ip == 0) + { + printk("RARP: SETRARP: requested PA is 0.0.0.0 !\n"); + return -EINVAL; + } + +/* + * Is it reachable directly ? + */ + + rt = ip_rt_route(ip, NULL, NULL); + if (rt == NULL) + return -ENETUNREACH; + +/* + * Is there an existing entry for this address? Find out... + */ + + cli(); + for (entry = rarp_tables; entry != NULL; entry = entry->next) + if (entry->ip == ip) + break; + +/* + * If no entry was found, create a new one. + */ + + if (entry == NULL) + { + entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table), + GFP_ATOMIC); + if (entry == NULL) + { + sti(); + return -ENOMEM; + } + if(initflag) + { + rarp_init(); + initflag=0; + } + + entry->next = rarp_tables; + rarp_tables = entry; + } + + entry->ip = ip; + entry->hlen = hlen; + entry->htype = htype; + memcpy(&entry->ha, &r.arp_ha.sa_data, hlen); + entry->dev = rt->rt_dev; + + sti(); + + return 0; +} + + +/* + * Get a RARP cache entry. + */ + +static int rarp_req_get(struct arpreq *req) +{ + struct arpreq r; + struct rarp_table *entry; + struct sockaddr_in *si; + unsigned long ip; + +/* + * We only understand about IP addresses... + */ + + memcpy_fromfs(&r, req, sizeof(r)); + + if (r.arp_pa.sa_family != AF_INET) + return -EPFNOSUPPORT; + +/* + * Is there an existing entry for this address? + */ + + si = (struct sockaddr_in *) &r.arp_pa; + ip = si->sin_addr.s_addr; + + cli(); + for (entry = rarp_tables; entry != NULL; entry = entry->next) + if (entry->ip == ip) + break; + + if (entry == NULL) + { + sti(); + return -ENXIO; + } + +/* + * We found it; copy into structure. + */ + + memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen); + r.arp_ha.sa_family = entry->htype; + sti(); + +/* + * Copy the information back + */ + + memcpy_tofs(req, &r, sizeof(r)); + return 0; +} + + +/* + * Handle a RARP layer I/O control request. + */ + +int rarp_ioctl(unsigned int cmd, void *arg) +{ + struct arpreq r; + struct sockaddr_in *si; + int err; + + switch(cmd) + { + case SIOCDRARP: + if (!suser()) + return -EPERM; + err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); + if(err) + return err; + memcpy_fromfs(&r, arg, sizeof(r)); + if (r.arp_pa.sa_family != AF_INET) + return -EPFNOSUPPORT; + si = (struct sockaddr_in *) &r.arp_pa; + rarp_destroy(si->sin_addr.s_addr); + return 0; + + case SIOCGRARP: + err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq)); + if(err) + return err; + return rarp_req_get((struct arpreq *)arg); + case SIOCSRARP: + if (!suser()) + return -EPERM; + err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq)); + if(err) + return err; + return rarp_req_set((struct arpreq *)arg); + default: + return -EINVAL; + } + + /*NOTREACHED*/ + return 0; +} + +int rarp_get_info(char *buffer, char **start, off_t offset, int length) +{ + int len=0; + off_t begin=0; + off_t pos=0; + int size; + struct rarp_table *entry; + char ipbuffer[20]; + unsigned long netip; + if(initflag) + { + size = sprintf(buffer,"RARP disabled until entries added to cache.\n"); + pos+=size; + len+=size; + } + else + { + size = sprintf(buffer, + "IP address HW type HW address\n"); + pos+=size; + len+=size; + + cli(); + for(entry=rarp_tables; entry!=NULL; entry=entry->next) + { + netip=htonl(entry->ip); /* switch to network order */ + sprintf(ipbuffer,"%d.%d.%d.%d", + (unsigned int)(netip>>24)&255, + (unsigned int)(netip>>16)&255, + (unsigned int)(netip>>8)&255, + (unsigned int)(netip)&255); + + size = sprintf(buffer+len, + "%-17s%-20s%02x:%02x:%02x:%02x:%02x:%02x\n", + ipbuffer, + "10Mbps Ethernet", + (unsigned int)entry->ha[0], + (unsigned int)entry->ha[1], + (unsigned int)entry->ha[2], + (unsigned int)entry->ha[3], + (unsigned int)entry->ha[4], + (unsigned int)entry->ha[5]); + + len+=size; + pos=begin+len; + + if(posoffset+length) + break; + } + sti(); + } + + *start=buffer+(offset-begin); /* Start of wanted data */ + len-=(offset-begin); /* Start slop */ + if(len>length) + len=length; /* Ending slop */ + return len; +} + +#endif diff -u --recursive --new-file v1.1.11/linux/net/inet/rarp.h linux/net/inet/rarp.h --- v1.1.11/linux/net/inet/rarp.h +++ linux/net/inet/rarp.h Sat May 7 14:12:58 1994 @@ -0,0 +1,14 @@ +/* linux/net/inet/rarp.h */ +#ifndef _RARP_H +#define _RARP_H + +extern int rarp_ioctl(unsigned int cmd, void *arg); +extern int rarp_rcv(struct sk_buff *skb, + struct device *dev, + struct packet_type *pt); +extern int rarp_get_info(char *buffer, + char **start, + off_t offset, + int length); +#endif /* _RARP_H */ + diff -u --recursive --new-file v1.1.11/linux/net/inet/sock.c linux/net/inet/sock.c --- v1.1.11/linux/net/inet/sock.c Thu Apr 21 09:46:36 1994 +++ linux/net/inet/sock.c Sat May 7 14:12:58 1994 @@ -52,6 +52,7 @@ * Alan Cox : Split socket option code * Alan Cox : Callbacks * Alan Cox : Nagle flag for Charles & Johannes stuff + * Alex : Removed restriction on inet fioctl * * To Fix: * @@ -86,6 +87,7 @@ #include "ip.h" #include "protocol.h" #include "arp.h" +#include "rarp.h" #include "route.h" #include "tcp.h" #include "udp.h" @@ -1482,6 +1484,11 @@ case SIOCSARP: return(arp_ioctl(cmd,(void *) arg)); + case SIOCDRARP: + case SIOCGRARP: + case SIOCSRARP: + return(rarp_ioctl(cmd,(void *) arg)); + case SIOCGIFCONF: case SIOCGIFFLAGS: case SIOCSIFFLAGS: @@ -1701,7 +1708,6 @@ /* Extract the minor number on which we work. */ minor = MINOR(inode->i_rdev); - if (minor != 0) return(-ENODEV); /* Now dispatch on the minor device. */ switch(minor) { @@ -1780,7 +1786,7 @@ struct inet_protocol *p; int i; - printk("Swansea University Computer Society NET3.010\n"); + printk("Swansea University Computer Society NET3.012\n"); /* * Set up our UNIX VFS major device. (compatibility) */ diff -u --recursive --new-file v1.1.11/linux/net/inet/tcp.c linux/net/inet/tcp.c --- v1.1.11/linux/net/inet/tcp.c Tue Apr 19 22:20:37 1994 +++ linux/net/inet/tcp.c Sat May 7 14:12:59 1994 @@ -15,6 +15,7 @@ * Charles Hedrick, * Linus Torvalds, * Alan Cox, + * Matthew Dillon, * * Fixes: * Alan Cox : Numerous verify_area() calls @@ -67,6 +68,7 @@ * Linus : Rewrote tcp_read() and URG handling * completely * Gerhard Koerting: Fixed some missing timer handling + * Matthew Dillon : Reworked TCP machine states as per RFC * * * To Fix: @@ -80,6 +82,40 @@ * 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. + * + * Description of States: + * + * TCP_SYN_SENT sent a connection request, waiting for ack + * + * TCP_SYN_RECV received a connection request, sent ack, + * waiting for final ack in three-way handshake. + * + * TCP_ESTABLISHED connection established + * + * TCP_FIN_WAIT1 our side has shutdown, waiting to complete + * transmission of remaining buffered data + * + * TCP_FIN_WAIT2 all buffered data sent, waiting for remote + * to shutdown + * + * TCP_CLOSING both sides have shutdown but we still have + * data we have to finish sending + * + * TCP_TIME_WAIT timeout to catch resent junk before entering + * closed, can only be entered from FIN_WAIT2 + * or CLOSING. Required because the other end + * may not have gotten our last ACK causing it + * to retransmit the data packet (which we ignore) + * + * TCP_CLOSE_WAIT remote side has shutdown and is waiting for + * us to finish writing our data and to shutdown + * (we have to close() to move on to LAST_ACK) + * + * TCP_LAST_ACK out side has shutdown after remote has + * shutdown. There may still be data in our + * buffer that we have to finish sending + * + * TCP_CLOSED socket is finished */ #include #include @@ -109,7 +145,6 @@ unsigned long seq_offset; struct tcp_mib tcp_statistics; -#define SUBNETSARELOCAL static __inline__ int min(unsigned int a, unsigned int b) @@ -1498,8 +1533,7 @@ /* - * Send a FIN without closing the connection. - * Not called at interrupt time. + * Shutdown the sending side of a connection. */ void tcp_shutdown(struct sock *sk, int how) @@ -1514,22 +1548,36 @@ * We need to grab some memory, and put together a FIN, * and then put it into the queue to be sent. * FIXME: + * * Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92. * Most of this is guesswork, so maybe it will work... */ + + if (!(how & SEND_SHUTDOWN)) + return; /* * If we've already sent a FIN, return. */ - if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) - return; - if (!(how & SEND_SHUTDOWN)) + if (sk->state == TCP_FIN_WAIT1 || + sk->state == TCP_FIN_WAIT2 || + sk->state == TCP_CLOSING || + sk->state == TCP_LAST_ACK || + sk->state == TCP_TIME_WAIT + ) { return; + } sk->inuse = 1; /* - * Clear out any half completed packets. + * flag that the sender has shutdown + */ + + sk->shutdown |= SEND_SHUTDOWN; + + /* + * Clear out any half completed packets. */ if (sk->partial) @@ -1560,13 +1608,24 @@ { /* * Finish anyway, treat this as a send that got lost. + * + * Enter FIN_WAIT1 on normal shutdown, which waits for + * written data to be completely acknowledged along + * with an acknowledge to our FIN. + * + * Enter FIN_WAIT2 on abnormal shutdown -- close before + * connection established. */ buff->free=1; prot->wfree(sk,buff->mem_addr, buff->mem_len); - if(sk->state==TCP_ESTABLISHED) - sk->state=TCP_FIN_WAIT1; + + if (sk->state == TCP_ESTABLISHED) + sk->state = TCP_FIN_WAIT1; + else if(sk->state == TCP_CLOSE_WAIT) + sk->state = TCP_LAST_ACK; else - sk->state=TCP_FIN_WAIT2; + sk->state = TCP_FIN_WAIT2; + release_sock(sk); DPRINTF((DBG_TCP, "Unable to build header for fin.\n")); return; @@ -1610,7 +1669,9 @@ if (sk->state == TCP_ESTABLISHED) sk->state = TCP_FIN_WAIT1; - else + else if (sk->state == TCP_CLOSE_WAIT) + sk->state = TCP_LAST_ACK; + else sk->state = TCP_FIN_WAIT2; release_sock(sk); @@ -1934,7 +1995,7 @@ if (sk->user_mss) newsk->mtu = sk->user_mss; else { -#ifdef SUBNETSARELOCAL +#ifdef CONFIG_INET_SNARL /* Sub Nets ARe Local */ if ((saddr ^ daddr) & default_mask(saddr)) #else if ((saddr ^ daddr) & dev->pa_mask) @@ -2081,7 +2142,18 @@ { case TCP_FIN_WAIT1: case TCP_FIN_WAIT2: - case TCP_LAST_ACK: + case TCP_CLOSING: + /* + * These states occur when we have already closed out + * our end. If there is no timeout, we do not do + * anything. We may still be in the middle of sending + * the remainder of our buffer, for example... + * resetting the timer would be inappropriate. + * + * XXX if retransmit count reaches limit, is tcp_close() + * called with timeout == 1 ? if not, we need to fix that. + */ +#ifdef NOTDEF /* * Start a timer. * original code was 4 * sk->rtt. In converting to the @@ -2089,11 +2161,16 @@ * it seems to make most sense to use the backed off value */ reset_timer(sk, TIME_CLOSE, 4 * sk->rto); +#endif if (timeout) tcp_time_wait(sk); release_sock(sk); return; /* break causes a double release - messy */ case TCP_TIME_WAIT: + case TCP_LAST_ACK: + /* + * A timeout from these states terminates the TCB. + */ if (timeout) { sk->state = TCP_CLOSE; @@ -2140,6 +2217,12 @@ if (tmp < 0) { kfree_skb(buff,FREE_WRITE); + + /* + * Enter FIN_WAIT1 to await completion of + * written out data and ACK to our FIN. + */ + if(sk->state==TCP_ESTABLISHED) sk->state=TCP_FIN_WAIT1; else @@ -2192,14 +2275,19 @@ skb_queue_tail(&sk->write_queue, buff); } - if (sk->state == TCP_CLOSE_WAIT) - { - sk->state = TCP_FIN_WAIT2; - } - else - { - sk->state = TCP_FIN_WAIT1; - } + /* + * If established (normal close), enter FIN_WAIT1. + * If in CLOSE_WAIT, enter LAST_ACK + * If in CLOSING, remain in CLOSING + * otherwise enter FIN_WAIT2 + */ + + if (sk->state == TCP_ESTABLISHED) + sk->state = TCP_FIN_WAIT1; + else if (sk->state == TCP_CLOSE_WAIT) + sk->state = TCP_LAST_ACK; + else if (sk->state != TCP_CLOSING) + sk->state = TCP_FIN_WAIT2; } release_sock(sk); } @@ -2276,7 +2364,10 @@ } -/* This routine deals with incoming acks, but not outgoing ones. */ +/* + * This routine deals with incoming acks, but not outgoing ones. + */ + static int tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) { @@ -2574,33 +2665,68 @@ tcp_send_partial(sk); } - /* See if we are done. */ - if (sk->state == TCP_TIME_WAIT) { + /* + * In the LAST_ACK case, the other end FIN'd us. We then FIN'd them, and + * we are now waiting for an acknowledge to our FIN. The other end is + * already in TIME_WAIT. + * + * Move to TCP_CLOSE on success. + */ + + if (sk->state == TCP_LAST_ACK) { if (!sk->dead) sk->state_change(sk); + DPRINTF((DBG_TCP, "TCP_LAST_ACK-A: %d/%d %d/%d ack/sent %d %d\n", + sk->rcv_ack_seq, + sk->write_seq, + sk->acked_seq, + sk->fin_seq, + ack, + sk->sent_seq + )); if (sk->rcv_ack_seq == sk->write_seq && sk->acked_seq == sk->fin_seq) { + DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk)); flag |= 1; sk->state = TCP_CLOSE; sk->shutdown = SHUTDOWN_MASK; } } - if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2) { - if (!sk->dead) sk->state_change(sk); + /* + * Incomming ACK to a FIN we sent in the case of our initiating the close. + * + * Move to FIN_WAIT2 to await a FIN from the other end. + */ + + if (sk->state == TCP_FIN_WAIT1) { + if (!sk->dead) + sk->state_change(sk); if (sk->rcv_ack_seq == sk->write_seq) { flag |= 1; if (sk->acked_seq != sk->fin_seq) { tcp_time_wait(sk); } else { - DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk)); - tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, - th, sk->daddr); sk->shutdown = SHUTDOWN_MASK; - sk->state = TCP_CLOSE; + sk->state = TCP_FIN_WAIT2; } } } + /* + * Incomming ACK to a FIN we sent in the case of a simultanious close. + * + * Move to TIME_WAIT + */ + + if (sk->state == TCP_CLOSING) { + if (!sk->dead) + sk->state_change(sk); + if (sk->rcv_ack_seq == sk->write_seq) { + flag |= 1; + tcp_time_wait(sk); + } + } + /* * I make no guarantees about the first clause in the following * test, i.e. "(!flag) || (flag&4)". I'm not entirely sure under @@ -2851,6 +2977,8 @@ DPRINTF((DBG_TCP, "data received on dead socket.\n")); } +#ifdef NOTDEF /* say what? this is handled by tcp_ack() */ + if (sk->state == TCP_FIN_WAIT2 && sk->acked_seq == sk->fin_seq && sk->rcv_ack_seq == sk->write_seq) { DPRINTF((DBG_TCP, "tcp_data: entering last_ack state sk = %X\n", sk)); @@ -2860,6 +2988,7 @@ sk->state = TCP_LAST_ACK; if (!sk->dead) sk->state_change(sk); } +#endif return(0); } @@ -2920,15 +3049,27 @@ /* - * This deals with incoming fins. 'Linus at 9 O'clock' 8-) + * This deals with incoming fins. 'Linus at 9 O'clock' 8-) + * + * If we are ESTABLISHED, a received fin moves us to CLOSE-WAIT + * (and thence onto LAST-ACK and finally, CLOSED, we never enter + * TIME-WAIT) + * + * If we are in FINWAIT-1, a received FIN indicates simultanious + * close and we go into CLOSING (and later onto TIME-WAIT) + * + * If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT. + * */ -static int tcp_fin(struct sock *sk, struct tcphdr *th, +static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th, unsigned long saddr, struct device *dev) { DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n", sk, th, saddr, dev)); + sk->fin_seq = th->seq + skb->len + th->syn + th->fin; + if (!sk->dead) { sk->state_change(sk); @@ -2939,9 +3080,12 @@ case TCP_SYN_RECV: case TCP_SYN_SENT: case TCP_ESTABLISHED: - /* Contains the one that needs to be acked */ + /* + * move to CLOSE_WAIT, tcp_data() already handled + * sending the ack. + */ reset_timer(sk, TIME_CLOSE, TCP_TIMEOUT_LEN); - sk->fin_seq = th->seq+1; + /*sk->fin_seq = th->seq+1;*/ tcp_statistics.TcpCurrEstab--; sk->state = TCP_CLOSE_WAIT; if (th->rst) @@ -2949,17 +3093,46 @@ break; case TCP_CLOSE_WAIT: - case TCP_FIN_WAIT2: - break; /* we got a retransmit of the fin. */ - - case TCP_FIN_WAIT1: - /* Contains the one that needs to be acked */ - sk->fin_seq = th->seq+1; - sk->state = TCP_FIN_WAIT2; + case TCP_CLOSING: + /* + * received a retransmission of the FIN, do + * nothing. + */ break; + case TCP_TIME_WAIT: + /* + * received a retransmission of the FIN, + * restart the TIME_WAIT timer. + */ + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + return(0); + case TCP_FIN_WAIT1: + /* + * This case occurs when a simultanious close + * happens, we must ack the received FIN and + * enter the CLOSING state. + * + * XXX timeout not set properly + */ + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + /*sk->fin_seq = th->seq+1;*/ + sk->state = TCP_CLOSING; + break; + case TCP_FIN_WAIT2: + /* + * received a FIN -- send ACK and enter TIME_WAIT + */ + reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); + /*sk->fin_seq = th->seq+1;*/ + sk->state = TCP_TIME_WAIT; + break; + case TCP_CLOSE: + /* + * already in CLOSE + */ + break; default: - case TCP_TIME_WAIT: sk->state = TCP_LAST_ACK; /* Start the timers. */ @@ -3364,6 +3537,7 @@ case TCP_ESTABLISHED: case TCP_CLOSE_WAIT: + case TCP_CLOSING: case TCP_FIN_WAIT1: case TCP_FIN_WAIT2: case TCP_TIME_WAIT: @@ -3437,7 +3611,7 @@ } /* Moved: you must do data then fin bit */ - if (th->fin && tcp_fin(sk, th, saddr, dev)) { + if (th->fin && tcp_fin(skb, sk, th, saddr, dev)) { kfree_skb(skb, FREE_READ); release_sock(sk); return(0); @@ -3622,7 +3796,7 @@ if (tcp_data(skb, sk, saddr, len)) kfree_skb(skb, FREE_READ); - if (th->fin) tcp_fin(sk, th, saddr, dev); + if (th->fin) tcp_fin(skb, sk, th, saddr, dev); release_sock(sk); return(0); } @@ -3645,7 +3819,7 @@ release_sock(sk); return(0); } - tcp_fin(sk, th, saddr, dev); + tcp_fin(skb, sk, th, saddr, dev); release_sock(sk); return(0); } @@ -3667,9 +3841,19 @@ if (sk->zapped) return; /* Afer a valid reset we can send no more */ - if (sk -> state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT && - sk -> state != TCP_FIN_WAIT1 && sk->state != TCP_FIN_WAIT2) + /* + * Write data can still be transmitted/retransmitted in the + * following states. If any other state is encountered, return. + */ + + if (sk->state != TCP_ESTABLISHED && + sk->state != TCP_CLOSE_WAIT && + sk->state != TCP_FIN_WAIT1 && + sk->state != TCP_LAST_ACK && + sk->state != TCP_CLOSING + ) { return; + } buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); if (buff == NULL) diff -u --recursive --new-file v1.1.11/linux/net/inet/timer.c linux/net/inet/timer.c --- v1.1.11/linux/net/inet/timer.c Tue Apr 19 10:54:06 1994 +++ linux/net/inet/timer.c Sat May 7 14:12:59 1994 @@ -195,7 +195,7 @@ DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n")); sk->err = ETIMEDOUT; if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 - || sk->state == TCP_LAST_ACK) { + || sk->state == TCP_CLOSING) { sk->state = TCP_TIME_WAIT; reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); } else { diff -u --recursive --new-file v1.1.11/linux/net/inet/udp.c linux/net/inet/udp.c --- v1.1.11/linux/net/inet/udp.c Thu Apr 21 09:46:36 1994 +++ linux/net/inet/udp.c Sat May 7 14:13:00 1994 @@ -36,6 +36,7 @@ * Alan Cox : Use ip_tos and ip_ttl * Alan Cox : SNMP Mibs * Alan Cox : MSG_DONTROUTE, and 0.0.0.0 support. + * Matt Dillon : UDP length checks. * * * This program is free software; you can redistribute it and/or @@ -653,7 +654,8 @@ { struct sock *sk; struct udphdr *uh; - + unsigned short ulen; + /* * Get the header. */ @@ -661,7 +663,22 @@ ip_statistics.IpInDelivers++; - + /* + * Validate the packet and the UDP length. + */ + + ulen = ntohs(uh->len); + + if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) + { + printk("UDP: short packet: %d/%d\n", ulen, len); + DPRINTF((DBG_UDP, "UDP: short packet %d/%d\n", ulen, len)); + udp_statistics.UdpInErrors++; + kfree_skb(skb, FREE_WRITE); + return(0); + } + len=ulen; + sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr); if (sk == NULL) {