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
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)
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
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
D: Mitsumi CD-ROM driver patches March version
VERSION = 1
PATCHLEVEL = 1
-SUBLEVEL = 11
+SUBLEVEL = 12
all: Version zImage
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
* 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
#include "tcp.h"
#endif
#include <linux/skbuff.h>
+#include <linux/if_arp.h>
#include "sock.h"
#include "slip.h"
#ifdef CONFIG_INET
dev->rmem_start = (unsigned long) NULL;
dev->mem_end = (unsigned long) NULL;
dev->mem_start = (unsigned long) NULL;
+ dev->type = ARPHRD_SLIP + sl->mode;
}
{
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
--- /dev/null
+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
/*
- SCSI Tape Driver for Linux
-
- Version 0.02 for Linux 0.98.4 and Eric Youngdale's new scsi driver
+ SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+ file README.st for more information.
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.
-
- Copyright 1992, 1993 Kai Makisara
+ Contribution and ideas from several people including (in alphabetical
+ order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and
+ Eric Youngdale.
+
+ 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 <linux/fs.h>
#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 */
+#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
-/* 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_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
#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;
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) ))
#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)
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)
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). */
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) {
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);
}
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;
}
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;
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 */
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) );
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
}
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;
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) {
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;
}
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
}
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)) {
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
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) {
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 */
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);
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;
(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)) {
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;
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;
}
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;
st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
}
else
-#endif
SCpnt->request.dev = -1; /* Mark as not busy */
return( total);
}
#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)
}
#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
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;
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 */
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);
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) {
(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
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
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;
return total;
}
+
+\f
+/* 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;
+}
+
\f
/* Internal ioctl function */
static int
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:
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:
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;
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;
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)
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;
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;
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;
#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)
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) {
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 */
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)
(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:
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) );
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)
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;
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;
}
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 ;
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);
}
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)) {
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);
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);
}
return scsi_ioctl(STp->device, cmd_in, (void *) arg);
}
-
\f
+/* 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 */
/* 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);
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;
typedef struct {
int in_use;
- struct mtget * mt_status;
int buffer_size;
int buffer_blocks;
int buffer_bytes;
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;
-/* $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
* 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 <stdarg.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/sched.h>
+#include <linux/malloc.h>
#include <asm/system.h>
#include <asm/dma.h>
#include <asm/io.h>
#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:
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).
-static int wd7000_host = 0;
-static unchar controlstat = 0;
+ 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 unchar rev_1 = 0, rev_2 = 0; /* filled in by wd7000_revision */
-#define wd7000_intr_ack() outb(0,INTR_ACK)
+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 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();
-
- if (scbfree == NULL) {
- panic("wd7000: can't allocate free SCB.\n");
- restore_flags(flags);
- return NULL;
+ 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 */
+
+ 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);
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);
}
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;
}
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();
- 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 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(" ICMB %d posted for SCB/ICB %06x, status %02x, vue %02x",
- icmb, scb, icmb_status, scb->vue );
+ printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host);
#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;
+ flag = inb(host->iobase+ASC_INTR_STAT);
+#ifdef DEBUG
+ printk("wd7000_intr_handle: intr stat = %02x\n",flag);
+#endif
- if (SCpnt->host_scribble != NULL)
- scsi_free(SCpnt->host_scribble,WD7000_SCRIBBLE);
- free_scb(scb);
+ 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;
+ }
- 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;
-
- cdb = (unchar *) SCpnt->cmnd;
- cdblen = COMMAND_SIZE(*cdb);
+ register Scb *scb;
+ register Sgb *sgb;
+ register unchar *cdb = (unchar *) SCpnt->cmnd;
+ register unchar idlun;
+ register short cdblen;
+ Adapter *host = (Adapter *) SCpnt->host->hostdata;
+
+ 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;
}
}
-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;
-
- 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;j<NUM_SIGNATURES;j++){
- if(!memcmp((void *)(wd_bases[i] + signatures[j].offset),
- (void *) signatures[j].signature,signatures[j].length)){
- base_address=wd_bases[i];
- printk("WD-7000 detected.\n");
- }
- }
- }
- if (base_address == NULL) return 0;
+ int i,j, present = 0;
+ const Config *cfg;
+ const Signature *sig;
+ Adapter *host = NULL;
+ struct Scsi_Host *sh;
- snarf_region(IO_BASE, 4); /* Register our ports */
- /* Store our host number */
- wd7000_host = hostnum;
+ /* Set up SCB free list, which is shared by all adapters */
+ init_scbs();
- wd7000_init();
- wd7000_revision(); /* will set scatter/gather by rev level */
+ cfg = configs;
+ for (i = 0; i < NUM_CONFIGS; i++) {
+ sig = signatures;
+ for (j = 0; j < NUM_SIGNATURES; j++) {
+ if (!memcmp(cfg->bios+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 */
+ }
- 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)
/*
- * This is borrowed directly from aha1542.c, but my disks are organized
- * this way, so I think it will work OK.
+ * 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 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;
/* if (ip[2] >= 1024) ip[2] = 1024; */
return 0;
}
-
*
* Header file for the WD-7000 driver for Linux
*
- * $Log: $
- * Revision 1.1 1992/07/24 06:27:38 root
- * Initial revision
- *
- * 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 ***
+ * John Boyd <boyd@cis.ohio-state.edu> Jan 1994:
+ * This file has been reduced to only the definitions needed for the
+ * WD7000 host structure.
*
*/
#include <linux/types.h>
-#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 *));
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.
+ * 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.
*
- * 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.
+ * 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
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);
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 */
{ 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 */
};
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 */
#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 */
--- /dev/null
+/*
+ * 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
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];
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;
#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) */
{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} \
}
* 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 */
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Corey Minyard <wf-rch!minyard@relay.EU.net>
* Donald J. Becker, <becker@super.org>
+ * Alan Cox, <A.Cox@swansea.ac.uk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
#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.
*/
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 */
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. */
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);
};
#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 */
unsigned short dest;
unsigned long seq;
unsigned long ack_seq;
+#if defined(__i386__)
unsigned short res1:4,
doff:4,
fin:1,
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;
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 */
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);
#ifdef CONFIG_BLK_DEV_HD
{ "hd=", hd_setup },
#endif
+#ifdef CONFIG_CHR_DEV_ST
+ { "st=", st_setup },
+#endif
#ifdef CONFIG_BUSMOUSE
{ "bmouse=", bmouse_setup },
#endif
}
}
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;
}
}
}
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;
}
/*
* 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))
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));
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)
* 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 <linux/types.h>
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 */
/*
* 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;
* 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);
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;
}
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;
}
* 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;
}
/*
* 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; i<ARP_TABLE_SIZE; i++)
{
- for (entry = arp_tables[i]; entry; entry = entry->next)
+ 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;k<HBUFFERLEN-3 && j<entry->hlen;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(pos<offset)
{
len=0;
begin=pos;
- req=(struct arpreq *) buffer;
}
if(pos>offset+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;
}
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 */
* 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;
ip_statistics.IpInReceives++;
DPRINTF((DBG_IP, "<<\n"));
-
+
/*
* Tag the ip header of this packet so we can find it
*/
--- /dev/null
+/* 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 <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/config.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#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(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+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
--- /dev/null
+/* 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 */
+
* 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:
*
#include "ip.h"
#include "protocol.h"
#include "arp.h"
+#include "rarp.h"
#include "route.h"
#include "tcp.h"
#include "udp.h"
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:
/* 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) {
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)
*/
* Charles Hedrick, <hedrick@klinzhai.rutgers.edu>
* Linus Torvalds, <torvalds@cs.helsinki.fi>
* Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Matthew Dillon, <dillon@apollo.west.oic.com>
*
* Fixes:
* Alan Cox : Numerous verify_area() calls
* 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:
* 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 <linux/types.h>
#include <linux/sched.h>
unsigned long seq_offset;
struct tcp_mib tcp_statistics;
-#define SUBNETSARELOCAL
static __inline__ int
min(unsigned int a, unsigned int b)
/*
- * 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)
* 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)
{
/*
* 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;
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);
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)
{
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
* 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;
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
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);
}
}
-/* 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)
{
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
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));
sk->state = TCP_LAST_ACK;
if (!sk->dead) sk->state_change(sk);
}
+#endif
return(0);
}
/*
- * 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);
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)
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. */
case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT:
+ case TCP_CLOSING:
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
case TCP_TIME_WAIT:
}
/* 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);
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);
}
release_sock(sk);
return(0);
}
- tcp_fin(sk, th, saddr, dev);
+ tcp_fin(skb, sk, th, saddr, dev);
release_sock(sk);
return(0);
}
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)
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 {
* 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
{
struct sock *sk;
struct udphdr *uh;
-
+ unsigned short ulen;
+
/*
* Get the header.
*/
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)
{