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

Import 1.1.12

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