Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
16119ae5
Commit
16119ae5
authored
Nov 23, 2007
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Import 1.1.12
parent
b7c2deb6
Changes
30
Hide whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
2735 additions
and
913 deletions
+2735
-913
CREDITS
CREDITS
+17
-1
Makefile
Makefile
+1
-1
config.in
config.in
+13
-0
drivers/net/slip.c
drivers/net/slip.c
+4
-2
drivers/scsi/README.st
drivers/scsi/README.st
+141
-0
drivers/scsi/st.c
drivers/scsi/st.c
+466
-225
drivers/scsi/st.h
drivers/scsi/st.h
+6
-1
drivers/scsi/wd7000.c
drivers/scsi/wd7000.c
+914
-302
drivers/scsi/wd7000.h
drivers/scsi/wd7000.h
+17
-170
fs/buffer.c
fs/buffer.c
+1
-1
fs/proc/net.c
fs/proc/net.c
+12
-5
include/linux/if_arp.h
include/linux/if_arp.h
+7
-0
include/linux/if_slip.h
include/linux/if_slip.h
+19
-0
include/linux/ip.h
include/linux/ip.h
+18
-0
include/linux/mtio.h
include/linux/mtio.h
+19
-0
include/linux/netdevice.h
include/linux/netdevice.h
+5
-8
include/linux/sockios.h
include/linux/sockios.h
+17
-0
include/linux/tcp.h
include/linux/tcp.h
+18
-5
init/main.c
init/main.c
+4
-0
kernel/sys.c
kernel/sys.c
+2
-2
kernel/vm86.c
kernel/vm86.c
+12
-5
net/inet/arp.c
net/inet/arp.c
+232
-131
net/inet/arp.h
net/inet/arp.h
+3
-0
net/inet/ip.c
net/inet/ip.c
+2
-2
net/inet/rarp.c
net/inet/rarp.c
+512
-0
net/inet/rarp.h
net/inet/rarp.h
+14
-0
net/inet/sock.c
net/inet/sock.c
+8
-2
net/inet/tcp.c
net/inet/tcp.c
+231
-47
net/inet/timer.c
net/inet/timer.c
+1
-1
net/inet/udp.c
net/inet/udp.c
+19
-2
No files found.
CREDITS
View file @
16119ae5
...
...
@@ -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
...
...
Makefile
View file @
16119ae5
VERSION
=
1
PATCHLEVEL
=
1
SUBLEVEL
=
1
1
SUBLEVEL
=
1
2
all
:
Version zImage
...
...
config.in
View file @
16119ae5
...
...
@@ -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
...
...
drivers/net/slip.c
View file @
16119ae5
...
...
@@ -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
...
...
drivers/scsi/README.st
0 → 100644
View file @
16119ae5
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
drivers/scsi/st.c
View file @
16119ae5
/*
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
);
print_sense
(
"st"
,
SCpnt
);
if
(
debugging
)
{
printk
(
"st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d
\n
"
,
dev
,
result
,
SCpnt
->
cmnd
[
0
],
SCpnt
->
cmnd
[
1
],
SCpnt
->
cmnd
[
2
],
SCpnt
->
cmnd
[
3
],
SCpnt
->
cmnd
[
4
],
SCpnt
->
cmnd
[
5
],
SCpnt
->
request_bufflen
);
if
(
driver_byte
(
result
)
&
DRIVER_SENSE
)
print_sense
(
"st"
,
SCpnt
);
}
#endif
/* if ((sense[0] & 0x70) == 0x70 &&
((sense[2] & 0x80) ))
...
...
@@ -142,6 +122,7 @@ 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,20 +252,19 @@ 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
,
(
STp
->
buffer
)
->
last_result
);
if
(
debugging
)
printk
(
"st%d: Async write error (flush) %x.
\n
"
,
dev
,
(
STp
->
buffer
)
->
last_result
);
#endif
if
((
STp
->
buffer
)
->
last_result
==
INT_MAX
)
return
(
-
ENOSPC
);
return
(
-
EIO
);
}
}
#endif
result
=
0
;
if
(
STp
->
dirty
==
1
)
{
...
...
@@ -262,7 +274,8 @@ flush_write_buffer(int dev)
transfer
=
((
offset
+
STp
->
block_size
-
1
)
/
STp
->
block_size
)
*
STp
->
block_size
;
#ifdef DEBUG
printk
(
"st%d: Flushing %d bytes.
\n
"
,
dev
,
transfer
);
if
(
debugging
)
printk
(
"st%d: Flushing %d bytes.
\n
"
,
dev
,
transfer
);
#endif
memset
((
STp
->
buffer
)
->
b_data
+
offset
,
0
,
transfer
-
offset
);
...
...
@@ -292,8 +305,11 @@ 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,14 +468,16 @@ 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
printk
(
"st%d: Block limits %d - %d bytes.
\n
"
,
dev
,
STp
->
min_block
,
STp
->
max_block
);
if
(
debugging
)
printk
(
"st%d: Block limits %d - %d bytes.
\n
"
,
dev
,
STp
->
min_block
,
STp
->
max_block
);
#endif
}
else
{
STp
->
min_block
=
STp
->
max_block
=
(
-
1
);
#ifdef DEBUG
printk
(
"st%d: Can't read block limits.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Can't read block limits.
\n
"
,
dev
);
#endif
}
...
...
@@ -465,13 +488,14 @@ 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
printk
(
"st%d: No Mode Sense.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: No Mode Sense.
\n
"
,
dev
);
#endif
(
STp
->
buffer
)
->
b_data
[
2
]
=
(
STp
->
buffer
)
->
b_data
[
3
]
=
0
;
...
...
@@ -479,9 +503,10 @@ scsi_tape_open(struct inode * inode, struct file * filp)
SCpnt
->
request
.
dev
=
-
1
;
/* Mark as not busy */
#ifdef DEBUG
printk
(
"st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d
\n
"
,
dev
,
(
STp
->
buffer
)
->
b_data
[
0
],
(
STp
->
buffer
)
->
b_data
[
1
],
(
STp
->
buffer
)
->
b_data
[
2
],
(
STp
->
buffer
)
->
b_data
[
3
]);
if
(
debugging
)
printk
(
"st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d
\n
"
,
dev
,
(
STp
->
buffer
)
->
b_data
[
0
],
(
STp
->
buffer
)
->
b_data
[
1
],
(
STp
->
buffer
)
->
b_data
[
2
],
(
STp
->
buffer
)
->
b_data
[
3
]);
#endif
if
((
STp
->
buffer
)
->
b_data
[
3
]
>=
8
)
{
...
...
@@ -490,13 +515,13 @@ 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
"
,
dev
,
STp
->
density
,
(
STp
->
buffer
)
->
b_data
[
5
]
*
65536
+
(
STp
->
buffer
)
->
b_data
[
6
]
*
256
+
(
STp
->
buffer
)
->
b_data
[
7
],
STp
->
block_size
,
STp
->
drv_buffer
);
if
(
debugging
)
printk
(
"st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d
\n
"
,
dev
,
STp
->
density
,
(
STp
->
buffer
)
->
b_data
[
5
]
*
65536
+
(
STp
->
buffer
)
->
b_data
[
6
]
*
256
+
(
STp
->
buffer
)
->
b_data
[
7
],
STp
->
block_size
,
STp
->
drv_buffer
);
#endif
if
(
STp
->
block_size
>
ST_BUFFER_SIZE
)
{
if
(
STp
->
block_size
>
st_buffer_size
)
{
printk
(
"st%d: Blocksize %d too large for buffer.
\n
"
,
dev
,
STp
->
block_size
);
(
STp
->
buffer
)
->
in_use
=
0
;
...
...
@@ -506,29 +531,31 @@ 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
printk
(
"st%d: Block size: %d, buffer size: %d (%d blocks).
\n
"
,
dev
,
STp
->
block_size
,
(
STp
->
buffer
)
->
buffer_size
,
(
STp
->
buffer
)
->
buffer_blocks
);
if
(
debugging
)
printk
(
"st%d: Block size: %d, buffer size: %d (%d blocks).
\n
"
,
dev
,
STp
->
block_size
,
(
STp
->
buffer
)
->
buffer_size
,
(
STp
->
buffer
)
->
buffer_blocks
);
#endif
if
((
STp
->
buffer
)
->
b_data
[
2
]
&
0x80
)
{
STp
->
write_prot
=
1
;
#ifdef DEBUG
printk
(
"st%d: Write protected
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Write protected
\n
"
,
dev
);
#endif
}
...
...
@@ -557,7 +584,8 @@ 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
printk
(
"st%d: Buffer flushed, EOF written
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Buffer flushed, EOF written
\n
"
,
dev
);
#endif
}
else
if
(
!
rewind
)
{
#ifndef ST_IN_FILE_POS
if
((
STp
->
eof
==
ST_FM
)
&&
!
STp
->
eof_hit
)
st_int_ioctl
(
inode
,
filp
,
MTBSF
,
1
);
/* Back over the EOF hit */
back_over_eof
(
dev
);
#else
flush_buffer
(
inode
,
filp
,
0
);
#endif
...
...
@@ -627,7 +660,7 @@ 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,13 +670,13 @@ 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
,
(
STp
->
buffer
)
->
last_result
);
if
(
debugging
)
printk
(
"st%d: Async write error (write) %x.
\n
"
,
dev
,
(
STp
->
buffer
)
->
last_result
);
#endif
if
((
STp
->
buffer
)
->
last_result
==
INT_MAX
)
{
retval
=
(
-
ENOSPC
);
/* All has been written */
...
...
@@ -654,20 +687,21 @@ 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
->
block_size
!=
0
&&
(
count
%
STp
->
block_size
)
!=
0
)
return
(
-
EIO
);
/* Write must be integral number of blocks */
write_threshold
=
1
;
#else
write_threshold
=
(
STp
->
buffer
)
->
buffer_size
;
#endif
if
(
!
STp
->
do_buffer_writes
)
{
if
(
STp
->
block_size
!=
0
&&
(
count
%
STp
->
block_size
)
!=
0
)
return
(
-
EIO
);
/* Write must be integral number of blocks */
write_threshold
=
1
;
}
else
write_threshold
=
(
STp
->
buffer
)
->
buffer_size
;
if
(
!
STp
->
do_async_writes
)
write_threshold
--
;
SCpnt
=
allocate_device
(
NULL
,
(
STp
->
device
)
->
index
,
1
);
...
...
@@ -680,16 +714,9 @@ 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,25 +729,27 @@ 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
printk
(
"st%d: Error on write:
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Error on write:
\n
"
,
dev
);
#endif
if
((
SCpnt
->
sense_buffer
[
0
]
&
0x70
)
==
0x70
&&
(
SCpnt
->
sense_buffer
[
2
]
&
0x40
))
{
...
...
@@ -738,23 +767,34 @@ 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
printk
(
"st%d: EOM with %d bytes unwritten.
\n
"
,
dev
,
transfer
);
if
(
debugging
)
printk
(
"st%d: EOM with %d bytes unwritten.
\n
"
,
dev
,
transfer
);
#endif
}
else
{
STp
->
eof
=
ST_EOM_ERROR
;
STp
->
drv_block
=
(
-
1
);
/* Too cautious? */
retval
=
(
-
EIO
);
/* EOM for old data */
#ifdef DEBUG
printk
(
"st%d: EOM with lost data.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: EOM with lost data.
\n
"
,
dev
);
#endif
}
}
else
else
{
STp
->
drv_block
=
(
-
1
);
/* Too cautious? */
retval
=
(
-
EIO
);
}
SCpnt
->
request
.
dev
=
-
1
;
/* Mark as not busy */
(
STp
->
buffer
)
->
buffer_bytes
=
0
;
...
...
@@ -767,6 +807,12 @@ 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,8 +921,17 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
if
(
STp
->
block_size
==
0
)
blks
=
bytes
=
count
;
else
{
blks
=
(
STp
->
buffer
)
->
buffer_blocks
;
bytes
=
blks
*
STp
->
block_size
;
if
(
STp
->
do_read_ahead
)
{
blks
=
(
STp
->
buffer
)
->
buffer_blocks
;
bytes
=
blks
*
STp
->
block_size
;
}
else
{
bytes
=
count
;
if
(
bytes
>
st_buffer_size
)
bytes
=
st_buffer_size
;
blks
=
bytes
/
STp
->
block_size
;
bytes
=
blks
*
STp
->
block_size
;
}
}
cmd
[
2
]
=
blks
>>
16
;
cmd
[
3
]
=
blks
>>
8
;
...
...
@@ -893,11 +951,12 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
if
((
STp
->
buffer
)
->
last_result_fatal
)
{
#ifdef DEBUG
printk
(
"st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x
\n
"
,
dev
,
SCpnt
->
sense_buffer
[
0
],
SCpnt
->
sense_buffer
[
1
],
SCpnt
->
sense_buffer
[
2
],
SCpnt
->
sense_buffer
[
3
],
SCpnt
->
sense_buffer
[
4
],
SCpnt
->
sense_buffer
[
5
],
SCpnt
->
sense_buffer
[
6
],
SCpnt
->
sense_buffer
[
7
]);
if
(
debugging
)
printk
(
"st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x
\n
"
,
dev
,
SCpnt
->
sense_buffer
[
0
],
SCpnt
->
sense_buffer
[
1
],
SCpnt
->
sense_buffer
[
2
],
SCpnt
->
sense_buffer
[
3
],
SCpnt
->
sense_buffer
[
4
],
SCpnt
->
sense_buffer
[
5
],
SCpnt
->
sense_buffer
[
6
],
SCpnt
->
sense_buffer
[
7
]);
#endif
if
((
SCpnt
->
sense_buffer
[
0
]
&
0x70
)
==
0x70
)
{
/* extended sense */
...
...
@@ -917,7 +976,7 @@ 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,14 +987,14 @@ 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
printk
(
"st%d: EOM detected (%d bytes read).
\n
"
,
dev
,
(
STp
->
buffer
)
->
buffer_bytes
);
if
(
debugging
)
printk
(
"st%d: EOM detected (%d bytes read).
\n
"
,
dev
,
(
STp
->
buffer
)
->
buffer_bytes
);
#endif
}
else
if
(
SCpnt
->
sense_buffer
[
2
]
&
0x80
)
{
...
...
@@ -944,20 +1003,22 @@ 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
printk
(
"st%d: EOF detected (%d bytes read, transferred %d bytes).
\n
"
,
dev
,
(
STp
->
buffer
)
->
buffer_bytes
,
total
);
if
(
debugging
)
printk
(
"st%d: EOF detected (%d bytes read, transferred %d bytes).
\n
"
,
dev
,
(
STp
->
buffer
)
->
buffer_bytes
,
total
);
#endif
}
/* end of EOF, EOM, ILI test */
}
else
{
/* nonzero sense key */
#ifdef DEBUG
printk
(
"st%d: Tape error while reading.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Tape error while reading.
\n
"
,
dev
);
#endif
SCpnt
->
request
.
dev
=
-
1
;
STp
->
drv_block
=
(
-
1
);
if
(
total
)
return
total
;
else
...
...
@@ -973,12 +1034,19 @@ 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
printk
(
"st%d: Spacing tape forward over %d filemarks.
\n
"
,
dev
,
cmd
[
2
]
*
65536
+
cmd
[
3
]
*
256
+
cmd
[
4
]);
if
(
debugging
)
printk
(
"st%d: Spacing tape forward over %d filemarks.
\n
"
,
dev
,
cmd
[
2
]
*
65536
+
cmd
[
3
]
*
256
+
cmd
[
4
]);
#endif
fileno
+=
arg
;
blkno
=
0
;
break
;
case
MTBSF
:
case
MTBSFM
:
...
...
@@ -1052,11 +1173,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd
[
3
]
=
(
ltmp
>>
8
);
cmd
[
4
]
=
ltmp
;
#ifdef DEBUG
if
(
cmd
[
2
]
&
0x80
)
ltmp
=
0xff000000
;
ltmp
=
ltmp
|
(
cmd
[
2
]
<<
16
)
|
(
cmd
[
3
]
<<
8
)
|
cmd
[
4
];
printk
(
"st%d: Spacing tape backward over %d filemarks.
\n
"
,
dev
,
(
-
ltmp
));
if
(
debugging
)
{
if
(
cmd
[
2
]
&
0x80
)
ltmp
=
0xff000000
;
ltmp
=
ltmp
|
(
cmd
[
2
]
<<
16
)
|
(
cmd
[
3
]
<<
8
)
|
cmd
[
4
];
printk
(
"st%d: Spacing tape backward over %ld filemarks.
\n
"
,
dev
,
(
-
ltmp
));
}
#endif
fileno
-=
arg
;
blkno
=
(
-
1
);
/* We can't know the block number */
break
;
case
MTFSR
:
cmd
[
0
]
=
SPACE
;
...
...
@@ -1065,9 +1190,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd
[
3
]
=
(
arg
>>
8
);
cmd
[
4
]
=
arg
;
#ifdef DEBUG
printk
(
"st%d: Spacing tape forward %d blocks.
\n
"
,
dev
,
cmd
[
2
]
*
65536
+
cmd
[
3
]
*
256
+
cmd
[
4
]);
if
(
debugging
)
printk
(
"st%d: Spacing tape forward %d blocks.
\n
"
,
dev
,
cmd
[
2
]
*
65536
+
cmd
[
3
]
*
256
+
cmd
[
4
]);
#endif
if
(
blkno
>=
0
)
blkno
+=
arg
;
break
;
case
MTBSR
:
cmd
[
0
]
=
SPACE
;
...
...
@@ -1077,11 +1205,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd
[
3
]
=
(
ltmp
>>
8
);
cmd
[
4
]
=
ltmp
;
#ifdef DEBUG
if
(
cmd
[
2
]
&
0x80
)
ltmp
=
0xff000000
;
ltmp
=
ltmp
|
(
cmd
[
2
]
<<
16
)
|
(
cmd
[
3
]
<<
8
)
|
cmd
[
4
];
printk
(
"st%d: Spacing tape backward %d blocks.
\n
"
,
dev
,
(
-
ltmp
));
if
(
debugging
)
{
if
(
cmd
[
2
]
&
0x80
)
ltmp
=
0xff000000
;
ltmp
=
ltmp
|
(
cmd
[
2
]
<<
16
)
|
(
cmd
[
3
]
<<
8
)
|
cmd
[
4
];
printk
(
"st%d: Spacing tape backward %ld blocks.
\n
"
,
dev
,
(
-
ltmp
));
}
#endif
if
(
blkno
>=
0
)
blkno
-=
arg
;
break
;
case
MTWEOF
:
if
(
STp
->
write_prot
)
...
...
@@ -1092,9 +1224,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd
[
4
]
=
arg
;
timeout
=
ST_TIMEOUT
;
#ifdef DEBUG
printk
(
"st%d: Writing %d filemarks.
\n
"
,
dev
,
cmd
[
2
]
*
65536
+
cmd
[
3
]
*
256
+
cmd
[
4
]);
if
(
debugging
)
printk
(
"st%d: Writing %d filemarks.
\n
"
,
dev
,
cmd
[
2
]
*
65536
+
cmd
[
3
]
*
256
+
cmd
[
4
]);
#endif
fileno
+=
arg
;
blkno
=
0
;
break
;
case
MTREW
:
cmd
[
0
]
=
REZERO_UNIT
;
...
...
@@ -1103,8 +1238,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
timeout
=
ST_TIMEOUT
;
#endif
#ifdef DEBUG
printk
(
"st%d: Rewinding tape.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Rewinding tape.
\n
"
,
dev
);
#endif
fileno
=
blkno
=
0
;
break
;
case
MTOFFL
:
cmd
[
0
]
=
START_STOP
;
...
...
@@ -1113,12 +1250,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
timeout
=
ST_TIMEOUT
;
#endif
#ifdef DEBUG
printk
(
"st%d: Unloading tape.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Unloading tape.
\n
"
,
dev
);
#endif
fileno
=
blkno
=
0
;
break
;
case
MTNOP
:
#ifdef DEBUG
printk
(
"st%d: No op on tape.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: No op on tape.
\n
"
,
dev
);
#endif
return
0
;
/* Should do something ? */
break
;
...
...
@@ -1130,15 +1270,26 @@ st_int_ioctl(struct inode * inode,struct file * file,
#endif
cmd
[
4
]
=
3
;
#ifdef DEBUG
printk
(
"st%d: Retensioning tape.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Retensioning tape.
\n
"
,
dev
);
#endif
fileno
=
blkno
=
0
;
break
;
case
MTEOM
:
/* space to the end of tape */
ioctl_result
=
st_int_ioctl
(
inode
,
file
,
MTFSF
,
0x3fff
);
fileno
=
(
STp
->
mt_status
)
->
mt_fileno
;
/* The next lines would hide the number of spaced FileMarks
That's why I inserted the previous lines. I had no luck
with detecting EOM with FSF, so we go now to EOM.
Joerg Weule */
cmd
[
0
]
=
SPACE
;
cmd
[
1
]
=
3
;
#ifdef DEBUG
printk
(
"st%d: Spacing to end of recorded medium.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Spacing to end of recorded medium.
\n
"
,
dev
);
#endif
blkno
=
(
-
1
);
break
;
case
MTERASE
:
if
(
STp
->
write_prot
)
...
...
@@ -1146,8 +1297,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
cmd
[
0
]
=
ERASE
;
cmd
[
1
]
=
1
;
/* To the end of tape */
#ifdef DEBUG
printk
(
"st%d: Erasing tape.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Erasing tape.
\n
"
,
dev
);
#endif
fileno
=
blkno
=
0
;
break
;
case
MTSEEK
:
if
((
STp
->
device
)
->
scsi_level
<
SCSI_2
)
{
...
...
@@ -1170,8 +1323,10 @@ 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,17 +1363,19 @@ st_int_ioctl(struct inode * inode,struct file * file,
(
STp
->
buffer
)
->
b_data
[
11
]
=
ltmp
;
timeout
=
ST_TIMEOUT
;
#ifdef DEBUG
if
(
cmd_in
==
MTSETBLK
)
printk
(
"st%d: Setting block size to %d bytes.
\n
"
,
dev
,
(
STp
->
buffer
)
->
b_data
[
9
]
*
65536
+
(
STp
->
buffer
)
->
b_data
[
10
]
*
256
+
(
STp
->
buffer
)
->
b_data
[
11
]);
else
if
(
cmd_in
==
MTSETDENSITY
)
printk
(
"st%d: Setting density code to %x.
\n
"
,
dev
,
(
STp
->
buffer
)
->
b_data
[
4
]);
else
printk
(
"st%d: Setting drive buffer code to %d.
\n
"
,
dev
,
((
STp
->
buffer
)
->
b_data
[
2
]
>>
4
)
&
7
);
if
(
debugging
)
{
if
(
cmd_in
==
MTSETBLK
)
printk
(
"st%d: Setting block size to %d bytes.
\n
"
,
dev
,
(
STp
->
buffer
)
->
b_data
[
9
]
*
65536
+
(
STp
->
buffer
)
->
b_data
[
10
]
*
256
+
(
STp
->
buffer
)
->
b_data
[
11
]);
else
if
(
cmd_in
==
MTSETDENSITY
)
printk
(
"st%d: Setting density code to %x.
\n
"
,
dev
,
(
STp
->
buffer
)
->
b_data
[
4
]);
else
printk
(
"st%d: Setting drive buffer code to %d.
\n
"
,
dev
,
((
STp
->
buffer
)
->
b_data
[
2
]
>>
4
)
&
7
);
}
#endif
break
;
default:
...
...
@@ -1230,7 +1387,7 @@ 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,7 +1504,11 @@ st_ioctl(struct inode * inode,struct file * file,
if
(
i
<
0
)
return
i
;
return
st_int_ioctl
(
inode
,
file
,
mtc
.
mt_op
,
mtc
.
mt_count
);
if
(
mtc
.
mt_op
==
MTSETDRVBUFFER
&&
(
mtc
.
mt_count
&
MT_ST_OPTIONS
)
!=
0
)
return
st_set_options
(
inode
,
mtc
.
mt_count
);
else
return
st_int_ioctl
(
inode
,
file
,
mtc
.
mt_op
,
mtc
.
mt_count
);
}
else
if
(
cmd
==
(
MTIOCGET
&
IOCCMD_MASK
))
{
...
...
@@ -1328,13 +1518,29 @@ 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
printk
(
"st%d: get tape position.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: get tape position.
\n
"
,
dev
);
#endif
if
(((
cmd_in
&
IOCSIZE_MASK
)
>>
IOCSIZE_SHIFT
)
!=
sizeof
(
struct
mtpos
))
return
(
-
EINVAL
);
...
...
@@ -1363,14 +1569,15 @@ 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
printk
(
"st%d: Can't read tape position.
\n
"
,
dev
);
if
(
debugging
)
printk
(
"st%d: Can't read tape position.
\n
"
,
dev
);
#endif
result
=
(
-
EIO
);
}
...
...
@@ -1397,8 +1604,25 @@ 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
;
...
...
drivers/scsi/st.h
View file @
16119ae5
...
...
@@ -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
;
...
...
drivers/scsi/wd7000.c
View file @
16119ae5
/* $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,17 +116,179 @@
#include "scsi.h"
#include "hosts.h"
/* #define DEBUG */
#define ANY2SCSI_INLINE
/* undef this to use old macros */
#undef DEBUG
#include "wd7000.h"
#ifdef DEBUG
#define DEB(x) x
#else
#define DEB(x)
#endif
/*
* Mailbox structure sizes.
* I prefer to keep the number of ICMBs much larger than the number of
* OGMBs. OGMBs are used very quickly by the driver to start one or
* more commands, while ICMBs are used by the host adapter per command.
*/
#define OGMB_CNT 16
#define ICMB_CNT 32
/*
* Scb's are shared by all active adapters. So, if they all become busy,
* callers may be made to wait in alloc_scbs for them to free. That can
* be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q. If you'd
* rather conserve memory, use a smaller number (> 0, of course) - things
* will should still work OK.
*/
#define MAX_SCBS 32
/*
* WD7000-specific mailbox structure
*
*/
typedef
volatile
struct
mailbox
{
unchar
status
;
unchar
scbptr
[
3
];
/* SCSI-style - MSB first (big endian) */
}
Mailbox
;
/*
* This structure should contain all per-adapter global data. I.e., any
* new global per-adapter data should put in here.
*
*/
typedef
struct
adapter
{
int
num
;
/* Index into Scsi_hosts array */
struct
Scsi_Host
*
sh
;
/* Pointer to Scsi_Host structure */
int
iobase
;
/* This adapter's I/O base address */
int
irq
;
/* This adapter's IRQ level */
int
dma
;
/* This adapter's DMA channel */
struct
{
/* This adapter's mailboxes */
Mailbox
ogmb
[
OGMB_CNT
];
/* Outgoing mailboxes */
Mailbox
icmb
[
ICMB_CNT
];
/* Incoming mailboxes */
}
mb
;
int
next_ogmb
;
/* to reduce contention at mailboxes */
unchar
control
;
/* shadows CONTROL port value */
unchar
rev1
,
rev2
;
/* filled in by wd7000_revision */
}
Adapter
;
/*
* The following is set up by wd7000_detect, and used thereafter by
* wd7000_intr_handle to map the irq level to the corresponding Adapter.
* Note that if request_irq instead of irqaction to allocate the IRQ,
* or if SA_INTERRUPT is not used, wd7000_intr_handle must be changed
* to pick up the IRQ level correctly.
*/
Adapter
*
irq2host
[
16
]
=
{
NULL
};
/* Possible IRQs are 0-15 */
/*
* Standard Adapter Configurations - used by wd7000_detect
*/
typedef
struct
{
const
void
*
bios
;
/* (linear) base address for ROM BIOS */
int
iobase
;
/* I/O ports base address */
int
irq
;
/* IRQ level */
int
dma
;
/* DMA channel */
}
Config
;
static
const
Config
configs
[]
=
{
{(
void
*
)
0xce000
,
0x350
,
15
,
6
},
/* defaults for single adapter */
{(
void
*
)
0xc8000
,
0x330
,
11
,
5
},
/* defaults for second adapter */
{(
void
*
)
0xd8000
,
0x350
,
15
,
6
},
/* Arghhh.... who added this ? */
};
#define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
/*
* The following list defines strings to look for in the BIOS that identify
* it as the WD7000-FASST2 SST BIOS. I suspect that something should be
* added for the Future Domain version.
*/
typedef
struct
signature
{
void
*
sig
;
/* String to look for */
unsigned
ofs
;
/* offset from BIOS base address */
unsigned
len
;
/* length of string */
}
Signature
;
static
const
Signature
signatures
[]
=
{
{
"SSTBIOS"
,
0x0000d
,
7
}
/* "SSTBIOS" @ offset 0x0000d */
};
#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
/*
* I/O Port Offsets and Bit Definitions
* 4 addresses are used. Those not defined here are reserved.
*/
#define ASC_STAT 0
/* Status, Read */
#define ASC_COMMAND 0
/* Command, Write */
#define ASC_INTR_STAT 1
/* Interrupt Status, Read */
#define ASC_INTR_ACK 1
/* Acknowledge, Write */
#define ASC_CONTROL 2
/* Control, Write */
/* ASC Status Port
*/
#define INT_IM 0x80
/* Interrupt Image Flag */
#define CMD_RDY 0x40
/* Command Port Ready */
#define CMD_REJ 0x20
/* Command Port Byte Rejected */
#define ASC_INIT 0x10
/* ASC Initialized Flag */
#define ASC_STATMASK 0xf0
/* The lower 4 Bytes are reserved */
/* COMMAND opcodes
*
* Unfortunately, I have no idea how to properly use some of these commands,
* as the OEM manual does not make it clear. I have not been able to use
* enable/disable unsolicited interrupts or the reset commands with any
* discernable effect whatsoever. I think they may be related to certain
* ICB commands, but again, the OEM manual doesn't make that clear.
*/
#define NO_OP 0
/* NO-OP toggles CMD_RDY bit in ASC_STAT */
#define INITIALIZATION 1
/* initialization (10 bytes) */
#define DISABLE_UNS_INTR 2
/* disable unsolicited interrupts */
#define ENABLE_UNS_INTR 3
/* enable unsolicited interrupts */
#define INTR_ON_FREE_OGMB 4
/* interrupt on free OGMB */
#define SOFT_RESET 5
/* SCSI bus soft reset */
#define HARD_RESET_ACK 6
/* SCSI bus hard reset acknowledge */
#define START_OGMB 0x80
/* start command in OGMB (n) */
#define SCAN_OGMBS 0xc0
/* start multiple commands, signature (n) */
/* where (n) = lower 6 bits */
/* For INITIALIZATION:
*/
typedef
struct
initCmd
{
unchar
op
;
/* command opcode (= 1) */
unchar
ID
;
/* Adapter's SCSI ID */
unchar
bus_on
;
/* Bus on time, x 125ns (see below) */
unchar
bus_off
;
/* Bus off time, "" "" */
unchar
rsvd
;
/* Reserved */
unchar
mailboxes
[
3
];
/* Address of Mailboxes, MSB first */
unchar
ogmbs
;
/* Number of outgoing MBs, max 64, 0,1 = 1 */
unchar
icmbs
;
/* Number of incoming MBs, "" "" */
}
InitCmd
;
#define BUS_ON 64
/* x 125ns = 8000ns (BIOS default) */
#define BUS_OFF 15
/* x 125ns = 1875ns (BIOS default) */
/* Interrupt Status Port - also returns diagnostic codes at ASC reset
*
* if msb is zero, the lower bits are diagnostic status
* Diagnostics:
* 01 No diagnostic error occurred
* 02 RAM failure
* 03 FIFO R/W failed
* 04 SBIC register read/write failed
* 05 Initialization D-FF failed
* 06 Host IRQ D-FF failed
* 07 ROM checksum error
* Interrupt status (bitwise):
* 10NNNNNN outgoing mailbox NNNNNN is free
* 11NNNNNN incoming mailbox NNNNNN needs service
*/
#define MB_INTR 0xC0
/* Mailbox Service possible/required */
#define IMB_INTR 0x40
/* 1 Incoming / 0 Outgoing */
#define MB_MASK 0x3f
/* mask for mailbox number */
/* CONTROL port bits
*/
#define INT_EN 0x08
/* Interrupt Enable */
#define DMA_EN 0x04
/* DMA Enable */
#define SCSI_RES 0x02
/* SCSI Reset */
#define ASC_RES 0x01
/* ASC Reset */
/*
Driver data structures:
...
...
@@ -63,103 +315,327 @@
indices need not be involved.
*/
static
struct
{
struct
wd_mailbox
ogmb
[
OGMB_CNT
];
struct
wd_mailbox
icmb
[
ICMB_CNT
];
}
mb
;
static
int
next_ogmb
=
0
;
/* to reduce contention at mailboxes */
/*
* WD7000-specific scatter/gather element structure
*/
typedef
struct
sgb
{
unchar
len
[
3
];
unchar
ptr
[
3
];
/* Also SCSI-style - MSB first */
}
Sgb
;
typedef
struct
scb
{
/* Command Control Block 5.4.1 */
unchar
op
;
/* Command Control Block Operation Code */
unchar
idlun
;
/* op=0,2:Target Id, op=1:Initiator Id */
/* Outbound data transfer, length is checked*/
/* Inbound data transfer, length is checked */
/* Logical Unit Number */
unchar
cdb
[
12
];
/* SCSI Command Block */
volatile
unchar
status
;
/* SCSI Return Status */
volatile
unchar
vue
;
/* Vendor Unique Error Code */
unchar
maxlen
[
3
];
/* Maximum Data Transfer Length */
unchar
dataptr
[
3
];
/* SCSI Data Block Pointer */
unchar
linkptr
[
3
];
/* Next Command Link Pointer */
unchar
direc
;
/* Transfer Direction */
unchar
reserved2
[
6
];
/* SCSI Command Descriptor Block */
/* end of hardware SCB */
Scsi_Cmnd
*
SCpnt
;
/* Scsi_Cmnd using this SCB */
Sgb
sgb
[
WD7000_SG
];
/* Scatter/gather list for this SCB */
Adapter
*
host
;
/* host adapter */
struct
scb
*
next
;
/* for lists of scbs */
}
Scb
;
/*
* This driver is written to allow host-only commands to be executed.
* These use a 16-byte block called an ICB. The format is extended by the
* driver to 18 bytes, to support the status returned in the ICMB and
* an execution phase code.
*
* There are other formats besides these; these are the ones I've tried
* to use. Formats for some of the defined ICB opcodes are not defined
* (notably, get/set unsolicited interrupt status) in my copy of the OEM
* manual, and others are ambiguous/hard to follow.
*/
#define ICB_OP_MASK 0x80
/* distinguishes scbs from icbs */
#define ICB_OP_OPEN_RBUF 0x80
/* open receive buffer */
#define ICB_OP_RECV_CMD 0x81
/* receive command from initiator */
#define ICB_OP_RECV_DATA 0x82
/* receive data from initiator */
#define ICB_OP_RECV_SDATA 0x83
/* receive data with status from init. */
#define ICB_OP_SEND_DATA 0x84
/* send data with status to initiator */
#define ICB_OP_SEND_STAT 0x86
/* send command status to initiator */
/* 0x87 is reserved */
#define ICB_OP_READ_INIT 0x88
/* read initialization bytes */
#define ICB_OP_READ_ID 0x89
/* read adapter's SCSI ID */
#define ICB_OP_SET_UMASK 0x8A
/* set unsolicited interrupt mask */
#define ICB_OP_GET_UMASK 0x8B
/* read unsolicited interrupt mask */
#define ICB_OP_GET_REVISION 0x8C
/* read firmware revision level */
#define ICB_OP_DIAGNOSTICS 0x8D
/* execute diagnostics */
#define ICB_OP_SET_EPARMS 0x8E
/* set execution parameters */
#define ICB_OP_GET_EPARMS 0x8F
/* read execution parameters */
typedef
struct
icbRecvCmd
{
unchar
op
;
unchar
IDlun
;
/* Initiator SCSI ID/lun */
unchar
len
[
3
];
/* command buffer length */
unchar
ptr
[
3
];
/* command buffer address */
unchar
rsvd
[
7
];
/* reserved */
volatile
unchar
vue
;
/* vendor-unique error code */
volatile
unchar
status
;
/* returned (icmb) status */
volatile
unchar
phase
;
/* used by interrupt handler */
}
IcbRecvCmd
;
typedef
struct
icbSendStat
{
unchar
op
;
unchar
IDlun
;
/* Target SCSI ID/lun */
unchar
stat
;
/* (outgoing) completion status byte 1 */
unchar
rsvd
[
12
];
/* reserved */
volatile
unchar
vue
;
/* vendor-unique error code */
volatile
unchar
status
;
/* returned (icmb) status */
volatile
unchar
phase
;
/* used by interrupt handler */
}
IcbSendStat
;
typedef
struct
icbRevLvl
{
unchar
op
;
volatile
unchar
primary
;
/* primary revision level (returned) */
volatile
unchar
secondary
;
/* secondary revision level (returned) */
unchar
rsvd
[
12
];
/* reserved */
volatile
unchar
vue
;
/* vendor-unique error code */
volatile
unchar
status
;
/* returned (icmb) status */
volatile
unchar
phase
;
/* used by interrupt handler */
}
IcbRevLvl
;
typedef
struct
icbUnsMask
{
/* I'm totally guessing here */
unchar
op
;
volatile
unchar
mask
[
14
];
/* mask bits */
#ifdef 0
unchar
rsvd
[
12
];
/* reserved */
#endif
volatile
unchar
vue
;
/* vendor-unique error code */
volatile
unchar
status
;
/* returned (icmb) status */
volatile
unchar
phase
;
/* used by interrupt handler */
}
IcbUnsMask
;
typedef
struct
icbDiag
{
unchar
op
;
unchar
type
;
/* diagnostics type code (0-3) */
unchar
len
[
3
];
/* buffer length */
unchar
ptr
[
3
];
/* buffer address */
unchar
rsvd
[
7
];
/* reserved */
volatile
unchar
vue
;
/* vendor-unique error code */
volatile
unchar
status
;
/* returned (icmb) status */
volatile
unchar
phase
;
/* used by interrupt handler */
}
IcbDiag
;
#define ICB_DIAG_POWERUP 0
/* Power-up diags only */
#define ICB_DIAG_WALKING 1
/* walking 1's pattern */
#define ICB_DIAG_DMA 2
/* DMA - system memory diags */
#define ICB_DIAG_FULL 3
/* do both 1 & 2 */
typedef
struct
icbParms
{
unchar
op
;
unchar
rsvd1
;
/* reserved */
unchar
len
[
3
];
/* parms buffer length */
unchar
ptr
[
3
];
/* parms buffer address */
unchar
idx
[
2
];
/* index (MSB-LSB) */
unchar
rsvd2
[
5
];
/* reserved */
volatile
unchar
vue
;
/* vendor-unique error code */
volatile
unchar
status
;
/* returned (icmb) status */
volatile
unchar
phase
;
/* used by interrupt handler */
}
IcbParms
;
typedef
struct
icbAny
{
unchar
op
;
unchar
data
[
14
];
/* format-specific data */
volatile
unchar
vue
;
/* vendor-unique error code */
volatile
unchar
status
;
/* returned (icmb) status */
volatile
unchar
phase
;
/* used by interrupt handler */
}
IcbAny
;
typedef
union
icb
{
unchar
op
;
/* ICB opcode */
IcbRecvCmd
recv_cmd
;
/* format for receive command */
IcbSendStat
send_stat
;
/* format for send status */
IcbRevLvl
rev_lvl
;
/* format for get revision level */
IcbDiag
diag
;
/* format for execute diagnostics */
IcbParms
eparms
;
/* format for get/set exec parms */
IcbAny
icb
;
/* generic format */
unchar
data
[
18
];
}
Icb
;
/*
* Driver SCB structure pool.
*
* The SCBs declared here are shared by all host adapters; hence, this
* structure is not part of the Adapter structure.
*/
static
Scb
scbs
[
MAX_SCBS
];
static
Scb
*
scbfree
=
NULL
;
static
Scb
*
scbfree
=
NULL
;
/* free list */
static
int
freescbs
=
MAX_SCBS
;
/* free list counter */
/*
* END of data/declarations - code follows.
*/
#ifdef ANY2SCSI_INLINE
/*
Since they're used a lot, I've redone the following from the macros
formerly in wd7000.h, hopefully to speed them up by getting rid of
all the shifting (it may not matter; GCC might have done as well anyway).
static
int
wd7000_host
=
0
;
static
unchar
controlstat
=
0
;
xany2scsi and xscsi2int were not being used, and are no longer defined.
(They were simply 4-byte versions of these routines).
*/
typedef
union
{
/* let's cheat... */
int
i
;
unchar
u
[
sizeof
(
int
)];
/* the sizeof(int) makes it more portable */
}
i_u
;
static
unchar
rev_1
=
0
,
rev_2
=
0
;
/* filled in by wd7000_revision */
#define wd7000_intr_ack() outb(0,INTR_ACK)
static
inline
void
any2scsi
(
unchar
*
scsi
,
int
any
)
{
*
scsi
++
=
((
i_u
)
any
).
u
[
2
];
*
scsi
++
=
((
i_u
)
any
).
u
[
1
];
*
scsi
++
=
((
i_u
)
any
).
u
[
0
];
}
#define WAITnexttimeout 3000000
static
inline
int
scsi2int
(
unchar
*
scsi
)
{
i_u
result
;
result
.
i
=
0
;
/* clears unused bytes */
*
(
result
.
u
+
2
)
=
*
scsi
++
;
*
(
result
.
u
+
1
)
=
*
scsi
++
;
*
(
result
.
u
)
=
*
scsi
++
;
return
result
.
i
;
}
#else
/*
These are the old ones - I've just moved them here...
*/
#undef any2scsi
#define any2scsi(up, p) \
(up)[0] = (((unsigned long)(p)) >> 16); \
(up)[1] = ((unsigned long)(p)) >> 8; \
(up)[2] = ((unsigned long)(p));
#undef scsi2int
#define scsi2int(up) ( (((unsigned long)*(up)) << 16) + \
(((unsigned long)(up)[1]) << 8) + ((unsigned long)(up)[2]) )
#endif
static
inline
void
wd7000_enable_intr
(
void
)
static
inline
void
wd7000_enable_intr
(
Adapter
*
host
)
{
controlstat
|=
INT_EN
;
outb
(
controlstat
,
CONTROL
);
host
->
control
|=
INT_EN
;
outb
(
host
->
control
,
host
->
iobase
+
ASC_
CONTROL
);
}
static
inline
void
wd7000_enable_dma
(
void
)
static
inline
void
wd7000_enable_dma
(
Adapter
*
host
)
{
controlstat
|=
DMA_EN
;
outb
(
controlstat
,
CONTROL
);
set_dma_mode
(
DMA_CH
,
DMA_MODE_CASCADE
);
enable_dma
(
DMA_CH
);
host
->
control
|=
DMA_EN
;
outb
(
host
->
control
,
host
->
iobase
+
ASC_
CONTROL
);
set_dma_mode
(
host
->
dma
,
DMA_MODE_CASCADE
);
enable_dma
(
host
->
dma
);
}
#define WAITnexttimeout 200
/* 2 seconds */
#define WAIT(port, mask, allof, noneof) \
{ register
WAITbits;
\
register
WAITtimeout = WAITnexttimeout;
\
{ register
volatile unsigned WAITbits;
\
register
unsigned long WAITtimeout = jiffies + WAITnexttimeout;
\
while (1) { \
WAITbits = inb(port) & (mask); \
if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
break; \
if (
--WAITtimeout == 0) goto fail;
\
if (
jiffies > WAITtimeout) goto fail;
\
} \
}
static
inline
void
delay
(
unsigned
how_long
)
{
unsigned
long
time
=
jiffies
+
how_long
;
register
unsigned
long
time
=
jiffies
+
how_long
;
while
(
jiffies
<
time
);
}
static
inline
int
command_out
(
unchar
*
cmdp
,
int
len
)
static
inline
int
command_out
(
Adapter
*
host
,
unchar
*
cmd
,
int
len
)
{
if
(
len
==
1
)
{
while
(
1
==
1
){
WAIT
(
ASC_STAT
,
STATMASK
,
CMD_RDY
,
0
);
cli
();
if
(
!
(
inb
(
ASC_STAT
)
&
CMD_RDY
))
{
sti
();
continue
;}
outb
(
*
cmdp
,
COMMAND
);
sti
();
return
1
;
}
}
else
{
cli
();
WAIT
(
host
->
iobase
+
ASC_STAT
,
ASC_STATMASK
,
CMD_RDY
,
0
);
while
(
len
--
)
{
WAIT
(
ASC_STAT
,
STATMASK
,
CMD_RDY
,
0
);
outb
(
*
cmdp
++
,
COMMAND
);
do
{
outb
(
*
cmd
,
host
->
iobase
+
ASC_COMMAND
);
WAIT
(
host
->
iobase
+
ASC_STAT
,
ASC_STATMASK
,
CMD_RDY
,
0
);
}
while
(
inb
(
host
->
iobase
+
ASC_STAT
)
&
CMD_REJ
);
cmd
++
;
}
sti
();
}
return
1
;
fail:
sti
();
printk
(
"wd7000_out WAIT failed(%d): "
,
len
+
1
);
printk
(
"wd7000 command_out: WAIT failed(%d)
\n
"
,
len
+
1
);
return
0
;
}
static
inline
Scb
*
alloc_scb
(
void
)
/*
* This version of alloc_scbs is in preparation for supporting multiple
* commands per lun and command chaining, by queueing pending commands.
* We will need to allocate Scbs in blocks since they will wait to be
* executed so there is the possibility of deadlock otherwise.
* Also, to keep larger requests from being starved by smaller requests,
* we limit access to this routine with an internal busy flag, so that
* the satisfiability of a request is not dependent on the size of the
* request.
*/
static
inline
Scb
*
alloc_scbs
(
int
needed
)
{
Scb
*
scb
;
unsigned
long
flags
;
register
Scb
*
scb
,
*
p
;
register
unsigned
long
flags
;
register
unsigned
long
timeout
=
jiffies
+
WAITnexttimeout
;
register
unsigned
long
now
;
static
int
busy
=
0
;
int
i
;
if
(
needed
<=
0
)
return
NULL
;
/* sanity check */
save_flags
(
flags
);
cli
();
if
(
scbfree
==
NULL
)
{
panic
(
"wd7000: can't allocate free SCB.
\n
"
);
restore_flags
(
flags
);
return
NULL
;
while
(
busy
)
{
/* someone else is allocating */
sti
();
now
=
jiffies
;
while
(
jiffies
==
now
)
/* wait a jiffy */
;
cli
();
}
busy
=
1
;
/* not busy now; it's our turn */
while
(
freescbs
<
needed
)
{
timeout
=
jiffies
+
WAITnexttimeout
;
do
{
sti
();
now
=
jiffies
;
while
(
jiffies
==
now
)
/* wait a jiffy */
;
cli
();
}
while
(
freescbs
<
needed
&&
jiffies
<=
timeout
);
/*
* If we get here with enough free Scbs, we can take them.
* Otherwise, we timed out and didn't get enough.
*/
if
(
freescbs
<
needed
)
{
busy
=
0
;
panic
(
"wd7000: can't get enough free SCBs.
\n
"
);
restore_flags
(
flags
);
return
NULL
;
}
}
scb
=
scbfree
;
scbfree
=
scb
->
next
;
memset
(
scb
,
0
,
sizeof
(
Scb
));
scb
->
next
=
NULL
;
scb
=
scbfree
;
freescbs
-=
needed
;
for
(
i
=
0
;
i
<
needed
;
i
++
)
{
p
=
scbfree
;
scbfree
=
p
->
next
;
}
p
->
next
=
NULL
;
busy
=
0
;
/* we're done */
restore_flags
(
flags
);
...
...
@@ -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... */
return
0
;
/*
* Alternatively, we might issue the "interrupt on free OGMB",
* and sleep, but it must be ensured that it isn't the init
* task running. Instead, this version assumes that the caller
* will be persistent, and try again. Since it's the adapter
* that marks OGMB's free, waiting even with interrupts off
* should work, since they are freed very quickly in most cases.
*/
#ifdef DEBUG
printk
(
", no free OGMBs.
\n
"
);
#endif
return
0
;
}
start_cmd
=
START_OGMB
|
ogmb
;
wd7000_enable_intr
();
do
{
command_out
(
&
start_cmd
,
1
);
WAIT
(
ASC_STAT
,
STATMASK
,
CMD_RDY
,
0
);
}
while
(
inb
(
ASC_STAT
)
&
CMD_REJ
);
wd7000_enable_intr
(
host
);
DEB
(
printk
(
", awaiting interrupt.
\n
"
);)
start_ogmb
=
START_OGMB
|
ogmb
;
command_out
(
host
,
&
start_ogmb
,
1
);
#ifdef DEBUG
printk
(
", awaiting interrupt.
\n
"
);
#endif
return
1
;
fail:
DEB
(
printk
(
", WAIT timed out.
\n
"
);)
return
0
;
}
...
...
@@ -300,135 +789,159 @@ 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
(
"
\n
wd7000_intr_handle: phantom interrupt...
\n
"
);)
wd7000_intr_ack
();
return
;
}
/* check for an incoming mailbox */
if
((
flag
&
0x40
)
==
0
)
{
/* for a free OGMB - need code for this case... */
DEB
(
printk
(
"wd7000_intr_handle: free outgoing mailbox
\n
"
);)
wd7000_intr_ack
();
return
;
}
/* The interrupt is for an incoming mailbox */
icmb
=
flag
&
0x3f
;
scb
=
(
struct
scb
*
)
scsi2int
(
mb
.
icmb
[
icmb
].
scbptr
);
icmb_status
=
mb
.
icmb
[
icmb
].
status
;
mb
.
icmb
[
icmb
].
status
=
0
;
#ifdef 0
/*
* Use irqp as the parm, and the following declaration, if request_irq
* is used or if SA_INTERRUPT is not used.
*/
register
int
irq
=
*
(((
int
*
)
irqp
)
-
2
);
#endif
register
int
flag
,
icmb
,
errstatus
,
icmb_status
;
register
int
host_error
,
scsi_error
;
register
Scb
*
scb
;
/* for SCSI commands */
register
IcbAny
*
icb
;
/* for host commands */
register
Scsi_Cmnd
*
SCpnt
;
Adapter
*
host
=
irq2host
[
irq
];
/* This MUST be set!!! */
Mailbox
*
icmbs
=
host
->
mb
.
icmb
;
#ifdef DEBUG
printk
(
" ICMB %d posted for SCB/ICB %06x, status %02x, vue %02x"
,
icmb
,
scb
,
icmb_status
,
scb
->
vue
);
printk
(
"wd7000_intr_handle: irq = %d, host = %06x
\n
"
,
irq
,
host
);
#endif
if
(
!
(
scb
->
op
&
0x80
))
{
/* an SCB is done */
SCpnt
=
scb
->
SCpnt
;
if
(
--
(
SCpnt
->
SCp
.
phase
)
<=
0
)
{
/* all scbs for SCpnt are done */
host_error
=
scb
->
vue
|
(
icmb_status
<<
8
);
scsi_error
=
scb
->
status
;
errstatus
=
make_code
(
host_error
,
scsi_error
);
SCpnt
->
result
=
errstatus
;
flag
=
inb
(
host
->
iobase
+
ASC_INTR_STAT
);
#ifdef DEBUG
printk
(
"wd7000_intr_handle: intr stat = %02x
\n
"
,
flag
);
#endif
if
(
SCpnt
->
host_scribble
!=
NULL
)
scsi_free
(
SCpnt
->
host_scribble
,
WD7000_SCRIBBLE
);
free_scb
(
scb
);
if
(
!
(
inb
(
host
->
iobase
+
ASC_STAT
)
&
INT_IM
))
{
/* NB: these are _very_ possible if IRQ 15 is being used, since
it's the "garbage collector" on the 2nd 8259 PIC. Specifically,
any interrupt signal into the 8259 which can't be identified
comes out as 7 from the 8259, which is 15 to the host. Thus, it
is a good thing the WD7000 has an interrupt status port, so we
can sort these out. Otherwise, electrical noise and other such
problems would be indistinguishable from valid interrupts...
*/
#ifdef DEBUG
printk
(
"wd7000_intr_handle: phantom interrupt...
\n
"
);
#endif
wd7000_intr_ack
(
host
);
return
;
}
SCpnt
->
scsi_done
(
SCpnt
);
}
}
else
{
/* an ICB is done */
icb
=
(
unchar
*
)
scb
;
icb
[
ICB_STATUS
]
=
icmb_status
;
icb
[
ICB_PHASE
]
=
0
;
if
(
flag
&
MB_INTR
)
{
/* The interrupt is for a mailbox */
if
(
!
(
flag
&
IMB_INTR
))
{
#ifdef DEBUG
printk
(
"wd7000_intr_handle: free outgoing mailbox"
);
#endif
/*
* If sleep_on() and the "interrupt on free OGMB" command are
* used in mail_out(), wake_up() should correspondingly be called
* here. For now, we don't need to do anything special.
*/
wd7000_intr_ack
(
host
);
return
;
}
else
{
/* The interrupt is for an incoming mailbox */
icmb
=
flag
&
MB_MASK
;
icmb_status
=
icmbs
[
icmb
].
status
;
if
(
icmb_status
&
0x80
)
{
/* unsolicited - result in ICMB */
#ifdef DEBUG
printk
(
"wd7000_intr_handle: unsolicited interrupt %02xh
\n
"
,
icmb_status
);
#endif
wd7000_intr_ack
(
host
);
return
;
}
scb
=
(
struct
scb
*
)
scsi2int
((
unchar
*
)
icmbs
[
icmb
].
scbptr
);
icmbs
[
icmb
].
status
=
0
;
if
(
!
(
scb
->
op
&
ICB_OP_MASK
))
{
/* an SCB is done */
SCpnt
=
scb
->
SCpnt
;
if
(
--
(
SCpnt
->
SCp
.
phase
)
<=
0
)
{
/* all scbs are done */
host_error
=
scb
->
vue
|
(
icmb_status
<<
8
);
scsi_error
=
scb
->
status
;
errstatus
=
make_code
(
host_error
,
scsi_error
);
SCpnt
->
result
=
errstatus
;
free_scb
(
scb
);
SCpnt
->
scsi_done
(
SCpnt
);
}
}
else
{
/* an ICB is done */
icb
=
(
IcbAny
*
)
scb
;
icb
->
status
=
icmb_status
;
icb
->
phase
=
0
;
}
}
/* incoming mailbox */
}
wd7000_intr_ack
();
DEB
(
printk
(
".
\n
"
);)
wd7000_intr_ack
(
host
);
return
;
}
int
wd7000_queuecommand
(
Scsi_Cmnd
*
SCpnt
,
void
(
*
done
)(
Scsi_Cmnd
*
))
{
Scb
*
scb
;
Sgb
*
sgb
;
unchar
*
cdb
;
unchar
idlun
;
short
cdblen
;
cdb
=
(
unchar
*
)
SCpnt
->
cmnd
;
cdblen
=
COMMAND_SIZE
(
*
cdb
);
register
Scb
*
scb
;
register
Sgb
*
sgb
;
register
unchar
*
cdb
=
(
unchar
*
)
SCpnt
->
cmnd
;
register
unchar
idlun
;
register
short
cdblen
;
Adapter
*
host
=
(
Adapter
*
)
SCpnt
->
host
->
hostdata
;
cdblen
=
COMMAND_SIZE
(
cdb
[
0
]
);
idlun
=
((
SCpnt
->
target
<<
5
)
&
0xe0
)
|
(
SCpnt
->
lun
&
7
);
SCpnt
->
scsi_done
=
done
;
SCpnt
->
SCp
.
phase
=
1
;
scb
=
alloc_scb
(
);
scb
=
alloc_scb
s
(
1
);
scb
->
idlun
=
idlun
;
memcpy
(
scb
->
cdb
,
cdb
,
cdblen
);
scb
->
direc
=
0x40
;
/* Disable direction check */
scb
->
SCpnt
=
SCpnt
;
/* so we can find stuff later */
SCpnt
->
host_scribble
=
NULL
;
DEB
(
printk
(
"request_bufflen is %x, bufflen is %x
\n
"
,
\
SCpnt
->
request_bufflen
,
SCpnt
->
bufflen
);)
SCpnt
->
host_scribble
=
(
unchar
*
)
scb
;
scb
->
host
=
host
;
if
(
SCpnt
->
use_sg
)
{
struct
scatterlist
*
sg
=
(
struct
scatterlist
*
)
SCpnt
->
request_buffer
;
unsigned
i
;
if
(
scsi_hosts
[
wd7000_host
].
sg_tablesize
<=
0
)
{
if
(
SCpnt
->
host
->
sg_tablesize
==
SG_NONE
)
{
panic
(
"wd7000_queuecommand: scatter/gather not supported.
\n
"
);
}
#ifdef DEBUG
printk
(
"Using scatter/gather with %d elements.
\n
"
,
SCpnt
->
use_sg
);
#endif
/*
Allocate memory for a scatter/gather-list in wd7000 format.
Save the pointer at host_scribble.
*/
#ifdef DEBUG
if
(
SCpnt
->
use_sg
>
WD7000_SG
)
panic
(
"WD7000: requesting too many scatterblocks
\n
"
);
#endif
SCpnt
->
host_scribble
=
(
unsigned
char
*
)
scsi_malloc
(
WD7000_SCRIBBLE
);
sgb
=
(
Sgb
*
)
SCpnt
->
host_scribble
;
if
(
sgb
==
NULL
)
panic
(
"wd7000_queuecommand: scsi_malloc() failed.
\n
"
);
sgb
=
scb
->
sgb
;
scb
->
op
=
1
;
any2scsi
(
scb
->
dataptr
,
sgb
);
any2scsi
(
scb
->
dataptr
,
(
int
)
sgb
);
any2scsi
(
scb
->
maxlen
,
SCpnt
->
use_sg
*
sizeof
(
Sgb
)
);
for
(
i
=
0
;
i
<
SCpnt
->
use_sg
;
i
++
)
{
any2scsi
(
sgb
->
ptr
,
sg
[
i
].
address
);
any2scsi
(
sgb
->
len
,
sg
[
i
].
length
);
sgb
++
;
any2scsi
(
sgb
[
i
].
ptr
,
(
int
)
sg
[
i
].
address
);
any2scsi
(
sgb
[
i
].
len
,
sg
[
i
].
length
);
}
DEB
(
printk
(
"Using %d bytes for %d scatter/gather blocks
\n
"
,
\
scsi2int
(
scb
->
maxlen
),
SCpnt
->
use_sg
);)
}
else
{
scb
->
op
=
0
;
any2scsi
(
scb
->
dataptr
,
SCpnt
->
request_buffer
);
any2scsi
(
scb
->
dataptr
,
(
int
)
SCpnt
->
request_buffer
);
any2scsi
(
scb
->
maxlen
,
SCpnt
->
request_bufflen
);
}
while
(
!
mail_out
(
host
,
scb
))
/* keep trying */
;
return
mail_out
(
scb
)
;
return
1
;
}
...
...
@@ -442,192 +955,292 @@ int wd7000_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
"
);
return
0
;
outb
(
ASC_RES
,
host
->
iobase
+
ASC_CONTROL
);
delay
(
1
);
/* reset pulse: this is 10ms, only need 25us */
outb
(
0
,
host
->
iobase
+
ASC_CONTROL
);
host
->
control
=
0
;
/* this must always shadow ASC_CONTROL */
WAIT
(
host
->
iobase
+
ASC_STAT
,
ASC_STATMASK
,
CMD_RDY
,
0
);
if
((
diag
=
inb
(
host
->
iobase
+
ASC_INTR_STAT
))
!=
1
)
{
printk
(
"wd7000_init: "
);
switch
(
diag
)
{
case
2
:
printk
(
"RAM failure.
\n
"
);
break
;
case
3
:
printk
(
"FIFO R/W failed
\n
"
);
break
;
case
4
:
printk
(
"SBIC register R/W failed
\n
"
);
break
;
case
5
:
printk
(
"Initialization D-FF failed.
\n
"
);
break
;
case
6
:
printk
(
"Host IRQ D-FF failed.
\n
"
);
break
;
case
7
:
printk
(
"ROM checksum error.
\n
"
);
break
;
default:
printk
(
"diagnostic code %02Xh received.
\n
"
,
diag
);
break
;
}
return
0
;
}
/* Clear mailboxes */
memset
(
&
mb
,
0
,
sizeof
(
mb
));
/* Set up SCB free list */
init_scbs
();
memset
(
&
(
host
->
mb
),
0
,
sizeof
(
host
->
mb
));
/* Set up init block */
any2scsi
(
init_block
+
5
,
&
mb
);
/* Execute init command */
if
(
!
command_out
(
init_block
,
sizeof
(
init_block
)))
{
panic
(
"WD-7000 Initialization failed.
\n
"
);
any2scsi
((
unchar
*
)
&
(
init_cmd
.
mailboxes
),
(
int
)
&
(
host
->
mb
));
if
(
!
command_out
(
host
,
(
unchar
*
)
&
init_cmd
,
sizeof
(
init_cmd
)))
{
printk
(
"wd7000_init: adapter initialization failed.
\n
"
);
return
0
;
}
/* Wait until init finished */
WAIT
(
ASC_STAT
,
STATMASK
,
CMD_RDY
|
ASC_INI
,
0
);
outb
(
DISABLE_UNS_INTR
,
COMMAND
);
WAIT
(
ASC_STAT
,
STATMASK
,
CMD_RDY
|
ASC_INI
,
0
);
/* Enable Interrupt and DMA */
if
(
request_irq
(
IRQ_LVL
,
wd7000_intr_handle
))
{
panic
(
"Unable to allocate IRQ for WD-7000.
\n
"
);
return
0
;
};
if
(
request_dma
(
DMA_CH
))
{
panic
(
"Unable to allocate DMA channel for WD-7000.
\n
"
);
free_irq
(
IRQ_LVL
);
return
0
;
};
wd7000_enable_dma
();
wd7000_enable_intr
();
WAIT
(
host
->
iobase
+
ASC_STAT
,
ASC_STATMASK
,
ASC_INIT
,
0
);
if
(
irqaction
(
host
->
irq
,
&
sa
))
{
printk
(
"wd7000_init: can't get IRQ %d.
\n
"
,
host
->
irq
);
return
0
;
}
if
(
request_dma
(
host
->
dma
))
{
printk
(
"wd7000_init: can't get DMA channel %d.
\n
"
,
host
->
dma
);
free_irq
(
host
->
irq
);
return
0
;
}
wd7000_enable_dma
(
host
);
wd7000_enable_intr
(
host
);
if
(
!
wd7000_diagnostics
(
host
,
ICB_DIAG_FULL
))
{
free_dma
(
host
->
dma
);
free_irq
(
host
->
irq
);
return
0
;
}
printk
(
"WD-7000 initialized.
\n
"
);
return
1
;
fail:
printk
(
"wd7000_init: WAIT timed out.
\n
"
);
return
0
;
/* 0 = not ok */
}
void
wd7000_revision
(
void
)
void
wd7000_revision
(
Adapter
*
host
)
{
volatile
unchar
icb
[
ICB_LEN
]
=
{
0x8c
};
/* read firmware revision level */
icb
[
ICB_PHASE
]
=
1
;
mail_out
(
(
struct
scb
*
)
icb
);
while
(
icb
[
ICB_PHASE
])
/* wait for completion */
;
rev_1
=
icb
[
1
];
rev_2
=
icb
[
2
];
static
IcbRevLvl
icb
=
{
ICB_OP_GET_REVISION
};
icb
.
phase
=
1
;
/*
For boards at rev 7.0 or later, enable scatter/gather.
*/
if
(
rev_1
>=
7
)
scsi_hosts
[
wd7000_host
].
sg_tablesize
=
WD7000_SG
;
* Like diagnostics, this is only done at init time, in fact, from
* wd7000_detect, so there should be OGMBs available. If it fails,
* the only damage will be that the revision will show up as 0.0,
* which in turn means that scatter/gather will be disabled.
*/
mail_out
(
host
,
(
struct
scb
*
)
&
icb
);
while
(
icb
.
phase
)
/* wait for completion */
;
host
->
rev1
=
icb
.
primary
;
host
->
rev2
=
icb
.
secondary
;
}
static
const
char
*
wd_bases
[]
=
{(
char
*
)
0xce000
,(
char
*
)
0xd8000
};
typedef
struct
{
char
*
signature
;
unsigned
offset
;
unsigned
length
;
}
Signature
;
static
const
Signature
signatures
[]
=
{{
"SSTBIOS"
,
0xd
,
0x7
}};
#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
int
wd7000_detect
(
int
hostnum
)
/*
* return non-zero on detection
* Returns the number of adapters this driver is supporting.
*
* The source for hosts.c says to wait to call scsi_register until 100%
* sure about an adapter. We need to do it a little sooner here; we
* need the storage set up by scsi_register before wd7000_init, and
* changing the location of an Adapter structure is more trouble than
* calling scsi_unregister.
*
*/
{
int
i
,
j
;
char
const
*
base_address
=
NULL
;
if
(
check_region
(
IO_BASE
,
4
))
return
0
;
/* IO ports in use */
for
(
i
=
0
;
i
<
(
sizeof
(
wd_bases
)
/
sizeof
(
char
*
));
i
++
){
for
(
j
=
0
;
j
<
NUM_SIGNATURES
;
j
++
){
if
(
!
memcmp
((
void
*
)(
wd_bases
[
i
]
+
signatures
[
j
].
offset
),
(
void
*
)
signatures
[
j
].
signature
,
signatures
[
j
].
length
)){
base_address
=
wd_bases
[
i
];
printk
(
"WD-7000 detected.
\n
"
);
}
}
}
if
(
base_address
==
NULL
)
return
0
;
int
i
,
j
,
present
=
0
;
const
Config
*
cfg
;
const
Signature
*
sig
;
Adapter
*
host
=
NULL
;
struct
Scsi_Host
*
sh
;
snarf_region
(
IO_BASE
,
4
);
/* Register our ports */
/* Store our host number */
wd7000_host
=
hostnum
;
/* Set up SCB free list, which is shared by all adapters */
init_scbs
();
wd7000_init
();
wd7000_revision
();
/* will set scatter/gather by rev level */
cfg
=
configs
;
for
(
i
=
0
;
i
<
NUM_CONFIGS
;
i
++
)
{
sig
=
signatures
;
for
(
j
=
0
;
j
<
NUM_SIGNATURES
;
j
++
)
{
if
(
!
memcmp
(
cfg
->
bios
+
sig
->
ofs
,
sig
->
sig
,
sig
->
len
))
{
/* matched this one */
#ifdef DEBUG
printk
(
"WD-7000 SST BIOS detected at %04X: checking...
\n
"
,
(
int
)
cfg
->
bios
);
#endif
/*
* We won't explicitly test the configuration (in this
* version); instead, we'll just see if it works to
* setup the adapter; if it does, we'll use it.
*/
if
(
check_region
(
cfg
->
iobase
,
4
))
{
/* ports in use */
printk
(
"IO %xh already in use.
\n
"
,
host
->
iobase
);
continue
;
}
/*
* We register here, to get a pointer to the extra space,
* which we'll use as the Adapter structure (host) for
* this adapter. It is located just after the registered
* Scsi_Host structure (sh), and is located by the empty
* array hostdata.
*/
sh
=
scsi_register
(
hostnum
,
sizeof
(
Adapter
)
);
host
=
(
Adapter
*
)
sh
->
hostdata
;
#ifdef DEBUG
printk
(
"wd7000_detect: adapter allocated at %06x
\n
"
,
(
int
)
host
);
#endif
memset
(
host
,
0
,
sizeof
(
Adapter
)
);
host
->
num
=
hostnum
;
host
->
sh
=
sh
;
host
->
irq
=
cfg
->
irq
;
host
->
iobase
=
cfg
->
iobase
;
host
->
dma
=
cfg
->
dma
;
irq2host
[
host
->
irq
]
=
host
;
if
(
!
wd7000_init
(
host
))
{
/* Initialization failed */
scsi_unregister
(
sh
,
sizeof
(
Adapter
)
);
continue
;
}
/*
* OK from here - we'll use this adapter/configuration.
*/
wd7000_revision
(
host
);
/* important for scatter/gather */
printk
(
"Western Digital WD-7000 (%d.%d) "
,
host
->
rev1
,
host
->
rev2
);
printk
(
"using IO %xh IRQ %d DMA %d.
\n
"
,
host
->
iobase
,
host
->
irq
,
host
->
dma
);
snarf_region
(
host
->
iobase
,
4
);
/* Register our ports */
/*
* For boards before rev 6.0, scatter/gather isn't supported.
*/
if
(
host
->
rev1
<
6
)
sh
->
sg_tablesize
=
SG_NONE
;
present
++
;
/* count it */
break
;
/* don't try any more sigs */
}
sig
++
;
/* try next signature with this configuration */
}
cfg
++
;
/* try next configuration */
}
return
1
;
return
present
;
}
static
void
wd7000_append_info
(
char
*
info
,
const
char
*
fmt
,
...
)
/*
*
This is just so I can use vsprintf
...
*
I have absolutely NO idea how to do an abort with the WD7000
...
*/
{
va_list
args
;
extern
int
vsprintf
(
char
*
buf
,
const
char
*
fmt
,
va_list
args
);
va_start
(
args
,
fmt
);
vsprintf
(
info
,
fmt
,
args
);
va_end
(
args
);
return
;
}
const
char
*
wd7000_info
(
void
)
{
static
char
info
[
80
]
=
"Western Digital WD-7000, Firmware Revision "
;
wd7000_revision
();
wd7000_append_info
(
info
+
strlen
(
info
),
"%d.%d.
\n
"
,
rev_1
,
rev_2
);
return
info
;
}
int
wd7000_abort
(
Scsi_Cmnd
*
SCpnt
,
int
i
)
{
#ifdef DEBUG
printk
(
"wd7000_abort: Scsi_Cmnd = 0x%0
8x, code = %d "
,
SCpnt
,
i
);
printk
(
"wd7000_abort: Scsi_Cmnd = 0x%0
6x, code = %d "
,
(
int
)
SCpnt
,
i
);
printk
(
"id %d lun %d cdb"
,
SCpnt
->
target
,
SCpnt
->
lun
);
{
int
j
;
unchar
*
cdbj
=
(
unchar
*
)
SCpnt
->
cmnd
;
for
(
j
=
0
;
j
<
COMMAND_SIZE
(
*
cdbj
);
j
++
)
printk
(
" %02x"
,
*
(
cdbj
++
));
printk
(
" result %08x
\n
"
,
SCpnt
->
result
);
{
int
j
;
unchar
*
cdbj
=
(
unchar
*
)
SCpnt
->
cmnd
;
for
(
j
=
0
;
j
<
COMMAND_SIZE
(
*
cdbj
);
j
++
)
printk
(
" %02x"
,
*
(
cdbj
++
));
printk
(
" result %08x
\n
"
,
SCpnt
->
result
);
}
#endif
return
0
;
}
/* We do not implement a reset function here, but the upper level code assumes
that it will get some kind of response for the command in SCpnt. We must
oblige, or the command will hang the scsi system */
/*
* I also have no idea how to do a reset...
*/
int
wd7000_reset
(
Scsi_Cmnd
*
SCpnt
)
{
#ifdef DEBUG
printk
(
"wd7000_reset
\n
"
);
printk
(
"wd7000_reset: Scsi_Cmnd = 0x%06x "
,
(
int
)
SCpnt
);
if
(
SCpnt
)
{
printk
(
"id %d lun %d cdb"
,
SCpnt
->
target
,
SCpnt
->
lun
);
{
int
j
;
unchar
*
cdbj
=
(
unchar
*
)
SCpnt
->
cmnd
;
for
(
j
=
0
;
j
<
COMMAND_SIZE
(
*
cdbj
);
j
++
)
printk
(
" %02x"
,
*
(
cdbj
++
));
printk
(
" result %08x"
,
SCpnt
->
result
);
}
}
printk
(
"
\n
"
);
#endif
if
(
SCpnt
)
SCpnt
->
flags
|=
NEEDS_JUMPSTART
;
return
0
;
}
int
wd7000_biosparam
(
int
size
,
int
dev
,
int
*
ip
)
/*
* This is borrowed directly from aha1542.c, but my disks are organized
* this way, so I think it will work OK.
* The info routine in the WD7000 structure isn't per-adapter, so it can't
* really return any useful information about an adapter. Because of this,
* I'm no longer using it to return rev. level.
*/
const
char
*
wd7000_info
(
void
)
{
static
char
info
[]
=
"Western Digital WD-7000"
;
return
info
;
}
/*
* This was borrowed directly from aha1542.c, but my disks are organized
* this way, so I think it will work OK. Someone who is ambitious can
* borrow a newer or more complete version from another driver.
*/
int
wd7000_biosparam
(
int
size
,
int
dev
,
int
*
ip
)
{
ip
[
0
]
=
64
;
ip
[
1
]
=
32
;
...
...
@@ -635,4 +1248,3 @@ int wd7000_biosparam(int size, int dev, int* ip)
/* if (ip[2] >= 1024) ip[2] = 1024; */
return
0
;
}
drivers/scsi/wd7000.h
View file @
16119ae5
...
...
@@ -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
fs/buffer.c
View file @
16119ae5
...
...
@@ -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
);
...
...
fs/proc/net.c
View file @
16119ae5
...
...
@@ -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
,{
13
5
,
9
,
"ipx_route"
},
{
13
6
,
3
,
"ipx"
}
,{
13
6
,
9
,
"ipx_route"
},
{
13
7
,
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
13
5
:
case
13
6
:
length
=
ipx_rt_get_info
(
page
,
&
start
,
file
->
f_pos
,
thistime
);
break
;
case
13
6
:
case
13
7
:
length
=
ipx_get_info
(
page
,
&
start
,
file
->
f_pos
,
thistime
);
break
;
#endif
/* CONFIG_IPX */
...
...
include/linux/if_arp.h
View file @
16119ae5
...
...
@@ -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 */
...
...
include/linux/if_slip.h
0 → 100644
View file @
16119ae5
/*
* 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
include/linux/ip.h
View file @
16119ae5
...
...
@@ -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
;
...
...
include/linux/mtio.h
View file @
16119ae5
...
...
@@ -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 */
include/linux/netdevice.h
View file @
16119ae5
...
...
@@ -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
);
};
...
...
include/linux/sockios.h
View file @
16119ae5
...
...
@@ -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 */
include/linux/tcp.h
View file @
16119ae5
...
...
@@ -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 */
init/main.c
View file @
16119ae5
...
...
@@ -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
...
...
kernel/sys.c
View file @
16119ae5
...
...
@@ -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
;
}
...
...
kernel/vm86.c
View file @
16119ae5
...
...
@@ -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
)
...
...
net/inet/arp.c
View file @
16119ae5
...
...
@@ -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,166 +465,249 @@ void arp_destroy(unsigned long ip_addr, int force)
int
arp_rcv
(
struct
sk_buff
*
skb
,
struct
device
*
dev
,
struct
packet_type
*
pt
)
{
/*
* We shouldn't use this type conversion. Check later.
*/
/*
* We shouldn't use this type conversion. Check later.
*/
struct
arphdr
*
arp
=
(
struct
arphdr
*
)
skb
->
h
.
raw
;
unsigned
char
*
arp_ptr
=
(
unsigned
char
*
)(
arp
+
1
);
struct
arp_table
*
entry
;
struct
arp_table
*
proxy_entry
;
int
addr_hint
;
unsigned
long
hash
;
int
addr_hint
,
hlen
,
htype
;
unsigned
long
hash
,
dest_hash
;
unsigned
char
ha
[
MAX_ADDR_LEN
];
/* So we can enable ints again. */
long
sip
,
tip
;
unsigned
char
*
sha
,
*
tha
;
/*
* If this test doesn't pass, its not IP, or we should ignore it anyway
*/
if
(
arp
->
ar_hln
!=
dev
->
addr_len
||
dev
->
type
!=
ntohs
(
arp
->
ar_hrd
)
||
dev
->
flags
&
IFF_NOARP
)
/*
* The hardware length of the packet should match the hardware length
* of the device. Similarly, the hardware types should match. The
* device should be ARP-able. Also, if pln is not 4, then the lookup
* is not from an IP number. We can't currently handle this, so toss
* it.
*/
if
(
arp
->
ar_hln
!=
dev
->
addr_len
||
dev
->
type
!=
ntohs
(
arp
->
ar_hrd
)
||
dev
->
flags
&
IFF_NOARP
||
arp
->
ar_pln
!=
4
)
{
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
/*
* For now we will only deal with IP addresses.
*/
if
(
/*
* Another test.
* The logic here is that the protocol being looked up by arp should
* match the protocol the device speaks. If it doesn't, there is a
* problem, so toss the packet.
*/
switch
(
dev
->
type
)
{
#ifdef CONFIG_AX25
(
arp
->
ar_pro
!=
htons
(
AX25_P_IP
)
&&
dev
->
type
==
ARPHRD_AX25
)
||
case
ARPHRD_AX25
:
if
(
arp
->
ar_pro
!=
htons
(
AX25_P_IP
))
{
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
break
;
#endif
(
arp
->
ar_pro
!=
htons
(
ETH_P_IP
)
&&
dev
->
type
!=
ARPHRD_AX25
)
||
arp
->
ar_pln
!=
4
)
{
/* This packet is not for us. Remove it. */
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
case
ARPHRD_ETHER
:
if
(
arp
->
ar_pro
!=
htons
(
ETH_P_IP
))
{
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
break
;
default:
printk
(
"ARP: dev->type mangled!
\n
"
);
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
/*
* Extract variable width fields
*/
/*
* Extract fields
*/
hlen
=
dev
->
addr_len
;
htype
=
dev
->
type
;
sha
=
arp_ptr
;
arp_ptr
+=
dev
->
addr_
len
;
arp_ptr
+=
h
len
;
memcpy
(
&
sip
,
arp_ptr
,
4
);
arp_ptr
+=
4
;
tha
=
arp_ptr
;
arp_ptr
+=
dev
->
addr_
len
;
arp_ptr
+=
h
len
;
memcpy
(
&
tip
,
arp_ptr
,
4
);
/*
* Check for bad requests for 127.0.0.1. If this is one such, delete it.
*/
if
(
tip
==
INADDR_LOOPBACK
)
{
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
/*
* Process entry
*/
/*
* Process entry. The idea here is we want to send a reply if it is a
* request for us or if it is a request for someone else that we hold
* a proxy for. We want to add an entry to our cache if it is a reply
* to us or if it is a request for our address.
* (The assumption for this last is that if someone is requesting our
* address, they are probably intending to talk to us, so it saves time
* if we cache their address. Their address is also probably not in
* our cache, since ours is not in their cache.)
*
* Putting this another way, we only care about replies if they are to
* us, in which case we add them to the cache. For requests, we care
* about those for us and those for our proxies. We reply to both,
* and in the case of requests for us we add the requester to the arp
* cache.
*/
addr_hint
=
ip_chk_addr
(
tip
);
hash
=
HASH
(
sip
);
proxy_entry
=
NULL
;
if
(
proxies
!=
0
&&
addr_hint
!=
IS_MYADDR
)
if
(
arp
->
ar_op
==
htons
(
ARPOP_REPLY
))
{
unsigned
long
dest_hash
=
HASH
(
tip
);
cli
();
proxy_entry
=
arp_tables
[
dest_hash
];
while
(
proxy_entry
!=
NULL
)
if
(
addr_hint
!=
IS_MYADDR
)
{
if
(
proxy_entry
->
ip
==
tip
&&
proxy_entry
->
htype
==
arp
->
ar_hrd
)
break
;
proxy_entry
=
proxy_entry
->
next
;
/*
* Replies to other machines get tossed.
*/
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
if
(
proxy_entry
&&
(
proxy_entry
->
flags
&
ATF_PUBL
))
memcpy
(
ha
,
proxy_entry
->
ha
,
dev
->
addr_len
);
else
proxy_entry
=
NULL
;
/*
* Fall through to code below that adds sender to cache.
*/
}
else
cli
();
{
/*
* It is now an arp request
*/
if
(
addr_hint
!=
IS_MYADDR
)
{
/*
* To get in here, it is a request for someone else. We need to
* check if that someone else is one of our proxies. If it isn't,
* we can toss it.
*/
if
(
proxies
==
0
)
{
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
dest_hash
=
HASH
(
tip
);
cli
();
for
(
proxy_entry
=
arp_tables
[
dest_hash
];
proxy_entry
;
proxy_entry
=
proxy_entry
->
next
)
{
if
(
proxy_entry
->
ip
==
tip
&&
proxy_entry
->
htype
==
htype
)
break
;
}
if
(
proxy_entry
&&
(
proxy_entry
->
flags
&
ATF_PUBL
))
{
memcpy
(
ha
,
proxy_entry
->
ha
,
hlen
);
sti
();
arp_send
(
ARPOP_REPLY
,
ETH_P_ARP
,
sip
,
dev
,
tip
,
sha
,
ha
);
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
else
{
sti
();
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
}
else
{
/*
* To get here, it must be an arp request for us. We need to reply.
*/
arp_send
(
ARPOP_REPLY
,
ETH_P_ARP
,
sip
,
dev
,
tip
,
sha
,
dev
->
dev_addr
);
}
}
for
(
entry
=
arp_tables
[
hash
];
entry
!=
NULL
;
entry
=
entry
->
next
)
if
(
entry
->
ip
==
sip
)
/*
* Now all replies are handled. Next, anything that falls through to here
* needs to be added to the arp cache, or have its entry updated if it is
* there.
*/
hash
=
HASH
(
sip
);
cli
();
for
(
entry
=
arp_tables
[
hash
];
entry
;
entry
=
entry
->
next
)
if
(
entry
->
ip
==
sip
&&
entry
->
htype
==
htype
)
break
;
if
(
entry
!=
NULL
)
if
(
entry
)
{
int
old_flags
=
entry
->
flags
;
memcpy
(
entry
->
ha
,
sha
,
arp
->
ar_hln
);
entry
->
hlen
=
arp
->
ar_hln
;
/* This seems sensible but not everyone gets it right ! */
entry
->
htype
=
ntohs
(
arp
->
ar_hrd
);
if
(
entry
->
htype
==
0
)
entry
->
htype
=
dev
->
type
;
/* Not good but we have no choice */
/*
* Entry found; update it.
*/
memcpy
(
entry
->
ha
,
sha
,
hlen
);
entry
->
hlen
=
hlen
;
entry
->
last_used
=
jiffies
;
if
(
!
(
entry
->
flags
&
ATF_COM
))
{
/*
* This entry was incomplete. Delete the retransmit timer
* and switch to complete status.
*/
del_timer
(
&
entry
->
timer
);
entry
->
flags
|=
ATF_COM
;
}
sti
();
if
(
!
(
old_flags
&
ATF_COM
))
{
/* Send out waiting packets. We might have problems,
if someone is manually removing entries right now.
I will fix this one. */
sti
();
/*
* Send out waiting packets. We might have problems, if someone is
* manually removing entries right now -- entry might become invalid
* underneath us.
*/
arp_send_q
(
entry
,
sha
);
}
if
(
addr_hint
!=
IS_MYADDR
&&
proxy_entry
==
NULL
)
else
{
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
sti
();
}
}
else
{
if
(
addr_hint
!=
IS_MYADDR
&&
proxy_entry
==
NULL
)
{
/* We don't do "smart arp" and cache all possible
entries. That just makes us more work. */
sti
();
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
entry
=
(
struct
arp_table
*
)
kmalloc
(
sizeof
(
struct
arp_table
),
GFP_ATOMIC
);
if
(
entry
==
NULL
)
/*
* No entry found. Need to add a new entry to the arp table.
*/
entry
=
(
struct
arp_table
*
)
kmalloc
(
sizeof
(
struct
arp_table
),
GFP_ATOMIC
);
if
(
entry
==
NULL
)
{
sti
();
kfree_skb
(
skb
,
FREE_READ
);
printk
(
"ARP: no memory for new arp entry
\n
"
);
kfree_skb
(
skb
,
FREE_READ
);
return
0
;
}
entry
->
ip
=
sip
;
entry
->
hlen
=
arp
->
ar_hl
n
;
entry
->
htype
=
arp
->
ar_hrd
;
entry
->
hlen
=
hle
n
;
entry
->
htype
=
htype
;
entry
->
flags
=
ATF_COM
;
memcpy
(
entry
->
ha
,
sha
,
arp
->
ar_hl
n
);
memcpy
(
entry
->
ha
,
sha
,
hle
n
);
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
;
entry
=
entry
->
next
)
for
(
entry
=
arp_tables
[
i
];
entry
!=
NULL
;
entry
=
entry
->
next
)
{
memset
(
req
,
0
,
sizeof
(
struct
arpreq
));
req
->
arp_pa
.
sa_family
=
AF_INET
;
memcpy
(
req
->
arp_pa
.
sa_data
,
&
entry
->
ip
,
4
);
req
->
arp_ha
.
sa_family
=
entry
->
htype
;
memcpy
(
req
->
arp_ha
.
sa_data
,
&
entry
->
ha
,
MAX_ADDR_LEN
);
req
->
arp_flags
=
entry
->
flags
;
req
++
;
len
+=
sizeof
(
struct
arpreq
);
pos
+=
sizeof
(
struct
arpreq
);
/*
* Convert hardware address to XX:XX:XX:XX ... form.
*/
for
(
k
=
0
,
j
=
0
;
k
<
HBUFFERLEN
-
3
&&
j
<
entry
->
hlen
;
j
++
)
{
hbuffer
[
k
++
]
=
hexbuf
[
(
entry
->
ha
[
j
]
>>
4
)
&
15
];
hbuffer
[
k
++
]
=
hexbuf
[
entry
->
ha
[
j
]
&
15
];
hbuffer
[
k
++
]
=
':'
;
}
hbuffer
[
--
k
]
=
0
;
size
=
sprintf
(
buffer
+
len
,
"%-17s0x%-10x0x%-10x%s
\n
"
,
in_ntoa
(
entry
->
ip
),
(
unsigned
int
)
entry
->
htype
,
entry
->
flags
,
hbuffer
);
len
+=
size
;
pos
=
begin
+
len
;
if
(
pos
<
offset
)
{
len
=
0
;
begin
=
pos
;
req
=
(
struct
arpreq
*
)
buffer
;
}
if
(
pos
>
offset
+
length
)
break
;
}
if
(
pos
>
offset
+
length
)
break
;
}
sti
();
*
start
=
buffer
+
(
offset
-
begin
);
len
-=
(
offset
-
begin
);
*
start
=
buffer
+
(
offset
-
begin
);
/* Start of wanted data */
len
-=
(
offset
-
begin
);
/* Start slop */
if
(
len
>
length
)
len
=
length
;
len
=
length
;
/* Ending slop */
return
len
;
}
...
...
net/inet/arp.h
View file @
16119ae5
...
...
@@ -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 */
net/inet/ip.c
View file @
16119ae5
...
...
@@ -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
;
...
...
@@ -1549,7 +1549,7 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
ip_statistics
.
IpInReceives
++
;
DPRINTF
((
DBG_IP
,
"<<
\n
"
));
/*
* Tag the ip header of this packet so we can find it
*/
...
...
net/inet/rarp.c
0 → 100644
View file @
16119ae5
/* 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
net/inet/rarp.h
0 → 100644
View file @
16119ae5
/* 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 */
net/inet/sock.c
View file @
16119ae5
...
...
@@ -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.01
0
\n
"
);
printk
(
"Swansea University Computer Society NET3.01
2
\n
"
);
/*
* Set up our UNIX VFS major device. (compatibility)
*/
...
...
net/inet/tcp.c
View file @
16119ae5
...
...
@@ -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,22 +1548,36 @@ void tcp_shutdown(struct sock *sk, int how)
* We need to grab some memory, and put together a FIN,
* and then put it into the queue to be sent.
* FIXME:
*
* Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
* Most of this is guesswork, so maybe it will work...
*/
if
(
!
(
how
&
SEND_SHUTDOWN
))
return
;
/*
* If we've already sent a FIN, return.
*/
if
(
sk
->
state
==
TCP_FIN_WAIT1
||
sk
->
state
==
TCP_FIN_WAIT2
)
return
;
if
(
!
(
how
&
SEND_SHUTDOWN
))
if
(
sk
->
state
==
TCP_FIN_WAIT1
||
sk
->
state
==
TCP_FIN_WAIT2
||
sk
->
state
==
TCP_CLOSING
||
sk
->
state
==
TCP_LAST_ACK
||
sk
->
state
==
TCP_TIME_WAIT
)
{
return
;
}
sk
->
inuse
=
1
;
/*
* Clear out any half completed packets.
* flag that the sender has shutdown
*/
sk
->
shutdown
|=
SEND_SHUTDOWN
;
/*
* Clear out any half completed packets.
*/
if
(
sk
->
partial
)
...
...
@@ -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,7 +1669,9 @@ void tcp_shutdown(struct sock *sk, int how)
if
(
sk
->
state
==
TCP_ESTABLISHED
)
sk
->
state
=
TCP_FIN_WAIT1
;
else
else
if
(
sk
->
state
==
TCP_CLOSE_WAIT
)
sk
->
state
=
TCP_LAST_ACK
;
else
sk
->
state
=
TCP_FIN_WAIT2
;
release_sock
(
sk
);
...
...
@@ -1934,7 +1995,7 @@ 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
{
sk
->
state
=
TCP_FIN_WAIT1
;
}
/*
* If established (normal close), enter FIN_WAIT1.
* If in CLOSE_WAIT, enter LAST_ACK
* If in CLOSING, remain in CLOSING
* otherwise enter FIN_WAIT2
*/
if
(
sk
->
state
==
TCP_ESTABLISHED
)
sk
->
state
=
TCP_FIN_WAIT1
;
else
if
(
sk
->
state
==
TCP_CLOSE_WAIT
)
sk
->
state
=
TCP_LAST_ACK
;
else
if
(
sk
->
state
!=
TCP_CLOSING
)
sk
->
state
=
TCP_FIN_WAIT2
;
}
release_sock
(
sk
);
}
...
...
@@ -2276,7 +2364,10 @@ 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
);
}
...
...
@@ -2920,15 +3049,27 @@ static inline int tcp_urg(struct sock *sk, struct tcphdr *th,
/*
* This deals with incoming fins. 'Linus at 9 O'clock' 8-)
* This deals with incoming fins. 'Linus at 9 O'clock' 8-)
*
* If we are ESTABLISHED, a received fin moves us to CLOSE-WAIT
* (and thence onto LAST-ACK and finally, CLOSED, we never enter
* TIME-WAIT)
*
* If we are in FINWAIT-1, a received FIN indicates simultanious
* close and we go into CLOSING (and later onto TIME-WAIT)
*
* If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT.
*
*/
static
int
tcp_fin
(
struct
sock
*
sk
,
struct
tcphdr
*
th
,
static
int
tcp_fin
(
struct
s
k_buff
*
skb
,
struct
s
ock
*
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
(
sk
b
,
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
(
sk
b
,
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
(
sk
b
,
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
)
...
...
net/inet/timer.c
View file @
16119ae5
...
...
@@ -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
{
...
...
net/inet/udp.c
View file @
16119ae5
...
...
@@ -36,6 +36,7 @@
* Alan Cox : Use ip_tos and ip_ttl
* Alan Cox : SNMP Mibs
* Alan Cox : MSG_DONTROUTE, and 0.0.0.0 support.
* Matt Dillon : UDP length checks.
*
*
* This program is free software; you can redistribute it and/or
...
...
@@ -653,7 +654,8 @@ 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,7 +663,22 @@ 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
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment