Commit 16119ae5 authored by Linus Torvalds's avatar Linus Torvalds

Import 1.1.12

parent b7c2deb6
...@@ -34,7 +34,7 @@ S: 76297 Stutensee ...@@ -34,7 +34,7 @@ S: 76297 Stutensee
S: Germany S: Germany
N: Donald Becker N: Donald Becker
E: becker@super.org E: becker@cesdis.gsfc.nasa.gov
D: General low-level networking hacker D: General low-level networking hacker
D: Most of the ethercard drivers D: Most of the ethercard drivers
D: Original author of the NFS server D: Original author of the NFS server
...@@ -107,6 +107,7 @@ S: Bellevue WA 98007 ...@@ -107,6 +107,7 @@ S: Bellevue WA 98007
S: USA S: USA
N: Alan Cox N: Alan Cox
E: A.Cox@swansea.ac.uk
E: iiitac@pyr.swan.ac.uk E: iiitac@pyr.swan.ac.uk
E: gw4pts@gw4pts.ampr.org E: gw4pts@gw4pts.ampr.org
E: GW4PTS@GB7SWN (packet radio) E: GW4PTS@GB7SWN (packet radio)
...@@ -418,6 +419,14 @@ S: Dragonvagen 1 A 13 ...@@ -418,6 +419,14 @@ S: Dragonvagen 1 A 13
S: FIN-00330 Helsingfors S: FIN-00330 Helsingfors
S: Finland 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 N: Kai Petzke
E: wpp@marie.physik.tu-berlin.de E: wpp@marie.physik.tu-berlin.de
D: Driver for Laser Magnetic Storage CD-ROM D: Driver for Laser Magnetic Storage CD-ROM
...@@ -451,6 +460,13 @@ N: Robert Sanders ...@@ -451,6 +460,13 @@ N: Robert Sanders
E: gt8134b@prism.gatech.edu E: gt8134b@prism.gatech.edu
D: Dosemu 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 N: Peter De Schrijver
E: stud11@cc4.kuleuven.ac.be E: stud11@cc4.kuleuven.ac.be
D: Mitsumi CD-ROM driver patches March version D: Mitsumi CD-ROM driver patches March version
......
VERSION = 1 VERSION = 1
PATCHLEVEL = 1 PATCHLEVEL = 1
SUBLEVEL = 11 SUBLEVEL = 12
all: Version zImage all: Version zImage
......
...@@ -13,6 +13,19 @@ bool 'Limit memory to low 16MB' CONFIG_MAX_16M n ...@@ -13,6 +13,19 @@ bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
bool 'System V IPC' CONFIG_SYSVIPC y bool 'System V IPC' CONFIG_SYSVIPC y
bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 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' comment 'Program binary formats'
bool 'Elf executables' CONFIG_BINFMT_ELF y bool 'Elf executables' CONFIG_BINFMT_ELF y
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
* Michael Riepe : Automatic CSLIP recognition added * Michael Riepe : Automatic CSLIP recognition added
* Charles Hedrick : CSLIP header length problem fix. * Charles Hedrick : CSLIP header length problem fix.
* Alan Cox : Corrected non-IP cases of the above. * 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 * FIXME: This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
...@@ -56,6 +57,7 @@ ...@@ -56,6 +57,7 @@
#include "tcp.h" #include "tcp.h"
#endif #endif
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/if_arp.h>
#include "sock.h" #include "sock.h"
#include "slip.h" #include "slip.h"
#ifdef CONFIG_INET #ifdef CONFIG_INET
...@@ -169,6 +171,7 @@ sl_initialize(struct slip *sl, struct device *dev) ...@@ -169,6 +171,7 @@ sl_initialize(struct slip *sl, struct device *dev)
dev->rmem_start = (unsigned long) NULL; dev->rmem_start = (unsigned long) NULL;
dev->mem_end = (unsigned long) NULL; dev->mem_end = (unsigned long) NULL;
dev->mem_start = (unsigned long) NULL; dev->mem_start = (unsigned long) NULL;
dev->type = ARPHRD_SLIP + sl->mode;
} }
...@@ -1072,15 +1075,14 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) ...@@ -1072,15 +1075,14 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
{ {
sl->dev->addr_len=7; /* sizeof an AX.25 addr */ sl->dev->addr_len=7; /* sizeof an AX.25 addr */
sl->dev->hard_header_len=17; /* We don't do digipeaters */ sl->dev->hard_header_len=17; /* We don't do digipeaters */
sl->dev->type=3; /* AF_AX25 not an AF_INET device */
} }
else else
{ {
sl->dev->addr_len=0; /* No mac addr in slip mode */ sl->dev->addr_len=0; /* No mac addr in slip mode */
sl->dev->hard_header_len=0; sl->dev->hard_header_len=0;
sl->dev->type=0;
} }
#endif #endif
sl->dev->type=ARPHRD_SLIP+sl->mode;
return(0); return(0);
case SIOCSIFHWADDR: case SIOCSIFHWADDR:
#ifdef CONFIG_AX25 #ifdef CONFIG_AX25
......
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 SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
file README.st for more information.
Version 0.02 for Linux 0.98.4 and Eric Youngdale's new scsi driver
History: History:
Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara. Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
Contribution and ideas from several people including Eric Youngdale and Contribution and ideas from several people including (in alphabetical
Wolfgang Denk. order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and
Eric Youngdale.
Features:
- support for different block sizes and internal buffering Copyright 1992, 1993, 1994 Kai Makisara
- 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
email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi 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 <linux/fs.h>
...@@ -57,44 +32,40 @@ ...@@ -57,44 +32,40 @@
#include "st.h" #include "st.h"
#include "constants.h" #include "constants.h"
/* Uncomment the following if you want the rewind, etc. commands return /* #define DEBUG */
before command completion. */
/* #define ST_NOWAIT */ /* #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 */ /* #define ST_IN_FILE_POS */
/* Uncomment the following if you want recovered write errors to be
fatal. */
/* #define ST_RECOVERED_WRITE_FATAL */ /* #define ST_RECOVERED_WRITE_FATAL */
/* Uncomment the following if you want all data from a write command to #define ST_BUFFER_WRITES 1
be written to tape before the command returns. Disables write-behind. */
/* #define ST_NO_DELAYED_WRITES */ #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_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE)
#define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE) #define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE)
#ifdef ST_NO_DELAYED_WRITES /* The buffer size should fit into the 24 bits for length in the
#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
6-byte SCSI read and write commands. */ 6-byte SCSI read and write commands. */
#if ST_BUFFER_SIZE >= (2 << 24 - 1) #if ST_BUFFER_SIZE >= (2 << 24 - 1)
#error "Buffer size should not exceed (2 << 24 - 1) bytes!" #error "Buffer size should not exceed (2 << 24 - 1) bytes!"
#endif #endif
/* #define DEBUG */ #ifdef DEBUG
static int debugging = 1;
#endif
#define MAX_RETRIES 0 #define MAX_RETRIES 0
#define MAX_READY_RETRIES 5 #define MAX_READY_RETRIES 5
...@@ -104,7 +75,10 @@ ...@@ -104,7 +75,10 @@
#define ST_LONG_TIMEOUT 200000 #define ST_LONG_TIMEOUT 200000
static int st_nbr_buffers; 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; static Scsi_Tape * scsi_tapes;
int NR_ST=0; int NR_ST=0;
...@@ -128,8 +102,14 @@ st_chk_result(Scsi_Cmnd * SCpnt) ...@@ -128,8 +102,14 @@ st_chk_result(Scsi_Cmnd * SCpnt)
if (!result && SCpnt->sense_buffer[0] == 0) if (!result && SCpnt->sense_buffer[0] == 0)
return 0; return 0;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Error: %x\n", dev, result); if (debugging) {
print_sense("st", SCpnt); 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 #endif
/* if ((sense[0] & 0x70) == 0x70 && /* if ((sense[0] & 0x70) == 0x70 &&
((sense[2] & 0x80) )) ((sense[2] & 0x80) ))
...@@ -142,6 +122,7 @@ st_chk_result(Scsi_Cmnd * SCpnt) ...@@ -142,6 +122,7 @@ st_chk_result(Scsi_Cmnd * SCpnt)
#endif #endif
) { ) {
scsi_tapes[dev].recover_count++; scsi_tapes[dev].recover_count++;
scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
if (SCpnt->cmnd[0] == READ_6) if (SCpnt->cmnd[0] == READ_6)
stp = "read"; stp = "read";
else if (SCpnt->cmnd[0] == WRITE_6) else if (SCpnt->cmnd[0] == WRITE_6)
...@@ -192,13 +173,12 @@ st_sleep_done (Scsi_Cmnd * SCpnt) ...@@ -192,13 +173,12 @@ st_sleep_done (Scsi_Cmnd * SCpnt)
wake_up( &(STp->waiting) ); wake_up( &(STp->waiting) );
} }
#ifdef DEBUG #ifdef DEBUG
else else if (debugging)
printk("st?: Illegal interrupt device %x\n", st_nbr); printk("st?: Illegal interrupt device %x\n", st_nbr);
#endif #endif
} }
#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
/* Handle the write-behind checking */ /* Handle the write-behind checking */
static void static void
write_behind_check(int dev) write_behind_check(int dev)
...@@ -222,11 +202,44 @@ write_behind_check(int dev) ...@@ -222,11 +202,44 @@ write_behind_check(int dev)
STbuffer->b_data + STbuffer->writing, STbuffer->b_data + STbuffer->writing,
STbuffer->buffer_bytes - STbuffer->writing); STbuffer->buffer_bytes - 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; STbuffer->writing = 0;
return; 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). */ /* Flush the write buffer (never need to write if variable blocksize). */
...@@ -239,20 +252,19 @@ flush_write_buffer(int dev) ...@@ -239,20 +252,19 @@ flush_write_buffer(int dev)
Scsi_Cmnd *SCpnt; Scsi_Cmnd *SCpnt;
Scsi_Tape *STp = &(scsi_tapes[dev]); Scsi_Tape *STp = &(scsi_tapes[dev]);
#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
if ((STp->buffer)->writing) { if ((STp->buffer)->writing) {
write_behind_check(dev); write_behind_check(dev);
if ((STp->buffer)->last_result_fatal) { if ((STp->buffer)->last_result_fatal) {
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Async write error %x.\n", dev, if (debugging)
(STp->buffer)->last_result); printk("st%d: Async write error (flush) %x.\n", dev,
(STp->buffer)->last_result);
#endif #endif
if ((STp->buffer)->last_result == INT_MAX) if ((STp->buffer)->last_result == INT_MAX)
return (-ENOSPC); return (-ENOSPC);
return (-EIO); return (-EIO);
} }
} }
#endif
result = 0; result = 0;
if (STp->dirty == 1) { if (STp->dirty == 1) {
...@@ -262,7 +274,8 @@ flush_write_buffer(int dev) ...@@ -262,7 +274,8 @@ flush_write_buffer(int dev)
transfer = ((offset + STp->block_size - 1) / transfer = ((offset + STp->block_size - 1) /
STp->block_size) * STp->block_size; STp->block_size) * STp->block_size;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Flushing %d bytes.\n", dev, transfer); if (debugging)
printk("st%d: Flushing %d bytes.\n", dev, transfer);
#endif #endif
memset((STp->buffer)->b_data + offset, 0, transfer - offset); memset((STp->buffer)->b_data + offset, 0, transfer - offset);
...@@ -292,8 +305,11 @@ flush_write_buffer(int dev) ...@@ -292,8 +305,11 @@ flush_write_buffer(int dev)
} }
else else
result = (-EIO); result = (-EIO);
STp->drv_block = (-1);
} }
else { else {
if (STp->drv_block >= 0)
STp->drv_block += blks;
STp->dirty = 0; STp->dirty = 0;
(STp->buffer)->buffer_bytes = 0; (STp->buffer)->buffer_bytes = 0;
} }
...@@ -332,7 +348,7 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next) ...@@ -332,7 +348,7 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next)
result = 0; result = 0;
if (!seek_next) { if (!seek_next) {
if ((STp->eof == ST_FM) && !STp->eof_hit) { 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) { if (!result) {
STp->eof = ST_NOEOF; STp->eof = ST_NOEOF;
STp->eof_hit = 0; STp->eof_hit = 0;
...@@ -400,31 +416,36 @@ scsi_tape_open(struct inode * inode, struct file * filp) ...@@ -400,31 +416,36 @@ scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev; SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt, scsi_do_cmd(SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data, (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); MAX_READY_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
(STp->mt_status)->mt_fileno = 0 ;
SCpnt->sense_buffer[0]=0; SCpnt->sense_buffer[0]=0;
memset ((void *) &cmd[0], 0, 10); memset ((void *) &cmd[0], 0, 10);
cmd[0] = TEST_UNIT_READY; cmd[0] = TEST_UNIT_READY;
SCpnt->request.dev = dev; SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt, scsi_do_cmd(SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data, (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); MAX_READY_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); 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 ((STp->buffer)->last_result_fatal != 0) {
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && 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); printk("st%d: No tape.\n", dev);
else } else {
printk("st%d: Error %x.\n", dev, SCpnt->result); printk("st%d: Error %x.\n", dev, SCpnt->result);
(STp->mt_status)->mt_fileno = STp->drv_block = (-1);
}
(STp->buffer)->in_use = 0; (STp->buffer)->in_use = 0;
STp->in_use = 0; STp->in_use = 0;
SCpnt->request.dev = -1; /* Mark as not busy */ SCpnt->request.dev = -1; /* Mark as not busy */
...@@ -437,7 +458,7 @@ scsi_tape_open(struct inode * inode, struct file * filp) ...@@ -437,7 +458,7 @@ scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev; SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt, scsi_do_cmd(SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data, (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) ); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
...@@ -447,14 +468,16 @@ scsi_tape_open(struct inode * inode, struct file * filp) ...@@ -447,14 +468,16 @@ scsi_tape_open(struct inode * inode, struct file * filp)
STp->min_block = ((STp->buffer)->b_data[4] << 8) | STp->min_block = ((STp->buffer)->b_data[4] << 8) |
(STp->buffer)->b_data[5]; (STp->buffer)->b_data[5];
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block, if (debugging)
STp->max_block); printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block,
STp->max_block);
#endif #endif
} }
else { else {
STp->min_block = STp->max_block = (-1); STp->min_block = STp->max_block = (-1);
#ifdef DEBUG #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 #endif
} }
...@@ -465,13 +488,14 @@ scsi_tape_open(struct inode * inode, struct file * filp) ...@@ -465,13 +488,14 @@ scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev; SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt, scsi_do_cmd(SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data, (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 (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
if ((STp->buffer)->last_result_fatal != 0) { if ((STp->buffer)->last_result_fatal != 0) {
#ifdef DEBUG #ifdef DEBUG
printk("st%d: No Mode Sense.\n", dev); if (debugging)
printk("st%d: No Mode Sense.\n", dev);
#endif #endif
(STp->buffer)->b_data[2] = (STp->buffer)->b_data[2] =
(STp->buffer)->b_data[3] = 0; (STp->buffer)->b_data[3] = 0;
...@@ -479,9 +503,10 @@ scsi_tape_open(struct inode * inode, struct file * filp) ...@@ -479,9 +503,10 @@ scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt->request.dev = -1; /* Mark as not busy */ SCpnt->request.dev = -1; /* Mark as not busy */
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev, if (debugging)
(STp->buffer)->b_data[0], (STp->buffer)->b_data[1], printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev,
(STp->buffer)->b_data[2], (STp->buffer)->b_data[3]); (STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
(STp->buffer)->b_data[2], (STp->buffer)->b_data[3]);
#endif #endif
if ((STp->buffer)->b_data[3] >= 8) { if ((STp->buffer)->b_data[3] >= 8) {
...@@ -490,13 +515,13 @@ scsi_tape_open(struct inode * inode, struct file * filp) ...@@ -490,13 +515,13 @@ scsi_tape_open(struct inode * inode, struct file * filp)
STp->block_size = (STp->buffer)->b_data[9] * 65536 + STp->block_size = (STp->buffer)->b_data[9] * 65536 +
(STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11]; (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
#ifdef DEBUG #ifdef DEBUG
printk( if (debugging)
"st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n", printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n",
dev, STp->density, (STp->buffer)->b_data[5] * 65536 + dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
(STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7], (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
STp->block_size, STp->drv_buffer); STp->block_size, STp->drv_buffer);
#endif #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, printk("st%d: Blocksize %d too large for buffer.\n", dev,
STp->block_size); STp->block_size);
(STp->buffer)->in_use = 0; (STp->buffer)->in_use = 0;
...@@ -506,29 +531,31 @@ scsi_tape_open(struct inode * inode, struct file * filp) ...@@ -506,29 +531,31 @@ scsi_tape_open(struct inode * inode, struct file * filp)
} }
else else
STp->block_size = ST_BLOCK_SIZE; STp->block_size = 512; /* "Educated Guess" (?) */
if (STp->block_size > 0) { 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_size =
(STp->buffer)->buffer_blocks * STp->block_size; (STp->buffer)->buffer_blocks * STp->block_size;
} }
else { else {
(STp->buffer)->buffer_blocks = 1; (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->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev, if (debugging)
STp->block_size, (STp->buffer)->buffer_size, printk("st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev,
(STp->buffer)->buffer_blocks); STp->block_size, (STp->buffer)->buffer_size,
(STp->buffer)->buffer_blocks);
#endif #endif
if ((STp->buffer)->b_data[2] & 0x80) { if ((STp->buffer)->b_data[2] & 0x80) {
STp->write_prot = 1; STp->write_prot = 1;
#ifdef DEBUG #ifdef DEBUG
printk( "st%d: Write protected\n", dev); if (debugging)
printk( "st%d: Write protected\n", dev);
#endif #endif
} }
...@@ -557,7 +584,8 @@ scsi_tape_close(struct inode * inode, struct file * filp) ...@@ -557,7 +584,8 @@ scsi_tape_close(struct inode * inode, struct file * filp)
result = flush_write_buffer(dev); result = flush_write_buffer(dev);
#ifdef DEBUG #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 #endif
if (result == 0 || result == (-ENOSPC)) { if (result == 0 || result == (-ENOSPC)) {
...@@ -570,24 +598,29 @@ scsi_tape_close(struct inode * inode, struct file * filp) ...@@ -570,24 +598,29 @@ scsi_tape_close(struct inode * inode, struct file * filp)
SCpnt->request.dev = dev; SCpnt->request.dev = dev;
scsi_do_cmd( SCpnt, scsi_do_cmd( SCpnt,
(void *) cmd, (void *) (STp->buffer)->b_data, (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 (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
if ((STp->buffer)->last_result_fatal != 0) if ((STp->buffer)->last_result_fatal != 0)
printk("st%d: Error on write filemark.\n", dev); 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 */ SCpnt->request.dev = -1; /* Mark as not busy */
} }
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Buffer flushed, EOF written\n", dev); if (debugging)
printk("st%d: Buffer flushed, EOF written\n", dev);
#endif #endif
} }
else if (!rewind) { else if (!rewind) {
#ifndef ST_IN_FILE_POS #ifndef ST_IN_FILE_POS
if ((STp->eof == ST_FM) && !STp->eof_hit) 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 #else
flush_buffer(inode, filp, 0); flush_buffer(inode, filp, 0);
#endif #endif
...@@ -627,7 +660,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -627,7 +660,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
if (STp->write_prot) if (STp->write_prot)
return (-EACCES); return (-EACCES);
if (STp->block_size == 0 && count > ST_BUFFER_SIZE) if (STp->block_size == 0 && count > st_buffer_size)
return (-EOVERFLOW); return (-EOVERFLOW);
if (STp->rw == ST_READING) { if (STp->rw == ST_READING) {
...@@ -637,13 +670,13 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -637,13 +670,13 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
STp->rw = ST_WRITING; STp->rw = ST_WRITING;
} }
#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
if ((STp->buffer)->writing) { if ((STp->buffer)->writing) {
write_behind_check(dev); write_behind_check(dev);
if ((STp->buffer)->last_result_fatal) { if ((STp->buffer)->last_result_fatal) {
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Async write error %x.\n", dev, if (debugging)
(STp->buffer)->last_result); printk("st%d: Async write error (write) %x.\n", dev,
(STp->buffer)->last_result);
#endif #endif
if ((STp->buffer)->last_result == INT_MAX) { if ((STp->buffer)->last_result == INT_MAX) {
retval = (-ENOSPC); /* All has been written */ retval = (-ENOSPC); /* All has been written */
...@@ -654,20 +687,21 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -654,20 +687,21 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
return retval; return retval;
} }
} }
#endif
if (STp->eof == ST_EOM_OK) if (STp->eof == ST_EOM_OK)
return (-ENOSPC); return (-ENOSPC);
else if (STp->eof == ST_EOM_ERROR) else if (STp->eof == ST_EOM_ERROR)
return (-EIO); return (-EIO);
#ifdef ST_NO_DELAYED_WRITES if (!STp->do_buffer_writes) {
if (STp->block_size != 0 && (count % STp->block_size) != 0) if (STp->block_size != 0 && (count % STp->block_size) != 0)
return (-EIO); /* Write must be integral number of blocks */ return (-EIO); /* Write must be integral number of blocks */
write_threshold = 1; write_threshold = 1;
#else }
write_threshold = (STp->buffer)->buffer_size; else
#endif write_threshold = (STp->buffer)->buffer_size;
if (!STp->do_async_writes)
write_threshold--;
SCpnt = allocate_device(NULL, (STp->device)->index, 1); SCpnt = allocate_device(NULL, (STp->device)->index, 1);
...@@ -680,16 +714,9 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -680,16 +714,9 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
STp->rw = ST_WRITING; STp->rw = ST_WRITING;
b_point = buf; b_point = buf;
while( while((STp->block_size == 0 && !STp->do_async_writes && count > 0) ||
#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS (STp->block_size != 0 &&
STp->block_size != 0 && (STp->buffer)->buffer_bytes + count > write_threshold))
((STp->buffer)->buffer_bytes + count) >
write_threshold)
#else
(STp->block_size == 0 && count > 0) ||
((STp->buffer)->buffer_bytes + count) >=
write_threshold)
#endif
{ {
if (STp->block_size == 0) if (STp->block_size == 0)
do_count = count; do_count = count;
...@@ -702,25 +729,27 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -702,25 +729,27 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
(STp->buffer)->buffer_bytes, b_point, do_count); (STp->buffer)->buffer_bytes, b_point, do_count);
if (STp->block_size == 0) if (STp->block_size == 0)
blks = do_count; blks = transfer = do_count;
else else {
blks = ((STp->buffer)->buffer_bytes + do_count) / blks = ((STp->buffer)->buffer_bytes + do_count) /
STp->block_size; STp->block_size;
transfer = blks * STp->block_size;
}
cmd[2] = blks >> 16; cmd[2] = blks >> 16;
cmd[3] = blks >> 8; cmd[3] = blks >> 8;
cmd[4] = blks; cmd[4] = blks;
SCpnt->sense_buffer[0] = 0; SCpnt->sense_buffer[0] = 0;
SCpnt->request.dev = dev; SCpnt->request.dev = dev;
scsi_do_cmd (SCpnt, scsi_do_cmd (SCpnt,
(void *) cmd, (STp->buffer)->b_data, (void *) cmd, (STp->buffer)->b_data, transfer,
(STp->buffer)->buffer_size,
st_sleep_done, ST_TIMEOUT, MAX_RETRIES); st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
if ((STp->buffer)->last_result_fatal != 0) { if ((STp->buffer)->last_result_fatal != 0) {
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Error on write:\n", dev); if (debugging)
printk("st%d: Error on write:\n", dev);
#endif #endif
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
(SCpnt->sense_buffer[2] & 0x40)) { (SCpnt->sense_buffer[2] & 0x40)) {
...@@ -738,23 +767,34 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -738,23 +767,34 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
if (transfer <= do_count) { if (transfer <= do_count) {
filp->f_pos += do_count - transfer; filp->f_pos += do_count - transfer;
count -= 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; STp->eof = ST_EOM_OK;
retval = (-ENOSPC); /* EOM within current request */ retval = (-ENOSPC); /* EOM within current request */
#ifdef DEBUG #ifdef DEBUG
printk("st%d: EOM with %d bytes unwritten.\n", if (debugging)
dev, transfer); printk("st%d: EOM with %d bytes unwritten.\n",
dev, transfer);
#endif #endif
} }
else { else {
STp->eof = ST_EOM_ERROR; STp->eof = ST_EOM_ERROR;
STp->drv_block = (-1); /* Too cautious? */
retval = (-EIO); /* EOM for old data */ retval = (-EIO); /* EOM for old data */
#ifdef DEBUG #ifdef DEBUG
printk("st%d: EOM with lost data.\n", dev); if (debugging)
printk("st%d: EOM with lost data.\n", dev);
#endif #endif
} }
} }
else else {
STp->drv_block = (-1); /* Too cautious? */
retval = (-EIO); retval = (-EIO);
}
SCpnt->request.dev = -1; /* Mark as not busy */ SCpnt->request.dev = -1; /* Mark as not busy */
(STp->buffer)->buffer_bytes = 0; (STp->buffer)->buffer_bytes = 0;
...@@ -767,6 +807,12 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -767,6 +807,12 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
filp->f_pos += do_count; filp->f_pos += do_count;
b_point += do_count; b_point += do_count;
count -= 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->buffer)->buffer_bytes = 0;
STp->dirty = 0; STp->dirty = 0;
} }
...@@ -784,9 +830,9 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -784,9 +830,9 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
return (STp->buffer)->last_result_fatal; return (STp->buffer)->last_result_fatal;
} }
#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS if (STp->do_async_writes &&
if ((STp->buffer)->buffer_bytes >= ST_WRITE_THRESHOLD || ((STp->buffer)->buffer_bytes >= STp->write_threshold ||
STp->block_size == 0) { STp->block_size == 0) ) {
/* Schedule an asynchronous write */ /* Schedule an asynchronous write */
if (STp->block_size == 0) if (STp->block_size == 0)
(STp->buffer)->writing = (STp->buffer)->buffer_bytes; (STp->buffer)->writing = (STp->buffer)->buffer_bytes;
...@@ -811,7 +857,6 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -811,7 +857,6 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
st_sleep_done, ST_TIMEOUT, MAX_RETRIES); st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
} }
else else
#endif
SCpnt->request.dev = -1; /* Mark as not busy */ SCpnt->request.dev = -1; /* Mark as not busy */
return( total); return( total);
...@@ -838,9 +883,13 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -838,9 +883,13 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
} }
#endif #endif
if (STp->block_size == 0 && count > ST_BUFFER_SIZE) if (STp->block_size == 0 && count > st_buffer_size)
return (-EOVERFLOW); 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) { if (STp->rw == ST_WRITING) {
transfer = flush_buffer(inode, filp, 0); transfer = flush_buffer(inode, filp, 0);
if (transfer) if (transfer)
...@@ -849,7 +898,7 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -849,7 +898,7 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
} }
#ifdef DEBUG #ifdef DEBUG
if (STp->eof != ST_NOEOF) if (debugging && STp->eof != ST_NOEOF)
printk("st%d: EOF flag up. Bytes %d\n", dev, printk("st%d: EOF flag up. Bytes %d\n", dev,
(STp->buffer)->buffer_bytes); (STp->buffer)->buffer_bytes);
#endif #endif
...@@ -872,8 +921,17 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -872,8 +921,17 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
if (STp->block_size == 0) if (STp->block_size == 0)
blks = bytes = count; blks = bytes = count;
else { else {
blks = (STp->buffer)->buffer_blocks; if (STp->do_read_ahead) {
bytes = blks * STp->block_size; 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[2] = blks >> 16;
cmd[3] = blks >> 8; cmd[3] = blks >> 8;
...@@ -893,11 +951,12 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -893,11 +951,12 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
if ((STp->buffer)->last_result_fatal) { if ((STp->buffer)->last_result_fatal) {
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev, if (debugging)
SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], printk("st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev,
SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]); SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
#endif #endif
if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
...@@ -917,7 +976,7 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -917,7 +976,7 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
if (STp->block_size == 0) { if (STp->block_size == 0) {
if (transfer <= 0) if (transfer <= 0)
transfer = 0; transfer = 0;
(STp->buffer)->buffer_bytes = count - transfer; (STp->buffer)->buffer_bytes = bytes - transfer;
} }
else { else {
printk("st%d: Incorrect block size.\n", dev); printk("st%d: Incorrect block size.\n", dev);
...@@ -928,14 +987,14 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -928,14 +987,14 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
else if (SCpnt->sense_buffer[2] & 0x40) { else if (SCpnt->sense_buffer[2] & 0x40) {
STp->eof = ST_EOM_OK; STp->eof = ST_EOM_OK;
if (STp->block_size == 0) if (STp->block_size == 0)
(STp->buffer)->buffer_bytes = count - transfer; (STp->buffer)->buffer_bytes = bytes - transfer;
else else
(STp->buffer)->buffer_bytes = (STp->buffer)->buffer_bytes =
((STp->buffer)->buffer_blocks - transfer) * bytes - transfer * STp->block_size;
STp->block_size;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: EOM detected (%d bytes read).\n", dev, if (debugging)
(STp->buffer)->buffer_bytes); printk("st%d: EOM detected (%d bytes read).\n", dev,
(STp->buffer)->buffer_bytes);
#endif #endif
} }
else if (SCpnt->sense_buffer[2] & 0x80) { else if (SCpnt->sense_buffer[2] & 0x80) {
...@@ -944,20 +1003,22 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -944,20 +1003,22 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
(STp->buffer)->buffer_bytes = 0; (STp->buffer)->buffer_bytes = 0;
else else
(STp->buffer)->buffer_bytes = (STp->buffer)->buffer_bytes =
((STp->buffer)->buffer_blocks - transfer) * bytes - transfer * STp->block_size;
STp->block_size;
#ifdef DEBUG #ifdef DEBUG
printk( if (debugging)
"st%d: EOF detected (%d bytes read, transferred %d bytes).\n", printk(
dev, (STp->buffer)->buffer_bytes, total); "st%d: EOF detected (%d bytes read, transferred %d bytes).\n",
dev, (STp->buffer)->buffer_bytes, total);
#endif #endif
} /* end of EOF, EOM, ILI test */ } /* end of EOF, EOM, ILI test */
} }
else { /* nonzero sense key */ else { /* nonzero sense key */
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Tape error while reading.\n", dev); if (debugging)
printk("st%d: Tape error while reading.\n", dev);
#endif #endif
SCpnt->request.dev = -1; SCpnt->request.dev = -1;
STp->drv_block = (-1);
if (total) if (total)
return total; return total;
else else
...@@ -973,12 +1034,19 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -973,12 +1034,19 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
else /* Read successful */ else /* Read successful */
(STp->buffer)->buffer_bytes = bytes; (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 && } /* if ((STp->buffer)->buffer_bytes == 0 &&
STp->eof == ST_NOEOF) */ STp->eof == ST_NOEOF) */
if ((STp->buffer)->buffer_bytes > 0) { if ((STp->buffer)->buffer_bytes > 0) {
#ifdef DEBUG #ifdef DEBUG
if (STp->eof != ST_NOEOF) if (debugging && STp->eof != ST_NOEOF)
printk("st%d: EOF up. Left %d, needed %d.\n", dev, printk("st%d: EOF up. Left %d, needed %d.\n", dev,
(STp->buffer)->buffer_bytes, count - total); (STp->buffer)->buffer_bytes, count - total);
#endif #endif
...@@ -995,8 +1063,11 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -995,8 +1063,11 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
else if (STp->eof != ST_NOEOF) { else if (STp->eof != ST_NOEOF) {
STp->eof_hit = 1; STp->eof_hit = 1;
SCpnt->request.dev = -1; /* Mark as not busy */ 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->eof = 0;
STp->drv_block = 0;
(STp->mt_status)->mt_fileno++;
}
if (total == 0 && STp->eof == ST_EOM_OK) if (total == 0 && STp->eof == ST_EOM_OK)
return (-EIO); /* ST_EOM_ERROR not used in read */ return (-EIO); /* ST_EOM_ERROR not used in read */
return total; return total;
...@@ -1012,6 +1083,49 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count) ...@@ -1012,6 +1083,49 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
return total; return total;
} }
/* Set the driver options */
static int
st_set_options(struct inode * inode, long options)
{
int dev, value;
Scsi_Tape *STp;
dev = MINOR(inode->i_rdev) & 127;
STp = &(scsi_tapes[dev]);
if ((options & MT_ST_OPTIONS) == MT_ST_BOOLEANS) {
STp->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
STp->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
STp->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
#ifdef DEBUG
debugging = (options & MT_ST_DEBUGGING) != 0;
printk(
"st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
dev, STp->do_buffer_writes, STp->do_async_writes,
STp->do_read_ahead);
printk(" debugging: %d\n", debugging);
#endif
}
else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
value = (options & ~MT_ST_OPTIONS) * ST_BLOCK_SIZE;
if (value < 1 || value > st_buffer_size) {
printk("st: Write threshold %d too small or too large.\n",
value);
return (-EIO);
}
STp->write_threshold = value;
#ifdef DEBUG
printk("st%d: Write threshold set to %d bytes.\n", dev,
STp->write_threshold);
#endif
}
else
return (-EIO);
return 0;
}
/* Internal ioctl function */ /* Internal ioctl function */
static int static int
...@@ -1025,11 +1139,15 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1025,11 +1139,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
unsigned char cmd[10]; unsigned char cmd[10];
Scsi_Cmnd * SCpnt; Scsi_Cmnd * SCpnt;
Scsi_Tape * STp; Scsi_Tape * STp;
int fileno, blkno, undone, datalen;
dev = dev & 127; dev = dev & 127;
STp = &(scsi_tapes[dev]); STp = &(scsi_tapes[dev]);
fileno = (STp->mt_status)->mt_fileno ;
blkno = STp->drv_block;
memset(cmd, 0, 10); memset(cmd, 0, 10);
datalen = 0;
switch (cmd_in) { switch (cmd_in) {
case MTFSF: case MTFSF:
case MTFSFM: case MTFSFM:
...@@ -1039,9 +1157,12 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1039,9 +1157,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[3] = (arg >> 8); cmd[3] = (arg >> 8);
cmd[4] = arg; cmd[4] = arg;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Spacing tape forward over %d filemarks.\n", dev, if (debugging)
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif #endif
fileno += arg;
blkno = 0;
break; break;
case MTBSF: case MTBSF:
case MTBSFM: case MTBSFM:
...@@ -1052,11 +1173,15 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1052,11 +1173,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[3] = (ltmp >> 8); cmd[3] = (ltmp >> 8);
cmd[4] = ltmp; cmd[4] = ltmp;
#ifdef DEBUG #ifdef DEBUG
if (cmd[2] & 0x80) if (debugging) {
ltmp = 0xff000000; if (cmd[2] & 0x80)
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; ltmp = 0xff000000;
printk("st%d: Spacing tape backward over %d filemarks.\n", dev, (-ltmp)); ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp));
}
#endif #endif
fileno -= arg;
blkno = (-1); /* We can't know the block number */
break; break;
case MTFSR: case MTFSR:
cmd[0] = SPACE; cmd[0] = SPACE;
...@@ -1065,9 +1190,12 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1065,9 +1190,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[3] = (arg >> 8); cmd[3] = (arg >> 8);
cmd[4] = arg; cmd[4] = arg;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Spacing tape forward %d blocks.\n", dev, if (debugging)
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); printk("st%d: Spacing tape forward %d blocks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif #endif
if (blkno >= 0)
blkno += arg;
break; break;
case MTBSR: case MTBSR:
cmd[0] = SPACE; cmd[0] = SPACE;
...@@ -1077,11 +1205,15 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1077,11 +1205,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[3] = (ltmp >> 8); cmd[3] = (ltmp >> 8);
cmd[4] = ltmp; cmd[4] = ltmp;
#ifdef DEBUG #ifdef DEBUG
if (cmd[2] & 0x80) if (debugging) {
ltmp = 0xff000000; if (cmd[2] & 0x80)
ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4]; ltmp = 0xff000000;
printk("st%d: Spacing tape backward %d blocks.\n", dev, (-ltmp)); ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
printk("st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp));
}
#endif #endif
if (blkno >= 0)
blkno -= arg;
break; break;
case MTWEOF: case MTWEOF:
if (STp->write_prot) if (STp->write_prot)
...@@ -1092,9 +1224,12 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1092,9 +1224,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[4] = arg; cmd[4] = arg;
timeout = ST_TIMEOUT; timeout = ST_TIMEOUT;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Writing %d filemarks.\n", dev, if (debugging)
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]); printk("st%d: Writing %d filemarks.\n", dev,
cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
#endif #endif
fileno += arg;
blkno = 0;
break; break;
case MTREW: case MTREW:
cmd[0] = REZERO_UNIT; cmd[0] = REZERO_UNIT;
...@@ -1103,8 +1238,10 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1103,8 +1238,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
timeout = ST_TIMEOUT; timeout = ST_TIMEOUT;
#endif #endif
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Rewinding tape.\n", dev); if (debugging)
printk("st%d: Rewinding tape.\n", dev);
#endif #endif
fileno = blkno = 0 ;
break; break;
case MTOFFL: case MTOFFL:
cmd[0] = START_STOP; cmd[0] = START_STOP;
...@@ -1113,12 +1250,15 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1113,12 +1250,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
timeout = ST_TIMEOUT; timeout = ST_TIMEOUT;
#endif #endif
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Unloading tape.\n", dev); if (debugging)
printk("st%d: Unloading tape.\n", dev);
#endif #endif
fileno = blkno = 0 ;
break; break;
case MTNOP: case MTNOP:
#ifdef DEBUG #ifdef DEBUG
printk("st%d: No op on tape.\n", dev); if (debugging)
printk("st%d: No op on tape.\n", dev);
#endif #endif
return 0; /* Should do something ? */ return 0; /* Should do something ? */
break; break;
...@@ -1130,15 +1270,26 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1130,15 +1270,26 @@ st_int_ioctl(struct inode * inode,struct file * file,
#endif #endif
cmd[4] = 3; cmd[4] = 3;
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Retensioning tape.\n", dev); if (debugging)
printk("st%d: Retensioning tape.\n", dev);
#endif #endif
fileno = blkno = 0 ;
break; break;
case MTEOM: 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[0] = SPACE;
cmd[1] = 3; cmd[1] = 3;
#ifdef DEBUG #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 #endif
blkno = (-1);
break; break;
case MTERASE: case MTERASE:
if (STp->write_prot) if (STp->write_prot)
...@@ -1146,8 +1297,10 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1146,8 +1297,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd[0] = ERASE; cmd[0] = ERASE;
cmd[1] = 1; /* To the end of tape */ cmd[1] = 1; /* To the end of tape */
#ifdef DEBUG #ifdef DEBUG
printk("st%d: Erasing tape.\n", dev); if (debugging)
printk("st%d: Erasing tape.\n", dev);
#endif #endif
fileno = blkno = 0 ;
break; break;
case MTSEEK: case MTSEEK:
if ((STp->device)->scsi_level < SCSI_2) { if ((STp->device)->scsi_level < SCSI_2) {
...@@ -1170,8 +1323,10 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1170,8 +1323,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
timeout = ST_TIMEOUT; timeout = ST_TIMEOUT;
#endif #endif
#ifdef DEBUG #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 #endif
fileno = blkno = (-1);
break; break;
case MTSETBLK: /* Set block length */ case MTSETBLK: /* Set block length */
case MTSETDENSITY: /* Set tape density */ case MTSETDENSITY: /* Set tape density */
...@@ -1181,12 +1336,12 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1181,12 +1336,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
if (cmd_in == MTSETBLK && if (cmd_in == MTSETBLK &&
arg != 0 && arg != 0 &&
(arg < STp->min_block || arg > STp->max_block || (arg < STp->min_block || arg > STp->max_block ||
arg > ST_BUFFER_SIZE)) { arg > st_buffer_size)) {
printk("st%d: Illegal block size.\n", dev); printk("st%d: Illegal block size.\n", dev);
return (-EINVAL); return (-EINVAL);
} }
cmd[0] = MODE_SELECT; cmd[0] = MODE_SELECT;
cmd[4] = 12; cmd[4] = datalen = 12;
memset((STp->buffer)->b_data, 0, 12); memset((STp->buffer)->b_data, 0, 12);
if (cmd_in == MTSETDRVBUFFER) if (cmd_in == MTSETDRVBUFFER)
...@@ -1208,17 +1363,19 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1208,17 +1363,19 @@ st_int_ioctl(struct inode * inode,struct file * file,
(STp->buffer)->b_data[11] = ltmp; (STp->buffer)->b_data[11] = ltmp;
timeout = ST_TIMEOUT; timeout = ST_TIMEOUT;
#ifdef DEBUG #ifdef DEBUG
if (cmd_in == MTSETBLK) if (debugging) {
printk("st%d: Setting block size to %d bytes.\n", dev, if (cmd_in == MTSETBLK)
(STp->buffer)->b_data[9] * 65536 + printk("st%d: Setting block size to %d bytes.\n", dev,
(STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[9] * 65536 +
(STp->buffer)->b_data[11]); (STp->buffer)->b_data[10] * 256 +
else if (cmd_in == MTSETDENSITY) (STp->buffer)->b_data[11]);
printk("st%d: Setting density code to %x.\n", dev, else if (cmd_in == MTSETDENSITY)
(STp->buffer)->b_data[4]); printk("st%d: Setting density code to %x.\n", dev,
else (STp->buffer)->b_data[4]);
printk("st%d: Setting drive buffer code to %d.\n", dev, else
((STp->buffer)->b_data[2] >> 4) & 7); printk("st%d: Setting drive buffer code to %d.\n", dev,
((STp->buffer)->b_data[2] >> 4) & 7);
}
#endif #endif
break; break;
default: default:
...@@ -1230,7 +1387,7 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1230,7 +1387,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
SCpnt->sense_buffer[0] = 0; SCpnt->sense_buffer[0] = 0;
SCpnt->request.dev = dev; SCpnt->request.dev = dev;
scsi_do_cmd(SCpnt, 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); st_sleep_done, timeout, MAX_RETRIES);
if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) ); if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
...@@ -1240,6 +1397,8 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1240,6 +1397,8 @@ st_int_ioctl(struct inode * inode,struct file * file,
SCpnt->request.dev = -1; /* Mark as not busy */ SCpnt->request.dev = -1; /* Mark as not busy */
if (!ioctl_result) { if (!ioctl_result) {
STp->drv_block = blkno;
(STp->mt_status)->mt_fileno = fileno;
if (cmd_in == MTBSFM) if (cmd_in == MTBSFM)
ioctl_result = st_int_ioctl(inode, file, MTFSF, 1); ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
else if (cmd_in == MTFSFM) else if (cmd_in == MTFSFM)
...@@ -1248,13 +1407,13 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1248,13 +1407,13 @@ st_int_ioctl(struct inode * inode,struct file * file,
STp->block_size = arg; STp->block_size = arg;
if (arg != 0) { if (arg != 0) {
(STp->buffer)->buffer_blocks = (STp->buffer)->buffer_blocks =
ST_BUFFER_SIZE / STp->block_size; st_buffer_size / STp->block_size;
(STp->buffer)->buffer_size = (STp->buffer)->buffer_size =
(STp->buffer)->buffer_blocks * STp->block_size; (STp->buffer)->buffer_blocks * STp->block_size;
} }
else { else {
(STp->buffer)->buffer_blocks = 1; (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)->buffer_bytes =
(STp->buffer)->read_pointer = 0; (STp->buffer)->read_pointer = 0;
...@@ -1263,7 +1422,7 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1263,7 +1422,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
STp->drv_buffer = arg; STp->drv_buffer = arg;
else if (cmd_in == MTSETDENSITY) else if (cmd_in == MTSETDENSITY)
STp->density = arg; 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 = ST_EOM_OK;
STp->eof_hit = 0; STp->eof_hit = 0;
} }
...@@ -1271,6 +1430,33 @@ st_int_ioctl(struct inode * inode,struct file * file, ...@@ -1271,6 +1430,33 @@ st_int_ioctl(struct inode * inode,struct file * file,
STp->eof = ST_NOEOF; STp->eof = ST_NOEOF;
STp->eof_hit = 0; 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 ; return ioctl_result ;
...@@ -1294,7 +1480,7 @@ st_ioctl(struct inode * inode,struct file * file, ...@@ -1294,7 +1480,7 @@ st_ioctl(struct inode * inode,struct file * file,
dev = dev & 127; dev = dev & 127;
STp = &(scsi_tapes[dev]); STp = &(scsi_tapes[dev]);
#ifdef DEBUG #ifdef DEBUG
if (!STp->in_use) { if (debugging && !STp->in_use) {
printk("st%d: Incorrect device.\n", dev); printk("st%d: Incorrect device.\n", dev);
return (-EIO); return (-EIO);
} }
...@@ -1318,7 +1504,11 @@ st_ioctl(struct inode * inode,struct file * file, ...@@ -1318,7 +1504,11 @@ st_ioctl(struct inode * inode,struct file * file,
if (i < 0) if (i < 0)
return i; 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)) { else if (cmd == (MTIOCGET & IOCCMD_MASK)) {
...@@ -1328,13 +1518,29 @@ st_ioctl(struct inode * inode,struct file * file, ...@@ -1328,13 +1518,29 @@ st_ioctl(struct inode * inode,struct file * file,
if (i) if (i)
return 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)); sizeof(struct mtget));
(STp->mt_status)->mt_erreg = 0; /* Clear after read */
return 0; return 0;
} }
else if (cmd == (MTIOCPOS & IOCCMD_MASK)) { else if (cmd == (MTIOCPOS & IOCCMD_MASK)) {
#ifdef DEBUG #ifdef DEBUG
printk("st%d: get tape position.\n", dev); if (debugging)
printk("st%d: get tape position.\n", dev);
#endif #endif
if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos))
return (-EINVAL); return (-EINVAL);
...@@ -1363,14 +1569,15 @@ st_ioctl(struct inode * inode,struct file * file, ...@@ -1363,14 +1569,15 @@ st_ioctl(struct inode * inode,struct file * file,
SCpnt->sense_buffer[0] = 0; SCpnt->sense_buffer[0] = 0;
scsi_do_cmd(SCpnt, scsi_do_cmd(SCpnt,
(void *) scmd, (void *) (STp->buffer)->b_data, (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 (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
if ((STp->buffer)->last_result_fatal != 0) { if ((STp->buffer)->last_result_fatal != 0) {
mt_pos.mt_blkno = (-1); mt_pos.mt_blkno = (-1);
#ifdef DEBUG #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 #endif
result = (-EIO); result = (-EIO);
} }
...@@ -1397,8 +1604,25 @@ st_ioctl(struct inode * inode,struct file * file, ...@@ -1397,8 +1604,25 @@ st_ioctl(struct inode * inode,struct file * file,
return scsi_ioctl(STp->device, cmd_in, (void *) arg); return scsi_ioctl(STp->device, cmd_in, (void *) arg);
} }
/* Set the boot options. Syntax: st=xxx,yyy
where xxx is buffer size in 512 byte blocks and yyy is write threshold
in 512 byte blocks. */
void
st_setup(char *str, int *ints)
{
if (ints[0] > 0 && ints[1] > 0)
st_buffer_size = ints[1] * ST_BLOCK_SIZE;
if (ints[0] > 1 && ints[2] > 0) {
st_write_threshold = ints[2] * ST_BLOCK_SIZE;
if (st_write_threshold > st_buffer_size)
st_write_threshold = st_buffer_size;
}
if (ints[0] > 2 && ints[3] > 0)
st_max_buffers = ints[3];
}
static struct file_operations st_fops = { static struct file_operations st_fops = {
NULL, /* lseek - default */ NULL, /* lseek - default */
st_read, /* read - general block-dev read */ st_read, /* read - general block-dev read */
...@@ -1426,7 +1650,8 @@ unsigned long st_init1(unsigned long mem_start, unsigned long mem_end){ ...@@ -1426,7 +1650,8 @@ unsigned long st_init1(unsigned long mem_start, unsigned long mem_end){
/* Driver initialization */ /* Driver initialization */
unsigned long st_init(unsigned long mem_start, unsigned long mem_end) 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)) { if (register_chrdev(MAJOR_NR,"st",&st_fops)) {
printk("Unable to get major %d for SCSI tapes\n",MAJOR_NR); printk("Unable to get major %d for SCSI tapes\n",MAJOR_NR);
...@@ -1435,40 +1660,56 @@ unsigned long st_init(unsigned long mem_start, unsigned long mem_end) ...@@ -1435,40 +1660,56 @@ unsigned long st_init(unsigned long mem_start, unsigned long mem_end)
if (NR_ST == 0) return mem_start; if (NR_ST == 0) return mem_start;
#ifdef DEBUG #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 #endif
for (i=0; i < NR_ST; ++i) { for (i=0, dev_nbr=(-1); i < NR_ST; ++i) {
scsi_tapes[i].capacity = 0xfffff; STp = &(scsi_tapes[i]);
scsi_tapes[i].dirty = 0; STp->capacity = 0xfffff;
scsi_tapes[i].rw = ST_IDLE; STp->dirty = 0;
scsi_tapes[i].eof = ST_NOEOF; STp->rw = ST_IDLE;
scsi_tapes[i].waiting = NULL; STp->eof = ST_NOEOF;
scsi_tapes[i].in_use = 0; STp->waiting = NULL;
scsi_tapes[i].drv_buffer = 1; /* Try buffering if no mode sense */ STp->in_use = 0;
scsi_tapes[i].density = 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 */ /* Allocate the buffers */
if (NR_ST == 1) st_nbr_buffers = NR_ST;
st_nbr_buffers = 1; if (st_nbr_buffers > st_max_buffers)
else st_nbr_buffers = st_max_buffers;
st_nbr_buffers = 2; st_buffers = (ST_buffer **)mem_start;
mem_start += st_nbr_buffers * sizeof(ST_buffer *);
for (i=0; i < st_nbr_buffers; i++) { for (i=0; i < st_nbr_buffers; i++) {
st_buffers[i] = (ST_buffer *) mem_start; st_buffers[i] = (ST_buffer *) mem_start;
#ifdef DEBUG #ifdef DEBUG
printk("st: Buffer address: %p\n", st_buffers[i]); /* printk("st: Buffer address: %p\n", st_buffers[i]); */
#endif #endif
mem_start += sizeof(ST_buffer) - 1 + ST_BUFFER_BLOCKS * ST_BLOCK_SIZE; mem_start += sizeof(ST_buffer) - 1 + st_buffer_size;
st_buffers[i]->mt_status = (struct mtget *) mem_start;
mem_start += sizeof(struct mtget);
st_buffers[i]->in_use = 0; st_buffers[i]->in_use = 0;
st_buffers[i]->writing = 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; return mem_start;
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
typedef struct { typedef struct {
int in_use; int in_use;
struct mtget * mt_status;
int buffer_size; int buffer_size;
int buffer_blocks; int buffer_blocks;
int buffer_bytes; int buffer_bytes;
...@@ -33,12 +32,18 @@ typedef struct { ...@@ -33,12 +32,18 @@ typedef struct {
unsigned in_use:1; unsigned in_use:1;
unsigned eof_hit:1; unsigned eof_hit:1;
unsigned drv_buffer:3; unsigned drv_buffer:3;
unsigned do_buffer_writes:1;
unsigned do_async_writes:1;
unsigned do_read_ahead:1;
unsigned char density; unsigned char density;
ST_buffer * buffer; ST_buffer * buffer;
int block_size; int block_size;
int min_block; int min_block;
int max_block; int max_block;
int write_threshold;
int recover_count; int recover_count;
int drv_block; /* The block where the drive head is */
struct mtget * mt_status;
Scsi_Cmnd SCpnt; Scsi_Cmnd SCpnt;
} Scsi_Tape; } Scsi_Tape;
......
/* $Id: wd7000.c,v 1.2 1994/01/15 06:02:32 drew Exp $ /* $Id: $
* linux/kernel/wd7000.c * linux/drivers/scsi/wd7000.c
* *
* Copyright (C) 1992 Thomas Wuensche * Copyright (C) 1992 Thomas Wuensche
* closely related to the aha1542 driver from Tommy Thorn * closely related to the aha1542 driver from Tommy Thorn
...@@ -9,6 +9,95 @@ ...@@ -9,6 +9,95 @@
* accomodate Eric Youngdale's modifications to scsi.c. Nov 1992. * accomodate Eric Youngdale's modifications to scsi.c. Nov 1992.
* *
* Additional changes to support scatter/gather. Dec. 1992. tw/jb * 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 <stdarg.h>
...@@ -17,6 +106,7 @@ ...@@ -17,6 +106,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/malloc.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -26,17 +116,179 @@ ...@@ -26,17 +116,179 @@
#include "scsi.h" #include "scsi.h"
#include "hosts.h" #include "hosts.h"
/* #define DEBUG */ #define ANY2SCSI_INLINE /* undef this to use old macros */
#undef DEBUG
#include "wd7000.h" #include "wd7000.h"
#ifdef DEBUG /*
#define DEB(x) x * Mailbox structure sizes.
#else * I prefer to keep the number of ICMBs much larger than the number of
#define DEB(x) * OGMBs. OGMBs are used very quickly by the driver to start one or
#endif * 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: Driver data structures:
...@@ -63,103 +315,327 @@ ...@@ -63,103 +315,327 @@
indices need not be involved. indices need not be involved.
*/ */
static struct { /*
struct wd_mailbox ogmb[OGMB_CNT]; * WD7000-specific scatter/gather element structure
struct wd_mailbox icmb[ICMB_CNT]; */
} mb; typedef struct sgb {
static int next_ogmb = 0; /* to reduce contention at mailboxes */ 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 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; xany2scsi and xscsi2int were not being used, and are no longer defined.
static unchar controlstat = 0; (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; host->control |= INT_EN;
outb(controlstat,CONTROL); 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; host->control |= DMA_EN;
outb(controlstat,CONTROL); outb(host->control,host->iobase+ASC_CONTROL);
set_dma_mode(DMA_CH, DMA_MODE_CASCADE); set_dma_mode(host->dma, DMA_MODE_CASCADE);
enable_dma(DMA_CH); enable_dma(host->dma);
} }
#define WAITnexttimeout 200 /* 2 seconds */
#define WAIT(port, mask, allof, noneof) \ #define WAIT(port, mask, allof, noneof) \
{ register WAITbits; \ { register volatile unsigned WAITbits; \
register WAITtimeout = WAITnexttimeout; \ register unsigned long WAITtimeout = jiffies + WAITnexttimeout; \
while (1) { \ while (1) { \
WAITbits = inb(port) & (mask); \ WAITbits = inb(port) & (mask); \
if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
break; \ break; \
if (--WAITtimeout == 0) goto fail; \ if (jiffies > WAITtimeout) goto fail; \
} \ } \
} }
static inline void delay( unsigned how_long ) static inline void delay( unsigned how_long )
{ {
unsigned long time = jiffies + how_long; register unsigned long time = jiffies + how_long;
while (jiffies < time); 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) { WAIT(host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0);
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();
while (len--) { while (len--) {
WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); do {
outb(*cmdp++, COMMAND); 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; return 1;
fail: fail:
sti(); printk("wd7000 command_out: WAIT failed(%d)\n", len+1);
printk("wd7000_out WAIT failed(%d): ", len+1);
return 0; 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; register Scb *scb, *p;
unsigned long flags; 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); save_flags(flags);
cli(); cli();
while (busy) { /* someone else is allocating */
if (scbfree == NULL) { sti();
panic("wd7000: can't allocate free SCB.\n"); now = jiffies; while (jiffies == now) /* wait a jiffy */;
restore_flags(flags); cli();
return NULL; }
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; scb = scbfree; freescbs -= needed;
memset(scb, 0, sizeof(Scb)); scb->next = NULL; for (i = 0; i < needed; i++) { p = scbfree; scbfree = p->next; }
p->next = NULL;
busy = 0; /* we're done */
restore_flags(flags); restore_flags(flags);
...@@ -169,13 +645,14 @@ static inline Scb *alloc_scb(void) ...@@ -169,13 +645,14 @@ static inline Scb *alloc_scb(void)
static inline void free_scb( Scb *scb ) static inline void free_scb( Scb *scb )
{ {
unsigned long flags; register unsigned long flags;
save_flags(flags); save_flags(flags);
cli(); cli();
memset(scb, 0, sizeof(Scb)); memset(scb, 0, sizeof(Scb));
scb->next = scbfree; scbfree = scb; scb->next = scbfree; scbfree = scb;
freescbs++;
restore_flags(flags); restore_flags(flags);
} }
...@@ -190,62 +667,74 @@ static inline void init_scbs(void) ...@@ -190,62 +667,74 @@ static inline void init_scbs(void)
cli(); cli();
scbfree = &(scbs[0]); 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].next = NULL;
scbs[MAX_SCBS-1].SCpnt = NULL;
restore_flags(flags); 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. * Note: this can also be used for ICBs; just cast to the parm type.
*/ */
{ {
int i, ogmb; register int i, ogmb;
unsigned char start_cmd; register unsigned long flags;
unsigned long flags; unchar start_ogmb;
Mailbox *ogmbs = host->mb.ogmb;
DEB(printk("wd7000_scb_out: %06x");) 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 */ /* We first look for a free outgoing mailbox */
save_flags(flags); save_flags(flags);
cli(); cli();
ogmb = next_ogmb; ogmb = *next_ogmb;
for (i = 0; i < OGMB_CNT; i++) { for (i = 0; i < OGMB_CNT; i++) {
if (mb.ogmb[ogmb].status == 0) { if (ogmbs[ogmb].status == 0) {
DEB(printk(" using OGMB %x",ogmb)); #ifdef DEBUG
mb.ogmb[ogmb].status = 1; printk(" using OGMB %x",ogmb);
any2scsi(mb.ogmb[ogmb].scbptr, scbptr); #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; break;
} else } else
ogmb = (++ogmb) % OGMB_CNT; ogmb = (++ogmb) % OGMB_CNT;
} }
restore_flags(flags); restore_flags(flags);
DEB(printk(", scb is %x",scbptr);) #ifdef DEBUG
printk(", scb is %x",(unsigned int) scbptr);
#endif
if (i >= OGMB_CNT) { if (i >= OGMB_CNT) {
DEB(printk(", no free OGMBs.\n");) /*
/* Alternatively, issue "interrupt on free OGMB", and sleep... */ * Alternatively, we might issue the "interrupt on free OGMB",
return 0; * 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(host);
wd7000_enable_intr();
do {
command_out(&start_cmd, 1);
WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
} while (inb(ASC_STAT) & CMD_REJ);
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; return 1;
fail:
DEB(printk(", WAIT timed out.\n");)
return 0;
} }
...@@ -300,135 +789,159 @@ int make_code(unsigned hosterr, unsigned scsierr) ...@@ -300,135 +789,159 @@ int make_code(unsigned hosterr, unsigned scsierr)
static void wd7000_scsi_done(Scsi_Cmnd * SCpnt) 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; SCpnt->SCp.phase = 0;
} }
#define wd7000_intr_ack(host) outb(0,host->iobase+ASC_INTR_ACK)
void wd7000_intr_handle(int irq) void wd7000_intr_handle(int irq)
{ {
int flag, icmb, errstatus, icmb_status; #ifdef 0
int host_error, scsi_error; /*
Scb *scb; /* for SCSI commands */ * Use irqp as the parm, and the following declaration, if request_irq
unchar *icb; /* for host commands */ * is used or if SA_INTERRUPT is not used.
Scsi_Cmnd *SCpnt; */
register int irq = *(((int *)irqp)-2);
flag = inb(INTR_STAT); #endif
DEB(printk("wd7000_intr_handle: intr stat = %02x",flag);) register int flag, icmb, errstatus, icmb_status;
register int host_error, scsi_error;
if (!(inb(ASC_STAT)&0x80)){ register Scb *scb; /* for SCSI commands */
DEB(printk("\nwd7000_intr_handle: phantom interrupt...\n");) register IcbAny *icb; /* for host commands */
wd7000_intr_ack(); register Scsi_Cmnd *SCpnt;
return; Adapter *host = irq2host[irq]; /* This MUST be set!!! */
} Mailbox *icmbs = host->mb.icmb;
/* check for an incoming mailbox */
if ((flag & 0x40) == 0) {
/* for a free OGMB - need code for this case... */
DEB(printk("wd7000_intr_handle: free outgoing mailbox\n");)
wd7000_intr_ack();
return;
}
/* The interrupt is for an incoming mailbox */
icmb = flag & 0x3f;
scb = (struct scb *) scsi2int(mb.icmb[icmb].scbptr);
icmb_status = mb.icmb[icmb].status;
mb.icmb[icmb].status = 0;
#ifdef DEBUG #ifdef DEBUG
printk(" ICMB %d posted for SCB/ICB %06x, status %02x, vue %02x", printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host);
icmb, scb, icmb_status, scb->vue );
#endif #endif
if (!(scb->op & 0x80)) { /* an SCB is done */ flag = inb(host->iobase+ASC_INTR_STAT);
SCpnt = scb->SCpnt; #ifdef DEBUG
if (--(SCpnt->SCp.phase) <= 0) { /* all scbs for SCpnt are done */ printk("wd7000_intr_handle: intr stat = %02x\n",flag);
host_error = scb->vue | (icmb_status << 8); #endif
scsi_error = scb->status;
errstatus = make_code(host_error,scsi_error);
SCpnt->result = errstatus;
if (SCpnt->host_scribble != NULL) if (!(inb(host->iobase+ASC_STAT) & INT_IM)) {
scsi_free(SCpnt->host_scribble,WD7000_SCRIBBLE); /* NB: these are _very_ possible if IRQ 15 is being used, since
free_scb(scb); 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); if (flag & MB_INTR) {
} /* The interrupt is for a mailbox */
} else { /* an ICB is done */ if (!(flag & IMB_INTR)) {
icb = (unchar *) scb; #ifdef DEBUG
icb[ICB_STATUS] = icmb_status; printk("wd7000_intr_handle: free outgoing mailbox");
icb[ICB_PHASE] = 0; #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(); wd7000_intr_ack(host);
DEB(printk(".\n");)
return; return;
} }
int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *)) int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
{ {
Scb *scb; register Scb *scb;
Sgb *sgb; register Sgb *sgb;
unchar *cdb; register unchar *cdb = (unchar *) SCpnt->cmnd;
unchar idlun; register unchar idlun;
short cdblen; register short cdblen;
Adapter *host = (Adapter *) SCpnt->host->hostdata;
cdb = (unchar *) SCpnt->cmnd;
cdblen = COMMAND_SIZE(*cdb); cdblen = COMMAND_SIZE(cdb[0]);
idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7); idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
SCpnt->scsi_done = done; SCpnt->scsi_done = done;
SCpnt->SCp.phase = 1; SCpnt->SCp.phase = 1;
scb = alloc_scb(); scb = alloc_scbs(1);
scb->idlun = idlun; scb->idlun = idlun;
memcpy(scb->cdb, cdb, cdblen); memcpy(scb->cdb, cdb, cdblen);
scb->direc = 0x40; /* Disable direction check */ scb->direc = 0x40; /* Disable direction check */
scb->SCpnt = SCpnt; /* so we can find stuff later */ scb->SCpnt = SCpnt; /* so we can find stuff later */
SCpnt->host_scribble = NULL; SCpnt->host_scribble = (unchar *) scb;
DEB(printk("request_bufflen is %x, bufflen is %x\n",\ scb->host = host;
SCpnt->request_bufflen, SCpnt->bufflen);)
if (SCpnt->use_sg) { if (SCpnt->use_sg) {
struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
unsigned i; 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"); panic("wd7000_queuecommand: scatter/gather not supported.\n");
} }
#ifdef DEBUG #ifdef DEBUG
printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg); printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg);
#endif #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; scb->op = 1;
any2scsi(scb->dataptr, sgb); any2scsi(scb->dataptr, (int) sgb);
any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) ); any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) );
for (i = 0; i < SCpnt->use_sg; i++) { for (i = 0; i < SCpnt->use_sg; i++) {
any2scsi(sgb->ptr, sg[i].address); any2scsi(sgb[i].ptr, (int) sg[i].address);
any2scsi(sgb->len, sg[i].length); any2scsi(sgb[i].len, sg[i].length);
sgb++;
} }
DEB(printk("Using %d bytes for %d scatter/gather blocks\n",\
scsi2int(scb->maxlen), SCpnt->use_sg);)
} else { } else {
scb->op = 0; scb->op = 0;
any2scsi(scb->dataptr, SCpnt->request_buffer); any2scsi(scb->dataptr, (int) SCpnt->request_buffer);
any2scsi(scb->maxlen, SCpnt->request_bufflen); any2scsi(scb->maxlen, SCpnt->request_bufflen);
} }
while (!mail_out(host, scb)) /* keep trying */;
return mail_out(scb); return 1;
} }
...@@ -442,192 +955,292 @@ int wd7000_command(Scsi_Cmnd *SCpnt) ...@@ -442,192 +955,292 @@ int wd7000_command(Scsi_Cmnd *SCpnt)
} }
int wd7000_init(void) int wd7000_diagnostics( Adapter *host, int code )
{ int i; {
unchar init_block[] = { static IcbDiag icb = {ICB_OP_DIAGNOSTICS};
INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT 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. 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.
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.
*/ */
delay(200); outb(ASC_RES, host->iobase+ASC_CONTROL);
delay(1); /* reset pulse: this is 10ms, only need 25us */
WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); outb(0,host->iobase+ASC_CONTROL);
DEB(printk("wd7000_init: Power-on Diagnostics finished\n");) host->control = 0; /* this must always shadow ASC_CONTROL */
if (((i=inb(INTR_STAT)) != 1) && (i != 7)) { WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
panic("wd7000_init: Power-on Diagnostics error\n");
return 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 */ /* Clear mailboxes */
memset(&mb,0,sizeof (mb)); memset(&(host->mb), 0, sizeof(host->mb));
/* Set up SCB free list */
init_scbs();
/* Set up init block */
any2scsi(init_block+5,&mb);
/* Execute init command */ /* Execute init command */
if (!command_out(init_block,sizeof(init_block))) { any2scsi((unchar *) &(init_cmd.mailboxes), (int) &(host->mb));
panic("WD-7000 Initialization failed.\n"); if (!command_out(host, (unchar *) &init_cmd, sizeof(init_cmd))) {
printk("wd7000_init: adapter initialization failed.\n");
return 0; return 0;
} }
WAIT(host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0);
/* Wait until init finished */
WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); if (irqaction(host->irq, &sa)) {
outb(DISABLE_UNS_INTR, COMMAND); printk("wd7000_init: can't get IRQ %d.\n", host->irq);
WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); return 0;
}
/* Enable Interrupt and DMA */ if (request_dma(host->dma)) {
if (request_irq(IRQ_LVL, wd7000_intr_handle)) { printk("wd7000_init: can't get DMA channel %d.\n", host->dma);
panic("Unable to allocate IRQ for WD-7000.\n"); free_irq(host->irq);
return 0; return 0;
}; }
if(request_dma(DMA_CH)) { wd7000_enable_dma(host);
panic("Unable to allocate DMA channel for WD-7000.\n"); wd7000_enable_intr(host);
free_irq(IRQ_LVL);
return 0; if (!wd7000_diagnostics(host,ICB_DIAG_FULL)) {
}; free_dma(host->dma);
wd7000_enable_dma(); free_irq(host->irq);
wd7000_enable_intr(); return 0;
}
printk("WD-7000 initialized.\n");
return 1; return 1;
fail: fail:
printk("wd7000_init: WAIT timed out.\n");
return 0; /* 0 = not ok */ return 0; /* 0 = not ok */
} }
void wd7000_revision(void) void wd7000_revision(Adapter *host)
{ {
volatile unchar icb[ICB_LEN] = {0x8c}; /* read firmware revision level */ static IcbRevLvl icb = {ICB_OP_GET_REVISION};
icb[ICB_PHASE] = 1;
mail_out( (struct scb *) icb );
while (icb[ICB_PHASE]) /* wait for completion */;
rev_1 = icb[1];
rev_2 = icb[2];
icb.phase = 1;
/* /*
For boards at rev 7.0 or later, enable scatter/gather. * Like diagnostics, this is only done at init time, in fact, from
*/ * wd7000_detect, so there should be OGMBs available. If it fails,
if (rev_1 >= 7) scsi_hosts[wd7000_host].sg_tablesize = WD7000_SG; * 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) 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; int i,j, present = 0;
char const *base_address = NULL; const Config *cfg;
const Signature *sig;
if(check_region(IO_BASE, 4)) return 0; /* IO ports in use */ Adapter *host = NULL;
for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){ struct Scsi_Host *sh;
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;
snarf_region(IO_BASE, 4); /* Register our ports */ /* Set up SCB free list, which is shared by all adapters */
/* Store our host number */ init_scbs();
wd7000_host = hostnum;
wd7000_init(); cfg = configs;
wd7000_revision(); /* will set scatter/gather by rev level */ 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) int wd7000_abort(Scsi_Cmnd * SCpnt, int i)
{ {
#ifdef DEBUG #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); 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++)); int j; unchar *cdbj = (unchar *) SCpnt->cmnd;
printk(" result %08x\n", SCpnt->result); for (j=0; j < COMMAND_SIZE(*cdbj); j++) printk(" %02x", *(cdbj++));
printk(" result %08x\n", SCpnt->result);
} }
#endif #endif
return 0; 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 * I also have no idea how to do a reset...
oblige, or the command will hang the scsi system */ */
int wd7000_reset(Scsi_Cmnd * SCpnt) int wd7000_reset(Scsi_Cmnd * SCpnt)
{ {
#ifdef DEBUG #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 #endif
if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART; if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
return 0; return 0;
} }
int wd7000_biosparam(int size, int dev, int* ip)
/* /*
* This is borrowed directly from aha1542.c, but my disks are organized * The info routine in the WD7000 structure isn't per-adapter, so it can't
* this way, so I think it will work OK. * 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[0] = 64;
ip[1] = 32; ip[1] = 32;
...@@ -635,4 +1248,3 @@ int wd7000_biosparam(int size, int dev, int* ip) ...@@ -635,4 +1248,3 @@ int wd7000_biosparam(int size, int dev, int* ip)
/* if (ip[2] >= 1024) ip[2] = 1024; */ /* if (ip[2] >= 1024) ip[2] = 1024; */
return 0; return 0;
} }
...@@ -4,169 +4,14 @@ ...@@ -4,169 +4,14 @@
* *
* Header file for the WD-7000 driver for Linux * Header file for the WD-7000 driver for Linux
* *
* $Log: $ * John Boyd <boyd@cis.ohio-state.edu> Jan 1994:
* Revision 1.1 1992/07/24 06:27:38 root * This file has been reduced to only the definitions needed for the
* Initial revision * WD7000 host structure.
*
* Revision 1.1 1992/07/05 08:32:32 root
* Initial revision
*
* Revision 1.1 1992/05/15 18:38:05 root
* Initial revision
*
* Revision 1.1 1992/04/02 03:23:13 drew
* Initial revision
*
* Revision 1.3 1992/01/27 14:46:29 tthorn
* *** empty log message ***
* *
*/ */
#include <linux/types.h> #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_detect(int);
int wd7000_command(Scsi_Cmnd *); int wd7000_command(Scsi_Cmnd *);
int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
...@@ -176,30 +21,32 @@ int wd7000_reset(Scsi_Cmnd *); ...@@ -176,30 +21,32 @@ int wd7000_reset(Scsi_Cmnd *);
int wd7000_biosparam(int, int, int*); int wd7000_biosparam(int, int, int*);
#ifndef NULL #ifndef NULL
#define NULL 0 #define NULL 0L
#endif #endif
/* /*
* Define WD7000_SG to be the number of Sgbs that will fit in a block of * In this version, sg_tablesize now defaults to WD7000_SG, and will
* size WD7000_SCRIBBLE. WD7000_SCRIBBLE must be 512, 1024, 2048, or 4096. * 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 * Also, it has been reported that boards at Revision 6 support scatter/
* rev 7.0), but will be changed to WD7000_SG when a newer board is * gather, so the new definition of an "older" board has been changed
* detected. * accordingly.
*/ */
#define WD7000_SCRIBBLE 512 #define WD7000_Q 16
#define WD7000_SG 16
#define WD7000_Q OGMB_CNT
#define WD7000_SG (WD7000_SCRIBBLE / sizeof(Sgb))
#define WD7000 {\ #define WD7000 {\
"Western Digital WD-7000", \ "Western Digital WD-7000", \
wd7000_detect, \ wd7000_detect, \
wd7000_info, wd7000_command, \ wd7000_info, \
wd7000_command, \
wd7000_queuecommand, \ wd7000_queuecommand, \
wd7000_abort, \ wd7000_abort, \
wd7000_reset, \ wd7000_reset, \
NULL, \ NULL, \
wd7000_biosparam, \ wd7000_biosparam, \
WD7000_Q, 7, SG_NONE, 1, 0, 1} WD7000_Q, 7, WD7000_SG, 1, 0, 1}
#endif #endif
...@@ -49,7 +49,7 @@ extern int check_mcd_media_change(int, int); ...@@ -49,7 +49,7 @@ extern int check_mcd_media_change(int, int);
static char buffersize_index[9] = {-1, 0, 1, -1, 2, -1, -1, -1, 3}; 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}; 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 grow_buffers(int pri, int size);
static int shrink_specific_buffers(unsigned int priority, int size); static int shrink_specific_buffers(unsigned int priority, int size);
......
...@@ -44,6 +44,7 @@ extern int tcp_get_info(char *, char **, off_t, int); ...@@ -44,6 +44,7 @@ extern int tcp_get_info(char *, char **, off_t, int);
extern int udp_get_info(char *, char **, off_t, int); extern int udp_get_info(char *, char **, off_t, int);
extern int raw_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 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 dev_get_info(char *, char **, off_t, int);
extern int rt_get_info(char *, char **, off_t, int); extern int rt_get_info(char *, char **, off_t, int);
#endif /* CONFIG_INET */ #endif /* CONFIG_INET */
...@@ -96,11 +97,14 @@ static struct proc_dir_entry net_dir[] = { ...@@ -96,11 +97,14 @@ static struct proc_dir_entry net_dir[] = {
{ 131,3,"dev" }, { 131,3,"dev" },
{ 132,3,"raw" }, { 132,3,"raw" },
{ 133,3,"tcp" }, { 133,3,"tcp" },
{ 134,3,"udp" } { 134,3,"udp" },
#ifdef CONFIG_INET_RARP
{ 135,4,"rarp"}
#endif
#endif /* CONFIG_INET */ #endif /* CONFIG_INET */
#ifdef CONFIG_IPX #ifdef CONFIG_IPX
,{ 135,9,"ipx_route" }, ,{ 136,9,"ipx_route" },
{ 136,3,"ipx" } { 137,3,"ipx" }
#endif /* CONFIG_IPX */ #endif /* CONFIG_IPX */
}; };
...@@ -212,12 +216,15 @@ static int proc_readnet(struct inode * inode, struct file * file, ...@@ -212,12 +216,15 @@ static int proc_readnet(struct inode * inode, struct file * file,
case 134: case 134:
length = udp_get_info(page,&start,file->f_pos,thistime); length = udp_get_info(page,&start,file->f_pos,thistime);
break; break;
case 135:
length = rarp_get_info(page,&start,file->f_pos,thistime);
break;
#endif /* CONFIG_INET */ #endif /* CONFIG_INET */
#ifdef CONFIG_IPX #ifdef CONFIG_IPX
case 135: case 136:
length = ipx_rt_get_info(page,&start,file->f_pos,thistime); length = ipx_rt_get_info(page,&start,file->f_pos,thistime);
break; break;
case 136: case 137:
length = ipx_get_info(page,&start,file->f_pos,thistime); length = ipx_get_info(page,&start,file->f_pos,thistime);
break; break;
#endif /* CONFIG_IPX */ #endif /* CONFIG_IPX */
......
...@@ -30,6 +30,13 @@ ...@@ -30,6 +30,13 @@
#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet- huh? */ #define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet- huh? */
#define ARPHRD_ARCNET 7 /* ARCnet */ #define ARPHRD_ARCNET 7 /* ARCnet */
#define ARPHRD_APPLETLK 8 /* APPLEtalk */ #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. */ /* ARP protocol opcodes. */
#define ARPOP_REQUEST 1 /* ARP request */ #define ARPOP_REQUEST 1 /* ARP request */
......
/*
* 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
...@@ -32,8 +32,17 @@ struct timestamp { ...@@ -32,8 +32,17 @@ struct timestamp {
unsigned char len; unsigned char len;
unsigned char ptr; unsigned char ptr;
union { union {
#if defined(__i386__)
unsigned char flags:4, unsigned char flags:4,
overflow: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; unsigned char full_char;
} x; } x;
unsigned long data[9]; unsigned long data[9];
...@@ -63,8 +72,17 @@ struct options { ...@@ -63,8 +72,17 @@ struct options {
struct iphdr { struct iphdr {
#if defined(__i386__)
unsigned char ihl:4, unsigned char ihl:4,
version: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 char tos;
unsigned short tot_len; unsigned short tot_len;
unsigned short id; unsigned short id;
......
...@@ -88,6 +88,7 @@ struct mtget { ...@@ -88,6 +88,7 @@ struct mtget {
#define MT_ISDDS1 0x51 /* DDS device without partitions */ #define MT_ISDDS1 0x51 /* DDS device without partitions */
#define MT_ISDDS2 0x52 /* DDS device with partitions */ #define MT_ISDDS2 0x52 /* DDS device with partitions */
#define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */ #define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */
#define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */
struct mt_tape_info { struct mt_tape_info {
long t_type; /* device type id (mt_type) */ long t_type; /* device type id (mt_type) */
...@@ -106,6 +107,7 @@ struct mt_tape_info { ...@@ -106,6 +107,7 @@ struct mt_tape_info {
{MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \ {MT_ISWT5099EEN24, "Wangtek 5099-een24, 60MB"}, \
{MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \ {MT_ISEVEREX_FT40A, "Everex FT40A, QIC-40"}, \
{MT_ISSCSI1, "Generic SCSI-1 tape"}, \ {MT_ISSCSI1, "Generic SCSI-1 tape"}, \
{MT_ISSCSI2, "Generic SCSI-2 tape"}, \
{0, NULL} \ {0, NULL} \
} }
...@@ -152,5 +154,22 @@ struct mtpos { ...@@ -152,5 +154,22 @@ struct mtpos {
* I think DDS drives are DAT drives. * 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 */ #endif /* _LINUX_MTIO_H */
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Corey Minyard <wf-rch!minyard@relay.EU.net> * Corey Minyard <wf-rch!minyard@relay.EU.net>
* Donald J. Becker, <becker@super.org> * Donald J. Becker, <becker@super.org>
* Alan Cox, <A.Cox@swansea.ac.uk>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -34,7 +35,7 @@ ...@@ -34,7 +35,7 @@
#define IS_MYADDR 1 /* address is (one of) our own */ #define IS_MYADDR 1 /* address is (one of) our own */
#define IS_LOOPBACK 2 /* address is for LOOPBACK */ #define IS_LOOPBACK 2 /* address is for LOOPBACK */
#define IS_BROADCAST 3 /* address is a valid broadcast */ #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. * The DEVICE structure.
...@@ -52,7 +53,7 @@ struct device ...@@ -52,7 +53,7 @@ struct device
*/ */
char *name; 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_end; /* shmem "recv" end */
unsigned long rmem_start; /* shmem "recv" start */ unsigned long rmem_start; /* shmem "recv" start */
unsigned long mem_end; /* sahared mem end */ unsigned long mem_end; /* sahared mem end */
...@@ -65,12 +66,6 @@ struct device ...@@ -65,12 +66,6 @@ struct device
tbusy, /* transmitter busy */ tbusy, /* transmitter busy */
interrupt; /* interrupt arrived */ 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; struct device *next;
/* The device initialization function. Called only once. */ /* The device initialization function. Called only once. */
...@@ -135,6 +130,8 @@ struct device ...@@ -135,6 +130,8 @@ struct device
int num_addrs, void *addrs); int num_addrs, void *addrs);
#define HAVE_SET_MAC_ADDR #define HAVE_SET_MAC_ADDR
int (*set_mac_address)(struct device *dev, void *addr); int (*set_mac_address)(struct device *dev, void *addr);
#define HAVE_PRIVATE_IOCTL
int (*do_ioctl)(struct device *dev, struct ifreq *ifr);
}; };
......
...@@ -67,4 +67,21 @@ ...@@ -67,4 +67,21 @@
#define SIOCGARP 0x8951 /* get ARP table entry */ #define SIOCGARP 0x8951 /* get ARP table entry */
#define SIOCSARP 0x8952 /* set 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 */ #endif /* _LINUX_SOCKIOS_H */
...@@ -26,6 +26,7 @@ struct tcphdr { ...@@ -26,6 +26,7 @@ struct tcphdr {
unsigned short dest; unsigned short dest;
unsigned long seq; unsigned long seq;
unsigned long ack_seq; unsigned long ack_seq;
#if defined(__i386__)
unsigned short res1:4, unsigned short res1:4,
doff:4, doff:4,
fin:1, fin:1,
...@@ -35,6 +36,21 @@ struct tcphdr { ...@@ -35,6 +36,21 @@ struct tcphdr {
ack:1, ack:1,
urg:1, urg:1,
res2:2; 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 window;
unsigned short check; unsigned short check;
unsigned short urg_ptr; unsigned short urg_ptr;
...@@ -45,17 +61,14 @@ enum { ...@@ -45,17 +61,14 @@ enum {
TCP_ESTABLISHED = 1, TCP_ESTABLISHED = 1,
TCP_SYN_SENT, TCP_SYN_SENT,
TCP_SYN_RECV, 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_WAIT1,
TCP_FIN_WAIT2, TCP_FIN_WAIT2,
TCP_TIME_WAIT, TCP_TIME_WAIT,
TCP_CLOSE, TCP_CLOSE,
TCP_CLOSE_WAIT, TCP_CLOSE_WAIT,
TCP_LAST_ACK, TCP_LAST_ACK,
TCP_LISTEN TCP_LISTEN,
TCP_CLOSING /* now a valid state */
}; };
#endif /* _LINUX_TCP_H */ #endif /* _LINUX_TCP_H */
...@@ -84,6 +84,7 @@ extern void bmouse_setup(char *str, int *ints); ...@@ -84,6 +84,7 @@ extern void bmouse_setup(char *str, int *ints);
extern void eth_setup(char *str, int *ints); extern void eth_setup(char *str, int *ints);
extern void xd_setup(char *str, int *ints); extern void xd_setup(char *str, int *ints);
extern void mcd_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 st0x_setup(char *str, int *ints);
extern void tmc8xx_setup(char *str, int *ints); extern void tmc8xx_setup(char *str, int *ints);
extern void t128_setup(char *str, int *ints); extern void t128_setup(char *str, int *ints);
...@@ -176,6 +177,9 @@ struct { ...@@ -176,6 +177,9 @@ struct {
#ifdef CONFIG_BLK_DEV_HD #ifdef CONFIG_BLK_DEV_HD
{ "hd=", hd_setup }, { "hd=", hd_setup },
#endif #endif
#ifdef CONFIG_CHR_DEV_ST
{ "st=", st_setup },
#endif
#ifdef CONFIG_BUSMOUSE #ifdef CONFIG_BUSMOUSE
{ "bmouse=", bmouse_setup }, { "bmouse=", bmouse_setup },
#endif #endif
......
...@@ -227,7 +227,7 @@ asmlinkage int sys_setregid(gid_t rgid, gid_t egid) ...@@ -227,7 +227,7 @@ asmlinkage int sys_setregid(gid_t rgid, gid_t egid)
} }
} }
if (rgid != (gid_t) -1 || if (rgid != (gid_t) -1 ||
egid != (gid_t) -1 && egid != old_rgid) (egid != (gid_t) -1 && egid != old_rgid))
current->sgid = current->egid; current->sgid = current->egid;
return 0; return 0;
} }
...@@ -314,7 +314,7 @@ asmlinkage int sys_setreuid(uid_t ruid, uid_t euid) ...@@ -314,7 +314,7 @@ asmlinkage int sys_setreuid(uid_t ruid, uid_t euid)
} }
} }
if (ruid != (uid_t) -1 || if (ruid != (uid_t) -1 ||
euid != (uid_t) -1 && euid != old_ruid) (euid != (uid_t) -1 && euid != old_ruid))
current->suid = current->euid; current->suid = current->euid;
return 0; return 0;
} }
......
...@@ -31,8 +31,8 @@ ...@@ -31,8 +31,8 @@
/* /*
* 8- and 16-bit register defines.. * 8- and 16-bit register defines..
*/ */
#define AL(regs) (((unsigned char *) ((regs)->eax))[0]) #define AL(regs) (((unsigned char *)&((regs)->eax))[0])
#define AH(regs) (((unsigned char *) ((regs)->eax))[1]) #define AH(regs) (((unsigned char *)&((regs)->eax))[1])
#define IP(regs) (*(unsigned short *)&((regs)->eip)) #define IP(regs) (*(unsigned short *)&((regs)->eip))
#define SP(regs) (*(unsigned short *)&((regs)->esp)) #define SP(regs) (*(unsigned short *)&((regs)->esp))
...@@ -289,9 +289,8 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned ...@@ -289,9 +289,8 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned
if (seg == BIOSSEG || regs->cs == BIOSSEG || if (seg == BIOSSEG || regs->cs == BIOSSEG ||
is_revectored(i, &current->vm86_info->int_revectored)) is_revectored(i, &current->vm86_info->int_revectored))
return_to_32bit(regs, VM86_INTx + (i << 8)); return_to_32bit(regs, VM86_INTx + (i << 8));
if (i==0x21 && is_revectored(AH(regs),&current->vm86_info->int21_revectored)) { if (i==0x21 && is_revectored(AH(regs),&current->vm86_info->int21_revectored))
return_to_32bit(regs, VM86_INTx + (i << 8)); return_to_32bit(regs, VM86_INTx + (i << 8));
}
pushw(ssp, sp, get_vflags(regs)); pushw(ssp, sp, get_vflags(regs));
pushw(ssp, sp, regs->cs); pushw(ssp, sp, regs->cs);
pushw(ssp, sp, IP(regs)); pushw(ssp, sp, IP(regs));
...@@ -305,7 +304,15 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned ...@@ -305,7 +304,15 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned
void handle_vm86_debug(struct vm86_regs * regs, long error_code) 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) void handle_vm86_fault(struct vm86_regs * regs, long error_code)
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* Alan Cox : Allow >4K in /proc * Alan Cox : Allow >4K in /proc
* Alan Cox : Make ARP add its own protocol entry * Alan Cox : Make ARP add its own protocol entry
* *
* Ross Martin : Rewrote arp_rcv() and arp_get_info()
*/ */
#include <linux/types.h> #include <linux/types.h>
...@@ -69,7 +70,7 @@ struct arp_table ...@@ -69,7 +70,7 @@ struct arp_table
unsigned long ip; /* ip address of entry */ unsigned long ip; /* ip address of entry */
unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */ unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */
unsigned char hlen; /* Length of 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 */ struct device *dev; /* Device the entry is tied to */
/* /*
...@@ -246,8 +247,9 @@ static void arp_release_entry(struct arp_table *entry) ...@@ -246,8 +247,9 @@ static void arp_release_entry(struct arp_table *entry)
* message. * message.
*/ */
static void arp_send(int type, unsigned long dest_ip, struct device *dev, void arp_send(int type, int ptype, unsigned long dest_ip,
unsigned long src_ip, unsigned char *dest_hw, unsigned char *src_hw) struct device *dev, unsigned long src_ip,
unsigned char *dest_hw, unsigned char *src_hw)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct arphdr *arp; struct arphdr *arp;
...@@ -280,7 +282,7 @@ static void arp_send(int type, unsigned long dest_ip, struct device *dev, ...@@ -280,7 +282,7 @@ static void arp_send(int type, unsigned long dest_ip, struct device *dev,
* Fill the device header for the ARP frame * 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. */ /* Fill out the arp protocol part. */
arp = (struct arphdr *) (skb->data + dev->hard_header_len); arp = (struct arphdr *) (skb->data + dev->hard_header_len);
...@@ -348,8 +350,8 @@ static void arp_expire_request (unsigned long arg) ...@@ -348,8 +350,8 @@ static void arp_expire_request (unsigned long arg)
entry->timer.expires = ARP_RES_TIME; entry->timer.expires = ARP_RES_TIME;
add_timer(&entry->timer); add_timer(&entry->timer);
restore_flags(flags); restore_flags(flags);
arp_send(ARPOP_REQUEST, ip, dev, dev->pa_addr, NULL, arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr,
dev->dev_addr); NULL, dev->dev_addr);
return; return;
} }
...@@ -463,166 +465,249 @@ void arp_destroy(unsigned long ip_addr, int force) ...@@ -463,166 +465,249 @@ void arp_destroy(unsigned long ip_addr, int force)
int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) 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; struct arphdr *arp = (struct arphdr *)skb->h.raw;
unsigned char *arp_ptr= (unsigned char *)(arp+1); unsigned char *arp_ptr= (unsigned char *)(arp+1);
struct arp_table *entry; struct arp_table *entry;
struct arp_table *proxy_entry; struct arp_table *proxy_entry;
int addr_hint; int addr_hint,hlen,htype;
unsigned long hash; unsigned long hash,dest_hash;
unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */ unsigned char ha[MAX_ADDR_LEN]; /* So we can enable ints again. */
long sip,tip; long sip,tip;
unsigned char *sha,*tha; unsigned char *sha,*tha;
/* /*
* If this test doesn't pass, its not IP, or we should ignore it anyway * 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
if (arp->ar_hln != dev->addr_len || dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP) * 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); kfree_skb(skb, FREE_READ);
return 0; return 0;
} }
/* /*
* For now we will only deal with IP addresses. * Another test.
*/ * The logic here is that the protocol being looked up by arp should
if ( * match the protocol the device speaks. If it doesn't, there is a
* problem, so toss the packet.
*/
switch(dev->type)
{
#ifdef CONFIG_AX25 #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 #endif
(arp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25) case ARPHRD_ETHER:
|| arp->ar_pln != 4) if(arp->ar_pro != htons(ETH_P_IP))
{ {
/* This packet is not for us. Remove it. */ kfree_skb(skb, FREE_READ);
kfree_skb(skb, FREE_READ); return 0;
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; sha=arp_ptr;
arp_ptr+=dev->addr_len; arp_ptr+=hlen;
memcpy(&sip,arp_ptr,4); memcpy(&sip,arp_ptr,4);
arp_ptr+=4; arp_ptr+=4;
tha=arp_ptr; tha=arp_ptr;
arp_ptr+=dev->addr_len; arp_ptr+=hlen;
memcpy(&tip,arp_ptr,4); 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); addr_hint = ip_chk_addr(tip);
hash = HASH(sip); if(arp->ar_op == htons(ARPOP_REPLY))
proxy_entry = NULL;
if (proxies != 0 && addr_hint != IS_MYADDR)
{ {
unsigned long dest_hash = HASH(tip); if(addr_hint!=IS_MYADDR)
cli();
proxy_entry = arp_tables[dest_hash];
while (proxy_entry != NULL)
{ {
if (proxy_entry->ip == tip && proxy_entry->htype==arp->ar_hrd) /*
break; * Replies to other machines get tossed.
proxy_entry = proxy_entry->next; */
kfree_skb(skb, FREE_READ);
return 0;
} }
if (proxy_entry && (proxy_entry->flags & ATF_PUBL)) /*
memcpy(ha, proxy_entry->ha, dev->addr_len); * Fall through to code below that adds sender to cache.
else */
proxy_entry = NULL;
} }
else 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; break;
if (entry != NULL) if(entry)
{ {
int old_flags = entry->flags; /*
memcpy(entry->ha, sha, arp->ar_hln); * Entry found; update it.
entry->hlen = arp->ar_hln; */
/* This seems sensible but not everyone gets it right ! */ memcpy(entry->ha, sha, hlen);
entry->htype = ntohs(arp->ar_hrd); entry->hlen = hlen;
if(entry->htype==0)
entry->htype = dev->type; /* Not good but we have no choice */
entry->last_used = jiffies; entry->last_used = jiffies;
if (!(entry->flags & ATF_COM)) if (!(entry->flags & ATF_COM))
{ {
/*
* This entry was incomplete. Delete the retransmit timer
* and switch to complete status.
*/
del_timer(&entry->timer); del_timer(&entry->timer);
entry->flags |= ATF_COM; entry->flags |= ATF_COM;
} sti();
sti(); /*
if (!(old_flags & ATF_COM)) * Send out waiting packets. We might have problems, if someone is
{ * manually removing entries right now -- entry might become invalid
/* Send out waiting packets. We might have problems, * underneath us.
if someone is manually removing entries right now. */
I will fix this one. */
arp_send_q(entry, sha); arp_send_q(entry, sha);
} }
if (addr_hint != IS_MYADDR && proxy_entry == NULL) else
{ {
kfree_skb(skb, FREE_READ); sti();
return 0;
} }
} }
else else
{ {
if (addr_hint != IS_MYADDR && proxy_entry == NULL) /*
{ * No entry found. Need to add a new entry to the arp table.
/* We don't do "smart arp" and cache all possible */
entries. That just makes us more work. */ entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
sti(); if(entry == NULL)
kfree_skb(skb, FREE_READ);
return 0;
}
entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),
GFP_ATOMIC);
if (entry == NULL)
{ {
sti(); sti();
kfree_skb(skb, FREE_READ);
printk("ARP: no memory for new arp entry\n"); printk("ARP: no memory for new arp entry\n");
kfree_skb(skb, FREE_READ);
return 0; return 0;
} }
entry->ip = sip; entry->ip = sip;
entry->hlen = arp->ar_hln; entry->hlen = hlen;
entry->htype = arp->ar_hrd; entry->htype = htype;
entry->flags = ATF_COM; entry->flags = ATF_COM;
memcpy(entry->ha, sha, arp->ar_hln); memcpy(entry->ha, sha, hlen);
entry->last_used = jiffies; entry->last_used = jiffies;
entry->next = arp_tables[hash];
arp_tables[hash] = entry;
entry->dev = skb->dev; entry->dev = skb->dev;
skb_queue_head_init(&entry->skb); skb_queue_head_init(&entry->skb);
sti(); entry->next = arp_tables[hash];
} arp_tables[hash] = entry;
/* From here on, interrupts are enabled. Never touch entry->..
any more. */
if (arp->ar_op != htons(ARPOP_REQUEST) sti();
|| 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;
} }
/* Either we respond with our own hw address, or we do proxy arp for /*
another machine. */ * Replies have been sent, and entries have been added. All done.
arp_send(ARPOP_REPLY, sip, dev, tip, sha, */
(addr_hint == IS_MYADDR)? dev->dev_addr : ha);
kfree_skb(skb, FREE_READ); kfree_skb(skb, FREE_READ);
return 0; return 0;
} }
...@@ -726,7 +811,8 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, ...@@ -726,7 +811,8 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
* If we didn't find an entry, we will try to send an ARP packet. * 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; return 1;
} }
...@@ -734,51 +820,66 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev, ...@@ -734,51 +820,66 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
/* /*
* Write the contents of the ARP cache to a PROCfs file. * 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) 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; 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(); 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; * Convert hardware address to XX:XX:XX:XX ... form.
memcpy(req->arp_pa.sa_data, &entry->ip, 4); */
req->arp_ha.sa_family = entry->htype; for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->hlen;j++)
memcpy(req->arp_ha.sa_data, &entry->ha, MAX_ADDR_LEN); {
req->arp_flags = entry->flags; hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
req++; hbuffer[k++]=hexbuf[ entry->ha[j]&15 ];
len+=sizeof(struct arpreq); hbuffer[k++]=':';
pos+=sizeof(struct arpreq); }
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) if(pos<offset)
{ {
len=0; len=0;
begin=pos; begin=pos;
req=(struct arpreq *) buffer;
} }
if(pos>offset+length) if(pos>offset+length)
break; break;
} }
if(pos>offset+length)
break;
} }
sti(); sti();
*start=buffer+(offset-begin);
len-=(offset-begin); *start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Start slop */
if(len>length) if(len>length)
len=length; len=length; /* Ending slop */
return len; return len;
} }
......
...@@ -10,5 +10,8 @@ extern int arp_find(unsigned char *haddr, unsigned long paddr, ...@@ -10,5 +10,8 @@ extern int arp_find(unsigned char *haddr, unsigned long paddr,
struct device *dev, unsigned long saddr, struct sk_buff *skb); 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_get_info(char *buffer, char **start, off_t origin, int length);
extern int arp_ioctl(unsigned int cmd, void *arg); 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 */ #endif /* _ARP_H */
...@@ -1273,7 +1273,7 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct ...@@ -1273,7 +1273,7 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct
* Set up data on packet * Set up data on packet
*/ */
skb2->arp = skb->arp; skb2->arp = 0;/*skb->arp;*/
skb2->free = skb->free; skb2->free = skb->free;
skb2->len = len + hlen; skb2->len = len + hlen;
skb2->h.raw=(char *) skb2->data; skb2->h.raw=(char *) skb2->data;
...@@ -1549,7 +1549,7 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) ...@@ -1549,7 +1549,7 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
ip_statistics.IpInReceives++; ip_statistics.IpInReceives++;
DPRINTF((DBG_IP, "<<\n")); DPRINTF((DBG_IP, "<<\n"));
/* /*
* Tag the ip header of this packet so we can find it * Tag the ip header of this packet so we can find it
*/ */
......
/* 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
/* 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 */
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
* Alan Cox : Split socket option code * Alan Cox : Split socket option code
* Alan Cox : Callbacks * Alan Cox : Callbacks
* Alan Cox : Nagle flag for Charles & Johannes stuff * Alan Cox : Nagle flag for Charles & Johannes stuff
* Alex : Removed restriction on inet fioctl
* *
* To Fix: * To Fix:
* *
...@@ -86,6 +87,7 @@ ...@@ -86,6 +87,7 @@
#include "ip.h" #include "ip.h"
#include "protocol.h" #include "protocol.h"
#include "arp.h" #include "arp.h"
#include "rarp.h"
#include "route.h" #include "route.h"
#include "tcp.h" #include "tcp.h"
#include "udp.h" #include "udp.h"
...@@ -1482,6 +1484,11 @@ inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) ...@@ -1482,6 +1484,11 @@ inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSARP: case SIOCSARP:
return(arp_ioctl(cmd,(void *) arg)); return(arp_ioctl(cmd,(void *) arg));
case SIOCDRARP:
case SIOCGRARP:
case SIOCSRARP:
return(rarp_ioctl(cmd,(void *) arg));
case SIOCGIFCONF: case SIOCGIFCONF:
case SIOCGIFFLAGS: case SIOCGIFFLAGS:
case SIOCSIFFLAGS: case SIOCSIFFLAGS:
...@@ -1701,7 +1708,6 @@ inet_fioctl(struct inode *inode, struct file *file, ...@@ -1701,7 +1708,6 @@ inet_fioctl(struct inode *inode, struct file *file,
/* Extract the minor number on which we work. */ /* Extract the minor number on which we work. */
minor = MINOR(inode->i_rdev); minor = MINOR(inode->i_rdev);
if (minor != 0) return(-ENODEV);
/* Now dispatch on the minor device. */ /* Now dispatch on the minor device. */
switch(minor) { switch(minor) {
...@@ -1780,7 +1786,7 @@ void inet_proto_init(struct ddi_proto *pro) ...@@ -1780,7 +1786,7 @@ void inet_proto_init(struct ddi_proto *pro)
struct inet_protocol *p; struct inet_protocol *p;
int i; 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) * Set up our UNIX VFS major device. (compatibility)
*/ */
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
* Charles Hedrick, <hedrick@klinzhai.rutgers.edu> * Charles Hedrick, <hedrick@klinzhai.rutgers.edu>
* Linus Torvalds, <torvalds@cs.helsinki.fi> * Linus Torvalds, <torvalds@cs.helsinki.fi>
* Alan Cox, <gw4pts@gw4pts.ampr.org> * Alan Cox, <gw4pts@gw4pts.ampr.org>
* Matthew Dillon, <dillon@apollo.west.oic.com>
* *
* Fixes: * Fixes:
* Alan Cox : Numerous verify_area() calls * Alan Cox : Numerous verify_area() calls
...@@ -67,6 +68,7 @@ ...@@ -67,6 +68,7 @@
* Linus : Rewrote tcp_read() and URG handling * Linus : Rewrote tcp_read() and URG handling
* completely * completely
* Gerhard Koerting: Fixed some missing timer handling * Gerhard Koerting: Fixed some missing timer handling
* Matthew Dillon : Reworked TCP machine states as per RFC
* *
* *
* To Fix: * To Fix:
...@@ -80,6 +82,40 @@ ...@@ -80,6 +82,40 @@
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or(at your option) any later 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/types.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -109,7 +145,6 @@ ...@@ -109,7 +145,6 @@
unsigned long seq_offset; unsigned long seq_offset;
struct tcp_mib tcp_statistics; struct tcp_mib tcp_statistics;
#define SUBNETSARELOCAL
static __inline__ int static __inline__ int
min(unsigned int a, unsigned int b) min(unsigned int a, unsigned int b)
...@@ -1498,8 +1533,7 @@ static int tcp_read(struct sock *sk, unsigned char *to, ...@@ -1498,8 +1533,7 @@ static int tcp_read(struct sock *sk, unsigned char *to,
/* /*
* Send a FIN without closing the connection. * Shutdown the sending side of a connection.
* Not called at interrupt time.
*/ */
void tcp_shutdown(struct sock *sk, int how) void tcp_shutdown(struct sock *sk, int how)
...@@ -1514,22 +1548,36 @@ void tcp_shutdown(struct sock *sk, int how) ...@@ -1514,22 +1548,36 @@ void tcp_shutdown(struct sock *sk, int how)
* We need to grab some memory, and put together a FIN, * We need to grab some memory, and put together a FIN,
* and then put it into the queue to be sent. * and then put it into the queue to be sent.
* FIXME: * FIXME:
*
* Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92. * Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
* Most of this is guesswork, so maybe it will work... * Most of this is guesswork, so maybe it will work...
*/ */
if (!(how & SEND_SHUTDOWN))
return;
/* /*
* If we've already sent a FIN, return. * If we've already sent a FIN, return.
*/ */
if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) if (sk->state == TCP_FIN_WAIT1 ||
return; sk->state == TCP_FIN_WAIT2 ||
if (!(how & SEND_SHUTDOWN)) sk->state == TCP_CLOSING ||
sk->state == TCP_LAST_ACK ||
sk->state == TCP_TIME_WAIT
) {
return; return;
}
sk->inuse = 1; 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) if (sk->partial)
...@@ -1560,13 +1608,24 @@ void tcp_shutdown(struct sock *sk, int how) ...@@ -1560,13 +1608,24 @@ void tcp_shutdown(struct sock *sk, int how)
{ {
/* /*
* Finish anyway, treat this as a send that got lost. * 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; buff->free=1;
prot->wfree(sk,buff->mem_addr, buff->mem_len); 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 else
sk->state=TCP_FIN_WAIT2; sk->state = TCP_FIN_WAIT2;
release_sock(sk); release_sock(sk);
DPRINTF((DBG_TCP, "Unable to build header for fin.\n")); DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
return; return;
...@@ -1610,7 +1669,9 @@ void tcp_shutdown(struct sock *sk, int how) ...@@ -1610,7 +1669,9 @@ void tcp_shutdown(struct sock *sk, int how)
if (sk->state == TCP_ESTABLISHED) if (sk->state == TCP_ESTABLISHED)
sk->state = TCP_FIN_WAIT1; sk->state = TCP_FIN_WAIT1;
else 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); release_sock(sk);
...@@ -1934,7 +1995,7 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb, ...@@ -1934,7 +1995,7 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
if (sk->user_mss) if (sk->user_mss)
newsk->mtu = sk->user_mss; newsk->mtu = sk->user_mss;
else { else {
#ifdef SUBNETSARELOCAL #ifdef CONFIG_INET_SNARL /* Sub Nets ARe Local */
if ((saddr ^ daddr) & default_mask(saddr)) if ((saddr ^ daddr) & default_mask(saddr))
#else #else
if ((saddr ^ daddr) & dev->pa_mask) if ((saddr ^ daddr) & dev->pa_mask)
...@@ -2081,7 +2142,18 @@ static void tcp_close(struct sock *sk, int timeout) ...@@ -2081,7 +2142,18 @@ static void tcp_close(struct sock *sk, int timeout)
{ {
case TCP_FIN_WAIT1: case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2: 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. * Start a timer.
* original code was 4 * sk->rtt. In converting to the * original code was 4 * sk->rtt. In converting to the
...@@ -2089,11 +2161,16 @@ static void tcp_close(struct sock *sk, int timeout) ...@@ -2089,11 +2161,16 @@ static void tcp_close(struct sock *sk, int timeout)
* it seems to make most sense to use the backed off value * it seems to make most sense to use the backed off value
*/ */
reset_timer(sk, TIME_CLOSE, 4 * sk->rto); reset_timer(sk, TIME_CLOSE, 4 * sk->rto);
#endif
if (timeout) if (timeout)
tcp_time_wait(sk); tcp_time_wait(sk);
release_sock(sk); release_sock(sk);
return; /* break causes a double release - messy */ return; /* break causes a double release - messy */
case TCP_TIME_WAIT: case TCP_TIME_WAIT:
case TCP_LAST_ACK:
/*
* A timeout from these states terminates the TCB.
*/
if (timeout) if (timeout)
{ {
sk->state = TCP_CLOSE; sk->state = TCP_CLOSE;
...@@ -2140,6 +2217,12 @@ static void tcp_close(struct sock *sk, int timeout) ...@@ -2140,6 +2217,12 @@ static void tcp_close(struct sock *sk, int timeout)
if (tmp < 0) if (tmp < 0)
{ {
kfree_skb(buff,FREE_WRITE); 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) if(sk->state==TCP_ESTABLISHED)
sk->state=TCP_FIN_WAIT1; sk->state=TCP_FIN_WAIT1;
else else
...@@ -2192,14 +2275,19 @@ static void tcp_close(struct sock *sk, int timeout) ...@@ -2192,14 +2275,19 @@ static void tcp_close(struct sock *sk, int timeout)
skb_queue_tail(&sk->write_queue, buff); skb_queue_tail(&sk->write_queue, buff);
} }
if (sk->state == TCP_CLOSE_WAIT) /*
{ * If established (normal close), enter FIN_WAIT1.
sk->state = TCP_FIN_WAIT2; * If in CLOSE_WAIT, enter LAST_ACK
} * If in CLOSING, remain in CLOSING
else * otherwise enter FIN_WAIT2
{ */
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 if (sk->state != TCP_CLOSING)
sk->state = TCP_FIN_WAIT2;
} }
release_sock(sk); release_sock(sk);
} }
...@@ -2276,7 +2364,10 @@ sort_send(struct sock *sk) ...@@ -2276,7 +2364,10 @@ sort_send(struct sock *sk)
} }
/* This routine deals with incoming acks, but not outgoing ones. */ /*
* This routine deals with incoming acks, but not outgoing ones.
*/
static int static int
tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
{ {
...@@ -2574,33 +2665,68 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len) ...@@ -2574,33 +2665,68 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
tcp_send_partial(sk); 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) if (!sk->dead)
sk->state_change(sk); 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) { 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; flag |= 1;
sk->state = TCP_CLOSE; sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK; 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) { if (sk->rcv_ack_seq == sk->write_seq) {
flag |= 1; flag |= 1;
if (sk->acked_seq != sk->fin_seq) { if (sk->acked_seq != sk->fin_seq) {
tcp_time_wait(sk); tcp_time_wait(sk);
} else { } 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->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 * I make no guarantees about the first clause in the following
* test, i.e. "(!flag) || (flag&4)". I'm not entirely sure under * test, i.e. "(!flag) || (flag&4)". I'm not entirely sure under
...@@ -2851,6 +2977,8 @@ tcp_data(struct sk_buff *skb, struct sock *sk, ...@@ -2851,6 +2977,8 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
DPRINTF((DBG_TCP, "data received on dead socket.\n")); 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 && if (sk->state == TCP_FIN_WAIT2 &&
sk->acked_seq == sk->fin_seq && sk->rcv_ack_seq == sk->write_seq) { 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)); DPRINTF((DBG_TCP, "tcp_data: entering last_ack state sk = %X\n", sk));
...@@ -2860,6 +2988,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk, ...@@ -2860,6 +2988,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
sk->state = TCP_LAST_ACK; sk->state = TCP_LAST_ACK;
if (!sk->dead) sk->state_change(sk); if (!sk->dead) sk->state_change(sk);
} }
#endif
return(0); return(0);
} }
...@@ -2920,15 +3049,27 @@ static inline int tcp_urg(struct sock *sk, struct tcphdr *th, ...@@ -2920,15 +3049,27 @@ static inline int tcp_urg(struct sock *sk, struct tcphdr *th,
/* /*
* 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) unsigned long saddr, struct device *dev)
{ {
DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n", DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n",
sk, th, saddr, dev)); sk, th, saddr, dev));
sk->fin_seq = th->seq + skb->len + th->syn + th->fin;
if (!sk->dead) if (!sk->dead)
{ {
sk->state_change(sk); sk->state_change(sk);
...@@ -2939,9 +3080,12 @@ static int tcp_fin(struct sock *sk, struct tcphdr *th, ...@@ -2939,9 +3080,12 @@ static int tcp_fin(struct sock *sk, struct tcphdr *th,
case TCP_SYN_RECV: case TCP_SYN_RECV:
case TCP_SYN_SENT: case TCP_SYN_SENT:
case TCP_ESTABLISHED: 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); reset_timer(sk, TIME_CLOSE, TCP_TIMEOUT_LEN);
sk->fin_seq = th->seq+1; /*sk->fin_seq = th->seq+1;*/
tcp_statistics.TcpCurrEstab--; tcp_statistics.TcpCurrEstab--;
sk->state = TCP_CLOSE_WAIT; sk->state = TCP_CLOSE_WAIT;
if (th->rst) if (th->rst)
...@@ -2949,17 +3093,46 @@ static int tcp_fin(struct sock *sk, struct tcphdr *th, ...@@ -2949,17 +3093,46 @@ static int tcp_fin(struct sock *sk, struct tcphdr *th,
break; break;
case TCP_CLOSE_WAIT: case TCP_CLOSE_WAIT:
case TCP_FIN_WAIT2: case TCP_CLOSING:
break; /* we got a retransmit of the fin. */ /*
* received a retransmission of the FIN, do
case TCP_FIN_WAIT1: * nothing.
/* Contains the one that needs to be acked */ */
sk->fin_seq = th->seq+1;
sk->state = TCP_FIN_WAIT2;
break; 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: default:
case TCP_TIME_WAIT:
sk->state = TCP_LAST_ACK; sk->state = TCP_LAST_ACK;
/* Start the timers. */ /* Start the timers. */
...@@ -3364,6 +3537,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); ...@@ -3364,6 +3537,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
case TCP_ESTABLISHED: case TCP_ESTABLISHED:
case TCP_CLOSE_WAIT: case TCP_CLOSE_WAIT:
case TCP_CLOSING:
case TCP_FIN_WAIT1: case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2: case TCP_FIN_WAIT2:
case TCP_TIME_WAIT: case TCP_TIME_WAIT:
...@@ -3437,7 +3611,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); ...@@ -3437,7 +3611,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
} }
/* Moved: you must do data then fin bit */ /* 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); kfree_skb(skb, FREE_READ);
release_sock(sk); release_sock(sk);
return(0); return(0);
...@@ -3622,7 +3796,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); ...@@ -3622,7 +3796,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
if (tcp_data(skb, sk, saddr, len)) if (tcp_data(skb, sk, saddr, len))
kfree_skb(skb, FREE_READ); 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); release_sock(sk);
return(0); return(0);
} }
...@@ -3645,7 +3819,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n"); ...@@ -3645,7 +3819,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
release_sock(sk); release_sock(sk);
return(0); return(0);
} }
tcp_fin(sk, th, saddr, dev); tcp_fin(skb, sk, th, saddr, dev);
release_sock(sk); release_sock(sk);
return(0); return(0);
} }
...@@ -3667,9 +3841,19 @@ static void tcp_write_wakeup(struct sock *sk) ...@@ -3667,9 +3841,19 @@ static void tcp_write_wakeup(struct sock *sk)
if (sk->zapped) if (sk->zapped)
return; /* Afer a valid reset we can send no more */ 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; return;
}
buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC); buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
if (buff == NULL) if (buff == NULL)
......
...@@ -195,7 +195,7 @@ net_timer (unsigned long data) ...@@ -195,7 +195,7 @@ net_timer (unsigned long data)
DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n")); DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n"));
sk->err = ETIMEDOUT; sk->err = ETIMEDOUT;
if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 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; sk->state = TCP_TIME_WAIT;
reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN); reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
} else { } else {
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
* Alan Cox : Use ip_tos and ip_ttl * Alan Cox : Use ip_tos and ip_ttl
* Alan Cox : SNMP Mibs * Alan Cox : SNMP Mibs
* Alan Cox : MSG_DONTROUTE, and 0.0.0.0 support. * 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 * This program is free software; you can redistribute it and/or
...@@ -653,7 +654,8 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, ...@@ -653,7 +654,8 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
{ {
struct sock *sk; struct sock *sk;
struct udphdr *uh; struct udphdr *uh;
unsigned short ulen;
/* /*
* Get the header. * Get the header.
*/ */
...@@ -661,7 +663,22 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, ...@@ -661,7 +663,22 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
ip_statistics.IpInDelivers++; 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); sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
if (sk == NULL) if (sk == NULL)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment