add directory net
This commit is contained in:
173
net/net-2/README
Normal file
173
net/net-2/README
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
|
||||||
|
Installation and Release Notes for NET-2
|
||||||
|
|
||||||
|
Version: 0.99.9 NET-2 Rev.B
|
||||||
|
|
||||||
|
Sat, 08 May 14:07:00 1993 METDST
|
||||||
|
|
||||||
|
|
||||||
|
Fred N. van Kempen,
|
||||||
|
|
||||||
|
<waltje@uwalt.hacktic.nl>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PREFACE
|
||||||
|
This document describes what yet has to be done in NET-2, how to install
|
||||||
|
it, and more of that stuff.
|
||||||
|
|
||||||
|
|
||||||
|
NEWS
|
||||||
|
New items in this (Rev.B) release of NET-2:
|
||||||
|
|
||||||
|
- about 48Kbyte worth of patches from Ross Biro. These should fix the
|
||||||
|
last "locking telnet" bugs, and some others.
|
||||||
|
|
||||||
|
- many, many small fixes like those in the Makefiles. You all know what
|
||||||
|
I am talking about here.
|
||||||
|
|
||||||
|
- rewrites of some system header files.
|
||||||
|
|
||||||
|
- better ICMP. Not complete yet, but I am getting there. Slowly...
|
||||||
|
|
||||||
|
- IP routing ("*** datagram routing IS NOW implemented ***"). Grin.
|
||||||
|
|
||||||
|
- Patches from Fred Baumgarten (and, I assume, Gerald Heim) to clean
|
||||||
|
out some remaining 'volatile' keywords, and to prepare for a decent
|
||||||
|
netstat(8) program.
|
||||||
|
|
||||||
|
- Mew distribution format, with this text included. Also: the *new*
|
||||||
|
net-base utils. *GRIN*
|
||||||
|
|
||||||
|
- The "current" versions of Donald Becker's driver kit. Donald, can
|
||||||
|
you check && maintain please?
|
||||||
|
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
|
||||||
|
Several known bugs remain:
|
||||||
|
|
||||||
|
- no IP fragmentation yet. We need 'mbuf's for that, on which work is
|
||||||
|
already being done. Will be after Linux 1.0, alas.
|
||||||
|
|
||||||
|
- for the same reason: DDI is not finished yet.
|
||||||
|
|
||||||
|
- ARP still acts weird. Any takers?
|
||||||
|
|
||||||
|
- There seems to be a memory leak in the NET-2 code. Especially when
|
||||||
|
using the IP routing ("gatewaying") code, the leak appears.
|
||||||
|
|
||||||
|
- No IP Option Processing yet. Partly because I don't yet feel like
|
||||||
|
it...
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
|
||||||
|
1. Create a source directory called /usr/src/linux, and unpack
|
||||||
|
the standard (0.99 PL9) kernel source there.
|
||||||
|
|
||||||
|
2. Remove the old NET code with the commands:
|
||||||
|
|
||||||
|
# rm -rf /usr/src/linux/net
|
||||||
|
|
||||||
|
3. Remove an old include file:
|
||||||
|
|
||||||
|
# rm -f /usr/src/linux/include/linux/sock_ioctl.h
|
||||||
|
|
||||||
|
4. Unpack the "net-ksrc.tgz" file:
|
||||||
|
|
||||||
|
# cd /usr/src/linux
|
||||||
|
# tar xfvz /tmp/net-ksrc.tgz
|
||||||
|
|
||||||
|
This contains the new NET code. Then, patch the rest of the
|
||||||
|
kernel:
|
||||||
|
|
||||||
|
# patch -p0 </tmp/net-kernel.cdif
|
||||||
|
|
||||||
|
(relax, the patch only fixes some typos, and adds a number of
|
||||||
|
system include files...). Note, that this patch overwrites any
|
||||||
|
current "Makefile", "config.in" or "Configure" files in the
|
||||||
|
top-level directory.
|
||||||
|
|
||||||
|
5. Check the definitions in net-2/inet/Makefile.
|
||||||
|
|
||||||
|
6. Make config && depend:
|
||||||
|
|
||||||
|
# cd /usr/src/linux
|
||||||
|
# make config
|
||||||
|
# make depend
|
||||||
|
|
||||||
|
Choose the "TCP/IP Networking software version 2" in the
|
||||||
|
configuration section.
|
||||||
|
|
||||||
|
7. Make the kernel.
|
||||||
|
|
||||||
|
|
||||||
|
NETWORK PROGRAMS
|
||||||
|
|
||||||
|
NET-2 has changed a lot, so a new set of "base utilities" comes with
|
||||||
|
it. These programs allow you to set up and maintain your network.
|
||||||
|
To compile and install:
|
||||||
|
|
||||||
|
1. Create the directory:
|
||||||
|
|
||||||
|
# mkdir /usr/src/commands/usr.net/net
|
||||||
|
|
||||||
|
2. Unpack the distribution:
|
||||||
|
|
||||||
|
# cd /usr/src/commands/usr.net/net
|
||||||
|
# tar xfvz /tmp/net-base.tgz
|
||||||
|
|
||||||
|
3. Make the stuff.
|
||||||
|
|
||||||
|
# make
|
||||||
|
|
||||||
|
Here is how I have things set up here:
|
||||||
|
|
||||||
|
/bin:
|
||||||
|
-r-xr-xr-x 1 bin bin 3736 Apr 23 19:39 hostname
|
||||||
|
|
||||||
|
/etc:
|
||||||
|
-rw-r--r-- 1 bin bin 8 Apr 30 20:47 HOSTNAME
|
||||||
|
-rw------- 1 bin bin 4 Feb 23 20:20 NETWORKING
|
||||||
|
-r-xr-xr-x 1 bin bin 5028 Apr 23 19:44 arp
|
||||||
|
lrwxrwxrwx 1 root staff 18 Mar 31 12:47 bootptab -> /conf/net/bootptab
|
||||||
|
lrwxrwxrwx 1 root staff 18 Mar 31 12:47 diphosts -> /conf/net/diphosts
|
||||||
|
lrwxrwxrwx 1 root staff 17 Mar 31 12:47 exports -> /conf/net/exports
|
||||||
|
lrwxrwxrwx 1 root staff 18 Mar 31 12:47 ftpusers -> /conf/net/ftpusers
|
||||||
|
lrwxrwxrwx 1 root staff 19 Mar 31 12:47 host.conf -> /conf/net/host.conf
|
||||||
|
lrwxrwxrwx 1 root staff 15 Mar 31 12:47 hosts -> /conf/net/hosts
|
||||||
|
lrwxrwxrwx 1 root staff 21 Mar 31 12:47 hosts.equiv -> /conf/net/hosts.equiv
|
||||||
|
lrwxrwxrwx 1 root staff 11 Mar 31 12:47 hosts.lpd -> hosts.equiv
|
||||||
|
-r-x------ 1 bin bin 6272 Apr 23 19:44 ifconfig
|
||||||
|
-r-x------ 1 bin bin 2692 Apr 23 19:44 iflink
|
||||||
|
-r-x------ 1 bin bin 3480 Apr 23 19:44 ifsetup
|
||||||
|
lrwxrwxrwx 1 root staff 19 Mar 31 12:47 inetd.conf -> /usr/etc/inetd.conf
|
||||||
|
lrwxrwxrwx 1 root staff 28 Mar 31 12:47 named.boot -> /conf/net/named.d/named.boot
|
||||||
|
-rw-r--r-- 1 root root 3 May 8 09:06 named.pid
|
||||||
|
lrwxrwxrwx 1 root staff 21 Mar 31 12:47 named.reload -> /usr/etc/named.reload
|
||||||
|
lrwxrwxrwx 1 root staff 22 Mar 31 12:47 named.restart -> /usr/etc/named.restart
|
||||||
|
lrwxrwxrwx 1 root staff 18 Mar 31 12:47 networks -> /conf/net/networks
|
||||||
|
lrwxrwxrwx 1 root staff 26 Mar 31 12:47 nntpserver -> /conf/news/nntp/nntpserver
|
||||||
|
lrwxrwxrwx 1 root staff 18 Mar 31 12:47 printcap -> /conf/net/printcap
|
||||||
|
lrwxrwxrwx 1 root staff 18 Mar 31 12:47 protocols -> /usr/etc/protocols
|
||||||
|
lrwxrwxrwx 1 root staff 21 Mar 31 12:47 resolv.conf -> /conf/net/resolv.conf
|
||||||
|
-r-x------ 1 bin bin 4492 Apr 23 19:44 route
|
||||||
|
lrwxrwxrwx 1 root staff 12 Mar 31 12:47 rpc -> /usr/etc/rpc
|
||||||
|
lrwxrwxrwx 1 root staff 17 Mar 31 12:47 services -> /usr/etc/services
|
||||||
|
lrwxrwxrwx 1 root staff 21 Mar 31 12:47 syslog.conf -> /conf/net/syslog.conf
|
||||||
|
-rw------- 1 root root 3 May 8 09:06 syslog.pid
|
||||||
|
-r-x------ 1 bin bin 27202 May 7 20:06 wdsetup
|
||||||
|
|
||||||
|
Note, that in LINUX/Pro, all system config files are kept in /conf, and
|
||||||
|
all non-needed binaries (like the TCP/IP and RPC servers) are in /usr/etc.
|
||||||
|
|
||||||
|
Have a look at the sample config files in the net-base distribution, and
|
||||||
|
pay close attention to the "rc.inet[12]" files, which set up your network.
|
||||||
|
|
||||||
|
|
||||||
|
SLIP
|
||||||
|
|
||||||
|
For SLIP users: yes, SLIP works. Have a look at the new "dip" program
|
||||||
|
and its config file. Dip is not perfect yet, but I am sure the guys
|
||||||
|
at Demon.Co.Uk will change this. Right, guys? :-)
|
||||||
|
|
||||||
BIN
net/net-2/amd920824upl67.tar.gz
Normal file
BIN
net/net-2/amd920824upl67.tar.gz
Normal file
Binary file not shown.
BIN
net/net-2/dip-313.tar.z
Normal file
BIN
net/net-2/dip-313.tar.z
Normal file
Binary file not shown.
BIN
net/net-2/dip334a.tar.gz
Normal file
BIN
net/net-2/dip334a.tar.gz
Normal file
Binary file not shown.
BIN
net/net-2/dip337-uri.tgz
Normal file
BIN
net/net-2/dip337-uri.tgz
Normal file
Binary file not shown.
BIN
net/net-2/net-0.20.tar.z
Normal file
BIN
net/net-2/net-0.20.tar.z
Normal file
Binary file not shown.
BIN
net/net-2/net-0.21.tar.z
Normal file
BIN
net/net-2/net-0.21.tar.z
Normal file
Binary file not shown.
BIN
net/net-2/net-007.tar.z
Normal file
BIN
net/net-2/net-007.tar.z
Normal file
Binary file not shown.
BIN
net/net-2/net-base.tar.z
Normal file
BIN
net/net-2/net-base.tar.z
Normal file
Binary file not shown.
BIN
net/net-2/net-ext.tar.z
Normal file
BIN
net/net-2/net-ext.tar.z
Normal file
Binary file not shown.
2382
net/net-2/net-kernel.cdif
Normal file
2382
net/net-2/net-kernel.cdif
Normal file
File diff suppressed because it is too large
Load Diff
BIN
net/net-2/net-ksrc-revb.tgz
Normal file
BIN
net/net-2/net-ksrc-revb.tgz
Normal file
Binary file not shown.
BIN
net/net-2/net-std.tar.z
Normal file
BIN
net/net-2/net-std.tar.z
Normal file
Binary file not shown.
2409
net/net-2/old/Linus.patch
Normal file
2409
net/net-2/old/Linus.patch
Normal file
File diff suppressed because it is too large
Load Diff
BIN
net/net-2/old/linus.tgz
Normal file
BIN
net/net-2/old/linus.tgz
Normal file
Binary file not shown.
BIN
net/net-2/old/net-005.tgz
Normal file
BIN
net/net-2/old/net-005.tgz
Normal file
Binary file not shown.
BIN
net/net-2/old/net2-src.tgz
Normal file
BIN
net/net-2/old/net2-src.tgz
Normal file
Binary file not shown.
BIN
net/net-2/patches.alpha.z
Normal file
BIN
net/net-2/patches.alpha.z
Normal file
Binary file not shown.
BIN
net/net-2/pop3d-1.00.4.tar.gz
Normal file
BIN
net/net-2/pop3d-1.00.4.tar.gz
Normal file
Binary file not shown.
358
net/net/READ.ME
Normal file
358
net/net/READ.ME
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
KA9Q TCP is an implementation of TCP/IP for the PC. This is a Unix
|
||||||
|
port based on a version from mid-1989. Unfortunately the more recent
|
||||||
|
versions of KA9Q are harder to port to Linux.
|
||||||
|
|
||||||
|
I don't have a manual for this exact version. There's builtin help.
|
||||||
|
The commands primarily set parameters (the ip and tcp commands), or
|
||||||
|
start services. In general the same command is used to set and
|
||||||
|
examine parameters. When you type the command with no value, it shows
|
||||||
|
the current value.
|
||||||
|
|
||||||
|
To use KA9Q, put startup.net and possibly domain.txt in your home
|
||||||
|
directory, and run "net". You will probably need to tailor
|
||||||
|
startup.net for your configuration. See the section at the end of
|
||||||
|
this document on configuration. Note that you can give an argument to
|
||||||
|
the "net" command. It will be used as the startup file in place of
|
||||||
|
startup.net. By default, startup.net and other files are in your home
|
||||||
|
directory (the value of the environment variable HOME). You can put
|
||||||
|
them somewhere else by using the environment variable NETHOME.
|
||||||
|
|
||||||
|
If you are using SLIP, note that you must first dial the modem and
|
||||||
|
establish a SLIP connection. The exact details on doing this are
|
||||||
|
different at each site. Unfortunately you're probably going to need
|
||||||
|
help from system or network support staff in order to use SLIP.
|
||||||
|
Because SLIP effectively connects you to someone's local network,
|
||||||
|
using it normally requires logging in with a password. I recommend
|
||||||
|
using kermit's script facility to dial and set up SLIP. It did not
|
||||||
|
seem work adding a similar facility to KA9Q.
|
||||||
|
|
||||||
|
If your site does not current support SLIP, they will probably have to
|
||||||
|
do a bit of systems work. For Unix systems, typically this will
|
||||||
|
involve adding a SLIP device driver, and then enabling routing. (An
|
||||||
|
appropriate driver is available for the Sun, although I don't know
|
||||||
|
where. I believe it can be installed without Sun source.) Many
|
||||||
|
terminal servers will support SLIP directly, so enabling it will
|
||||||
|
merely involve changing a configuration file.
|
||||||
|
|
||||||
|
Aside from configuration commands (which typically go into
|
||||||
|
startup.net), the most common commands are probably telnet and ftp.
|
||||||
|
Both take a host name. Once you are connected, the "escape character"
|
||||||
|
(initially ^]) lets you do some special things. You type it followed
|
||||||
|
by another character. Except for the escape character, the characters
|
||||||
|
are folded, so that c, C, and ^C are equivalent. Here's a list of the
|
||||||
|
special characters:
|
||||||
|
|
||||||
|
escape character (i.e. two in a row) - send a real escape character
|
||||||
|
x or ^x - return to command mode
|
||||||
|
b or ^b - send break
|
||||||
|
c or ^c - send interrupt process
|
||||||
|
o or ^c - abort output
|
||||||
|
t or ^t - are you there?
|
||||||
|
h or ^h - send telnet erase character
|
||||||
|
u or ^u - send telnet erase line
|
||||||
|
? - print this help message
|
||||||
|
|
||||||
|
Once you return to command mode, the "session" command can be used to
|
||||||
|
switch between connections. With no arguments, it shows the current
|
||||||
|
sessions. With a number, it will reconnect you to that session.
|
||||||
|
There's a disconnect command to kill a session (also some stronger
|
||||||
|
ways of doing it).
|
||||||
|
|
||||||
|
Telnet normally operates in full duplex, with the other system
|
||||||
|
echoing. However if that system refuses echoing, or if you use the
|
||||||
|
command "echo refuse", KA9Q will echo locally. In this case you have
|
||||||
|
the normal line editing functions. Several control characters have
|
||||||
|
special functions. Note that in local echo mode, you do not need to
|
||||||
|
hit the escape character first.
|
||||||
|
|
||||||
|
escape character - return to command mode
|
||||||
|
^v - quote the next character
|
||||||
|
^b - send break
|
||||||
|
^c - send interrupt process
|
||||||
|
^o - abort output
|
||||||
|
^t - are you there?
|
||||||
|
|
||||||
|
Telnet will pass your TERM environment variable as the terminal type
|
||||||
|
if the other end asks for it. It will implement flow control locally
|
||||||
|
if the other end understand the remote flow control option. If the
|
||||||
|
other end does a "telnet sync" correctly, it will suppress output when
|
||||||
|
appropriate. E.g. when talking to a Unix system, if you type ^C or ^O
|
||||||
|
in the middle of a large output, output should stop immediately.
|
||||||
|
However there may be a pause before getting the next prompt, as some
|
||||||
|
previously buffered data is probably still going to be transmitted.
|
||||||
|
|
||||||
|
The "ip stat" and "tcp stat" commands are useful to get statistics.
|
||||||
|
"Ip stat" shows both the IP and SLIP level. If header compression is
|
||||||
|
being used, it will show how many packets were compressed and how many
|
||||||
|
were oot.
|
||||||
|
|
||||||
|
FTP is sort of odd. It looks like you are in an interactive
|
||||||
|
conversation with the FTP server at the other end, with no local
|
||||||
|
command prompt. Most commands are passed directly to the remote
|
||||||
|
server, but in fact there is a local command parser, which will
|
||||||
|
intervene for a few commands:
|
||||||
|
|
||||||
|
cd, dir, list, get, ls, mkdir, nlst, rmdir, put, type
|
||||||
|
|
||||||
|
get and put should be used to transfer files.
|
||||||
|
|
||||||
|
There's very minimal support for X11. If you are running under
|
||||||
|
X on your Linux system, you can use the command "start x".
|
||||||
|
This will cause KA9Q to front-end the Linux X11 software, so
|
||||||
|
that you can run X-based software on other hosts. As usual,
|
||||||
|
you set a DISPLAY environment variable of the form
|
||||||
|
|
||||||
|
myhost:0.0
|
||||||
|
|
||||||
|
where myhost is the host name or IP address of your Linux system (i.e.
|
||||||
|
the address that KA9Q is using). This function is currently very
|
||||||
|
primitive. There is no access control -- once you've started X,
|
||||||
|
anyone can connect to your display from anywhere on the network. KA9Q
|
||||||
|
will put out a message whenever someone connects, so at least you'll
|
||||||
|
know. If people find this feature useful, I'll put in access control.
|
||||||
|
With the current implementation, there are still two things you could
|
||||||
|
do: (1) use xauth, (2) turn on the X server only when you actually
|
||||||
|
want to start up X software. You can do "stop x". This will not
|
||||||
|
close down existing X connections. It will just prevent new ones,
|
||||||
|
until the next "start x". Unfortunately, X involves a lot of network
|
||||||
|
traffic. Over a 9600 bps line -- which is the primary audience for
|
||||||
|
this implementation -- it's probably too slow to be useful.
|
||||||
|
|
||||||
|
It's possible to run a server which others can connect to, though I
|
||||||
|
haven't tried it except for ftp. The command is "start ftp", etc.
|
||||||
|
For ftp, a login is required. The file ftpusers, in your home
|
||||||
|
directory, contains a list of users and passwords.
|
||||||
|
|
||||||
|
Here's a list of commands:
|
||||||
|
|
||||||
|
! - call a recursive shell
|
||||||
|
arp - set arp parameters
|
||||||
|
ax25 - configure ham interface
|
||||||
|
attach - define an interface - must be used for each interface
|
||||||
|
bootp - request our IP address from a server
|
||||||
|
connect - for AX25, define call sign for interface
|
||||||
|
cd
|
||||||
|
close
|
||||||
|
disconnect
|
||||||
|
dir - do a directory on the local system
|
||||||
|
domain - configure domain name system
|
||||||
|
eaglestat
|
||||||
|
echo - refuse or accept - request to disable telnet remote echo
|
||||||
|
eol - Unix or standard end of line options [standard is recommended,
|
||||||
|
even for use on Unix systems]
|
||||||
|
escape - change escape character
|
||||||
|
etherstat
|
||||||
|
exit
|
||||||
|
finger
|
||||||
|
forward - divert packets from one interface to another
|
||||||
|
ftp - open an ftp connection
|
||||||
|
hapnstat
|
||||||
|
help - print command list
|
||||||
|
hostname - set/examine host name
|
||||||
|
kick
|
||||||
|
log
|
||||||
|
ip - set/examine IP level parameters
|
||||||
|
memstat
|
||||||
|
mbox, mode - AX25 commands
|
||||||
|
mulport
|
||||||
|
netrom
|
||||||
|
nrstat
|
||||||
|
param - set/examine interafce parameters
|
||||||
|
ping - send ping to a host
|
||||||
|
pwd
|
||||||
|
rdate [offset] [host] - set date/time from UDP time daemon. see below
|
||||||
|
record
|
||||||
|
remote
|
||||||
|
reset - kill connection
|
||||||
|
route - set up routes
|
||||||
|
session - see above: move to a different session
|
||||||
|
shell - call a subshell
|
||||||
|
smtp - mail handling
|
||||||
|
start - start server
|
||||||
|
stop - stop server
|
||||||
|
tcp - set/examine tcp parameters
|
||||||
|
telnet - open telnet connection
|
||||||
|
trace - enable low-level debugging (no arguments - show current status)
|
||||||
|
trace to <file> - put trace output on file (use "con" for console)
|
||||||
|
trace <device> [<bits>] - trace packets on device
|
||||||
|
device is typically "sl0" or "loopback"
|
||||||
|
bits are in hex:
|
||||||
|
1 - output, 10 - input (else no tracing)
|
||||||
|
100 - dump data in ascii, 200 - hex (else headers only)
|
||||||
|
trace cmdmode|allmode - show trace just when at command level?
|
||||||
|
trace telnet [on | off] - show telnet options and urgent data
|
||||||
|
udp - set/examine udp parameters
|
||||||
|
upload
|
||||||
|
wait - control mail select
|
||||||
|
|
||||||
|
The rdate command uses the old-style time protocol (often known as
|
||||||
|
"rdate"), on UDP port 37, to get the date and time, and then sets the
|
||||||
|
system clock. Note that the protocol always gives time in GMT. If
|
||||||
|
your system clock is running on GMT (which is the normal way Unix
|
||||||
|
should run), this is fine. Otherwise, you'll need to specify the
|
||||||
|
offset in minutes. For time zones west of GMT, this should be a
|
||||||
|
negative number. For the Eastern U.S., when not in daylight time, the
|
||||||
|
offset is -300, because EST is 5 hours (300 minutes) west of GMT.
|
||||||
|
If no host is specified, a UDP broadcast is used.
|
||||||
|
|
||||||
|
This configuration of KA9Q has been tested only with SLIP. Code is
|
||||||
|
also included for the KISS device (for ham radio) and Linux generic
|
||||||
|
Ethernet support using /dev/eth. /dev/eth should either work or come
|
||||||
|
close to working: it's based on diffs made from a recent version of
|
||||||
|
KA9Q. I don't know of anyone who has tested KISS. Past experience
|
||||||
|
suggests that users will prefer Kermit to SLIP unless you use SLIP
|
||||||
|
with header compression.
|
||||||
|
|
||||||
|
Note that this program depends up non-blocking I/O and the select
|
||||||
|
system call. In Linux 0.12, these did not work. There are patches,
|
||||||
|
nonblock.tar.Z and select.patch, on the major distribution machines.
|
||||||
|
Don't forget: after patching a kernel source file, you must do a make
|
||||||
|
in each directory where you changed a file. The main make file
|
||||||
|
doesn't rebuild things all the time when it should. Linux 0.95
|
||||||
|
and later has all the necessary patches.
|
||||||
|
|
||||||
|
|
||||||
|
CONFIGURATION:
|
||||||
|
|
||||||
|
Here's some advice on preparing a configuration file. I assume you're
|
||||||
|
planning to use SLIP (dialup IP) or Ethernet. I'm going to explain my
|
||||||
|
configuration file line by line, as it's likely to be close to what
|
||||||
|
you need:
|
||||||
|
|
||||||
|
attach asy 0 /dev/tty64 cslip sl0 2048 1500 9600
|
||||||
|
|
||||||
|
You must include an attach command in every configuration file. It
|
||||||
|
defines the device. For SLIP, the line above should be about right.
|
||||||
|
Some variationos:
|
||||||
|
- /dev/tty64 is the serial line. Obviously you can adjust this
|
||||||
|
to use a different line.
|
||||||
|
- cslip specifies SLIP with Van Jacobsen header compression.
|
||||||
|
If you're using normal slip, use "slip" instead of "cslip".
|
||||||
|
In my opinion, response on normal SLIP is too slow for it
|
||||||
|
to be very useful.
|
||||||
|
- 9600 is the line speed. Use whatever speed you are set for.
|
||||||
|
If you have a "smart" modem, we recommend turning off
|
||||||
|
any error correction or compression. These features
|
||||||
|
introduce enough delay to be objectionable.
|
||||||
|
|
||||||
|
The following attach command could be used for Linux generic
|
||||||
|
Ethernet (/dev/eth)
|
||||||
|
attach linux arpa eth0 1500 1.2.3.fa.fb.fc
|
||||||
|
The last item is your Ethernet hardware address, in hex. It should
|
||||||
|
be printed when your system boots. E.g. if it prints 01 02 03 04 05 06
|
||||||
|
then you put in 1.2.3.4.5.6. Each of the six numbers is in hex, and
|
||||||
|
may be one or two digits.
|
||||||
|
|
||||||
|
wait 0
|
||||||
|
|
||||||
|
This should only be needed with older version of the kernel. It
|
||||||
|
disables the select system call. Instead, runs in a tight loop
|
||||||
|
continuously testing for input. Unfortunately select has a history of
|
||||||
|
problems under Linux. It appears to be fine in version 0.96. However
|
||||||
|
in earlier version, select did not work properly when there was
|
||||||
|
another program running. (In some cases it works OK unless the other
|
||||||
|
program uses select. E.g. if you enter emacs in another window, your
|
||||||
|
FTP comes to a halt.) If you are trying to use KA9Q while another
|
||||||
|
program is running, and you find that the connection hangs, you might
|
||||||
|
try "wait 0". Otherwise, you should leave the default setting, which
|
||||||
|
is "wait 100". The argument to wait is in milliseconds. It's the
|
||||||
|
amount of time to wait in the main loop. The select will trigger if
|
||||||
|
input arrives on the serial line, or you hit a key. Otherwise it
|
||||||
|
waits this amount of time. I don't recommend values larger than 100.
|
||||||
|
Smaller values might possibly be useful in some siuations.
|
||||||
|
|
||||||
|
route add default sl0
|
||||||
|
|
||||||
|
This says that all packets should be sent out the SLIP line. Presumably
|
||||||
|
this is what you want. If you are using Ethernet, of course you'd
|
||||||
|
use the name of your Ethernet device instead of sl0, e.g.
|
||||||
|
route add default eth0
|
||||||
|
|
||||||
|
bootp
|
||||||
|
|
||||||
|
This says that the system will find out its IP address by sending a
|
||||||
|
BOOTP request out the serial line. Since dialups are normally
|
||||||
|
in "hunt groups", you will probably get a different line each time
|
||||||
|
you dial up. Thus your IP address will probably not be the same.
|
||||||
|
BOOTP is a way of asking the terminal server (or whatever else
|
||||||
|
you're connected to) to tell you your IP address. If your terminal
|
||||||
|
server does not support BOOTP, you explicitly set your IP address
|
||||||
|
using a command of the form
|
||||||
|
ip address [1.2.3.4]
|
||||||
|
where 1.2.3.4 is your IP address. You would use this command
|
||||||
|
instead of the BOOTP command.
|
||||||
|
|
||||||
|
tcp mss 64
|
||||||
|
tcp window 256
|
||||||
|
|
||||||
|
These commands set TCP performance parameters. These control a
|
||||||
|
tradeoff between overall throughput, jerkiness of output, etc. These
|
||||||
|
parameters seem to give good performance for interactive connectionos.
|
||||||
|
For FTP you might want larger values, particularly if you are going
|
||||||
|
over a long-haul network like the Internet. For FTP the best value
|
||||||
|
for mss is at least 512, and probably more like 1024, with window
|
||||||
|
between 2 and 4 times mss. However Linux has fairly small buffers for
|
||||||
|
the serial line, so you may not be able to get away with parameters
|
||||||
|
much larger than the ones shown here. If you are using Ethernet,
|
||||||
|
of course you'll want larger values. I suggest starting with
|
||||||
|
tcp mss 1460
|
||||||
|
tcp window 5840
|
||||||
|
This should work for traffic on the local Ethernet. If your
|
||||||
|
traffic tends to go through routers, a safer set of values is
|
||||||
|
tcp mss 512
|
||||||
|
tcp window 2048
|
||||||
|
|
||||||
|
domain suffix rutgers.edu
|
||||||
|
domain add [128.6.4.4]
|
||||||
|
|
||||||
|
These parameters control the domain system. That's the set of servers
|
||||||
|
you use to look up host names. You'll need to adjust these for your
|
||||||
|
campus. The suffix is tacked onto all host names you type, unless
|
||||||
|
they already have a dot in them. This lets you type "athos" instead
|
||||||
|
of "athos.rutgers.edu". Set it for the domain you most commonly use.
|
||||||
|
The address 128.6.4.4 should be the address of a domain server on your
|
||||||
|
campus. All name lookups will be directed to that server. You can
|
||||||
|
define more than one, by using several "domain add" lines.
|
||||||
|
|
||||||
|
If your campus does not support the domain system, you can use a host
|
||||||
|
table instead. In that case you would omit the "domain" commands.
|
||||||
|
The host table is called "domain.txt", in your home directory. It's
|
||||||
|
in the format of a domain master file. The most important entries
|
||||||
|
look like
|
||||||
|
athos.rutgers.edu. IN A 128.6.4.4
|
||||||
|
Note that IN and A must be in upper case, and the name must include
|
||||||
|
the whole suffix, and end in a dot.
|
||||||
|
|
||||||
|
Note that this is a very old domain implementation, and is not up to
|
||||||
|
the level of the current MS/DOS version. It does not use TTL's.
|
||||||
|
Every time you type a name, it first looks in domain.txt. If there's
|
||||||
|
an entry there, it will be used. Otherwise, it will query the domain
|
||||||
|
server you specified. If it gets an answer, that information will be
|
||||||
|
added to domain.txt. If a host changes its address, you'll be in
|
||||||
|
trouble, because you'll continue getting the old information from
|
||||||
|
domain.txt. Thus it's probably a good idea to remove domain.txt now
|
||||||
|
and then.
|
||||||
|
|
||||||
|
start finger
|
||||||
|
start telnet
|
||||||
|
|
||||||
|
These start daemons to respond to incoming finger and telnet
|
||||||
|
connections. This is optional, but most people will probably find
|
||||||
|
them useful to help people find you. The finger server uses files
|
||||||
|
stored in a subdirectory "finger" of your home directory. Files
|
||||||
|
should have names of the form xxx.txt, where xxx is a user name.
|
||||||
|
Finger with no arguments will list all users on the system (i.e. it
|
||||||
|
will look for all files ~/finger/*.txt, and list the names). Finger
|
||||||
|
with a specific user name will display the text of that file, i.e.
|
||||||
|
~/finger/xxx.txt.
|
||||||
|
|
||||||
|
The telnet server is really more like a talk link. When someone
|
||||||
|
telnets to your machine, you'll get a message saying that there is an
|
||||||
|
incoming telnet connection. At that point you should escape to the
|
||||||
|
coommand level (using ^] or whatever your escape character is), do
|
||||||
|
"session" to see the session number of the incoming connection and do
|
||||||
|
"session NN" to connect to it. At that point you'll be in an
|
||||||
|
interactive link with the person. There is also a server designed to
|
||||||
|
allow "real" telnet connections, i.e. connections that create a real
|
||||||
|
login session. It is activated with the command "start telunix".
|
||||||
|
However that code is incomplete, and currently does nothing useful.
|
||||||
BIN
net/net/net-0.3.tar.z
Normal file
BIN
net/net/net-0.3.tar.z
Normal file
Binary file not shown.
54
net/net/nfsd-domain-bug.patch
Normal file
54
net/net/nfsd-domain-bug.patch
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
From Thomas.Koenig@ciw.uni-karlsruhe.de Sat Aug 20 11:34:02 EDT 1994
|
||||||
|
Article: 2607 of comp.os.linux.announce
|
||||||
|
Newsgroups: comp.os.linux.announce
|
||||||
|
Path: bigblue.oit.unc.edu!concert!news-feed-1.peachnet.edu!news.duke.edu!zombie.ncsc.mil!MathWorks.Com!news.kei.com!travelers.mail.cornell.edu!cornell!bounce-bounce
|
||||||
|
From: ig25@fg70.rz.uni-karlsruhe.de (Thomas Koenig)
|
||||||
|
Subject: nfsd bug and fix
|
||||||
|
Message-ID: <ann-10345.777397799@cs.cornell.edu>
|
||||||
|
Followup-To: comp.os.linux.misc
|
||||||
|
Keywords: nfs, bug report, patch
|
||||||
|
Sender: mdw@cs.cornell.edu (Matt Welsh)
|
||||||
|
Reply-To: Thomas.Koenig@ciw.uni-karlsruhe.de
|
||||||
|
Organization: University of Karlsruhe, Germany
|
||||||
|
Date: Sat, 20 Aug 1994 15:50:13 GMT
|
||||||
|
Approved: linux-announce@tc.cornell.edu (Matt Welsh)
|
||||||
|
Lines: 36
|
||||||
|
|
||||||
|
Hi;
|
||||||
|
|
||||||
|
there's a bug in the nfs suite 1.5.10 (for example on
|
||||||
|
|
||||||
|
/sunsite.unc.edu:pub/Linux/system/Network/aris/sources/nfsd/nfsd-1.5.10.tar.gz
|
||||||
|
|
||||||
|
which prevents a directory from being exported to different domains.
|
||||||
|
|
||||||
|
Consider a line in /etc/export:
|
||||||
|
|
||||||
|
/export *.foo.do.main(ro) *.bar.another.domain(ro)
|
||||||
|
|
||||||
|
This would only allow importing the file system /export from
|
||||||
|
machines from *.bar.another.domain.
|
||||||
|
|
||||||
|
The patch to fix this, one forgotten continue, follows:
|
||||||
|
|
||||||
|
--- nfsd-1.5.10-old/auth_clnt.c Fri Nov 5 04:18:43 1993
|
||||||
|
+++ nfsd-1.5.10/auth_clnt.c Wed Aug 17 22:53:12 1994
|
||||||
|
@@ -153,6 +153,7 @@
|
||||||
|
goto found_it;
|
||||||
|
}
|
||||||
|
cpp = &(cp->next); /* the normal iteration */
|
||||||
|
+ continue;
|
||||||
|
}
|
||||||
|
else if ((hent = gethostbyname(cp->clnt_name)) != NULL) {
|
||||||
|
cp->clnt_addr = *((struct in_addr *) hent->h_addr);
|
||||||
|
--
|
||||||
|
Thomas Koenig, Thomas.Koenig@ciw.uni-karlsruhe.de, ig25@dkauni2.bitnet.
|
||||||
|
The joy of engineering is to find a straight line on a double
|
||||||
|
logarithmic diagram.
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
Send submissions for comp.os.linux.announce to: linux-announce@tc.cornell.edu
|
||||||
|
Be sure to include Keywords: and a short description of your software.
|
||||||
|
|
||||||
|
|
||||||
15
net/net/tkppp-1.0.lsm
Normal file
15
net/net/tkppp-1.0.lsm
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
Begin3
|
||||||
|
Title: tkppp
|
||||||
|
Version: 1.0
|
||||||
|
Entered-date: Tue Aug 30 22:06:51 EDT 1994
|
||||||
|
Description: A tcl/tk tool for controlling a PPP connection.
|
||||||
|
Keywords: PPP, tcl, tk
|
||||||
|
Author: jeschke@cs.indiana.edu (Eric Jeschke)
|
||||||
|
Maintained-by: jeschke@cs.indiana.edu (Eric Jeschke)
|
||||||
|
Primary-site: sunsite.unc.edu /pub/Linux/X11/xapps/comm
|
||||||
|
3042 tkppp-1.0.tar.gz
|
||||||
|
Alternate-site:
|
||||||
|
Original-site:
|
||||||
|
Platform: tcl/tk
|
||||||
|
Copying-policy: Public Domain
|
||||||
|
End
|
||||||
BIN
net/net/tkppp-1.0.tar.gz
Normal file
BIN
net/net/tkppp-1.0.tar.gz
Normal file
Binary file not shown.
BIN
net/net/wd8003.tar
Normal file
BIN
net/net/wd8003.tar
Normal file
Binary file not shown.
BIN
net/net/xftp-2.2.athena+term.tgz
Normal file
BIN
net/net/xftp-2.2.athena+term.tgz
Normal file
Binary file not shown.
5
net/tcpip/README.hostcvt
Normal file
5
net/tcpip/README.hostcvt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
This is a copy of hostcvt compiled for linux. It helps to convert
|
||||||
|
from /etc/hosts to named configuration files.
|
||||||
|
|
||||||
|
It may not be used to make money, so I don't think you can charge to
|
||||||
|
redistribute this.
|
||||||
7
net/tcpip/README.inetd
Normal file
7
net/tcpip/README.inetd
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
inetd.tar.Z has the following files in it
|
||||||
|
inetd (put in /usr/etc/inet)
|
||||||
|
inetd.8 (put in /usr/man/man8)
|
||||||
|
inetd-diffs
|
||||||
|
|
||||||
|
The diffs only include changes to the *.[ch] files. You will need to
|
||||||
|
edit the makefile to recompile inetd.
|
||||||
13
net/tcpip/README.named
Normal file
13
net/tcpip/README.named
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
named-bin.tar.Z consists of the following files:
|
||||||
|
named (put in /usr/etc/inet)
|
||||||
|
named-xfer (put in /usr/etc/int)
|
||||||
|
nsquery (put in /usr/bin or /usr/etc/inet)
|
||||||
|
nslookup (put in /usr/bin or /usr/etc/inet)
|
||||||
|
named.8 (put in /usr/man/man8)
|
||||||
|
nslookup.1 (put in /usr/man/man1)
|
||||||
|
named-diffs
|
||||||
|
nslkp.diffs
|
||||||
|
|
||||||
|
The diff files only include changes to the *.[ch] files. You
|
||||||
|
will need to edit the makefiles yourself and you may need to include
|
||||||
|
the file res/res_debug.c.
|
||||||
3
net/tcpip/README.tcp98pl5-3
Normal file
3
net/tcpip/README.tcp98pl5-3
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
This is the third tcp patch to 0.98pl5. All three go in without any problem.
|
||||||
|
This one fixes raw sockets and a few other minor problems.
|
||||||
|
|
||||||
6
net/tcpip/README.tcp98pl5-p1
Normal file
6
net/tcpip/README.tcp98pl5-p1
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
This patch was made against pl4 + tcp diffs I sent to Linus. You may need
|
||||||
|
to apply some of them by hand.
|
||||||
|
|
||||||
|
Ross Biro bir7@leland.stanford.edu
|
||||||
|
Member League for Programming Freedom (LPF)
|
||||||
|
mail lpf@uunet.uu.net for information
|
||||||
7
net/tcpip/README.tcp98pl5-p2
Normal file
7
net/tcpip/README.tcp98pl5-p2
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
This diff fixes some problems with the first diff, and corrects a few
|
||||||
|
error returns. Hopefully some of the problems with accept, connect,
|
||||||
|
and shutdown should have vanished. Again these are not really against
|
||||||
|
pl5 but against pl4 + some of the pl5 diffs + patch1. You may need to
|
||||||
|
do these by hand also.
|
||||||
|
|
||||||
3
net/tcpip/README.telnetd-buggy
Normal file
3
net/tcpip/README.telnetd-buggy
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
This is the same BUGGY version of telnetd that was in earlier
|
||||||
|
releases. It has been recompiled to use a newer library and to
|
||||||
|
work with .98pl5. We still need a new version.
|
||||||
11
net/tcpip/depca-0.7.README
Normal file
11
net/tcpip/depca-0.7.README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
From: "pb@cs" <100136.3530@compuserve.com>
|
||||||
|
To: <arl@cs.hut.fi>
|
||||||
|
Subject: new depca-0.7 for pl13
|
||||||
|
Date: 26 Sep 93 18:31:24 EDT
|
||||||
|
|
||||||
|
Find enclosed depca-0.7. This version patches against the limux99pl13
|
||||||
|
kernel sources and shoud go where the other depca-driver went (I think
|
||||||
|
somewhere in Linux/BETA/depca). From the older versions there only the
|
||||||
|
0.5 version is worth keeping, because it patches against pl10 and 11.
|
||||||
|
|
||||||
|
Gruss PB
|
||||||
BIN
net/tcpip/depca-0.7.tar.z
Normal file
BIN
net/tcpip/depca-0.7.tar.z
Normal file
Binary file not shown.
347
net/tcpip/ether-995/3c503.c
Normal file
347
net/tcpip/ether-995/3c503.c
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with the 3c503 and 3c503/16. It must be used
|
||||||
|
in shared memory mode.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version = "3c503.c:v0.30 1/30/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
|
||||||
|
#include "8390.h"
|
||||||
|
#include "3c503reg.h"
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int etherlink2 = 0;
|
||||||
|
|
||||||
|
int el2autoprobe(int ioaddr, struct device *dev);
|
||||||
|
int el2probe(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void el2_reset_8390(struct device *dev);
|
||||||
|
static void el2_init_card(struct device *dev);
|
||||||
|
static void el2_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static int el2_block_input(struct device *dev, int count, char *buf,
|
||||||
|
int ring_offset);
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
el2autoprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *addr, addrs[] = { 0xddfff, 0xd9fff, 0xcdfff, 0xc9fff, 0};
|
||||||
|
int ports[] = {0, 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2a0, 0x2e0, 0};
|
||||||
|
|
||||||
|
/* Non-autoprobe case first: */
|
||||||
|
if (ioaddr > 0)
|
||||||
|
return el2probe(ioaddr, dev);
|
||||||
|
|
||||||
|
/* We check for a memory-mapped 3c503 board by looking at the
|
||||||
|
end of boot PROM space (works even if a PROM isn't there). */
|
||||||
|
for (addr = addrs; *addr; addr++) {
|
||||||
|
unsigned int base_bits = *(unsigned char *)*addr, i;
|
||||||
|
/* Find first set bit. */
|
||||||
|
for(i = 8; i; i--, base_bits >>= 1)
|
||||||
|
if (base_bits & 0x1)
|
||||||
|
break;
|
||||||
|
if (base_bits == 1 && el2probe(ports[i], dev))
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
#ifdef notdef
|
||||||
|
/* If it's not memory mapped, we don't care to find it. I haven't
|
||||||
|
tested the non-memory-mapped code. */
|
||||||
|
/* It's not memory mapped -- try all of the locations that aren't
|
||||||
|
obviously empty. */
|
||||||
|
{ int *port;
|
||||||
|
for (port = &ports[1]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && el2probe(*port, dev))
|
||||||
|
return dev->base_addr = *port;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Probe for the Etherlink II card at I/O port base IOADDR,
|
||||||
|
returning non-zero on sucess. If found, set the station
|
||||||
|
address and memory parameters in DEVICE. */
|
||||||
|
int
|
||||||
|
el2probe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i, found, mem_jumpers;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
|
||||||
|
/* We verify that it's a 3C503 board by checking the first three octets
|
||||||
|
of its ethernet address. */
|
||||||
|
printk("3c503 probe at %#3x:", ioaddr);
|
||||||
|
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
|
||||||
|
outb_p(ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
/* Map the station addr PROM into the lower I/O ports. */
|
||||||
|
outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||||
|
printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
|
||||||
|
}
|
||||||
|
/* Map the 8390 back into the window. */
|
||||||
|
outb(ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
found =( station_addr[0] == 0x02
|
||||||
|
&& station_addr[1] == 0x60
|
||||||
|
&& station_addr[2] == 0x8c);
|
||||||
|
if (! found) {
|
||||||
|
printk(" 3C503 not found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dev->base_addr = ioaddr;
|
||||||
|
/* Probe for, turn on and clear the board's shared memory. */
|
||||||
|
mem_jumpers = inb(ioaddr + 0x404); /* E33G_ROMBASE */
|
||||||
|
if (ei_debug > 2) printk(" memory jumpers %2.2x ", mem_jumpers);
|
||||||
|
outb(EGACFR_IRQOFF, ioaddr + 0x405); /* Enable RAM */
|
||||||
|
if ((mem_jumpers & 0xf0) == 0) {
|
||||||
|
dev->mem_start = 0;
|
||||||
|
if (ei_debug > 1) printk(" no shared memory ");
|
||||||
|
} else {
|
||||||
|
dev->mem_start = ((mem_jumpers & 0xc0) ? 0xD8000 : 0xC8000) +
|
||||||
|
((mem_jumpers & 0xA0) ? 0x4000 : 0);
|
||||||
|
|
||||||
|
#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
|
||||||
|
#ifdef EL2MEMTEST
|
||||||
|
{ /* Check the card's memory. */
|
||||||
|
int *mem_base = (int *)dev->mem_start;
|
||||||
|
int memtest_value = 0xbbadf00d;
|
||||||
|
mem_base[0] = 0xba5eba5e;
|
||||||
|
for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
|
||||||
|
mem_base[i] = memtest_value;
|
||||||
|
if (mem_base[0] != 0xba5eba5e
|
||||||
|
|| mem_base[i] != memtest_value) {
|
||||||
|
printk(" memory failure or memory address conflict.\n");
|
||||||
|
dev->mem_start = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memtest_value += 0x55555555;
|
||||||
|
mem_base[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* EL2MEMTEST */
|
||||||
|
/* Divide the on-board memory into a single maximum-sized transmit
|
||||||
|
(double-sized for ping-pong transmit) buffer at the base, and
|
||||||
|
use the rest as a receive ring. */
|
||||||
|
dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
|
||||||
|
dev->rmem_start = TX_PAGES*256 + dev->mem_start;
|
||||||
|
}
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
|
||||||
|
dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
|
||||||
|
|
||||||
|
/* Finish setting the board's parameters. */
|
||||||
|
etherlink2 = 1;
|
||||||
|
ei_status.tx_start_page = EL2SM_START_PG;
|
||||||
|
ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = EL2SM_STOP_PG;
|
||||||
|
ei_status.reset_8390 = &el2_reset_8390;
|
||||||
|
ei_status.block_input = &el2_block_input;
|
||||||
|
ei_status.block_output = &el2_block_output;
|
||||||
|
/* This should be probed for (or set via an ioctl()) at run-time someday. */
|
||||||
|
#if defined(EI8390_THICK) || defined(EL2_AUI)
|
||||||
|
ei_status.thin_bit = 0;
|
||||||
|
#else
|
||||||
|
ei_status.thin_bit = ECNTRL_THIN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int irqlist[] = {5, 2, 3, 4, 0};
|
||||||
|
int *irqp = irqlist;
|
||||||
|
do {
|
||||||
|
if (irqaction (dev->irq = *irqp, &ei_sigaction) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (*++irqp);
|
||||||
|
if (*irqp == 0) {
|
||||||
|
printk("\n3c503: Unable to find an free IRQ line.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dev->irq == 2)
|
||||||
|
dev->irq = 9;
|
||||||
|
else if (dev->irq > 5 && dev->irq != 9) {
|
||||||
|
printk("\n3c503: configured interrupt number %d out of range.\n",
|
||||||
|
dev->irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (irqaction (dev->irq, &ei_sigaction)) {
|
||||||
|
printk ("\n3c503: Unable to get IRQ%d.\n", dev->irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
el2_init_card(dev);
|
||||||
|
|
||||||
|
if (dev->mem_start)
|
||||||
|
printk("3c503 found, memory at %#6x, IRQ %d\n",
|
||||||
|
dev->mem_start, dev->irq);
|
||||||
|
else
|
||||||
|
printk(" 3c503 found, no shared memory, IRQ %d\n", dev->irq);
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
return ioaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called whenever we have a unrecoverable failure:
|
||||||
|
transmit timeout
|
||||||
|
Bad ring buffer packet header
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
el2_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
if (ei_debug > 1) printk("3c503: Resetting the board...");
|
||||||
|
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
el2_init_card(dev);
|
||||||
|
if (ei_debug > 1) printk("done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the 3c503 GA registers after a reset. */
|
||||||
|
static void
|
||||||
|
el2_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
/* Unmap the station PROM and select the DIX or BNC connector. */
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* Set ASIC copy of rx's first and last+1 buffer pages */
|
||||||
|
/* These must be the same as in the 8390. */
|
||||||
|
outb(ei_status.rx_start_page, E33G_STARTPG);
|
||||||
|
outb(ei_status.stop_page, E33G_STOPPG);
|
||||||
|
|
||||||
|
/* Point the vector pointer registers somewhere ?harmless?. */
|
||||||
|
outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
|
||||||
|
outb(0xff, E33G_VP1);
|
||||||
|
outb(0x00, E33G_VP0);
|
||||||
|
/* Turn off all interrupts until we're opened. */
|
||||||
|
outb_p(0x00, dev->base_addr + EN0_IMR);
|
||||||
|
outb_p(EGACFR_IRQOFF, E33G_GACFR);
|
||||||
|
|
||||||
|
/* Set the interrupt line. */
|
||||||
|
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
|
||||||
|
outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
|
||||||
|
outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
|
||||||
|
outb_p(0x00, E33G_DMAAL);
|
||||||
|
return; /* We always succeed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either use the shared memory (if enabled on the board) or put the packet
|
||||||
|
out through the ASIC FIFO. The latter is probably much slower. */
|
||||||
|
static void
|
||||||
|
el2_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page)
|
||||||
|
{
|
||||||
|
int i; /* Buffer index */
|
||||||
|
int boguscount = 0; /* timeout counter */
|
||||||
|
if (dev->mem_start) { /* Shared memory transfer */
|
||||||
|
void *dest_addr = (void *)(dev->mem_start +
|
||||||
|
((start_page - ei_status.tx_start_page) << 8));
|
||||||
|
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM */
|
||||||
|
memcpy(dest_addr, buf, count);
|
||||||
|
if (ei_debug > 2 && memcmp(dest_addr, buf, count))
|
||||||
|
printk("3c503: send_packet() bad memory copy @ %#5x.\n",
|
||||||
|
dest_addr);
|
||||||
|
else if (ei_debug > 4)
|
||||||
|
printk("3c503: send_packet() good memory copy @ %#5x.\n",
|
||||||
|
dest_addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Set up then start the internal memory transfer to Tx Start Page */
|
||||||
|
outb(0x00, E33G_DMAAL);
|
||||||
|
outb(start_page, E33G_DMAAH);
|
||||||
|
outb(ei_status.thin_bit | ECNTRL_OUTPUT | ECNTRL_START, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* This is the byte copy loop: it should probably be tuned for
|
||||||
|
for speed once everything is working. I think it is possible
|
||||||
|
to output 8 bytes between each check of the status bit. */
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
if (count % 8 == 7)
|
||||||
|
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||||
|
if (++boguscount > 32) {
|
||||||
|
printk(EI_NAME": fifo blocked in el2_block_output.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outb(buf[i], E33G_FIFOH);
|
||||||
|
}
|
||||||
|
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the new ring pointer. */
|
||||||
|
static int
|
||||||
|
el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int boguscount = 0;
|
||||||
|
int end_of_ring = dev->rmem_end;
|
||||||
|
ring_offset -= (EL2SM_START_PG<<8);
|
||||||
|
|
||||||
|
/* Maybe enable shared memory just be to be safe... nahh.*/
|
||||||
|
if (dev->mem_start) { /* Use the shared memory. */
|
||||||
|
if (dev->mem_start + ring_offset + count > end_of_ring) {
|
||||||
|
/* We must wrap the input move. */
|
||||||
|
int semi_count = end_of_ring - (dev->mem_start + ring_offset);
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("3c503: block_input() @ %#5x+%x=%5x.\n",
|
||||||
|
dev->mem_start, ring_offset,
|
||||||
|
(char *)dev->mem_start + ring_offset);
|
||||||
|
memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
|
||||||
|
count -= semi_count;
|
||||||
|
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||||
|
return dev->rmem_start + count;
|
||||||
|
}
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("3c503: block_input() @ %#5x+%x=%5x.\n",
|
||||||
|
dev->mem_start, ring_offset,
|
||||||
|
(char *)dev->mem_start + ring_offset);
|
||||||
|
memcpy(buf, (char *)dev->mem_start + ring_offset, count);
|
||||||
|
return ring_offset + count;
|
||||||
|
} else { /* No shared memory, use the fifo. */
|
||||||
|
int i;
|
||||||
|
outb(ring_offset & 0xff, E33G_DMAAL);
|
||||||
|
outb((ring_offset >> 8) & 0xff, E33G_DMAAH);
|
||||||
|
outb(ei_status.thin_bit | ECNTRL_INPUT | ECNTRL_START, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* This is the byte copy loop: it should probably be tuned for
|
||||||
|
for speed once everything is working. */
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
if (count % 8 == 7)
|
||||||
|
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||||
|
if (++boguscount > 32) {
|
||||||
|
printk(EI_NAME": fifo blocked in el2_block_input().\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
buf[i] = inb(E33G_FIFOH);
|
||||||
|
}
|
||||||
|
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
ring_offset += count;
|
||||||
|
if (ring_offset >= end_of_ring)
|
||||||
|
ring_offset = dev->rmem_start + ring_offset - end_of_ring;
|
||||||
|
return ring_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
59
net/tcpip/ether-995/3c503reg.h
Normal file
59
net/tcpip/ether-995/3c503reg.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/* Definitions for the 3Com 3c503 Etherlink 2. */
|
||||||
|
/* This file is part of Donald Becker's 8390 drivers.
|
||||||
|
This file is distributed under the Linux GPL.
|
||||||
|
Some of these names and comments are from the Crynwr packet drivers. */
|
||||||
|
|
||||||
|
#define EL2H (dev->base_addr + 0x400)
|
||||||
|
#define EL2L (dev->base_addr)
|
||||||
|
|
||||||
|
/* Shared memory management parameters */
|
||||||
|
|
||||||
|
#define EL2SM_START_PG (0x20) /* First page of TX buffer */
|
||||||
|
#define EL2SM_STOP_PG (0x40) /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
/* 3Com 3c503 ASIC registers */
|
||||||
|
#define E33G_STARTPG (EL2H+0) /* Start page, must match EN0_STARTPG */
|
||||||
|
#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
|
||||||
|
#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
|
||||||
|
#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
|
||||||
|
/* (non-useful, but it also appears at the end of EPROM space) */
|
||||||
|
#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
|
||||||
|
#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
|
||||||
|
#define E33G_CNTRL (EL2H+6) /* Board's main control register */
|
||||||
|
#define E33G_STATUS (EL2H+7) /* Status on completions. */
|
||||||
|
#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
|
||||||
|
/* (Which IRQ to assert, DMA chan to use) */
|
||||||
|
#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
|
||||||
|
#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
|
||||||
|
/* "Vector pointer" - if this address matches a read, the EPROM (rather than
|
||||||
|
shared RAM) is mapped into memory space. */
|
||||||
|
#define E33G_VP2 (EL2H+11)
|
||||||
|
#define E33G_VP1 (EL2H+12)
|
||||||
|
#define E33G_VP0 (EL2H+13)
|
||||||
|
#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
|
||||||
|
#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
|
||||||
|
|
||||||
|
/* Bits in E33G_CNTRL register: */
|
||||||
|
|
||||||
|
#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
|
||||||
|
#define ECNTRL_THIN (0x02) /* Onboard thin-net xcvr enable */
|
||||||
|
#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
|
||||||
|
#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
|
||||||
|
#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
|
||||||
|
#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
|
||||||
|
#define ECNTRL_START (0x80) /* Start the DMA logic */
|
||||||
|
|
||||||
|
/* Bits in E33G_STATUS register: */
|
||||||
|
|
||||||
|
#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
|
||||||
|
#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
|
||||||
|
#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
|
||||||
|
#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
|
||||||
|
#define ESTAT_DIP (0x08) /* DMA In Progress */
|
||||||
|
|
||||||
|
/* Bits in E33G_GACFR register: */
|
||||||
|
|
||||||
|
#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */
|
||||||
|
#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */
|
||||||
|
|
||||||
|
/* End of 3C503 parameter definitions */
|
||||||
731
net/tcpip/ether-995/8390.c
Normal file
731
net/tcpip/ether-995/8390.c
Normal file
@@ -0,0 +1,731 @@
|
|||||||
|
/* 8390.c: A general NS8390 ethernet driver core for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with many 8390-based ethernet adaptors.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"8390.c:v0.43 2/12/93 for 0.99.5+ Donald Becker (becker@super.org)\n";
|
||||||
|
#include <linux/config.h>
|
||||||
|
#if !defined(EL2) && !defined(NE2000) && !defined(WD80x3) && !defined(HPLAN)
|
||||||
|
/* They don't know what they want -- give it all to them! */
|
||||||
|
#define EL2
|
||||||
|
#define NE2000
|
||||||
|
#define WD80x3
|
||||||
|
#define HPLAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Braindamage remaining:
|
||||||
|
|
||||||
|
Ethernet devices should use a chr_drv device interface, with ioctl()s to
|
||||||
|
configure the card, bring the interface up or down, allow access to
|
||||||
|
statistics, and maybe read() and write() access to raw packets.
|
||||||
|
This won't be done until after Linux 1.00.
|
||||||
|
|
||||||
|
This driver should support multiple, diverse boards simultaneousely.
|
||||||
|
This won't be done until after Linux 1.00.
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
The National Semiconductor LAN Databook, and 3Com databooks, both companies
|
||||||
|
provided information readily. The NE* info came from the Crynwr packet
|
||||||
|
driver, and figuring out that the those boards are similar to the NatSemi
|
||||||
|
evaluation board described in AN-729. Thanks NS, no thanks to Novell/Eagle.
|
||||||
|
Cabletron provided only info I had already gotten from other sources -- hiss.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/tty.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include <asm/segment.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <linux/fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "eth.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "ip.h"
|
||||||
|
#include "tcp.h"
|
||||||
|
#include "sock.h"
|
||||||
|
#include "arp.h"
|
||||||
|
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
#define ei_reset_8390 (ei_status.reset_8390)
|
||||||
|
#define ei_block_output (ei_status.block_output)
|
||||||
|
#define ei_block_input (ei_status.block_input)
|
||||||
|
|
||||||
|
#define EN_CMD (e8390_base)
|
||||||
|
#define E8390_BASE (e8390_base)
|
||||||
|
|
||||||
|
/* use 0 for production, 1 for verification, >2 for debug */
|
||||||
|
#ifdef EI_DEBUG
|
||||||
|
int ei_debug = EI_DEBUG;
|
||||||
|
#else
|
||||||
|
int ei_debug = 2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int e8390_base;
|
||||||
|
|
||||||
|
static struct device *eifdev; /* For single-board consistency checking. */
|
||||||
|
extern int etherlink2;
|
||||||
|
|
||||||
|
struct ei_device ei_status = { EI_NAME, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
/* The statistics, perhaps these should be in the above structure. */
|
||||||
|
static int tx_packets = 0;
|
||||||
|
static int rx_packets = 0;
|
||||||
|
static int tx_errors = 0;
|
||||||
|
static int soft_rx_errors = 0;
|
||||||
|
static int soft_rx_err_bits = 0;
|
||||||
|
static int missed_packets = 0;
|
||||||
|
static int rx_overrun_packets = 0;
|
||||||
|
/* Max number of packets received at one Intr. */
|
||||||
|
/*static int high_water_mark = 0;*/
|
||||||
|
|
||||||
|
/* Index to functions. */
|
||||||
|
/* Put in the device structure. */
|
||||||
|
static int ei_open(struct device *dev);
|
||||||
|
static void ei_send_packet(struct sk_buff *skb, struct device *dev);
|
||||||
|
/* Dispatch from interrupts. */
|
||||||
|
static void ei_interrupt(int reg_ptr);
|
||||||
|
static void ei_tx_intr(struct device *dev);
|
||||||
|
static void ei_receive(struct device *dev);
|
||||||
|
static void ei_rx_overrun(struct device *dev);
|
||||||
|
|
||||||
|
/* Routines generic to NS8390-based boards. */
|
||||||
|
void NS8390_init(struct device *dev, int startp);
|
||||||
|
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||||
|
int start_page);
|
||||||
|
|
||||||
|
extern int el2autoprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int el2probe(int ioaddr, struct device *dev);
|
||||||
|
extern int neprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int wdprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int hpprobe(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
|
||||||
|
|
||||||
|
/* Open/initialize the board. This routine goes all-out, setting everything
|
||||||
|
up anew at each open, even though many of these registers should only
|
||||||
|
need to be set once at boot.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ei_open(struct device *dev)
|
||||||
|
{
|
||||||
|
if ( ! ei_status.exists) {
|
||||||
|
printk(EI_NAME ": Opening a non-existent physical device\n");
|
||||||
|
return 1; /* ENXIO would be more accurate. */
|
||||||
|
}
|
||||||
|
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
|
||||||
|
/* The old local flags... */
|
||||||
|
ei_status.txing = 0;
|
||||||
|
ei_status.in_interrupt = 0;
|
||||||
|
ei_status.open = 1;
|
||||||
|
/* ... are now global. */
|
||||||
|
dev->tbusy = 0;
|
||||||
|
dev->interrupt = 0;
|
||||||
|
dev->start = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ei_start_xmit(struct sk_buff *skb, struct device *dev)
|
||||||
|
{
|
||||||
|
if ( ! ei_status.exists)
|
||||||
|
return 0; /* We should be able to do ENODEV, but nooo. */
|
||||||
|
|
||||||
|
if (ei_status.txing) { /* Do timeouts, just like the 8003 driver. */
|
||||||
|
int txsr = inb(E8390_BASE+EN0_TSR);
|
||||||
|
int tickssofar = jiffies - dev->trans_start;
|
||||||
|
if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printk(EI_NAME": transmit timed out, TX status %#2x, ISR %#2x.\n",
|
||||||
|
txsr, inb(E8390_BASE+EN0_ISR));
|
||||||
|
/* It's possible to check for an IRQ conflict here.
|
||||||
|
I may have to do that someday. */
|
||||||
|
if ((txsr & ~0x02) == ENTSR_PTX) /* Strip an undefined bit. */
|
||||||
|
printk(EI_NAME": Possible IRQ conflict?\n");
|
||||||
|
else
|
||||||
|
printk(EI_NAME": Possible network cable problem?\n");
|
||||||
|
/* It futile, but try to restart it anyway. */
|
||||||
|
ei_reset_8390(dev);
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is new: it means some higher layer thinks we've missed an
|
||||||
|
tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
|
||||||
|
itself. */
|
||||||
|
if (skb == NULL) {
|
||||||
|
#ifdef pre_995
|
||||||
|
/* Alternative is ei_tx_intr(dev); */
|
||||||
|
ei_status.txing = 1;
|
||||||
|
if (dev_tint(NULL, dev) == 0)
|
||||||
|
ei_status.txing = 0;
|
||||||
|
#else
|
||||||
|
dev_tint(dev);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Fill in the ethernet header. */
|
||||||
|
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
|
||||||
|
skb->dev = dev;
|
||||||
|
arp_queue (skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->trans_start = jiffies;
|
||||||
|
cli();
|
||||||
|
#ifdef PINGPONG
|
||||||
|
ei_tx_intr(dev);
|
||||||
|
#endif
|
||||||
|
ei_send_packet(skb, dev);
|
||||||
|
sti();
|
||||||
|
if (skb->free)
|
||||||
|
kfree_skb (skb, FREE_WRITE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The typical workload of the driver:
|
||||||
|
Handle the ether interface interrupts. */
|
||||||
|
static void
|
||||||
|
ei_interrupt(int reg_ptr)
|
||||||
|
{
|
||||||
|
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
|
||||||
|
struct device *dev;
|
||||||
|
int interrupts, boguscount = 0;
|
||||||
|
|
||||||
|
/* We do the same thing as the 8013 driver, but this is mostly bogus. */
|
||||||
|
for (dev = dev_base; dev != NULL; dev = dev->next) {
|
||||||
|
if (dev->irq == irq) break;
|
||||||
|
}
|
||||||
|
dev->interrupt = 1;
|
||||||
|
ei_status.in_interrupt++;
|
||||||
|
sti(); /* Allow other interrupts. */
|
||||||
|
|
||||||
|
#ifdef notneeded
|
||||||
|
/* If we a getting a reset-complete interrupt the 8390 might not be
|
||||||
|
mapped in for the 3c503. */
|
||||||
|
if (etherlink2)
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL),
|
||||||
|
outb_p(0x00, E33G_STATUS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Change to page 0 and read the intr status reg. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, EN_CMD);
|
||||||
|
if (ei_debug > 3)
|
||||||
|
printk(EI_NAME": interrupt(isr=%#2.2x).\n",
|
||||||
|
inb_p(E8390_BASE + EN0_ISR));
|
||||||
|
|
||||||
|
if (ei_status.in_interrupt > 1)
|
||||||
|
printk(EI_NAME ": Reentering the interrupt driver!\n");
|
||||||
|
if (dev == NULL) {
|
||||||
|
printk (EI_NAME ": irq %d for unknown device\n", irq);
|
||||||
|
ei_status.in_interrupt--;
|
||||||
|
return;
|
||||||
|
} else if (ei_debug > 0 && eifdev != dev) {
|
||||||
|
printk (EI_NAME": device mismatch on irq %d.\n", irq);
|
||||||
|
dev = eifdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* !!Assumption!! -- we stay in page 0. Don't break this. */
|
||||||
|
while ((interrupts = inb_p(E8390_BASE + EN0_ISR)) != 0
|
||||||
|
&& ++boguscount < 20) {
|
||||||
|
/* The reset interrupt is the most important... */
|
||||||
|
if (interrupts & ENISR_RDC) {
|
||||||
|
outb_p(ENISR_RDC, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
}
|
||||||
|
if (interrupts & ENISR_OVER) {
|
||||||
|
ei_status.overruns++;
|
||||||
|
ei_rx_overrun(dev);
|
||||||
|
} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
|
||||||
|
/* Got a good (?) packet. */
|
||||||
|
ei_receive(dev);
|
||||||
|
}
|
||||||
|
/* Push the next to-transmit packet through. */
|
||||||
|
if (interrupts & ENISR_TX) {
|
||||||
|
ei_tx_intr(dev);
|
||||||
|
} else if (interrupts & ENISR_COUNTERS) {
|
||||||
|
/* Gotta read the counter to clear the irq, even if we
|
||||||
|
don't care about their values. */
|
||||||
|
inb_p(E8390_BASE + EN0_COUNTER0);
|
||||||
|
inb_p(E8390_BASE + EN0_COUNTER1);
|
||||||
|
missed_packets += inb_p(E8390_BASE + EN0_COUNTER2);
|
||||||
|
outb_p(ENISR_COUNTERS, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, EN_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore the transmit errs and reset intr for now. */
|
||||||
|
if (interrupts & ENISR_TX_ERR) {
|
||||||
|
outb_p(ENISR_TX_ERR, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
}
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, EN_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupts && ei_debug) {
|
||||||
|
printk(EI_NAME ": unknown interrupt %#2x\n", interrupts);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
|
||||||
|
outb_p(0xff, E8390_BASE + EN0_ISR); /* Ack. all intrs. */
|
||||||
|
}
|
||||||
|
ei_status.in_interrupt--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
static int lasttx = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is stuffed into the dev struct to be called by dev.c:dev_tint().
|
||||||
|
Evenually this should be replaced by the block_output() routines. */
|
||||||
|
static void
|
||||||
|
ei_send_packet(struct sk_buff *skb, struct device *dev)
|
||||||
|
{
|
||||||
|
int length = skb->len;
|
||||||
|
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
|
||||||
|
|
||||||
|
if (length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
if (ei_status.tx1 == 0) { /* First buffer empty */
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1),
|
||||||
|
ei_status.tx_start_page);
|
||||||
|
ei_status.tx1 = send_length;
|
||||||
|
} else if (ei_status.tx2 == 0) { /* Second buffer empty */
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1),
|
||||||
|
ei_status.tx_start_page+6);
|
||||||
|
ei_status.tx2 = send_length;
|
||||||
|
} else {
|
||||||
|
printk("%s: Internal error, no transmit buffer space tx1=%d tx2=%d lasttx=%d.\n",
|
||||||
|
ei_status.name, ei_status.tx1, ei_status.tx2, lasttx);
|
||||||
|
}
|
||||||
|
ei_status.txqueue++;
|
||||||
|
/* The following should be merged with ei_tx_intr(). */
|
||||||
|
if (lasttx = 0) {
|
||||||
|
if (ei_status.tx1 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx1,
|
||||||
|
ei_status.tx_start_page + 6),
|
||||||
|
ei_status.tx1 = -1,
|
||||||
|
lasttx = 1;
|
||||||
|
} else if (ei_status.tx2 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx2,
|
||||||
|
ei_status.tx_start_page + 6),
|
||||||
|
ei_status.tx2 = -1,
|
||||||
|
lasttx = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1), ei_status.tx_start_page);
|
||||||
|
NS8390_trigger_send(dev, send_length, ei_status.tx_start_page);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have finished a transmit: check for errors and then trigger the next
|
||||||
|
packet to be sent. */
|
||||||
|
static void
|
||||||
|
ei_tx_intr(struct device *dev)
|
||||||
|
{
|
||||||
|
int status = inb(E8390_BASE + EN0_TSR);
|
||||||
|
outb_p(ENISR_TX, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
if ((status & ENTSR_PTX) == 0)
|
||||||
|
tx_errors++;
|
||||||
|
else
|
||||||
|
tx_packets++;
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
ei_status.txqueue--;
|
||||||
|
if (ei_status.tx1 < 0) {
|
||||||
|
uif (lasttx != 1)
|
||||||
|
printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
|
||||||
|
ei_status.name, lasttx, ei_status.tx1);
|
||||||
|
ei_status.tx1 = 0;
|
||||||
|
lasttx = 0;
|
||||||
|
if (ei_status.tx2 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx2,
|
||||||
|
ei_status.tx_start_page + 6),
|
||||||
|
ei_status.tx2 = -1,
|
||||||
|
lasttx = 2;
|
||||||
|
}
|
||||||
|
} else if (ei_status.tx2 < 0) {
|
||||||
|
if (lasttx != 2)
|
||||||
|
printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
|
||||||
|
ei_status.name, lasttx, ei_status.tx2);
|
||||||
|
ei_status.tx2 = 0;
|
||||||
|
lasttx = 0;
|
||||||
|
if (ei_status.tx1 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx1,
|
||||||
|
ei_status.tx_start_page),
|
||||||
|
ei_status.tx1 = -1;
|
||||||
|
lasttx = 1;
|
||||||
|
}
|
||||||
|
} /*else
|
||||||
|
printk(EI_NAME": unexpected TX interrupt.\n");*/
|
||||||
|
while ((ei_status.tx1 == 0) || (ei_status.tx2 == 0)) {
|
||||||
|
dev->tbusy = 0;
|
||||||
|
dev_tint(dev);
|
||||||
|
if (dev->tbusy)
|
||||||
|
return;
|
||||||
|
else if (lasttx == 0) {
|
||||||
|
if (ei_status.tx1 == 0 || ei_status.tx2 != 0)
|
||||||
|
printk(EI_NAME": Unexpected tx buffer busy tx1=%d tx2=%d.\n",
|
||||||
|
ei_status.tx1, ei_status.tx2);
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx1,
|
||||||
|
ei_status.tx_start_page),
|
||||||
|
ei_status.tx1 = -1;
|
||||||
|
lasttx = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ei_status.txing = 0;
|
||||||
|
dev->tbusy = 0;
|
||||||
|
#ifdef pre_995
|
||||||
|
dev_tint(NULL, dev)
|
||||||
|
#else
|
||||||
|
mark_bh (INET_BH);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have a good packet(s), get it/them out of the buffers. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
ei_receive(struct device *dev)
|
||||||
|
{
|
||||||
|
int rxing_page, this_frame, next_frame, current_offset;
|
||||||
|
int boguscount = 0;
|
||||||
|
struct e8390_pkt_hdr rx_frame;
|
||||||
|
int num_rx_pages = ei_status.stop_page-ei_status.rx_start_page;
|
||||||
|
|
||||||
|
while (++boguscount < 10) {
|
||||||
|
int size;
|
||||||
|
|
||||||
|
cli();
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE1, EN_CMD); /* Get the rec. page. */
|
||||||
|
rxing_page = inb_p(E8390_BASE+EN1_CURPAG);/* (Incoming packet pointer).*/
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, EN_CMD);
|
||||||
|
sti();
|
||||||
|
|
||||||
|
/* Remove one frame from the ring. Boundary is alway a page behind. */
|
||||||
|
this_frame = inb_p(E8390_BASE + EN0_BOUNDARY) + 1;
|
||||||
|
if (this_frame >= ei_status.stop_page)
|
||||||
|
this_frame = ei_status.rx_start_page;
|
||||||
|
|
||||||
|
/* Someday we'll omit the previous step, iff we never get this message.*/
|
||||||
|
if (ei_debug > 0 && this_frame != ei_status.current_page)
|
||||||
|
printk(EI_NAME": mismatched read page pointers %2x vs %2x.\n",
|
||||||
|
this_frame, ei_status.current_page);
|
||||||
|
|
||||||
|
if (this_frame == rxing_page) /* Read all the frames? */
|
||||||
|
break; /* Done for now */
|
||||||
|
|
||||||
|
current_offset = this_frame << 8;
|
||||||
|
ei_block_input(dev, sizeof(rx_frame), (void *)&rx_frame,
|
||||||
|
current_offset);
|
||||||
|
|
||||||
|
size = rx_frame.count - sizeof(rx_frame);
|
||||||
|
|
||||||
|
next_frame = this_frame + 1 + ((size+4)>>8);
|
||||||
|
|
||||||
|
/* Check for bogosity warned by 3c503 book: the status byte is never
|
||||||
|
written. This happened a lot during testing! This code should be
|
||||||
|
cleaned up someday, and the printk()s should be PRINTK()s. */
|
||||||
|
if ( rx_frame.next != next_frame
|
||||||
|
&& rx_frame.next != next_frame + 1
|
||||||
|
&& rx_frame.next != next_frame - num_rx_pages
|
||||||
|
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
|
||||||
|
#ifndef EI_DEBUG
|
||||||
|
ei_status.current_page = rxing_page;
|
||||||
|
outb(ei_status.current_page-1, E8390_BASE+EN0_BOUNDARY);
|
||||||
|
continue;
|
||||||
|
#else
|
||||||
|
static int last_rx_bogosity = -1;
|
||||||
|
printk(EI_NAME": bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
|
||||||
|
rx_frame.status, rx_frame.next, rx_frame.count, current_offset);
|
||||||
|
|
||||||
|
if (rx_packets != last_rx_bogosity) {
|
||||||
|
/* Maybe we can avoid resetting the chip... empty the packet ring. */
|
||||||
|
ei_status.current_page = rxing_page;
|
||||||
|
printk(EI_NAME": setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
|
||||||
|
ei_status.current_page, next_frame,
|
||||||
|
rx_frame.next, rx_frame.status);
|
||||||
|
last_rx_bogosity = rx_packets;
|
||||||
|
outb(ei_status.current_page-1, E8390_BASE+EN0_BOUNDARY);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* Oh no Mr Bill! Last ditch error recovery. */
|
||||||
|
printk(EI_NAME": multiple sequential lossage, resetting at packet #%d.",
|
||||||
|
rx_packets);
|
||||||
|
sti();
|
||||||
|
ei_reset_8390(dev);
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
printk("restarting.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif /* EI8390_NOCHECK */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size < 32 || size > 1535) && ei_debug)
|
||||||
|
printk(EI_NAME": bogus packet size, status=%#2x nxpg=%#2x size=%#x\n",
|
||||||
|
rx_frame.status, rx_frame.next, rx_frame.count);
|
||||||
|
if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
|
||||||
|
int sksize = sizeof(struct sk_buff) + size;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
skb = kmalloc(sksize, GFP_ATOMIC);
|
||||||
|
if (skb != NULL) {
|
||||||
|
skb->lock = 0;
|
||||||
|
skb->mem_len = sksize;
|
||||||
|
skb->mem_addr = skb;
|
||||||
|
/* 'skb+1' points to the start of sk_buff data area. */
|
||||||
|
ei_block_input(dev, size, (void *)(skb+1),
|
||||||
|
current_offset + sizeof(rx_frame));
|
||||||
|
if(dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
|
||||||
|
printk(EI_NAME": receive buffers full.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (ei_debug) {
|
||||||
|
printk(EI_NAME": Couldn't allocate a sk_buff of size %d.\n", sksize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rx_packets++;
|
||||||
|
} else {
|
||||||
|
if (ei_debug)
|
||||||
|
printk(EI_NAME": bogus packet, status=%#2x nxpg=%#2x size=%d\n",
|
||||||
|
rx_frame.status, rx_frame.next, rx_frame.count);
|
||||||
|
soft_rx_err_bits |= rx_frame.status,
|
||||||
|
soft_rx_errors++;
|
||||||
|
}
|
||||||
|
next_frame = rx_frame.next;
|
||||||
|
|
||||||
|
/* This should never happen, it's here for debugging. */
|
||||||
|
if (next_frame >= ei_status.stop_page) {
|
||||||
|
printk(EI_NAME": next frame inconsistency, %#2x..", next_frame);
|
||||||
|
next_frame = ei_status.rx_start_page;
|
||||||
|
}
|
||||||
|
ei_status.current_page += 1 + ((size+4)>>8);
|
||||||
|
#ifdef notdef
|
||||||
|
if (ei_status.current_page > ei_status.stop_page)
|
||||||
|
ei_status.current_page -= ei_status.stop_page-ei_status.rx_start_page;
|
||||||
|
if (ei_status.current_page != next_frame) {
|
||||||
|
printk(EI_NAME": inconsistency in next_frame %#2x!=%#2x.\n",
|
||||||
|
this_frame, next_frame);
|
||||||
|
/* Assume this packet frame is scrogged by the NIC, use magic to
|
||||||
|
skip to the next frame. Actually we should stop and restart.*/
|
||||||
|
next_frame = size > 1535 ? rx_frame.status : rx_frame.next;
|
||||||
|
ei_status.current_page = next_frame;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ei_status.current_page = next_frame;
|
||||||
|
outb(next_frame-1, E8390_BASE+EN0_BOUNDARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tell the upper levels we're done. */
|
||||||
|
while (dev_rint(NULL, 0, 0, dev) == 1
|
||||||
|
&& ++boguscount < 20)
|
||||||
|
;
|
||||||
|
/* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
|
||||||
|
outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, E8390_BASE+EN0_ISR); /* Ack intr. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have a receiver overrun: we have to kick the 8390 to get it started
|
||||||
|
again. Overruns are detected on a per-256byte-page basis. */
|
||||||
|
static void
|
||||||
|
ei_rx_overrun(struct device *dev)
|
||||||
|
{
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
/* We should already be stopped and in page0. Remove after testing. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD);
|
||||||
|
|
||||||
|
if (ei_debug)
|
||||||
|
printk(EI_NAME ": Receiver overrun.\n");
|
||||||
|
|
||||||
|
/* The we.c driver does dummy = inb_p( RBCR[01] ); at this point.
|
||||||
|
It might mean something -- magic to speed up a reset? A 8390 bug?*/
|
||||||
|
|
||||||
|
/* Wait for reset in case the NIC is doing a tx or rx. This could take up to
|
||||||
|
1.5msec, but we have no way of timing something in that range. The 'jiffies'
|
||||||
|
are just a sanity check. */
|
||||||
|
while ((inb_p(E8390_BASE+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 1) {
|
||||||
|
printk(EI_NAME": reset did not complete at ei_rx_overrun.\n");
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
int old_rx_packets = rx_packets;
|
||||||
|
/* Remove packets right away. */
|
||||||
|
ei_receive(dev);
|
||||||
|
rx_overrun_packets += (rx_packets - old_rx_packets);
|
||||||
|
}
|
||||||
|
outb_p(0xff, E8390_BASE+EN0_ISR);
|
||||||
|
/* Generic 8390 insns to start up again, same as in open_8390(). */
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, EN_CMD);
|
||||||
|
outb_p(E8390_TXCONFIG, E8390_BASE + EN0_TXCR); /* xmit on. */
|
||||||
|
#ifdef notneeded
|
||||||
|
outb_p(E8390_RXCONFIG, E8390_BASE + EN0_RXCR); /* rx on, */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ethif_init(struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
eifdev = dev; /* Store for debugging. */
|
||||||
|
|
||||||
|
if (ei_debug > 3)
|
||||||
|
printk(version);
|
||||||
|
if (1
|
||||||
|
#ifdef WD80x3
|
||||||
|
&& ! wdprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef EL2
|
||||||
|
&& ! el2autoprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef NE2000
|
||||||
|
&& ! neprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef HPLAN
|
||||||
|
&& ! hpprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
&& 1 ) {
|
||||||
|
dev->open = &ei_open;
|
||||||
|
printk("No ethernet device found.\n");
|
||||||
|
ei_status.exists = 0;
|
||||||
|
return 1; /* ENODEV or EAGAIN would be more accurate. */
|
||||||
|
}
|
||||||
|
|
||||||
|
e8390_base = dev->base_addr;
|
||||||
|
|
||||||
|
/* Initialize the rest of the device structure. Many of these could
|
||||||
|
be in Space.c. */
|
||||||
|
for (i = 0; i < DEV_NUMBUFFS; i++)
|
||||||
|
dev->buffs[i] = NULL;
|
||||||
|
|
||||||
|
ei_status.exists = 1;
|
||||||
|
dev->hard_header = eth_hard_header;
|
||||||
|
dev->add_arp = eth_add_arp;
|
||||||
|
dev->queue_xmit = dev_queue_xmit;
|
||||||
|
dev->rebuild_header = eth_rebuild_header;
|
||||||
|
dev->type_trans = eth_type_trans;
|
||||||
|
|
||||||
|
dev->send_packet = &ei_send_packet;
|
||||||
|
dev->open = &ei_open;
|
||||||
|
dev->hard_start_xmit = &ei_start_xmit;
|
||||||
|
|
||||||
|
dev->type = ETHER_TYPE;
|
||||||
|
dev->hard_header_len = sizeof (struct enet_header);
|
||||||
|
dev->mtu = 1500; /* eth_mtu */
|
||||||
|
dev->addr_len = ETHER_ADDR_LEN;
|
||||||
|
for (i = 0; i < dev->addr_len; i++) {
|
||||||
|
dev->broadcast[i]=0xff;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This page of functions should be 8390 generic */
|
||||||
|
/* Follow National Semi's recommendations for initializing the "NIC". */
|
||||||
|
void NS8390_init(struct device *dev, int startp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int endcfg = ei_status.word16 ? (0x48 | ENDCFG_WTS) : 0x48;
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
|
||||||
|
/* Follow National Semi's recommendations for initing the DP83902. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
|
||||||
|
outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
|
||||||
|
/* Clear the remote byte count registers. */
|
||||||
|
outb_p(0x00, e8390_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, e8390_base + EN0_RCNTHI);
|
||||||
|
/* Set to monitor and loopback mode -- this is vital!. */
|
||||||
|
outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
|
||||||
|
outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
|
||||||
|
/* Set the transmit page and receive ring. */
|
||||||
|
outb_p(ei_status.tx_start_page, e8390_base + EN0_TPSR);
|
||||||
|
ei_status.tx1 = ei_status.tx2 = 0;
|
||||||
|
outb_p(ei_status.rx_start_page, e8390_base + EN0_STARTPG);
|
||||||
|
outb_p(ei_status.stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
|
||||||
|
ei_status.current_page = ei_status.rx_start_page; /* assert boundary+1 */
|
||||||
|
outb_p(ei_status.stop_page, e8390_base + EN0_STOPPG);
|
||||||
|
/* Clear the pending interrupts and mask. */
|
||||||
|
outb_p(0xFF, e8390_base + EN0_ISR);
|
||||||
|
outb_p(0x00, e8390_base + EN0_IMR);
|
||||||
|
|
||||||
|
/* Copy the station address into the DS8390 registers,
|
||||||
|
and set the multicast hash bitmap to receive all multicasts. */
|
||||||
|
cli();
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
|
||||||
|
for(i = 0; i < 6; i++) {
|
||||||
|
outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
|
||||||
|
}
|
||||||
|
for(i = 0; i < 8; i++)
|
||||||
|
outb_p(0xff, e8390_base + EN1_MULT + i);
|
||||||
|
|
||||||
|
outb_p(ei_status.rx_start_page, e8390_base + EN1_CURPAG);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
|
||||||
|
sti();
|
||||||
|
if (startp) {
|
||||||
|
outb_p(0xff, e8390_base + EN0_ISR);
|
||||||
|
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
|
||||||
|
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
|
||||||
|
/* 3c503 TechMan says rxconfig only after the NIC is started. */
|
||||||
|
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trigger a transmit start, assuming the length is valid. */
|
||||||
|
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||||
|
int start_page)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
|
||||||
|
ei_status.txing = 1;
|
||||||
|
dev->tbusy = 1;
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
|
||||||
|
|
||||||
|
if (inb_p(EN_CMD) & E8390_TRANS) {
|
||||||
|
printk(EI_NAME": trigger_send() called with the transmitter busy.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
|
||||||
|
outb_p(length >> 8, e8390_base + EN0_TCNTHI);
|
||||||
|
outb_p(start_page, e8390_base + EN0_TPSR);
|
||||||
|
outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
|
||||||
|
outb_p(ENISR_RDC, e8390_base + EN0_ISR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -DPINGPONG -I/usr/src/linux/net/tcp -c 8390.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
129
net/tcpip/ether-995/8390.h
Normal file
129
net/tcpip/ether-995/8390.h
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/* Generic NS8390 register definitions. */
|
||||||
|
/* This file is part of Donald Becker's 8390 drivers.
|
||||||
|
This file is distributed under the Linux GPL.
|
||||||
|
Some of these names and comments are from the Crynwr packet drivers. */
|
||||||
|
|
||||||
|
#ifndef e8390_h
|
||||||
|
#define e8390_h
|
||||||
|
#ifndef EI_NAME
|
||||||
|
#define EI_NAME "eth_if"
|
||||||
|
#endif
|
||||||
|
/*#define PINGPONG*/
|
||||||
|
#ifdef PINGPONG
|
||||||
|
#define TX_PAGES 12
|
||||||
|
#else
|
||||||
|
#define TX_PAGES 6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ETHER_ADDR_LEN 6
|
||||||
|
|
||||||
|
/* From auto_irq.c */
|
||||||
|
extern void autoirq_setup(int waittime);
|
||||||
|
extern int autoirq_report(int waittime);
|
||||||
|
|
||||||
|
/* Most of these entries should be in 'struct device' (or most
|
||||||
|
things in there should be here!) */
|
||||||
|
struct ei_device { /* These should be stored per-board */
|
||||||
|
char *name;
|
||||||
|
int exists:1; /* perhaps in dev->private. */
|
||||||
|
int open:1;
|
||||||
|
int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
|
||||||
|
void (*reset_8390)(struct device *);
|
||||||
|
void (*block_output)(struct device *, int, const unsigned char *, int);
|
||||||
|
int (*block_input)(struct device *, int, char *, int);
|
||||||
|
unsigned char tx_start_page, rx_start_page, stop_page;
|
||||||
|
unsigned char current_page; /* Read pointer in buffer */
|
||||||
|
unsigned char thin_bit; /* Value to write to the 3c503 E33G_CNTRL */
|
||||||
|
unsigned char txing; /* Transmit Active, don't confuse the upper levels. */
|
||||||
|
unsigned char txqueue; /* Tx Packet buffer queue length. */
|
||||||
|
unsigned char in_interrupt;
|
||||||
|
short tx1, tx2; /* Packet lengths for ping-pong tx. */
|
||||||
|
int overruns; /* Rx overruns. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Some generic ethernet register configurations. */
|
||||||
|
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
|
||||||
|
#define E8390_RX_IRQ_MASK 0x5
|
||||||
|
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
|
||||||
|
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
|
||||||
|
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
|
||||||
|
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
|
||||||
|
|
||||||
|
/* Register accessed at EN_CMD, the 8390 base addr. */
|
||||||
|
#define E8390_STOP 0x01 /* Stop and reset the chip */
|
||||||
|
#define E8390_START 0x02 /* Start the chip, clear reset */
|
||||||
|
#define E8390_TRANS 0x04 /* Transmit a frame */
|
||||||
|
#define E8390_RREAD 0x08 /* Remote read */
|
||||||
|
#define E8390_RWRITE 0x10 /* Remote write */
|
||||||
|
#define E8390_NODMA 0x20 /* Remote DMA */
|
||||||
|
#define E8390_PAGE0 0x00 /* Select page chip registers */
|
||||||
|
#define E8390_PAGE1 0x40 /* using the two high-order bits */
|
||||||
|
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
|
||||||
|
|
||||||
|
/* Page 0 register offsets. */
|
||||||
|
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
|
||||||
|
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
|
||||||
|
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
|
||||||
|
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
|
||||||
|
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
|
||||||
|
#define EN0_TSR 0x04 /* Transmit status reg RD */
|
||||||
|
#define EN0_TPSR 0x04 /* Transmit starting page WR */
|
||||||
|
#define EN0_NCR 0x05 /* Number of collision reg RD */
|
||||||
|
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
|
||||||
|
#define EN0_FIFO 0x06 /* FIFO RD */
|
||||||
|
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
|
||||||
|
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
|
||||||
|
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
|
||||||
|
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
|
||||||
|
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
|
||||||
|
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
|
||||||
|
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
|
||||||
|
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
|
||||||
|
#define EN0_RSR 0x0c /* rx status reg RD */
|
||||||
|
#define EN0_RXCR 0x0c /* RX configuration reg WR */
|
||||||
|
#define EN0_TXCR 0x0d /* TX configuration reg WR */
|
||||||
|
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
|
||||||
|
#define EN0_DCFG 0x0e /* Data configuration reg WR */
|
||||||
|
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
|
||||||
|
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
|
||||||
|
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
|
||||||
|
|
||||||
|
/* Bits in EN0_ISR - Interrupt status register */
|
||||||
|
#define ENISR_RX 0x01 /* Receiver, no error */
|
||||||
|
#define ENISR_TX 0x02 /* Transmitter, no error */
|
||||||
|
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
|
||||||
|
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
|
||||||
|
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
|
||||||
|
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
|
||||||
|
#define ENISR_RDC 0x40 /* remote dma complete */
|
||||||
|
#define ENISR_RESET 0x80 /* Reset completed */
|
||||||
|
#define ENISR_ALL 0x3f /* Interrupts we will enable */
|
||||||
|
|
||||||
|
/* Bits in EN0_DCFG - Data config register */
|
||||||
|
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
|
||||||
|
|
||||||
|
/* Page 1 register offsets. */
|
||||||
|
#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
|
||||||
|
#define EN1_CURPAG 0x07 /* Current memory page RD WR */
|
||||||
|
#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
|
||||||
|
|
||||||
|
/* Bits in received packet status byte and EN0_RSR*/
|
||||||
|
#define ENRSR_RXOK 0x01 /* Received a good packet */
|
||||||
|
#define ENRSR_CRC 0x02 /* CRC error */
|
||||||
|
#define ENRSR_FAE 0x04 /* frame alignment error */
|
||||||
|
#define ENRSR_FO 0x08 /* FIFO overrun */
|
||||||
|
#define ENRSR_MPA 0x10 /* missed pkt */
|
||||||
|
#define ENRSR_PHY 0x20 /* physical/multicase address */
|
||||||
|
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
|
||||||
|
#define ENRSR_DEF 0x80 /* deferring */
|
||||||
|
|
||||||
|
/* Transmitted packet status, EN0_TSR. */
|
||||||
|
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
|
||||||
|
|
||||||
|
/* The per-packet-header format. */
|
||||||
|
struct e8390_pkt_hdr {
|
||||||
|
unsigned char status; /* status */
|
||||||
|
unsigned char next; /* pointer to next packet. */
|
||||||
|
unsigned short count; /* header + packet lenght in bytes */
|
||||||
|
};
|
||||||
|
#endif /* e8390_h */
|
||||||
45
net/tcpip/ether-995/ChangeLog
Normal file
45
net/tcpip/ether-995/ChangeLog
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
Fri Feb 12 15:45:13 1993 Donald J. Becker (becker at metropolis)
|
||||||
|
|
||||||
|
* 8390.c: Commented out 'high_water_mark', since it's
|
||||||
|
never set and results in an 'unused variable' warning.
|
||||||
|
|
||||||
|
* hp.c: Deleted the 'int i;' from hp_block_output() --
|
||||||
|
it's unused now that port_write_b() is used instead
|
||||||
|
of an explicit loop.
|
||||||
|
|
||||||
|
Thu Feb 11 16:04:27 1993 Donald J. Becker (becker at metropolis)
|
||||||
|
|
||||||
|
* ne.c: Added explicit support for D-Link ethercards.
|
||||||
|
|
||||||
|
Sat Jan 30 03:16:30 1993 Donald J. Becker (becker at metropolis)
|
||||||
|
|
||||||
|
* hp.c: Changed the 8-bit output and input loops to use
|
||||||
|
port_{read,write?_b() instead. Assigned dev->irq before
|
||||||
|
sigaction() is call to avoid the 3c503 problem (below).
|
||||||
|
|
||||||
|
* 3c503.c: Fixed a wild I/O port write in the probe code that had
|
||||||
|
been added to mask interrupts during probes. Also assigned dev->irq
|
||||||
|
before sigaction() is called to correctly handle that spurious
|
||||||
|
first interrupt.
|
||||||
|
|
||||||
|
* wd.c: Changed the IRQ from the unreliable autoIRQ to reading the
|
||||||
|
configuation register. I also go a report that it still works
|
||||||
|
with the 8003
|
||||||
|
|
||||||
|
Fri Jan 29 11:29:10 1993 Donald J. Becker (becker at metropolis)
|
||||||
|
|
||||||
|
* 3c503: Added EL2_AUI as an alias for EI8390_THICK, and rewrote
|
||||||
|
the IRQ selection code. It now does assigns the first free IRQ
|
||||||
|
from the list { 5 2/9 3 4 }.
|
||||||
|
|
||||||
|
* GNUmakefile: Changed the default name from "eth_if" to "eth0" to
|
||||||
|
be compatible. Also changed the name of EI8390_THICK to EL2_AUI.
|
||||||
|
|
||||||
|
* 8390.c: Omitted incrementing ei_debug during a rx_overrun.
|
||||||
|
|
||||||
|
* ne.c: I put the a ctron check inside of #ifdef EI_8BIT
|
||||||
|
so that 8-bit Cabletron card are recognized.
|
||||||
|
Thanks to Eric Wallace, wallace@chezmoto.ai.mit.edu.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
36
net/tcpip/ether-995/GNUmakefile
Normal file
36
net/tcpip/ether-995/GNUmakefile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# This file must be named 'GNUmakefile'. When it has that name it
|
||||||
|
# loaded in preference to the regular 'Makefile' (which it include
|
||||||
|
# right here) so this has the effect of appending lines to the Makefile.
|
||||||
|
|
||||||
|
include Makefile
|
||||||
|
|
||||||
|
# Add a few files to tcpip.a.
|
||||||
|
newobjs = 8390.o 3c503.o ne.o wd.o hp.o auto_irq.o
|
||||||
|
OBJS := $(OBJS) $(newobjs)
|
||||||
|
tcpip.a: $(newobjs)
|
||||||
|
|
||||||
|
# Set the address and IRQ here. The ne.c and 3c503 driver will autoprobe
|
||||||
|
# if you set the address or IRQ to zero, so we do that by default.
|
||||||
|
#
|
||||||
|
# Add -DEI_NAME="eth0" if you want to be exactly compatible with the default
|
||||||
|
# driver. This will only work if you don't use the distributed 'we' driver!
|
||||||
|
#
|
||||||
|
ether_options := -DEI_NAME=\"eth0\" -DEI8390=0 -DEI8390_IRQ=0
|
||||||
|
Space.o: Space.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) $(ether_options) -c Space.c -o $@
|
||||||
|
|
||||||
|
# Change this to define the set of ethercards your kernel will support.
|
||||||
|
8390.o: 8390.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -DNE2000 -DWD80x3 -DHPLAN -DEL2 -c 8390.c -o $@
|
||||||
|
|
||||||
|
# Change this to -DEI_8BIT if you have an 8-bit ethercard (NE1000, E10xx).
|
||||||
|
ne.o: ne.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -UEI_8BIT -c ne.c -o $@
|
||||||
|
|
||||||
|
# Change this to -DEL2_AUI if you use the AUI port.
|
||||||
|
3c503.o: 3c503.c 3c503reg.h GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -UEL2_AUI -c 3c503.c -o $@
|
||||||
|
|
||||||
|
# Change this to -DSHMEM=0xd0000 if you have an old non-EEPROM wd8003
|
||||||
|
wd.o: wd.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -UWD_SHMEM -c wd.c -o $@
|
||||||
82
net/tcpip/ether-995/INSTALL
Normal file
82
net/tcpip/ether-995/INSTALL
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
Installation Directions:
|
||||||
|
|
||||||
|
EMail me (becker@super.org) telling me which version you have gotten.
|
||||||
|
I need to know how many people have tried, succeeded and
|
||||||
|
failed before this is released as part of the official Linux.
|
||||||
|
|
||||||
|
Use Linux 0.99.2 or later. Make certain you can make a working kernel
|
||||||
|
_before_ you install the ethercard driver.
|
||||||
|
|
||||||
|
Put the all of the files into linux/net/tcp/. You'll need all of the
|
||||||
|
files in this directory.
|
||||||
|
GNUmakefile 8390.c 8390.h auto_irq.c Space.c wd.c ne.c 3c503.[ch] and hp.c.
|
||||||
|
Space.c is the only tricky one -- it overwrites the old Space.c.
|
||||||
|
Stock versions of Space.c leave the "we" driver enabled and will not work.
|
||||||
|
|
||||||
|
Change the GNUmakefile to reflect your configuration. Use the guide at
|
||||||
|
the end of these instructions and in the README file.
|
||||||
|
|
||||||
|
Make and install your new kernel.
|
||||||
|
|
||||||
|
To actually use this driver you must get the TCP/IP package and edit
|
||||||
|
your /usr/etc/inet/rc.net file to config whatever you named your
|
||||||
|
ethernet device. (You can edit the GNUmakefile to use something
|
||||||
|
besides the "eth0" name. Note that the default name has changed to
|
||||||
|
the now-standard "eth0" from the old "eth_if"!)
|
||||||
|
|
||||||
|
If you try to 'config' an interface that doesn't exist your kernel
|
||||||
|
will report "invalid ioctl()" for anthing that tries to use the card.
|
||||||
|
Note that the ethercard devices aren't (yet) real '/dev/eth_if' devices --
|
||||||
|
they only exist in the socket namespace and thus you don't need to
|
||||||
|
'mknode' them.
|
||||||
|
|
||||||
|
________________
|
||||||
|
Important defines
|
||||||
|
|
||||||
|
For Space.c
|
||||||
|
#define EI8390 0 /* The base address of your ethercard. */
|
||||||
|
#define EI8390_IRQ 0 /* and the interrupt you want to use. */
|
||||||
|
/* '0' means autoconfigure */
|
||||||
|
For 8390.c
|
||||||
|
#define EI_DEBUG 2 /* Use '0' for no messages. */
|
||||||
|
#define EL2 /* For the 3c503 driver. */
|
||||||
|
#define NE2000 /* For the NE1000/NE2000/Ctron driver. */
|
||||||
|
#define WD80x3 /* For the WD8003/WD8013 driver. */
|
||||||
|
#define HPLAN /* For the HP27xxx driver. */
|
||||||
|
|
||||||
|
For the individual drivers
|
||||||
|
#undef EI_8BIT /* Define for ne.c iff you have an 8 bit card. */
|
||||||
|
#undef EL2_AUI /* Define to use the 3c503 AUI/DIX transceiver. */
|
||||||
|
|
||||||
|
EI8390 Define (probably in autoconf.h or config.site.h) this to the base
|
||||||
|
address of your ethernet card.
|
||||||
|
EI8390_IRQ Define (probably in autoconf.h or config.site.h) this to the
|
||||||
|
IRQ line of your ethernet card. Most drivers convert a IRQ2 to an
|
||||||
|
IRQ9 for you, so don't be surprised.
|
||||||
|
EI_DEBUG Set to the desired numeric debugging level. Use 3 or
|
||||||
|
greater when actively debugging a problem, '1' for a
|
||||||
|
casual interest in what's going on, and '0' for normal
|
||||||
|
use. (Most of the debugging stuff has been taken out recently,
|
||||||
|
so this won't have much effect.)
|
||||||
|
EI_PINGPONG
|
||||||
|
Not included or broken the alpha version. Define this if you want
|
||||||
|
ping-pong transmit buffers.
|
||||||
|
EI_8BIT
|
||||||
|
If you are using the ne.c driver and have an 8-bit card (NE1000 or
|
||||||
|
E1xxx Cabletron) you must define this. It's not needed for the other
|
||||||
|
drivers, and I hope to find a way to clean this up in the future.
|
||||||
|
EL2_AUI
|
||||||
|
Define for this if you are using the 3c503 and use the AUI/DIX
|
||||||
|
connector rather than the built-in thin-net transceiver.
|
||||||
|
WD_SHMEM
|
||||||
|
Define this to override the shared memory address used by the
|
||||||
|
WD driver. This should only be necessary for old, jumpered '8003's.
|
||||||
|
|
||||||
|
If you have a Cabletron ethercard you might want to look at ne.c:neprobe()
|
||||||
|
for info on how to enable more packet buffer space.
|
||||||
|
|
||||||
|
ETHERLINK1_IRQ
|
||||||
|
ETHERLINK1 Define these to the base address and IRQ of a 3c501 (NOT 3c503)
|
||||||
|
card. Refer to net/tcp/Space.c.
|
||||||
|
|
||||||
|
|
||||||
101
net/tcpip/ether-995/README
Normal file
101
net/tcpip/ether-995/README
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
This is the current version of the 8390 ethercard driver.
|
||||||
|
The ether-994 drivers are for 0.99.4 and earlier.
|
||||||
|
The ether-995 drivers are for 0.99.5.
|
||||||
|
|
||||||
|
You should consult the list below for the specific #define for your
|
||||||
|
ethercard. If you don't have one specific #define the driver will
|
||||||
|
probe for all supported ethercard types.
|
||||||
|
|
||||||
|
Source Header #define to get Supported cards
|
||||||
|
8390.c 8390.h EI8390 (generic, needed for all)
|
||||||
|
3c503.c 3c503reg.h EL2 3c503, 3c503/16
|
||||||
|
ne.c NE2000 NE1000, NE2000, NatSemi, Cabletron
|
||||||
|
wd.c wd.h WD80x3 WD8003, WD8013, SMC elite
|
||||||
|
hp.c HPLAN HP LAN adaptors
|
||||||
|
|
||||||
|
Notes on each file
|
||||||
|
INSTALL
|
||||||
|
The installation directions -- "read this first, uhmmm, second".
|
||||||
|
|
||||||
|
GNUmakefile
|
||||||
|
The 'GNUmakefile' name is "magic". You just put it into the directory
|
||||||
|
and it is loaded instead of the regular Makefile (which it thenincludes).
|
||||||
|
Most people should never need to edit the Makefile, just the GNUmakefile.
|
||||||
|
|
||||||
|
3c503.c
|
||||||
|
o You'll need to '#define EI8390_THICK' or EL2_AUI if you are using the AUI port
|
||||||
|
instead of the thinnet connector. Russ Nelson <nelson@crynwr.com> sent me an
|
||||||
|
run-time method of selecting between the two, but I haven't put it in yet.
|
||||||
|
This _may_ generate a spurious error message when transmitting the first
|
||||||
|
packet, I haven't yet tracked down this bug report.
|
||||||
|
o If you want to check the shared memory, #define EL2MEMTEST. I don't think
|
||||||
|
the memory check is worth the effort -- it's missed obvious problems.
|
||||||
|
o You must jumper the board to use shared memory -- I don't test the
|
||||||
|
non-shared-memory mode and it's probably broken.
|
||||||
|
o Thanks to Chance Reschke at USRA <creschke@usra.edu> for providing the
|
||||||
|
3c503 and anonymous FTP space.
|
||||||
|
|
||||||
|
ne.c
|
||||||
|
o You'll need to #define EI_8BIT if you are using an 8-bit ethercard
|
||||||
|
such as the NE1000 or Cabletron E10xx. I might someday make a run-time
|
||||||
|
selection between the NE1000 and NE2000 (right now it screws up the
|
||||||
|
non-default bus-width) but I don't know how to distinguish the Cabletron
|
||||||
|
ethercards. I'm hoping to find a general way to identify the board's bus
|
||||||
|
width, but don't hold your breath.
|
||||||
|
|
||||||
|
wd.c
|
||||||
|
o Thanks to Russ Nelson (nelson@crynwr.com) for loaning me a WD8013.
|
||||||
|
o The 8013 doesn't work if it's probed by some other driver first: keep
|
||||||
|
it first in the probe list.
|
||||||
|
I could reset the board before looking at it to fix the probe problem, but
|
||||||
|
this involves an outb() which may disturb non-WD8013 devices.
|
||||||
|
o You _must_ run this board with shared memory enabled. Don't set the
|
||||||
|
jumper to the no-shared-memory setting.
|
||||||
|
o The driver now reads the EEPROM setting, so you can use the EEPROM
|
||||||
|
settings! And there is a new Linux 'wdsetup' program that replaces
|
||||||
|
MS-DOS 'EZSETUP'!
|
||||||
|
o You machine may fail to do a soft reboot if the packet buffer shared memory
|
||||||
|
is mapped in -- the machine might think its a boot PROM (since it
|
||||||
|
intentionally shares the same memory space as the optional on-board
|
||||||
|
boot PROM). "Live with it" for now.
|
||||||
|
o If the packet buffer memory is mapped into a cacheable region it won't work!
|
||||||
|
It's really easy to this with the map-it-anywhere EEPROM. Check your BIOS
|
||||||
|
setup.
|
||||||
|
o If you have an old non-EEPROM wd8003 and need to change the shared memory
|
||||||
|
address from the default 0xd0000 set WD_SHMEM to the desired address.
|
||||||
|
|
||||||
|
hp.c
|
||||||
|
o This has only been tested with a 27245A, 27247A, and 27250. It doesn't yet
|
||||||
|
work with the new 27247B!
|
||||||
|
o Thanks to Chance Reschke at USRA <creschke@usra.edu> for loaning me the
|
||||||
|
27247A ethercard.
|
||||||
|
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
|
||||||
|
The "bogus packet header" is a bug in the 8390. I drop all of
|
||||||
|
the packets in the ring (usually just one) when this happens.
|
||||||
|
If you get this more than just occasionally please report it to me.
|
||||||
|
|
||||||
|
I don't yet distinguish between the problems with IRQ conflicts and
|
||||||
|
problems with the ethernet cable not being connected. If you get
|
||||||
|
a "transmit timed out" message check both! Your ethercard
|
||||||
|
probably will NOT work without proper termination.
|
||||||
|
|
||||||
|
A small number of people report continuous "RX transfer address mismatch"
|
||||||
|
errors with the NE2000. This is a bug in the 8390, and the reason
|
||||||
|
most designers use shared memory or design their own packet buffer access
|
||||||
|
circuitry rather than use what's provided by the 8390. An occasional
|
||||||
|
"mismatch" message (say, once a week) won't impact performance.
|
||||||
|
|
||||||
|
I've fixed most of the spurious "receiver overrun" messages. It
|
||||||
|
was a bug in the 8390 -- the overrun flag is sometimes set
|
||||||
|
when the boundary pointer is written. If you still get
|
||||||
|
isolated overrun errors please send me email.
|
||||||
|
|
||||||
|
The 3c501 driver isn't complete. This card is severely brain-damaged
|
||||||
|
anyway -- you won't notice the performance difference between working
|
||||||
|
and non-working versions anyway. If this source code is here, it is
|
||||||
|
only provided for a few people that wanted to write code for their 3c501
|
||||||
|
cards. Don't send me email about it unless it's a bug fix.
|
||||||
|
|
||||||
134
net/tcpip/ether-995/Space.c
Normal file
134
net/tcpip/ether-995/Space.c
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/* Space.c */
|
||||||
|
/* Holds initial configuration information for devices. */
|
||||||
|
#include "dev.h"
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/config.h>
|
||||||
|
|
||||||
|
#define NEXT_DEV NULL
|
||||||
|
|
||||||
|
#ifdef ETHERLINK1
|
||||||
|
extern int etherlink_init(struct device *);
|
||||||
|
|
||||||
|
#ifndef ETHERLINK1_IRQ
|
||||||
|
#define ETHERLINK1_IRQ 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct device el_dev = {
|
||||||
|
"if3c501",
|
||||||
|
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
|
||||||
|
ETHERLINK1, ETHERLINK1_IRQ, /* base i/o address, irq. */
|
||||||
|
0,0,0,0,0,
|
||||||
|
NEXT_DEV,
|
||||||
|
etherlink_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (&el_dev)
|
||||||
|
|
||||||
|
#endif /* ETHERLINK1 */
|
||||||
|
|
||||||
|
#if defined(EI8390) || defined(EL2) || defined(NE2000) \
|
||||||
|
|| defined(WD80x3) || defined(HPLAN)
|
||||||
|
extern int ethif_init(struct device *);
|
||||||
|
|
||||||
|
#ifndef EI8390_IRQ
|
||||||
|
#define EI8390_IRQ 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct device ei8390_dev = {
|
||||||
|
#ifdef EI_NAME
|
||||||
|
EI_NAME,
|
||||||
|
#else
|
||||||
|
"eth_if",
|
||||||
|
#endif
|
||||||
|
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
|
||||||
|
EI8390, EI8390_IRQ, 0,0,0,0,0, /* base i/o address, irq, and flags. */
|
||||||
|
NEXT_DEV,
|
||||||
|
ethif_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (&ei8390_dev)
|
||||||
|
|
||||||
|
#endif /* The EI8390 drivers. */
|
||||||
|
|
||||||
|
#ifdef WD8003
|
||||||
|
extern int wd8003_init(struct device *);
|
||||||
|
|
||||||
|
static struct device wd8003_dev =
|
||||||
|
{
|
||||||
|
"eth0",
|
||||||
|
0xd2000, /* recv memory end. */
|
||||||
|
0xd0600, /* recv memory start. */
|
||||||
|
0xd2000, /* memory end. */
|
||||||
|
0xd0000, /* memory start. */
|
||||||
|
0x280, /* base i/o address. */
|
||||||
|
5, /* irq */
|
||||||
|
0,0,0,0,0, /* flags */
|
||||||
|
NEXT_DEV,
|
||||||
|
wd8003_init,
|
||||||
|
/* wd8003_init should set up the rest. */
|
||||||
|
0, /* trans start. */
|
||||||
|
{NULL}, /* buffs */
|
||||||
|
NULL, /* backlog */
|
||||||
|
NULL, /* open */
|
||||||
|
NULL, /* stop */
|
||||||
|
NULL, /* hard_start_xmit */
|
||||||
|
NULL, /* hard_header */
|
||||||
|
NULL, /* add arp */
|
||||||
|
NULL, /* queue xmit */
|
||||||
|
NULL, /* rebuild header */
|
||||||
|
NULL, /* type_trans */
|
||||||
|
NULL, /* send_packet */
|
||||||
|
NULL, /* private */
|
||||||
|
0, /* type. */
|
||||||
|
0, /* hard_header_len */
|
||||||
|
0, /* mtu */
|
||||||
|
{0,}, /* broadcast address */
|
||||||
|
{0,}, /* device address */
|
||||||
|
0 /* addr len */
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (&wd8003_dev)
|
||||||
|
|
||||||
|
#endif /* WD8003 */
|
||||||
|
|
||||||
|
extern int loopback_init(struct device *dev);
|
||||||
|
|
||||||
|
static struct device loopback_dev =
|
||||||
|
{
|
||||||
|
"loopback",
|
||||||
|
-1, /* recv memory end. */
|
||||||
|
0x0, /* recv memory start. */
|
||||||
|
-1, /* memory end. */
|
||||||
|
0, /* memory start. */
|
||||||
|
0, /* base i/o address. */
|
||||||
|
0, /* irq */
|
||||||
|
0,0,1,0,0, /* flags */
|
||||||
|
NEXT_DEV, /* next device */
|
||||||
|
loopback_init,
|
||||||
|
/* loopback_init should set up the rest. */
|
||||||
|
0, /* trans start. */
|
||||||
|
{NULL}, /* buffs */
|
||||||
|
NULL, /* backlog */
|
||||||
|
NULL, /* open */
|
||||||
|
NULL, /* stop */
|
||||||
|
NULL, /* hard_start_xmit */
|
||||||
|
NULL, /* hard_header */
|
||||||
|
NULL, /* add arp */
|
||||||
|
NULL, /* queue xmit */
|
||||||
|
NULL, /* rebuild header */
|
||||||
|
NULL, /* type_trans */
|
||||||
|
NULL, /* send_packet */
|
||||||
|
NULL, /* private */
|
||||||
|
0, /* type. */
|
||||||
|
0, /* hard_header_len */
|
||||||
|
0, /* mtu */
|
||||||
|
{0,}, /* broadcast address */
|
||||||
|
{0,}, /* device address */
|
||||||
|
0 /* addr len */
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (error no next device &loopback_dev)
|
||||||
|
|
||||||
|
struct device *dev_base = &loopback_dev;
|
||||||
100
net/tcpip/ether-995/auto_irq.c
Normal file
100
net/tcpip/ether-995/auto_irq.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/* auto_irq.c: Auto-configure IRQ lines for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
|
||||||
|
This code is a general-purpose IRQ line detector for devices with
|
||||||
|
jumpered IRQ lines. If you can make the device raise an IRQ (and
|
||||||
|
that IRQ line isn't already being used), these routines will tell
|
||||||
|
you what IRQ line it's using -- perfect for those oh-so-cool boot-time
|
||||||
|
device probes!
|
||||||
|
|
||||||
|
To use this, first call autoirq_setup(timeout). TIMEOUT is how many
|
||||||
|
'jiffies' (1/18 sec.) to detect other devices that have active IRQ lines,
|
||||||
|
and can usually be zero at boot.
|
||||||
|
Next, set up your device to trigger an interrupt.
|
||||||
|
Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
|
||||||
|
most recently active. The TIMEOUT should usually be zero, but may
|
||||||
|
be set to the number of jiffies to wait for an active IRQ.
|
||||||
|
|
||||||
|
The idea of using the setup timeout to filter out bogus IRQs came from
|
||||||
|
the serial driver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef version
|
||||||
|
static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*#include <linux/config.h>*/
|
||||||
|
/*#include <linux/kernel.h>*/
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/bitops.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
/*#include <asm/system.h>*/
|
||||||
|
|
||||||
|
static volatile int irq_number; /* The latest irq number we actually found. */
|
||||||
|
static volatile int irq_bitmap; /* The irqs we actually found. */
|
||||||
|
static int irq_handled; /* The irq lines we have a handler on. */
|
||||||
|
|
||||||
|
static void autoirq_probe(int irq)
|
||||||
|
{
|
||||||
|
irq_number = irq;
|
||||||
|
set_bit(irq, (void *)&irq_bitmap);
|
||||||
|
/*irq_bitmap |= 1 << irq;*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct sigaction autoirq_sigaction = { autoirq_probe, 0, SA_INTERRUPT, NULL};
|
||||||
|
|
||||||
|
void autoirq_setup(int waittime)
|
||||||
|
{
|
||||||
|
int i, mask;
|
||||||
|
int timeout = jiffies+waittime;
|
||||||
|
|
||||||
|
irq_number = 0;
|
||||||
|
irq_bitmap = 0;
|
||||||
|
irq_handled = 0;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (!irqaction(i, &autoirq_sigaction))
|
||||||
|
set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
|
||||||
|
}
|
||||||
|
/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
|
||||||
|
while (timeout >= jiffies)
|
||||||
|
;
|
||||||
|
|
||||||
|
for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
|
||||||
|
if (irq_bitmap & irq_handled & mask) {
|
||||||
|
irq_handled &= ~mask;
|
||||||
|
printk(" Spurious interrupt on IRQ %d\n", i);
|
||||||
|
free_irq(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int autoirq_report(int waittime)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int timeout = jiffies+waittime;
|
||||||
|
|
||||||
|
/* Hang out at least <waittime> jiffies waiting for the IRQ. */
|
||||||
|
while (timeout >= jiffies)
|
||||||
|
if (irq_number)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Retract the irq handlers that we installed. */
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (test_bit(i, (void *)&irq_handled))
|
||||||
|
free_irq(i);
|
||||||
|
}
|
||||||
|
return irq_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
303
net/tcpip/ether-995/hp.c
Normal file
303
net/tcpip/ether-995/hp.c
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
/* hp.c: A HP LAN ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This is a driver for the HP LAN adaptors.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version = "hp.c:v0.43 2/12/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||||
|
#define port_read(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define port_read_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
|
||||||
|
#define HP_ID 0x07
|
||||||
|
#define HP_CONFIGURE 0x08 /* Configuration register. */
|
||||||
|
#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
|
||||||
|
#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
|
||||||
|
#define HP_DATAON 0x10 /* Turn on dataport */
|
||||||
|
#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
|
||||||
|
|
||||||
|
#define HP_START_PG 0x00 /* First page of TX buffer */
|
||||||
|
#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
|
||||||
|
#define HP_16BSTOP_PG 0xFF /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int hpprobe(int ioaddr, struct device *dev);
|
||||||
|
int hpprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void hp_reset_8390(struct device *dev);
|
||||||
|
static int hp_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void hp_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static void hp_init_card(struct device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
/* Probe for an HP LAN adaptor.
|
||||||
|
Also initialize the card and fill in STATION_ADDR with the station
|
||||||
|
address. */
|
||||||
|
|
||||||
|
int hpprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return hpprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && hpprobe1(*port, dev))
|
||||||
|
return dev->base_addr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hpprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
unsigned char SA_prom[6];
|
||||||
|
int tmp;
|
||||||
|
int hplan;
|
||||||
|
|
||||||
|
printk("HP-LAN ethercard probe at %#3x:", ioaddr);
|
||||||
|
tmp = inb_p(ioaddr);
|
||||||
|
if (tmp == 0xFF) {
|
||||||
|
printk(" not found (nothing there).\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < sizeof(SA_prom); i++) {
|
||||||
|
SA_prom[i] = inb(ioaddr + i);
|
||||||
|
if (i < ETHER_ADDR_LEN && station_addr) {
|
||||||
|
printk(" %2.2x", SA_prom[i]);
|
||||||
|
station_addr[i] = SA_prom[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hplan = (SA_prom[0] == 0x08 && SA_prom[1] == 0x00 && SA_prom[2] == 0x09);
|
||||||
|
if (hplan == 0) {
|
||||||
|
printk(" not found (invalid station address prefix).\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_status.tx_start_page = HP_START_PG;
|
||||||
|
ei_status.rx_start_page = HP_START_PG + TX_PAGES;
|
||||||
|
/* Set up the rest of the parameters. */
|
||||||
|
if ((tmp = inb_p(HP_ID)) & 0x80) {
|
||||||
|
ei_status.name = "HP27247";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */
|
||||||
|
} else {
|
||||||
|
ei_status.name = "HP27250";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the base address to point to the NIC! */
|
||||||
|
dev->base_addr = ioaddr + NIC_OFFSET;
|
||||||
|
|
||||||
|
if (dev->irq == 2)
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
|
||||||
|
int irq_8list[] = { 7, 5, 3, 4, 9, 0};
|
||||||
|
int *irq = ei_status.word16 ? irq_16list : irq_8list;
|
||||||
|
for (; *irq; irq++) {
|
||||||
|
if (irqaction(dev->irq = *irq, &ei_sigaction) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*irq == 0) {
|
||||||
|
printk (" unable to get an IRQ.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(" %s found, using IRQ %d.\n", ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
ei_status.reset_8390 = &hp_reset_8390;
|
||||||
|
ei_status.block_input = &hp_block_input;
|
||||||
|
ei_status.block_output = &hp_block_output;
|
||||||
|
hp_init_card(dev);
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hp_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int hp_base = dev->base_addr - NIC_OFFSET;
|
||||||
|
int saved_config = inb_p(hp_base + HP_CONFIGURE);
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
if (ei_debug > 1) printk("resetting the 8390 time=%d...", jiffies);
|
||||||
|
outb_p(0x00, hp_base + HP_CONFIGURE);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb_p(saved_config, hp_base + HP_CONFIGURE);
|
||||||
|
while ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk(EI_NAME": hp_reset_8390() did not complete.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ei_debug > 1) printk("8390 reset done.", jiffies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||||
|
porting to a new ethercard look at the packet driver source for hints.
|
||||||
|
The HP LAN doesn't use shared memory -- we put the packet
|
||||||
|
out through the "remote DMA" dataport. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
hp_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
int xfer_count = count;
|
||||||
|
|
||||||
|
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, nic_base);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_read(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
|
||||||
|
if (count & 0x01)
|
||||||
|
buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
|
||||||
|
} else {
|
||||||
|
port_read_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
/* Check only the lower 8 bits so we can ignore ring wrap. */
|
||||||
|
if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
|
||||||
|
printk(EI_NAME": RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
|
||||||
|
ring_offset + xfer_count, addr);
|
||||||
|
}
|
||||||
|
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hp_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page)
|
||||||
|
{
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
|
||||||
|
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
/* Round the count up for word writes. Do we need to do this?
|
||||||
|
What effect will an odd byte count have on the 8390?
|
||||||
|
I should check someday. */
|
||||||
|
if (ei_status.word16 && (count & 0x01))
|
||||||
|
count++;
|
||||||
|
/* We should already be in page 0, but to be safe... */
|
||||||
|
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
|
||||||
|
|
||||||
|
#ifdef ei8390_bug
|
||||||
|
/* Handle the read-before-write bug the same way as the
|
||||||
|
Crynwr packet driver -- the NatSemi method doesn't work. */
|
||||||
|
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
/* Make certain that the dummy read has occured. */
|
||||||
|
inb_p(0x61);
|
||||||
|
inb_p(0x61);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||||
|
|
||||||
|
outb_p(E8390_RWRITE+E8390_START, nic_base);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
/* Use the 'rep' sequence for 16 bit boards. */
|
||||||
|
port_write(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
|
||||||
|
} else {
|
||||||
|
port_write_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
|
||||||
|
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
if ((start_page << 8) + count != addr)
|
||||||
|
printk(EI_NAME": TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
|
||||||
|
(start_page << 8) + count, addr);
|
||||||
|
}
|
||||||
|
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static void
|
||||||
|
hp_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
int irq = dev->irq;
|
||||||
|
/* default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
|
||||||
|
static char irqval[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
outb_p(irqval[irq&0x0f] | HP_RUN,
|
||||||
|
dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c hp.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
379
net/tcpip/ether-995/ne.c
Normal file
379
net/tcpip/ether-995/ne.c
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with many 8390-based ethernet boards. Currently
|
||||||
|
it support the NE1000, NE2000 (and clones), and some Cabletron products.
|
||||||
|
8-bit ethercard support is enabled with #define EI_8BIT
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Routines for the NatSemi-based designs (NE[12]000). */
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"ne.c:v0.50 2/19/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||||
|
#define port_read(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define port_read_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define EN_CMD (dev->base_addr)
|
||||||
|
#define NE_BASE (dev->base_addr)
|
||||||
|
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
|
||||||
|
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
|
||||||
|
|
||||||
|
#define NE1SM_START_PG 0x20 /* First page of TX buffer */
|
||||||
|
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||||
|
#define NESM_START_PG 0x40 /* First page of TX buffer */
|
||||||
|
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int neprobe(int ioaddr, struct device *dev);
|
||||||
|
static int neprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void ne_reset_8390(struct device *dev);
|
||||||
|
static int ne_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void ne_block_output(struct device *dev, const int count,
|
||||||
|
const unsigned char *buf, const int start_page);
|
||||||
|
static void ne_init_card(struct device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
/* Probe for the NE1000 and NE2000. NEx000-like boards have 0x57,0x57 in
|
||||||
|
bytes 0x0e,0x0f of the SAPROM, but if we read by 16 bit words the NE1000
|
||||||
|
appears to have 0x00, 0x42. */
|
||||||
|
/* Also initialize the card and fill in STATION_ADDR with the station
|
||||||
|
address. The station address (and other data) is stored in the
|
||||||
|
packet buffer memory space, 32 bytes starting at remote DMA address 0. */
|
||||||
|
|
||||||
|
int neprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return neprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && neprobe1(*port, dev))
|
||||||
|
return dev->base_addr = *port;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int neprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
unsigned char SA_prom[32];
|
||||||
|
int cmdreg;
|
||||||
|
int ne2000 = 0, ne1000 = 0, ctron = 0, dlink = 0;
|
||||||
|
|
||||||
|
printk("8390 ethercard probe at %#3x:", ioaddr);
|
||||||
|
|
||||||
|
cmdreg = inb_p(ioaddr);
|
||||||
|
if (cmdreg == 0xFF) {
|
||||||
|
printk(" not found (%#2.2x).\n", cmdreg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Ooops, we must first initialize registers -- we can't just read the PROM
|
||||||
|
address right away. (Learned the hard way.) */
|
||||||
|
/* NS8390_init(eifdev, 0);*/
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, ioaddr);
|
||||||
|
#ifdef EI_8BIT
|
||||||
|
outb_p(0x48, ioaddr + EN0_DCFG); /* Set byte-wide for probe. */
|
||||||
|
#else
|
||||||
|
outb_p(0x49, ioaddr + EN0_DCFG); /* Set word-wide for probe. */
|
||||||
|
#endif
|
||||||
|
/* Even though we'll set them soon, we must clear them! */
|
||||||
|
outb_p(0x00, ioaddr + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, ioaddr + EN0_RCNTHI);
|
||||||
|
|
||||||
|
outb_p(0x00, ioaddr + EN0_IMR); /* Mask completion irq. */
|
||||||
|
outb_p(0xFF, ioaddr + EN0_ISR);
|
||||||
|
|
||||||
|
/* Set to monitor and loopback mode. */
|
||||||
|
outb_p(E8390_RXOFF, ioaddr + EN0_RXCR); /* 0x20 */
|
||||||
|
outb_p(E8390_TXOFF, ioaddr + EN0_TXCR); /* 0x02 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef EI_8BIT
|
||||||
|
outb_p(sizeof(SA_prom), ioaddr + EN0_RCNTLO);
|
||||||
|
#else
|
||||||
|
/* Double count 0x20 words, the SA PROM is only byte wide. */
|
||||||
|
outb_p(2*sizeof(SA_prom), ioaddr + EN0_RCNTLO);
|
||||||
|
#endif
|
||||||
|
outb_p(0x00, ioaddr + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, ioaddr + EN0_RSARLO); /* DMA starting at 0x0000. */
|
||||||
|
outb_p(0x00, ioaddr + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, ioaddr);
|
||||||
|
for(i = 0; i < sizeof(SA_prom); i++) {
|
||||||
|
SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
|
||||||
|
if (i < ETHER_ADDR_LEN && station_addr) {
|
||||||
|
printk(" %2.2x", SA_prom[i]);
|
||||||
|
station_addr[i] = SA_prom[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef EI_8BIT
|
||||||
|
ne1000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
|
||||||
|
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
|
||||||
|
dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
|
||||||
|
#else
|
||||||
|
ne2000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
|
||||||
|
ne1000 = (SA_prom[14] == 0x00 && SA_prom[15] == 0x42);
|
||||||
|
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
|
||||||
|
dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Set up the rest of the parameters. */
|
||||||
|
if (ne1000 || dlink) {
|
||||||
|
ei_status.name = ne1000 ? "NE1000" : "D-Link";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
ei_status.tx_start_page = NE1SM_START_PG;
|
||||||
|
ei_status.rx_start_page = NE1SM_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = NE1SM_STOP_PG;
|
||||||
|
} else if (ne2000) {
|
||||||
|
ei_status.name = "NE2000";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
ei_status.tx_start_page = NESM_START_PG;
|
||||||
|
ei_status.rx_start_page = NESM_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = NESM_STOP_PG;
|
||||||
|
} else if (ctron) {
|
||||||
|
/* You'll have to set these yourself, but this info might be useful.
|
||||||
|
Cabletron packet buffer locations:
|
||||||
|
E1010 starts at 0x100 and ends at 0x2000.
|
||||||
|
E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
|
||||||
|
E2010 starts at 0x100 and ends at 0x4000.
|
||||||
|
E2010-x starts at 0x100 and ends at 0xffff. */
|
||||||
|
ei_status.name = "Cabletron";
|
||||||
|
#ifdef EI_8BIT
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
#else
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
#endif
|
||||||
|
ei_status.tx_start_page = 0x01;
|
||||||
|
ei_status.rx_start_page = 0x01 + TX_PAGES;
|
||||||
|
#ifndef CTRON_MEMSIZE
|
||||||
|
#define CTRON_MEMSIZE 0x20 /* Extra safe... */
|
||||||
|
#endif
|
||||||
|
ei_status.stop_page = CTRON_MEMSIZE;
|
||||||
|
} else {
|
||||||
|
printk(" not found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->base_addr = ioaddr;
|
||||||
|
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
autoirq_setup(0);
|
||||||
|
outb_p(0x50, nic_base + EN0_IMR); /* Enable one interrupt. */
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */
|
||||||
|
outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
|
||||||
|
dev->irq = autoirq_report(0);
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk(" autoirq is %d", dev->irq);
|
||||||
|
} else if (dev->irq == 2)
|
||||||
|
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||||
|
or don't know which one to set. */
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||||
|
if (irqval) {
|
||||||
|
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(" %s found, using IRQ %d.\n", ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
ei_status.reset_8390 = &ne_reset_8390;
|
||||||
|
ei_status.block_input = &ne_block_input;
|
||||||
|
ei_status.block_output = &ne_block_output;
|
||||||
|
ne_init_card(dev);
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ne_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int tmp = inb_p(NE_BASE + NE_RESET);
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
if (ei_debug > 1) printk("resetting the 8390 t=%d...", jiffies);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
outb_p(tmp, NE_BASE + NE_RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb_p(tmp, NE_BASE + NE_RESET);
|
||||||
|
while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk(EI_NAME": ne_reset_8390() did not complete.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||||
|
porting to a new ethercard look at the packet driver source for hints.
|
||||||
|
The NEx000 doesn't share it on-board packet memory -- you have to put
|
||||||
|
the packet out through the "remote DMA" dataport using outb. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int xfer_count = count;
|
||||||
|
int nic_base = NE_BASE;
|
||||||
|
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_read(NE_BASE + NE_DATAPORT,buf,count>>1);
|
||||||
|
if (count & 0x01)
|
||||||
|
buf[count-1] = inb(NE_BASE + NE_DATAPORT), xfer_count++;
|
||||||
|
} else {
|
||||||
|
port_read_b(NE_BASE + NE_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This was for the ALPHA version only, but enough people have
|
||||||
|
encountering problems that it is still here. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int addr, tries = 10;
|
||||||
|
do {
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
|
||||||
|
-- it's broken! Check the "DMA" address instead. */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
addr = (high << 8) + low;
|
||||||
|
if (((ring_offset + xfer_count) & 0xff) == low)
|
||||||
|
return ring_offset + count;
|
||||||
|
} while (--tries > 0);
|
||||||
|
printk(EI_NAME": RX transfer address mismatch, %#4.4x (should be) vs. %#4.4x (actual).\n",
|
||||||
|
ring_offset + xfer_count, addr);
|
||||||
|
}
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ne_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const int start_page)
|
||||||
|
{
|
||||||
|
int retries = 0;
|
||||||
|
int nic_base = NE_BASE;
|
||||||
|
|
||||||
|
/* Round the count up for word writes. Do we need to do this?
|
||||||
|
What effect will an odd byte count have on the 8390?
|
||||||
|
I should check someday. */
|
||||||
|
if (ei_status.word16 && (count & 0x01))
|
||||||
|
count++;
|
||||||
|
/* We should already be in page 0, but to be safe... */
|
||||||
|
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, EN_CMD);
|
||||||
|
|
||||||
|
retry:
|
||||||
|
#if defined(rw_bugfix)
|
||||||
|
/* Handle the read-before-write bug the same way as the
|
||||||
|
Crynwr packet driver -- the NatSemi method doesn't work.
|
||||||
|
Actually this doesn't aways work either, but if you have
|
||||||
|
problems with your NEx000 this is better than nothing! */
|
||||||
|
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x42, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
/* Make certain that the dummy read has occured. */
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
#endif /* rw_bugfix */
|
||||||
|
|
||||||
|
/* Now the normal output. */
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||||
|
|
||||||
|
outb_p(E8390_RWRITE+E8390_START, EN_CMD);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
|
||||||
|
} else {
|
||||||
|
port_write_b(NE_BASE + NE_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This was for the ALPHA version only, but enough people have
|
||||||
|
encountering problems that it is still here. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int addr, tries = 10;
|
||||||
|
do {
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
|
||||||
|
-- it's broken! Check the "DMA" address instead. */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
addr = (high << 8) + low;
|
||||||
|
if ((start_page << 8) + count == addr)
|
||||||
|
return;
|
||||||
|
} while (--tries > 0);
|
||||||
|
printk(EI_NAME": Packet buffer transfer address mismatch on TX, %#4.4x vs. %#4.4x.\n",
|
||||||
|
(start_page << 8) + count, addr);
|
||||||
|
if (retries++ == 0)
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static void
|
||||||
|
ne_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
334
net/tcpip/ether-995/wd.c
Normal file
334
net/tcpip/ether-995/wd.c
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
/* wd.c: A WD80x3 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This is a driver for the WD8003 and WD8013 ethercards.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
|
||||||
|
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"wd.c:v0.30 1/30/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int wdprobe(int ioaddr, struct device *dev);
|
||||||
|
int wdprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void wd_reset_8390(struct device *dev);
|
||||||
|
static int wd_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void wd_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static int wd_close_card(struct device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
#define WD_START_PG 0x00 /* First page of TX buffer */
|
||||||
|
#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
|
||||||
|
#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
#define WD_CMDREG 0 /* Offset to ASIC command register. */
|
||||||
|
#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
|
||||||
|
#define WD_MEMENB 0x40 /* Enable the shared memory. */
|
||||||
|
#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
|
||||||
|
#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
|
||||||
|
#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
|
||||||
|
#define WD_NIC_OFFSET 16 /* Offset to the 8390 NIC from the base_addr. */
|
||||||
|
|
||||||
|
/* Probe for the WD8003 and WD8013. These cards have the station
|
||||||
|
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
|
||||||
|
following. The routine also initializes the card and fills the
|
||||||
|
station address field. */
|
||||||
|
|
||||||
|
int wdprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x280, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return wdprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb(*port) != 0xff && wdprobe1(*port, dev))
|
||||||
|
return *port;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wdprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
int checksum = 0;
|
||||||
|
int bits16 = 0;
|
||||||
|
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||||
|
printk("WD80x3 ethercard at %#3x:", ioaddr);
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printk(" %2.2X", inb(ioaddr+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
printk("WD80x3 ethercard at %#3x:", ioaddr+i);
|
||||||
|
for (;i < 33; i++) {
|
||||||
|
printk(" %2.2X", inb(ioaddr+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
#endif
|
||||||
|
printk("WD80x3 ethercard probe at %#3x:", ioaddr);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
int inval = inb(ioaddr + 8 + i);
|
||||||
|
checksum += inval;
|
||||||
|
if (i < 6)
|
||||||
|
printk(" %2.2X", (station_addr[i] = inval));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((checksum & 0xff) != 0xFF) {
|
||||||
|
printk(" not found (%#2.2x).\n", checksum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_status.name = "WD8003";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
|
||||||
|
/* This method of checking for a 16-bit board is borrowed from the
|
||||||
|
we.c driver. A simpler method is just to look in ASIC reg. 0x03.
|
||||||
|
I'm comparing the two method in alpha test to make certain they
|
||||||
|
return the same result. */
|
||||||
|
#ifndef FORCE_8BIT /* Same define as we.c. */
|
||||||
|
/* check for 16 bit board - it doesn't have register 0/8 aliasing */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
int tmp;
|
||||||
|
if( inb(ioaddr+8+i) != inb(ioaddr+i) ){
|
||||||
|
tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
|
||||||
|
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
|
||||||
|
if ((tmp & 0x01) == (inb( ioaddr+1) & 0x01)) {
|
||||||
|
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
|
||||||
|
bits16 = 1; /* use word mode of operation */
|
||||||
|
/* Magic to set ASIC to word-wide mode. */
|
||||||
|
outb( ISA16 | NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
|
||||||
|
outb(tmp, ioaddr+1);
|
||||||
|
ei_status.name = "WD8013";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
break; /* We have a 16bit board here! */
|
||||||
|
}
|
||||||
|
outb(tmp, ioaddr+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bits8 = 1;
|
||||||
|
#endif /* FORCE_8BIT */
|
||||||
|
|
||||||
|
#ifndef final_version
|
||||||
|
if ((inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01))
|
||||||
|
printk("\nWD80x3: Bus width conflict, %d (probe) != %d (reg report).\n",
|
||||||
|
ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
|
||||||
|
/* Allow an override for alpha testing. */
|
||||||
|
dev->mem_start = WD_SHMEM;
|
||||||
|
#else
|
||||||
|
if (dev->mem_start == 0) {
|
||||||
|
dev->mem_start = ((inb(ioaddr)&0x3f) << 13) +
|
||||||
|
(ei_status.word16 ? (inb(ioaddr+WD_CMDREG5)&0x1f)<<19 : 0x80000);
|
||||||
|
printk(" address %#x,", dev->mem_start);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The 8390 isn't at the base address -- the ASIC regs are there! */
|
||||||
|
dev->base_addr = ioaddr+WD_NIC_OFFSET;
|
||||||
|
|
||||||
|
ei_status.tx_start_page = WD_START_PG;
|
||||||
|
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = ei_status.word16 ? WD13_STOP_PG : WD03_STOP_PG;
|
||||||
|
|
||||||
|
dev->rmem_start = dev->mem_start + TX_PAGES*256;
|
||||||
|
dev->mem_end = dev->rmem_end
|
||||||
|
= dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 3
|
||||||
|
memset((void*)dev->mem_start, 0x42052465,
|
||||||
|
(ei_status.stop_page - WD_START_PG)*256);
|
||||||
|
#endif
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int irqmap[] = {9,3,5,7,10,11,15,4};
|
||||||
|
dev->irq = irqmap[((inb(ioaddr+4) >> 5) & 0x03)
|
||||||
|
+ (inb(ioaddr+1) & 0x04)];
|
||||||
|
} else if (dev->irq == 2)
|
||||||
|
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||||
|
or don't know which one to set. */
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||||
|
if (irqval) {
|
||||||
|
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(" %s found, using IRQ %d.\n", ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk("%s: Address read from register is %#x, setting address %#x\n",
|
||||||
|
ei_status.name,
|
||||||
|
((inb(ioaddr+WD_CMDREG5)&0x1f)<<19) + ((inb(ioaddr)&0x3f) << 13),
|
||||||
|
dev->mem_start);
|
||||||
|
/* Map in the shared memory. This is a little risky, since using
|
||||||
|
the stuff the user supplied is probably a bad idea. */
|
||||||
|
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
|
||||||
|
if (ei_status.word16)
|
||||||
|
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
|
||||||
|
|
||||||
|
ei_status.reset_8390 = &wd_reset_8390;
|
||||||
|
ei_status.block_input = &wd_block_input;
|
||||||
|
ei_status.block_output = &wd_block_output;
|
||||||
|
dev->stop = &wd_close_card;
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wd_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
outb(WD_RESET, wd_cmd_port);
|
||||||
|
if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(0x00, wd_cmd_port);
|
||||||
|
while ((inb(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk(EI_NAME": wd_reset_8390() did not complete.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printk("WD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||||
|
}
|
||||||
|
printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||||
|
for (;i < 33; i++) {
|
||||||
|
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* Set up the ASIC registers, just in case something changed them. */
|
||||||
|
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); /* WD_CMDREG */
|
||||||
|
if (ei_status.word16)
|
||||||
|
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output are easy on shared memory ethercards, and trivial
|
||||||
|
on the Western digital card where there is no choice of how to do it. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
void *xfer_start = (void *)(dev->mem_start + ring_offset - (WD_START_PG<<8));
|
||||||
|
#ifdef mapout
|
||||||
|
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
int mem_val = inb(wd_cmdreg);
|
||||||
|
/* Map in the shared memory. */
|
||||||
|
outb(mem_val|WD_MEMENB, wd_cmdreg);
|
||||||
|
#endif
|
||||||
|
if (xfer_start + count > (void*) dev->rmem_end) {
|
||||||
|
/* We must wrap the input move. */
|
||||||
|
int semi_count = (void*)dev->rmem_end - xfer_start;
|
||||||
|
memcpy(buf, xfer_start, semi_count);
|
||||||
|
count -= semi_count;
|
||||||
|
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||||
|
return dev->rmem_start + count;
|
||||||
|
}
|
||||||
|
memcpy(buf, xfer_start, count);
|
||||||
|
if (ei_debug > 4) {
|
||||||
|
unsigned short *board = xfer_start;
|
||||||
|
printk("wd8013: wd_block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
|
||||||
|
count, ring_offset, xfer_start, board[-1], board[0], board[1]);
|
||||||
|
}
|
||||||
|
#ifdef mapout
|
||||||
|
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||||
|
#endif
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This could only be outputting to the transmit buffer. The
|
||||||
|
ping-pong transmit setup doesn't work with this yet. */
|
||||||
|
static void
|
||||||
|
wd_block_output(struct device *dev, int count, const unsigned char *buf, int start_page)
|
||||||
|
{
|
||||||
|
unsigned char *shmem = (void *)dev->mem_start + ((start_page - WD_START_PG)<<8);
|
||||||
|
#ifdef mapout
|
||||||
|
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
int mem_val = inb(wd_cmdreg);
|
||||||
|
/* Map in the shared memory. */
|
||||||
|
outb(mem_val|WD_MEMENB, wd_cmdreg);
|
||||||
|
#endif
|
||||||
|
memcpy(shmem, buf, count);
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("wd8013: wd_block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n",
|
||||||
|
shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count));
|
||||||
|
#ifdef mapout
|
||||||
|
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static int
|
||||||
|
wd_close_card(struct device *dev)
|
||||||
|
{
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk("%s: shutting down ethercard.\n", ei_status.name);
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
/* Turn off the shared memory. */
|
||||||
|
outb((((dev->mem_start>>13) & 0x3f)),
|
||||||
|
dev->base_addr-WD_NIC_OFFSET); /* WD_CMDREG */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
BIN
net/tcpip/ftp-rl.taz
Normal file
BIN
net/tcpip/ftp-rl.taz
Normal file
Binary file not shown.
BIN
net/tcpip/ftpd-dik.taz
Normal file
BIN
net/tcpip/ftpd-dik.taz
Normal file
Binary file not shown.
1
net/tcpip/ne2000/111.source
Normal file
1
net/tcpip/ne2000/111.source
Normal file
@@ -0,0 +1 @@
|
|||||||
|
stammt von super.org:/pub/linux
|
||||||
368
net/tcpip/ne2000/3c501.c
Normal file
368
net/tcpip/ne2000/3c501.c
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
/* 3c501.c: A 3Com 3c501 ethernet driver for linux. */
|
||||||
|
#include <linux/config.h>
|
||||||
|
#ifdef ETHERLINK1
|
||||||
|
/*
|
||||||
|
Copyright (C) 1992 Donald Becker
|
||||||
|
|
||||||
|
This is alpha test code. No general redistribution is permitted.
|
||||||
|
This is a device driver for the 3Com Etherlink 3c501.
|
||||||
|
Do not purchase this card, even as a joke. It's performance is horrible,
|
||||||
|
and it breaks in many ways.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Braindamage remaining:
|
||||||
|
The 3c501 board.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/tty.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include <asm/segment.h>
|
||||||
|
/* This should be checked for necessity after testing. */
|
||||||
|
#define REALLY_SLOW_IO
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <linux/fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "eth.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "ip.h"
|
||||||
|
#include "tcp.h"
|
||||||
|
#include "sock.h"
|
||||||
|
#include "arp.h"
|
||||||
|
|
||||||
|
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||||
|
|
||||||
|
#define port_read_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define DEBUG 10 /* use 0 for production, 1 for devel., >2 for debug */
|
||||||
|
|
||||||
|
|
||||||
|
/* Index to functions. */
|
||||||
|
static void el_interrupt(int reg_ptr);
|
||||||
|
static void el_send_packet(struct sk_buff *skb, struct device *dev);
|
||||||
|
static void el_receive(struct device *dev);
|
||||||
|
|
||||||
|
#define EL_NAME "EtherLink 3c501"
|
||||||
|
|
||||||
|
static int el_debug = DEBUG; /* Anything above 5 is wordy death! */
|
||||||
|
static int el_base;
|
||||||
|
static struct device *eldev; /* Only for consistency checking. */
|
||||||
|
|
||||||
|
/* We could put everything in a struct to be malloc()ed per-board, but
|
||||||
|
who would want more than one 3c501?. */
|
||||||
|
static struct { /* This should be stored per-board */
|
||||||
|
char *name;
|
||||||
|
int exists:1; /* perhaps in dev->private. */
|
||||||
|
int open:1;
|
||||||
|
int txing:1; /* Transmit Active, don't confuse the 8390 */
|
||||||
|
int in_interrupt:4;
|
||||||
|
int overruns, rx_errors, tx_errors;
|
||||||
|
} el_status = { "Etherlink I", 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
static int collisions; /* Tx collisions this packet */
|
||||||
|
static int tx_pkt_length; /* To reset GP after each collision. */
|
||||||
|
static int runt_packets = 0; /* # of runt packets picked up so far. */
|
||||||
|
/* static int rx_missed = 0; * # of packets we missed. */
|
||||||
|
|
||||||
|
static struct sigaction el_sigaction = { el_interrupt, 0, 0, NULL, };
|
||||||
|
|
||||||
|
#define RX_STATUS (el_base + 0x06)
|
||||||
|
#define TX_STATUS (el_base + 0x07)
|
||||||
|
#define GP_LOW (el_base + 0x08)
|
||||||
|
#define GP_HIGH (el_base + 0x09)
|
||||||
|
#define RX_LOW (el_base + 0x0A)
|
||||||
|
#define RX_HIGH (el_base + 0x0B)
|
||||||
|
#define SAPROM (el_base + 0x0C)
|
||||||
|
#define AX_STATUS (el_base + 0x0E)
|
||||||
|
#define DATAPORT (el_base + 0x0F)
|
||||||
|
#define TX_RDY 0x08 /* In TX_STATUS */
|
||||||
|
|
||||||
|
/* Writes to the ax command register. */
|
||||||
|
#define AX_OFF 0x40 /* Irq off, buffer access on */
|
||||||
|
#define AX_SYS 0x41 /* Load the buffer */
|
||||||
|
#define AX_XMIT 0x45 /* Transmit a packet */
|
||||||
|
#define AX_RX 0x49 /* Receive a packet */
|
||||||
|
#define AX_LOOP 0x4D /* Loopback */
|
||||||
|
|
||||||
|
/* Normal receive mode written to RX_STATUS. We must intr on short packets
|
||||||
|
to avoid bogus rx lockups. */
|
||||||
|
#define RX_NORM 0xA8
|
||||||
|
/* TX_STATUS register. */
|
||||||
|
#define TX_COLLISION 0x02
|
||||||
|
|
||||||
|
/* Open/initialize the board. */
|
||||||
|
static int
|
||||||
|
el_open(struct device *dev)
|
||||||
|
{
|
||||||
|
if ( ! el_status.exists) {
|
||||||
|
printk(EL_NAME ": Opening a non-existent physical device\n");
|
||||||
|
return 1; /* We should have a better error return. */
|
||||||
|
}
|
||||||
|
if (el_debug > 2)
|
||||||
|
printk(EL_NAME ": Doing el_open(%s)...",
|
||||||
|
dev == eldev ? dev->name : " on unknown dev");
|
||||||
|
el_status.txing = 0;
|
||||||
|
el_status.in_interrupt = 0;
|
||||||
|
|
||||||
|
|
||||||
|
outb_p(AX_LOOP, AX_STATUS); /* Aux control, irq and loopback enabled */
|
||||||
|
outb_p(0x00, RX_LOW); /* Set rx packet area to 0. */
|
||||||
|
outb_p(RX_NORM, RX_STATUS); /* Set Rx commands. */
|
||||||
|
outb_p(AX_RX, AX_STATUS); /* Aux control, irq and receive enabled */
|
||||||
|
|
||||||
|
el_status.open = 1;
|
||||||
|
if (el_debug > 2)
|
||||||
|
printk("finished el_open().\n");
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
el_start_xmit(struct sk_buff *skb, struct device *dev)
|
||||||
|
{
|
||||||
|
int axsr;
|
||||||
|
|
||||||
|
if ( ! el_status.exists)
|
||||||
|
return 0; /* We should have a better error return. */
|
||||||
|
if (el_debug > 2)
|
||||||
|
printk(EL_NAME": Doing el_start_xmit(<sk_buff%x>,%s).\n", skb,
|
||||||
|
dev == eldev ? "EtherLink" : "unknown dev");
|
||||||
|
cli();
|
||||||
|
if (el_debug > 4) printk(EL_NAME": interrupts suspended...");
|
||||||
|
axsr = inb_p(AX_STATUS);
|
||||||
|
if (el_status.txing || axsr & 0x01) {
|
||||||
|
if (jiffies - dev->trans_start < 30) {
|
||||||
|
sti();
|
||||||
|
if (el_debug > 2)
|
||||||
|
printk(EL_NAME": transmit deferred, no timeout.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printk (EL_NAME ": transmit timed out with tx status %#2x.\n",
|
||||||
|
inb(TX_STATUS));
|
||||||
|
}
|
||||||
|
if (el_debug > 4) printk("doing sti()...");
|
||||||
|
sti();
|
||||||
|
if (el_debug > 4) printk("filling in hdr...");
|
||||||
|
|
||||||
|
/* This is new: it means some higher layer thinks we've missed an
|
||||||
|
tx-done interrupt. */
|
||||||
|
if (skb == NULL) {
|
||||||
|
/* Alternative is ei_tx_intr(dev); */
|
||||||
|
el_status.txing = 1;
|
||||||
|
if (dev_tint(NULL, dev) == 0)
|
||||||
|
el_status.txing = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Fill in the ethernet header. */
|
||||||
|
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
|
||||||
|
skb->dev = dev;
|
||||||
|
arp_queue (skb);
|
||||||
|
if (el_debug > 1)
|
||||||
|
printk(" Exiting from xmit_start() via rebuild header?\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dev->trans_start = jiffies;
|
||||||
|
el_status.txing = 1;
|
||||||
|
outb_p(0x0A, TX_STATUS); /* tx irq on done, collision */
|
||||||
|
el_send_packet(skb, dev);
|
||||||
|
if (skb->free)
|
||||||
|
kfree_skb (skb, FREE_WRITE);
|
||||||
|
if (el_debug > 3)
|
||||||
|
printk(EL_NAME": Returning from el_start_xmit().\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The typical workload of the driver:
|
||||||
|
Handle the ether interface interrupts. */
|
||||||
|
static void
|
||||||
|
el_interrupt(int reg_ptr)
|
||||||
|
{
|
||||||
|
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
|
||||||
|
int axsr; /* Aux. status reg. */
|
||||||
|
int txsr; /* Tx. status reg. */
|
||||||
|
int rxsr; /* Rx. status reg. */
|
||||||
|
|
||||||
|
if (eldev->irq != irq) {
|
||||||
|
printk (EL_NAME ": irq %d for unknown device\n", irq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
el_status.in_interrupt++;
|
||||||
|
sti(); /* Turn interrupts back on. */
|
||||||
|
axsr = inb_p(AX_STATUS);
|
||||||
|
txsr = inb_p(TX_STATUS);
|
||||||
|
rxsr = inb_p(RX_STATUS);
|
||||||
|
if (el_debug > 1)
|
||||||
|
printk(EL_NAME": in el_interrupt(), axsr=%#2x, txsr=%#2x, rxsr=%#2x.\n",
|
||||||
|
axsr, txsr, rxsr);
|
||||||
|
if (el_status.in_interrupt > 1) {
|
||||||
|
/* We should probably return here -- the 3c501 glitches the
|
||||||
|
interrupt line when you write to the rx or tx command register. */
|
||||||
|
printk(EL_NAME ": Reentering the interrupt driver!\n");
|
||||||
|
}
|
||||||
|
if (rxsr & 0x08)
|
||||||
|
runt_packets++; /* Just reading rxstatus fixes this. */
|
||||||
|
else if (rxsr & 0x20)
|
||||||
|
el_receive(eldev);
|
||||||
|
else if (txsr & TX_COLLISION) {
|
||||||
|
if (++collisions > 16) {
|
||||||
|
printk(EL_NAME": Transmit failed 16 times, ethernet jammed?\n");
|
||||||
|
/* Turn receiving back on. */
|
||||||
|
el_status.txing = 0;
|
||||||
|
outb_p(0x00, RX_LOW);
|
||||||
|
outb_p(AX_RX, AX_STATUS);
|
||||||
|
} else { /* Retrigger xmit. */
|
||||||
|
int gp_start = 0x800 - tx_pkt_length;
|
||||||
|
outb_p(gp_start, GP_LOW);
|
||||||
|
outb_p(gp_start>>8, GP_HIGH);
|
||||||
|
outb_p(AX_XMIT, AX_STATUS);
|
||||||
|
}
|
||||||
|
} else if (txsr & TX_RDY) {
|
||||||
|
if (dev_tint(NULL, eldev) == 0)
|
||||||
|
el_status.txing = 0; /* We could turn off the tx... */
|
||||||
|
}
|
||||||
|
el_status.in_interrupt--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is stuffed into the dev struct to be called by dev.c:dev_tint(). */
|
||||||
|
static void
|
||||||
|
el_send_packet(struct sk_buff *skb, struct device *dev)
|
||||||
|
{
|
||||||
|
tx_pkt_length = skb->len;
|
||||||
|
collisions = 0;
|
||||||
|
if (el_debug > 3) printk(" el_send_packet(%d)...", tx_pkt_length);
|
||||||
|
/* Should we check for tiny (or huge) lengths here? */
|
||||||
|
if (tx_pkt_length) {
|
||||||
|
int gp_start = 0x800 - tx_pkt_length;
|
||||||
|
unsigned char *buf = (void *)(skb+1);
|
||||||
|
outb_p(AX_OFF, AX_STATUS); /* irq disabled, rx off */
|
||||||
|
outb_p(gp_start, GP_LOW);
|
||||||
|
outb_p(gp_start>>8, GP_HIGH);
|
||||||
|
/* After testing use port_write(), defined above. */
|
||||||
|
for (; gp_start < 0x800; gp_start++)
|
||||||
|
outb_p(*buf++, DATAPORT);
|
||||||
|
outb_p(AX_XMIT, AX_STATUS); /* Trigger xmit. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have a good packet; well, not really "good", just mostly not broken.
|
||||||
|
We must check everything to see if it is good. */
|
||||||
|
static void
|
||||||
|
el_receive(struct device *dev)
|
||||||
|
{
|
||||||
|
int state = 0, sksize, length;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
if (el_debug > 2)
|
||||||
|
printk("in el_receive...");
|
||||||
|
/* Painfully read it out of the local memory. */
|
||||||
|
outb_p(AX_SYS, AX_STATUS);
|
||||||
|
length = inb_p(RX_LOW) + (inb_p(RX_HIGH)<<8);
|
||||||
|
if ((length < 60 || length > 1535)) {
|
||||||
|
if (el_debug)
|
||||||
|
printk(EL_NAME": bogus packet, length=%d\n", length);
|
||||||
|
/* We should reset to receive... */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sksize = sizeof(struct sk_buff) + length;
|
||||||
|
skb = kmalloc(sksize, GFP_ATOMIC);
|
||||||
|
outb_p(0x00, GP_LOW);
|
||||||
|
outb_p(0x00, GP_HIGH);
|
||||||
|
if (skb != NULL) {
|
||||||
|
unsigned char *buf = (void *)(skb+1);
|
||||||
|
skb->mem_len = sksize;
|
||||||
|
skb->mem_addr = skb;
|
||||||
|
/* After testing use port_read(), defined above. */
|
||||||
|
while (length-- > 0)
|
||||||
|
*buf++ = inb_p(DATAPORT);
|
||||||
|
state = dev_rint((void *)skb, length, IN_SKBUFF, dev);
|
||||||
|
} else if (el_debug) {
|
||||||
|
printk("Couldn't allocate a sk_buff of size %d.\n", sksize);
|
||||||
|
}
|
||||||
|
dev_rint(NULL, 0,0, dev); /* Inform upper level */
|
||||||
|
if (el_debug > 2)
|
||||||
|
printk("done.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
etherlink_init(struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
eldev = dev; /* Store for debugging. */
|
||||||
|
el_base = dev->base_addr;
|
||||||
|
|
||||||
|
printk("3c501 probe at %#3.3x: ", el_base);
|
||||||
|
outb(0x00, GP_HIGH);
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
outb(i, GP_LOW); /* Set station address prom addr */
|
||||||
|
dev->dev_addr[i] = inb(SAPROM); /* Read Station address prom */
|
||||||
|
printk(" %2.2x", dev->dev_addr[i]);
|
||||||
|
}
|
||||||
|
/* Check the first three octets of the S.A. for 3Com's code. */
|
||||||
|
if (dev->dev_addr[0] != 0x02 || dev->dev_addr[1] != 0x60
|
||||||
|
|| dev->dev_addr[2] != 0x8c) {
|
||||||
|
printk(" Etherlink not found.\n", dev->base_addr);
|
||||||
|
el_status.exists = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
el_status.exists = 1;
|
||||||
|
printk(" Etherlink found.\n");
|
||||||
|
|
||||||
|
/* Initialize the rest of the device structure. Most of these should
|
||||||
|
be in Space.c. */
|
||||||
|
for (i = 0; i < DEV_NUMBUFFS; i++)
|
||||||
|
dev->buffs[i] = NULL;
|
||||||
|
|
||||||
|
dev->hard_header = eth_hard_header;
|
||||||
|
dev->add_arp = eth_add_arp;
|
||||||
|
dev->queue_xmit = dev_queue_xmit;
|
||||||
|
dev->rebuild_header = eth_rebuild_header;
|
||||||
|
dev->type_trans = eth_type_trans;
|
||||||
|
|
||||||
|
dev->send_packet = &el_send_packet;
|
||||||
|
dev->open = &el_open;
|
||||||
|
dev->hard_start_xmit = &el_start_xmit;
|
||||||
|
|
||||||
|
dev->type = ETHER_TYPE;
|
||||||
|
dev->hard_header_len = sizeof (struct enet_header);
|
||||||
|
dev->mtu = 1500; /* eth_mtu */
|
||||||
|
dev->addr_len = ETHER_ADDR_LEN;
|
||||||
|
for (i = 0; i < dev->addr_len; i++) {
|
||||||
|
dev->broadcast[i]=0xff;
|
||||||
|
}
|
||||||
|
/* Turn off interrupts. */
|
||||||
|
|
||||||
|
/* Snarf the assigned interrupt. */
|
||||||
|
{ int irqval = irqaction (dev->irq, &el_sigaction);
|
||||||
|
if (irqval) {
|
||||||
|
printk (" unable to get IRQ%d, error=%d.\n", dev->irq, irqval);
|
||||||
|
return; /* Return failure someday */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outb(0x8C, 0x0e); /* setup Aux. control reg. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif /* ETHERLINK1 */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -DMAX_16M -I/usr/linux-master/net/tcp -c -o 3c501.o 3c501.c"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
1053
net/tcpip/ne2000/3c503.c
Normal file
1053
net/tcpip/ne2000/3c503.c
Normal file
File diff suppressed because it is too large
Load Diff
55
net/tcpip/ne2000/3c503reg.h
Normal file
55
net/tcpip/ne2000/3c503reg.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/* Definitions for the Etherlink 2. */
|
||||||
|
#include "8390.h"
|
||||||
|
#define EL2H (dev->base_addr + 0x400)
|
||||||
|
#define EL2L (dev->base_addr)
|
||||||
|
|
||||||
|
/* 3Com 3c503 ASIC registers */
|
||||||
|
#define E33G_STARTPG (EL2H+0) /* Start page, must match EN0_STARTPG */
|
||||||
|
#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
|
||||||
|
#define E33G_NBURST (EL2H+2) /* Size of DMA burst before relinquishing bus */
|
||||||
|
#define E33G_IOBASE (EL2H+3) /* Bit coded: where I/O regs are jumpered. */
|
||||||
|
/* (Which you have to know already to read it) */
|
||||||
|
#define E33G_ROMBASE (EL2H+4) /* Bit coded: Where/whether EEPROM&DPRAM exist */
|
||||||
|
#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
|
||||||
|
#define E33G_CNTRL (EL2H+6) /* Board's main control register */
|
||||||
|
#define E33G_STATUS (EL2H+7) /* Status on completions. */
|
||||||
|
#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
|
||||||
|
/* (Which IRQ to assert, DMA chan to use) */
|
||||||
|
#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
|
||||||
|
#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
|
||||||
|
#define E33G_VP2 (EL2H+11) /* Vector pointer - for clearing RAM select */
|
||||||
|
#define E33G_VP1 (EL2H+12) /* on a system reset, to re-enable EPROM. */
|
||||||
|
#define E33G_VP0 (EL2H+13) /* 3Com says set this to Ctrl-Alt-Del handler */
|
||||||
|
#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O data moves ... */
|
||||||
|
#define E33G_FIFOL (EL2H+15) /* .. low byte of above. */
|
||||||
|
|
||||||
|
/* Bits in E33G_CNTRL register: */
|
||||||
|
|
||||||
|
#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
|
||||||
|
#define ECNTRL_THIN (0x02) /* Onboard thin-net xcvr enable */
|
||||||
|
#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
|
||||||
|
#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
|
||||||
|
#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
|
||||||
|
#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
|
||||||
|
#define ECNTRL_START (0x80) /* Start the DMA logic */
|
||||||
|
|
||||||
|
/* Bits in E33G_STATUS register: */
|
||||||
|
|
||||||
|
#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
|
||||||
|
#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
|
||||||
|
#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
|
||||||
|
#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
|
||||||
|
#define ESTAT_DIP (0x08) /* DMA In Progress */
|
||||||
|
|
||||||
|
/* Bits in E33G_GACFR register: */
|
||||||
|
|
||||||
|
#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */
|
||||||
|
#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */
|
||||||
|
|
||||||
|
/* Shared memory management parameters */
|
||||||
|
|
||||||
|
#define EL2SM_TSTART_PG (0x20) /* First page of TX buffer */
|
||||||
|
#define EL2SM_RSTART_PG (0x26) /* Starting page of RX ring */
|
||||||
|
#define EL2SM_RSTOP_PG (0x40) /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
/* End of 3C503 parameter definitions */
|
||||||
90
net/tcpip/ne2000/8390.h
Normal file
90
net/tcpip/ne2000/8390.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/* Generic NS8390 register definitions. */
|
||||||
|
/* This file was originally written for the 3c503 driver, but should be
|
||||||
|
usable for most 8390-based network boards.
|
||||||
|
Some of these names are from the clarkson packet drivers. */
|
||||||
|
|
||||||
|
/* Some generic ethernet register configurations. */
|
||||||
|
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
|
||||||
|
#define E8390_RX_IRQ_MASK 0x5
|
||||||
|
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
|
||||||
|
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
|
||||||
|
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
|
||||||
|
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
|
||||||
|
|
||||||
|
/* Register accessed at EN_CMD, the 8390 base addr. */
|
||||||
|
#define E8390_STOP 0x01 /* Stop the chip, software reset */
|
||||||
|
#define E8390_START 0x02 /* Start the chip after stopping */
|
||||||
|
#define E8390_TRANS 0x04 /* Transmit a frame */
|
||||||
|
#define E8390_RREAD 0x08 /* Remote read */
|
||||||
|
#define E8390_RWRITE 0x10 /* Remote write */
|
||||||
|
#define E8390_NODMA 0x20 /* No remote DMA used on this card */
|
||||||
|
#define E8390_PAGE0 0x00 /* Select page chip registers */
|
||||||
|
#define E8390_PAGE1 0x40 /* using the two high-order bits */
|
||||||
|
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
|
||||||
|
|
||||||
|
/* Page 0 register offsets. */
|
||||||
|
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
|
||||||
|
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
|
||||||
|
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
|
||||||
|
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
|
||||||
|
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
|
||||||
|
#define EN0_TSR 0x04 /* Transmit status reg RD */
|
||||||
|
#define EN0_TPSR 0x04 /* Transmit starting page WR */
|
||||||
|
#define EN0_NCR 0x05 /* Number of collision reg RD */
|
||||||
|
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
|
||||||
|
#define EN0_FIFO 0x06 /* FIFO RD */
|
||||||
|
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
|
||||||
|
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
|
||||||
|
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
|
||||||
|
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
|
||||||
|
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
|
||||||
|
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
|
||||||
|
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
|
||||||
|
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
|
||||||
|
#define EN0_RSR 0x0c /* rx status reg RD */
|
||||||
|
#define EN0_RXCR 0x0c /* RX configuration reg WR */
|
||||||
|
#define EN0_TXCR 0x0d /* TX configuration reg WR */
|
||||||
|
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
|
||||||
|
#define EN0_DCFG 0x0e /* Data configuration reg WR */
|
||||||
|
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
|
||||||
|
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
|
||||||
|
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
|
||||||
|
|
||||||
|
/* Bits in EN0_ISR - Interrupt status register */
|
||||||
|
#define ENISR_RX 0x01 /* Receiver, no error */
|
||||||
|
#define ENISR_TX 0x02 /* Transmitter, no error */
|
||||||
|
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
|
||||||
|
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
|
||||||
|
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
|
||||||
|
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
|
||||||
|
#define ENISR_RDC 0x40 /* remote dma complete */
|
||||||
|
#define ENISR_RESET 0x80 /* Reset completed */
|
||||||
|
#define ENISR_ALL 0x3f /* Interrupts we will enable */
|
||||||
|
|
||||||
|
/* Bits in EN0_DCFG - Data config register */
|
||||||
|
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
|
||||||
|
|
||||||
|
/* Page 1 register offsets. */
|
||||||
|
#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
|
||||||
|
#define EN1_CURPAG 0x07 /* Current memory page RD WR */
|
||||||
|
#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
|
||||||
|
|
||||||
|
/* Bits in received packet status byte and EN0_RSR*/
|
||||||
|
#define ENRSR_RXOK 0x01 /* Received a good packet */
|
||||||
|
#define ENRSR_CRC 0x02 /* CRC error */
|
||||||
|
#define ENRSR_FAE 0x04 /* frame alignment error */
|
||||||
|
#define ENRSR_FO 0x08 /* FIFO overrun */
|
||||||
|
#define ENRSR_MPA 0x10 /* missed pkt */
|
||||||
|
#define ENRSR_PHY 0x20 /* physical/multicase address */
|
||||||
|
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
|
||||||
|
#define ENRSR_DEF 0x80 /* deferring */
|
||||||
|
|
||||||
|
/* Transmitted packet status, EN0_TSR. */
|
||||||
|
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
|
||||||
|
|
||||||
|
/* The per-packet-header format. */
|
||||||
|
struct e8390_pkt_hdr {
|
||||||
|
unsigned char status; /* status */
|
||||||
|
unsigned char next; /* pointer to next packet. */
|
||||||
|
unsigned short count; /* header + packet lenght in bytes */
|
||||||
|
};
|
||||||
86
net/tcpip/ne2000/README
Normal file
86
net/tcpip/ne2000/README
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
Directory: ~ftp/pub/linux/
|
||||||
|
|
||||||
|
WARNING -- read the bugs section at the end of this file before getting
|
||||||
|
the ethernet board driver!!!
|
||||||
|
|
||||||
|
Files:
|
||||||
|
README This file, updated Tuesday Nov. 24, 1992 18:34.
|
||||||
|
3c501.c The 3c501 ethernet driver source
|
||||||
|
3c503.c The 3c503, 3c503/16, NE1000 and NE2000 ethernet driver source.
|
||||||
|
3c503reg.h Definitions specific to the 3c503
|
||||||
|
8390.h Generic definitions for the NS8390 chip.
|
||||||
|
Space.c Added the table entries for the new ethernet drivers.
|
||||||
|
This file is modified from the 0.98.4 release.
|
||||||
|
|
||||||
|
Image A pre-built Linux 0.98.4 kernel with MathEmu, no SCSI, mount
|
||||||
|
patch, serial patch, CapsLck mapped to Cntrl, and the
|
||||||
|
3c503 and NE2000 ethernet drivers. This kernel usually has
|
||||||
|
massive ethernet debugging turned on (debugging the kernel
|
||||||
|
and setting ei_debug = 0 and el_debug = 0 will turn it off)
|
||||||
|
and this version may not really work. Also the address and
|
||||||
|
IRQ settings are hard-coded for most ethercards. Making your
|
||||||
|
own kernel is usually a much better choice.
|
||||||
|
|
||||||
|
linux-0.98.3-E If it exists, the last Image file that worked OK.
|
||||||
|
|
||||||
|
|
||||||
|
Directions:
|
||||||
|
|
||||||
|
You might want to rename "3c503.c" to "ne2000.c".
|
||||||
|
|
||||||
|
Put the files (3c503.c, 3c503reg.h, 8390.h, and Space.c) into linux/net/tcp/.
|
||||||
|
Space.c is the only tricky one -- it overwrites the old Space.c, and
|
||||||
|
you'll need to edit it to add your IRQ and base address.
|
||||||
|
|
||||||
|
Next add 3c503.o to to linux/net/tcp/Makefile
|
||||||
|
tcpip.o: ... 3c503.o
|
||||||
|
|
||||||
|
Edit linux/include/linux/config.h and/or config.site.h and insert the following
|
||||||
|
(see below for other details).
|
||||||
|
#undef CONFIG_DISTRIBUTION /* Use config.site.h instead. */
|
||||||
|
#define EI8390 0x300 /* Then these should be in config.site.h */
|
||||||
|
#define EI8390IRQ 5 /* Not used yet, edit Space.c instead. */
|
||||||
|
#define EI_DEBUG 2
|
||||||
|
|
||||||
|
Make and install your new kernel.
|
||||||
|
|
||||||
|
To actually use this driver you must get the TCP/IP package and edit
|
||||||
|
your /usr/etc/inet/rc.net file to config device "eth_if" instead of
|
||||||
|
"eth0" (the WD8003).
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
None know right now. I haven't tested the NS8390 overrun code -- it
|
||||||
|
worked OK the last time I had other bugs to trigger it, but I've
|
||||||
|
changed it since.
|
||||||
|
|
||||||
|
Two errors in some versions of the 8390/83901/83902 may not
|
||||||
|
be handled correctly: the first is a bug where the RX status bytes
|
||||||
|
is missing from the header -- I try to recover first rather than
|
||||||
|
resetting the chip (which I do afterwards).
|
||||||
|
|
||||||
|
The second is the 8390 problem with writes to the ethercard
|
||||||
|
RAM: the first write may be corrupted if you don't do a read
|
||||||
|
first -- I've never seen this corruption happen, but it may
|
||||||
|
just be masked by higher level protocols.
|
||||||
|
I have put in the read-first code, but it's a frequent
|
||||||
|
source of problems... The timing is really tricky.
|
||||||
|
|
||||||
|
The 3c501 driver isn't complete. This card severely brain-damaged
|
||||||
|
anyway -- you won't notice the performance diffence when it does work.
|
||||||
|
|
||||||
|
Please send me email if you do try out the drivers, even if you don't
|
||||||
|
encounter bugs.
|
||||||
|
|
||||||
|
Important defines
|
||||||
|
|
||||||
|
EI_DEBUG Set to the desired numeric debugging level. Use 3 or
|
||||||
|
greater when actively debugging a problem, '1' for a
|
||||||
|
casual interest in what's going on, and '0' for normal
|
||||||
|
use. (Most of the debugging stuff has been taken out recently,
|
||||||
|
so this won't have much effect.)
|
||||||
|
EI8390 Define (probably in config.site.h) this to the base address
|
||||||
|
of your ethernet card. You will also have to set the base
|
||||||
|
address and irq in net/tcp/Space.c.
|
||||||
|
ETHERLINK1 Define this to the base address of a 3c501 card. You will
|
||||||
|
also have to set the base address and irq in net/tcp/Space.c.
|
||||||
113
net/tcpip/ne2000/Space.c
Normal file
113
net/tcpip/ne2000/Space.c
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/* Space.c */
|
||||||
|
/* Holds initial configuration information for devices. */
|
||||||
|
#include "dev.h"
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/config.h>
|
||||||
|
|
||||||
|
#ifdef ETHERLINK1
|
||||||
|
extern void etherlink_init(struct device *);
|
||||||
|
|
||||||
|
static struct device el_dev = {
|
||||||
|
"if3c501",
|
||||||
|
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
|
||||||
|
ETHERLINK1, 9, 0,0,0,0,0, /* base i/o address, irq, and flags. */
|
||||||
|
NULL, etherlink_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef EI8390
|
||||||
|
extern void ethif_init(struct device *);
|
||||||
|
|
||||||
|
static struct device el3c503_dev = {
|
||||||
|
"eth_if",
|
||||||
|
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
|
||||||
|
0x300, 5, 0,0,0,0,0, /* base i/o address, irq, and flags. */
|
||||||
|
#ifdef ETHERLINK1
|
||||||
|
&el_dev,
|
||||||
|
#else
|
||||||
|
NULL,
|
||||||
|
#endif
|
||||||
|
ethif_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
extern void wd8003_init(struct device *);
|
||||||
|
|
||||||
|
static struct device wd8003_dev =
|
||||||
|
{
|
||||||
|
"eth0",
|
||||||
|
0xd2000, /* recv memory end. */
|
||||||
|
0xd0600, /* recv memory start. */
|
||||||
|
0xd2000, /* memory end. */
|
||||||
|
0xd0000, /* memory start. */
|
||||||
|
0x280, /* base i/o address. */
|
||||||
|
5, /* irq */
|
||||||
|
0,0,0,0,0, /* flags */
|
||||||
|
#ifdef EI8390
|
||||||
|
&el3c503_dev, /* next device */
|
||||||
|
#elif defined(ETHERLINK1)
|
||||||
|
&el_dev, /* next device */
|
||||||
|
#else
|
||||||
|
NULL, /* next device */
|
||||||
|
#endif
|
||||||
|
wd8003_init,
|
||||||
|
/* wd8003_init should set up the rest. */
|
||||||
|
0, /* trans start. */
|
||||||
|
{NULL}, /* buffs */
|
||||||
|
NULL, /* backlog */
|
||||||
|
NULL, /* open */
|
||||||
|
NULL, /* stop */
|
||||||
|
NULL, /* hard_start_xmit */
|
||||||
|
NULL, /* hard_header */
|
||||||
|
NULL, /* add arp */
|
||||||
|
NULL, /* queue xmit */
|
||||||
|
NULL, /* rebuild header */
|
||||||
|
NULL, /* type_trans */
|
||||||
|
NULL, /* send_packet */
|
||||||
|
NULL, /* private */
|
||||||
|
0, /* type. */
|
||||||
|
0, /* hard_header_len */
|
||||||
|
0, /* mtu */
|
||||||
|
{0,}, /* broadcast address */
|
||||||
|
{0,}, /* device address */
|
||||||
|
0 /* addr len */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void loopback_init(struct device *dev);
|
||||||
|
|
||||||
|
static struct device loopback_dev =
|
||||||
|
{
|
||||||
|
"loopback",
|
||||||
|
-1, /* recv memory end. */
|
||||||
|
0x0, /* recv memory start. */
|
||||||
|
-1, /* memory end. */
|
||||||
|
0, /* memory start. */
|
||||||
|
0, /* base i/o address. */
|
||||||
|
0, /* irq */
|
||||||
|
0,0,1,0,0, /* flags */
|
||||||
|
&wd8003_dev, /* next device */
|
||||||
|
loopback_init,
|
||||||
|
/* loopback_init should set up the rest. */
|
||||||
|
0, /* trans start. */
|
||||||
|
{NULL}, /* buffs */
|
||||||
|
NULL, /* backlog */
|
||||||
|
NULL, /* open */
|
||||||
|
NULL, /* stop */
|
||||||
|
NULL, /* hard_start_xmit */
|
||||||
|
NULL, /* hard_header */
|
||||||
|
NULL, /* add arp */
|
||||||
|
NULL, /* queue xmit */
|
||||||
|
NULL, /* rebuild header */
|
||||||
|
NULL, /* type_trans */
|
||||||
|
NULL, /* send_packet */
|
||||||
|
NULL, /* private */
|
||||||
|
0, /* type. */
|
||||||
|
0, /* hard_header_len */
|
||||||
|
0, /* mtu */
|
||||||
|
{0,}, /* broadcast address */
|
||||||
|
{0,}, /* device address */
|
||||||
|
0 /* addr len */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct device *dev_base = &loopback_dev;
|
||||||
13
net/tcpip/net-0.1.README
Normal file
13
net/tcpip/net-0.1.README
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
README for net-src-0.1.tar.Z and net-bin-0.1.tar.Z
|
||||||
|
|
||||||
|
Included are ftp, ftpd, telnet, telnetd, inetd, named, nslookup and ping.
|
||||||
|
|
||||||
|
These are libc-4.1 versions of various net programs, both
|
||||||
|
source and binaries. They are from bsd-net2 with very minimal
|
||||||
|
changes. The telnet port is based on the one from Pete Chown.
|
||||||
|
|
||||||
|
Yes, this telnet/telnetd really works. I think. This is only
|
||||||
|
a pre-release. Let me know if there are any problems.
|
||||||
|
|
||||||
|
Rick Sladkey
|
||||||
|
jrs@world.std.com
|
||||||
98
net/tcpip/net-0.2.README
Normal file
98
net/tcpip/net-0.2.README
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
This is a compliation of some net sources, mostly from bsd-net2. I
|
||||||
|
have made an effort to get the programs to compile with few or no
|
||||||
|
changes except for a new Makefile. This resulted in a bsd include
|
||||||
|
directory and a bsd library. Take a look at them to see what types of
|
||||||
|
things are required to get BSD sources to compile correctly without
|
||||||
|
changes. Some of the tricks are pretty ugly. Please don't laugh...
|
||||||
|
|
||||||
|
Everything should compile straight with the libc-4.2 libraries. Just
|
||||||
|
type "make" in this directory. You may have to do some surgery on
|
||||||
|
libbsd/libbsd.a to get things to compile with libc-4.1.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
=====
|
||||||
|
|
||||||
|
bsd: The directory "bsd" contains include files that, for the most
|
||||||
|
part, just overload include files of the same name from /usr/include.
|
||||||
|
It provides some BSD things like "union wait" and others.
|
||||||
|
|
||||||
|
libbsd: The libbsd.a archive is a small library of some functions that
|
||||||
|
are not in our libc as well as some emulation support functions
|
||||||
|
referenced in the bsd header files. Notably, it includes a
|
||||||
|
half-decent transparent sgtty emulation package.
|
||||||
|
|
||||||
|
inetd: Works. Only pathname changes.
|
||||||
|
|
||||||
|
ftp: Works. No changes.
|
||||||
|
|
||||||
|
ftpd: I find that ls with ftpd doesn't work very often. It reports
|
||||||
|
"Transport endpoint already connected" 9 times out of 10. I think
|
||||||
|
this is a bug in the kernel which will be fixed in a subsequent
|
||||||
|
release. There was a memory allocation bug in the original source,
|
||||||
|
look for "#ifdef linux". Also, the shadow password stuff is not
|
||||||
|
compiled in as I haven't had a chance to mess with that.
|
||||||
|
|
||||||
|
telnet: Works but see the man page for info about the .telnetrc file
|
||||||
|
and other new options. This is a really nice telnet with rlogin
|
||||||
|
emulation and lots of other stuff not present in older telnets. The
|
||||||
|
original Linux port of telnet/telnetd was done by Pete Chown.
|
||||||
|
|
||||||
|
telnetd: Several people have reported that the 0.1 telnetd sometimes
|
||||||
|
disconnected before getting to the login prompt. The 0.2 version
|
||||||
|
omits the vhangup stuff which might have been causing the problem.
|
||||||
|
Also fixed is the problem with /etc/wtmp not being updated correctly
|
||||||
|
on logout. It was trying to use /var/adm/wtmp.
|
||||||
|
|
||||||
|
finger: Works. One minor change for POSIX timezone handling.
|
||||||
|
|
||||||
|
fingerd: Works, no changes.
|
||||||
|
|
||||||
|
ping: Works, no changes. Must be suid to root.
|
||||||
|
|
||||||
|
named: Works. A few changes for pathnames and net device
|
||||||
|
configuration stuff.
|
||||||
|
|
||||||
|
nslookup: Works, but it's fussier than older nslookups about named
|
||||||
|
being setup correctly. Note the -I option is needed for flex -- that
|
||||||
|
was a tough one to find. No changes.
|
||||||
|
|
||||||
|
named-xfer: Not tested.
|
||||||
|
|
||||||
|
rcp: Works. Must be suid to root.
|
||||||
|
|
||||||
|
rsh: Works. Must be suid to root.
|
||||||
|
|
||||||
|
rshd: Works.
|
||||||
|
|
||||||
|
rlogin: Works OK but needs more testing. This one depends pretty heavily
|
||||||
|
on the sgtty emulation in libbsd which is not complete yet. There is
|
||||||
|
an odd timing bug with select and termio settings. Look for "sleep(1)"
|
||||||
|
to see what I mean. Must be suid to root.
|
||||||
|
|
||||||
|
rlogind: Works with the URGENT stuff commented out. There was a nasty
|
||||||
|
bug with shared libraries because environ was redefined.
|
||||||
|
|
||||||
|
talk: Works. This is the new byte-order independent talk, not the old
|
||||||
|
talk that, e.g., native SunOS uses. The old one wouldn't work with
|
||||||
|
them anyway. Not the fault of Linux. Includes a one line patch for a
|
||||||
|
bug in our curses and another for a bug in the original source dealing
|
||||||
|
with select.
|
||||||
|
|
||||||
|
ntalkd: Works. No changes.
|
||||||
|
|
||||||
|
tftp: Works. No changes.
|
||||||
|
|
||||||
|
tftpd: Works. No changes.
|
||||||
|
|
||||||
|
Sample Entries for inetd.conf
|
||||||
|
=============================
|
||||||
|
telnet stream tcp nowait root /usr/etc/inet/telnetd telnetd
|
||||||
|
ntalk dgram udp wait root /usr/etc/inet/ntalkd ntalkd
|
||||||
|
ftp stream tcp nowait root /usr/etc/inet/ftpd ftpd -l
|
||||||
|
finger stream tcp nowait root /usr/etc/inet/fingerd finger
|
||||||
|
shell stream tcp nowait root /usr/etc/inet/rshd rshd
|
||||||
|
login stream tcp nowait root /usr/etc/inet/rlogind rlogind
|
||||||
|
tftp dgram udp wait root /usr/etc/inet/tftpd tftpd
|
||||||
|
|
||||||
|
Rick Sladkey
|
||||||
|
jrs@world.std.com
|
||||||
377
net/tcpip/newether-pingpong/3c503.c
Normal file
377
net/tcpip/newether-pingpong/3c503.c
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with the 3c503 and 3c503/16. It must be used
|
||||||
|
in shared memory mode.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version = "3c503.c:v0.67 3/8/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
|
||||||
|
#include "8390.h"
|
||||||
|
#include "3c503reg.h"
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
|
||||||
|
int el2autoprobe(int ioaddr, struct device *dev);
|
||||||
|
int el2probe(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void el2_reset_8390(struct device *dev);
|
||||||
|
static void el2_init_card(struct device *dev);
|
||||||
|
static void el2_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static int el2_block_input(struct device *dev, int count, char *buf,
|
||||||
|
int ring_offset);
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
el2autoprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
|
||||||
|
int ports[] = {0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
|
||||||
|
|
||||||
|
/* Non-autoprobe case first: */
|
||||||
|
if (ioaddr > 0)
|
||||||
|
return el2probe(ioaddr, dev);
|
||||||
|
|
||||||
|
/* We check for a memory-mapped 3c503 board by looking at the
|
||||||
|
port location bitmap at the end of the jumpered boot PROM space.
|
||||||
|
This works even if a PROM isn't there. */
|
||||||
|
for (addr = addrs; *addr; addr++) {
|
||||||
|
int i;
|
||||||
|
unsigned int base_bits = *(unsigned char *)*addr;
|
||||||
|
/* Find first set bit. */
|
||||||
|
for(i = 7; i >= 0; i--, base_bits >>= 1)
|
||||||
|
if (base_bits & 0x1)
|
||||||
|
break;
|
||||||
|
if (base_bits == 1 && el2probe(ports[i], dev))
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
#ifndef ignore_nonshared_memory
|
||||||
|
/* It's not memory mapped, bummer. Try all of the locations
|
||||||
|
that aren't obviously empty. */
|
||||||
|
{ int i;
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
if (inb_p(ports[i] + 0x403) == (0x80 >> i) /* Preliminary check */
|
||||||
|
&& el2probe(ports[i], dev))
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
#endif /* probe_nonshared_memory */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Probe for the Etherlink II card at I/O port base IOADDR,
|
||||||
|
returning non-zero on sucess. If found, set the station
|
||||||
|
address and memory parameters in DEVICE. */
|
||||||
|
int
|
||||||
|
el2probe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i, iobase_reg, membase_reg, saved_406;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
|
||||||
|
/* We verify that it's a 3C503 board by checking the first three octets
|
||||||
|
of its ethernet address. */
|
||||||
|
printk("3c503 probe at %#3x:", ioaddr);
|
||||||
|
iobase_reg = inb(ioaddr+0x403);
|
||||||
|
membase_reg = inb(ioaddr+0x404);
|
||||||
|
/* Verify ASIC register that should be 0 or have a single bit set. */
|
||||||
|
if ( (iobase_reg & (iobase_reg - 1))
|
||||||
|
|| (membase_reg & (membase_reg - 1))) {
|
||||||
|
printk(" not found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
saved_406 = inb_p(ioaddr + 0x406);
|
||||||
|
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
|
||||||
|
outb_p(ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
/* Map the station addr PROM into the lower I/O ports. */
|
||||||
|
outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||||
|
printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
|
||||||
|
}
|
||||||
|
if ( station_addr[0] != 0x02
|
||||||
|
|| station_addr[1] != 0x60
|
||||||
|
|| station_addr[2] != 0x8c) {
|
||||||
|
printk(" 3C503 not found.\n");
|
||||||
|
/* Restore the register we frobbed. */
|
||||||
|
outb_p(saved_406, ioaddr + 0x406);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Map the 8390 back into the window. */
|
||||||
|
outb(ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
dev->base_addr = ioaddr;
|
||||||
|
/* Probe for, turn on and clear the board's shared memory. */
|
||||||
|
if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
|
||||||
|
outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
|
||||||
|
if ((membase_reg & 0xf0) == 0) {
|
||||||
|
dev->mem_start = 0;
|
||||||
|
} else {
|
||||||
|
dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
|
||||||
|
((membase_reg & 0xA0) ? 0x4000 : 0);
|
||||||
|
|
||||||
|
#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
|
||||||
|
#ifdef EL2MEMTEST
|
||||||
|
/* This has never found an error, but someone might care. */
|
||||||
|
{ /* Check the card's memory. */
|
||||||
|
int *mem_base = (int *)dev->mem_start;
|
||||||
|
int memtest_value = 0xbbadf00d;
|
||||||
|
mem_base[0] = 0xba5eba5e;
|
||||||
|
for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
|
||||||
|
mem_base[i] = memtest_value;
|
||||||
|
if (mem_base[0] != 0xba5eba5e
|
||||||
|
|| mem_base[i] != memtest_value) {
|
||||||
|
printk(" memory failure or memory address conflict.\n");
|
||||||
|
dev->mem_start = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memtest_value += 0x55555555;
|
||||||
|
mem_base[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* EL2MEMTEST */
|
||||||
|
/* Divide the on-board memory into a single maximum-sized transmit
|
||||||
|
(double-sized for ping-pong transmit) buffer at the base, and
|
||||||
|
use the rest as a receive ring. */
|
||||||
|
dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
|
||||||
|
dev->rmem_start = TX_PAGES*256 + dev->mem_start;
|
||||||
|
}
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
|
||||||
|
dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
|
||||||
|
|
||||||
|
/* Finish setting the board's parameters. */
|
||||||
|
ei_status.name = "3C503";
|
||||||
|
ei_status.tx_start_page = EL2SM_START_PG;
|
||||||
|
ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = EL2SM_STOP_PG;
|
||||||
|
ei_status.reset_8390 = &el2_reset_8390;
|
||||||
|
ei_status.block_input = &el2_block_input;
|
||||||
|
ei_status.block_output = &el2_block_output;
|
||||||
|
/* This should be probed for (or set via an ioctl()) at run-time someday. */
|
||||||
|
#if defined(EI8390_THICK) || defined(EL2_AUI)
|
||||||
|
ei_status.thin_bit = 0;
|
||||||
|
#else
|
||||||
|
ei_status.thin_bit = ECNTRL_THIN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int irqlist[] = {5, 9, 3, 4, 0};
|
||||||
|
int *irqp = irqlist;
|
||||||
|
do {
|
||||||
|
if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
|
||||||
|
/* Twinkle the interrupt, and check if it's seen. */
|
||||||
|
autoirq_setup(0);
|
||||||
|
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
|
||||||
|
outb_p(0x00, E33G_IDCFR);
|
||||||
|
if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
|
||||||
|
&& request_irq (dev->irq, &ei_interrupt) == 0) {
|
||||||
|
printk(" got IRQ %d", dev->irq);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
printk(" IRQ%d bad..", dev->irq);
|
||||||
|
}
|
||||||
|
} while (*++irqp);
|
||||||
|
if (*irqp == 0) {
|
||||||
|
printk(" unable to find an free IRQ line.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dev->irq == 2)
|
||||||
|
dev->irq = 9;
|
||||||
|
else if (dev->irq > 5 && dev->irq != 9) {
|
||||||
|
printk("\n3c503: configured interrupt number %d out of range.\n",
|
||||||
|
dev->irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (request_irq(dev->irq, &ei_interrupt)) {
|
||||||
|
printk (" unable to get IRQ%d.\n", dev->irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->start = 0;
|
||||||
|
el2_init_card(dev);
|
||||||
|
|
||||||
|
if (dev->mem_start)
|
||||||
|
printk("\n%s: %s using IRQ %d with shared memory at %#6x-%#6x,\n",
|
||||||
|
dev->name, ei_status.name, dev->irq,
|
||||||
|
dev->mem_start, dev->mem_end-1);
|
||||||
|
else
|
||||||
|
printk("\n%s: %s using IRQ %d with programmed I/O.\n",
|
||||||
|
dev->name, ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
return ioaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called whenever we have a unrecoverable failure:
|
||||||
|
transmit timeout
|
||||||
|
Bad ring buffer packet header
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
el2_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
if (ei_debug > 1) {
|
||||||
|
printk("%s: Resetting the 3c503 board...", dev->name);
|
||||||
|
printk("%#x=%#02x %#x=%#02x %#x=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
|
||||||
|
E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
|
||||||
|
}
|
||||||
|
|
||||||
|
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
el2_init_card(dev);
|
||||||
|
if (ei_debug > 1) printk("done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the 3c503 GA registers after a reset. */
|
||||||
|
static void
|
||||||
|
el2_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
/* Unmap the station PROM and select the DIX or BNC connector. */
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* Set ASIC copy of rx's first and last+1 buffer pages */
|
||||||
|
/* These must be the same as in the 8390. */
|
||||||
|
outb(ei_status.rx_start_page, E33G_STARTPG);
|
||||||
|
outb(ei_status.stop_page, E33G_STOPPG);
|
||||||
|
|
||||||
|
/* Point the vector pointer registers somewhere ?harmless?. */
|
||||||
|
outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
|
||||||
|
outb(0xff, E33G_VP1);
|
||||||
|
outb(0x00, E33G_VP0);
|
||||||
|
/* Turn off all interrupts until we're opened. */
|
||||||
|
outb_p(0x00, dev->base_addr + EN0_IMR);
|
||||||
|
/* Enable IRQs iff started. */
|
||||||
|
outb(EGACFR_NORM, E33G_GACFR);
|
||||||
|
|
||||||
|
/* Set the interrupt line. */
|
||||||
|
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
|
||||||
|
outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
|
||||||
|
outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
|
||||||
|
outb_p(0x00, E33G_DMAAL);
|
||||||
|
return; /* We always succeed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either use the shared memory (if enabled on the board) or put the packet
|
||||||
|
out through the ASIC FIFO. The latter is probably much slower. */
|
||||||
|
static void
|
||||||
|
el2_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page)
|
||||||
|
{
|
||||||
|
int i; /* Buffer index */
|
||||||
|
int boguscount = 0; /* timeout counter */
|
||||||
|
|
||||||
|
/* This should really be set with during an open(). */
|
||||||
|
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
|
||||||
|
|
||||||
|
if (dev->mem_start) { /* Shared memory transfer */
|
||||||
|
void *dest_addr = (void *)(dev->mem_start +
|
||||||
|
((start_page - ei_status.tx_start_page) << 8));
|
||||||
|
memcpy(dest_addr, buf, count);
|
||||||
|
if (ei_debug > 2 && memcmp(dest_addr, buf, count))
|
||||||
|
printk("%s: 3c503 send_packet() bad memory copy @ %#5x.\n",
|
||||||
|
dev->name, dest_addr);
|
||||||
|
else if (ei_debug > 4)
|
||||||
|
printk("%s: 3c503 send_packet() good memory copy @ %#5x.\n",
|
||||||
|
dev->name, dest_addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* No shared memory, put the packet out the slow way. */
|
||||||
|
/* Set up then start the internal memory transfer to Tx Start Page */
|
||||||
|
outb(0x00, E33G_DMAAL);
|
||||||
|
outb_p(start_page, E33G_DMAAH);
|
||||||
|
outb_p(ei_status.thin_bit | ECNTRL_OUTPUT | ECNTRL_START, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* This is the byte copy loop: it should probably be tuned for
|
||||||
|
for speed once everything is working. I think it is possible
|
||||||
|
to output 8 bytes between each check of the status bit. */
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
if (i % 8 == 0)
|
||||||
|
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||||
|
if (++boguscount > (i<<3) + 32) {
|
||||||
|
printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
|
||||||
|
dev->name, i, count, boguscount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outb(buf[i], E33G_FIFOH);
|
||||||
|
}
|
||||||
|
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the new ring pointer. */
|
||||||
|
static int
|
||||||
|
el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int boguscount = 0;
|
||||||
|
int end_of_ring = dev->rmem_end;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Maybe enable shared memory just be to be safe... nahh.*/
|
||||||
|
if (dev->mem_start) { /* Use the shared memory. */
|
||||||
|
ring_offset -= (EL2SM_START_PG<<8);
|
||||||
|
if (dev->mem_start + ring_offset + count > end_of_ring) {
|
||||||
|
/* We must wrap the input move. */
|
||||||
|
int semi_count = end_of_ring - (dev->mem_start + ring_offset);
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
|
||||||
|
dev->name, dev->mem_start, ring_offset,
|
||||||
|
(char *)dev->mem_start + ring_offset);
|
||||||
|
memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
|
||||||
|
count -= semi_count;
|
||||||
|
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||||
|
return dev->rmem_start + count;
|
||||||
|
}
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("%s: 3c503 block_input() @ %#5x+%x=%5x.\n",
|
||||||
|
dev->name, dev->mem_start, ring_offset,
|
||||||
|
(char *)dev->mem_start + ring_offset);
|
||||||
|
memcpy(buf, (char *)dev->mem_start + ring_offset, count);
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
/* No shared memory, use programmed I/O. */
|
||||||
|
outb(ring_offset & 0xff, E33G_DMAAL);
|
||||||
|
outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
|
||||||
|
outb_p(ei_status.thin_bit | ECNTRL_INPUT | ECNTRL_START, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* This is the byte copy loop: it should probably be tuned for
|
||||||
|
for speed once everything is working. */
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
if (i % 8 == 0)
|
||||||
|
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||||
|
if (++boguscount > (i<<3) + 32) {
|
||||||
|
printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
|
||||||
|
dev->name, i, count, boguscount);
|
||||||
|
boguscount = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf[i] = inb_p(E33G_FIFOH);
|
||||||
|
}
|
||||||
|
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
59
net/tcpip/newether-pingpong/3c503reg.h
Normal file
59
net/tcpip/newether-pingpong/3c503reg.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/* Definitions for the 3Com 3c503 Etherlink 2. */
|
||||||
|
/* This file is part of Donald Becker's 8390 drivers.
|
||||||
|
This file is distributed under the Linux GPL.
|
||||||
|
Some of these names and comments are from the Crynwr packet drivers. */
|
||||||
|
|
||||||
|
#define EL2H (dev->base_addr + 0x400)
|
||||||
|
#define EL2L (dev->base_addr)
|
||||||
|
|
||||||
|
/* Shared memory management parameters */
|
||||||
|
|
||||||
|
#define EL2SM_START_PG (0x20) /* First page of TX buffer */
|
||||||
|
#define EL2SM_STOP_PG (0x40) /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
/* 3Com 3c503 ASIC registers */
|
||||||
|
#define E33G_STARTPG (EL2H+0) /* Start page, must match EN0_STARTPG */
|
||||||
|
#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
|
||||||
|
#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
|
||||||
|
#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
|
||||||
|
/* (non-useful, but it also appears at the end of EPROM space) */
|
||||||
|
#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
|
||||||
|
#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
|
||||||
|
#define E33G_CNTRL (EL2H+6) /* Board's main control register */
|
||||||
|
#define E33G_STATUS (EL2H+7) /* Status on completions. */
|
||||||
|
#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
|
||||||
|
/* (Which IRQ to assert, DMA chan to use) */
|
||||||
|
#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
|
||||||
|
#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
|
||||||
|
/* "Vector pointer" - if this address matches a read, the EPROM (rather than
|
||||||
|
shared RAM) is mapped into memory space. */
|
||||||
|
#define E33G_VP2 (EL2H+11)
|
||||||
|
#define E33G_VP1 (EL2H+12)
|
||||||
|
#define E33G_VP0 (EL2H+13)
|
||||||
|
#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
|
||||||
|
#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
|
||||||
|
|
||||||
|
/* Bits in E33G_CNTRL register: */
|
||||||
|
|
||||||
|
#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
|
||||||
|
#define ECNTRL_THIN (0x02) /* Onboard thin-net xcvr enable */
|
||||||
|
#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
|
||||||
|
#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
|
||||||
|
#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
|
||||||
|
#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
|
||||||
|
#define ECNTRL_START (0x80) /* Start the DMA logic */
|
||||||
|
|
||||||
|
/* Bits in E33G_STATUS register: */
|
||||||
|
|
||||||
|
#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
|
||||||
|
#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
|
||||||
|
#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
|
||||||
|
#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
|
||||||
|
#define ESTAT_DIP (0x08) /* DMA In Progress */
|
||||||
|
|
||||||
|
/* Bits in E33G_GACFR register: */
|
||||||
|
|
||||||
|
#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */
|
||||||
|
#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */
|
||||||
|
|
||||||
|
/* End of 3C503 parameter definitions */
|
||||||
689
net/tcpip/newether-pingpong/8390.c
Normal file
689
net/tcpip/newether-pingpong/8390.c
Normal file
@@ -0,0 +1,689 @@
|
|||||||
|
/* 8390.c: A general NS8390 ethernet driver core for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with many 8390-based ethernet adaptors.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"8390.c:v0.68 3/9/93 for 0.99.6 Donald Becker (becker@super.org)\n";
|
||||||
|
#include <linux/config.h>
|
||||||
|
#if !defined(EL2) && !defined(NE2000) && !defined(WD80x3) && !defined(HPLAN)
|
||||||
|
/* They don't know what they want -- give it all to them! */
|
||||||
|
#define EL2
|
||||||
|
#define NE2000
|
||||||
|
#define WD80x3
|
||||||
|
#define HPLAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Braindamage remaining:
|
||||||
|
|
||||||
|
Ethernet devices should use a chr_drv device interface, with ioctl()s to
|
||||||
|
configure the card, bring the interface up or down, allow access to
|
||||||
|
statistics, and maybe read() and write() access to raw packets.
|
||||||
|
This won't be done until after Linux 1.00.
|
||||||
|
|
||||||
|
This driver should support multiple, diverse boards simultaneousely.
|
||||||
|
This won't be done until after Linux 1.00.
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
|
||||||
|
The NE* programming info came from the Crynwr packet driver, and figuring
|
||||||
|
out that the those boards are similar to the NatSemi evaluation board
|
||||||
|
described in AN-729. Thanks NS, no thanks to Novell/Eagle.
|
||||||
|
Cabletron provided only info I had already gotten from other sources -- hiss.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/tty.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include <asm/segment.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <linux/fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "eth.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "ip.h"
|
||||||
|
#include "tcp.h"
|
||||||
|
#include "sock.h"
|
||||||
|
#include "arp.h"
|
||||||
|
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
#define ei_reset_8390 (ei_local->reset_8390)
|
||||||
|
#define ei_block_output (ei_local->block_output)
|
||||||
|
#define ei_block_input (ei_local->block_input)
|
||||||
|
|
||||||
|
/* use 0 for production, 1 for verification, >2 for debug */
|
||||||
|
#ifdef EI_DEBUG
|
||||||
|
int ei_debug = EI_DEBUG;
|
||||||
|
#else
|
||||||
|
int ei_debug = 2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct device *irq2dev_map[16] = {0,0,0, /* zeroed...*/};
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
static int lasttx = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Max number of packets received at one Intr. */
|
||||||
|
/*static int high_water_mark = 0;*/
|
||||||
|
|
||||||
|
/* Index to functions. */
|
||||||
|
/* Put in the device structure. */
|
||||||
|
static int ei_open(struct device *dev);
|
||||||
|
/* Dispatch from interrupts. */
|
||||||
|
void ei_interrupt(int reg_ptr);
|
||||||
|
static void ei_tx_intr(struct device *dev);
|
||||||
|
static void ei_receive(struct device *dev);
|
||||||
|
static void ei_rx_overrun(struct device *dev);
|
||||||
|
|
||||||
|
int ethdev_init(struct device *dev);
|
||||||
|
/* Routines generic to NS8390-based boards. */
|
||||||
|
void NS8390_init(struct device *dev, int startp);
|
||||||
|
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||||
|
int start_page);
|
||||||
|
|
||||||
|
extern int el2autoprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int el2probe(int ioaddr, struct device *dev);
|
||||||
|
extern int neprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int wdprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int hpprobe(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
|
||||||
|
|
||||||
|
/* Open/initialize the board. This routine goes all-out, setting everything
|
||||||
|
up anew at each open, even though many of these registers should only
|
||||||
|
need to be set once at boot.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ei_open(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ei_device *ei_local = dev->private;
|
||||||
|
|
||||||
|
if ( ! ei_local) {
|
||||||
|
printk("%s: Opening a non-existent physical device\n", dev->name);
|
||||||
|
return 1; /* ENXIO would be more accurate. */
|
||||||
|
}
|
||||||
|
|
||||||
|
irq2dev_map[dev->irq] = dev;
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
ei_local->tx1 = ei_local->tx2 = 0;
|
||||||
|
/* The old local flags... */
|
||||||
|
ei_local->txing = 0;
|
||||||
|
/* ... are now global. */
|
||||||
|
dev->tbusy = 0;
|
||||||
|
dev->interrupt = 0;
|
||||||
|
dev->start = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ei_start_xmit(struct sk_buff *skb, struct device *dev)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
struct ei_device *ei_local = dev->private;
|
||||||
|
|
||||||
|
if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
|
||||||
|
int txsr = inb(e8390_base+EN0_TSR), isr;
|
||||||
|
int tickssofar = jiffies - dev->trans_start;
|
||||||
|
if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
isr = inb(e8390_base+EN0_ISR);
|
||||||
|
printk("%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
|
||||||
|
dev->name, txsr, isr);
|
||||||
|
/* It's possible to check for an IRQ conflict here.
|
||||||
|
I may have to do that someday. */
|
||||||
|
if (isr)
|
||||||
|
printk("%s: Possible IRQ conflict on IRQ%d?", dev->name, dev->irq);
|
||||||
|
else
|
||||||
|
printk("%s: Possible network cable problem?\n", dev->name);
|
||||||
|
/* It futile, but try to restart it anyway. */
|
||||||
|
ei_reset_8390(dev);
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is new: it means some higher layer thinks we've missed an
|
||||||
|
tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
|
||||||
|
itself. */
|
||||||
|
if (skb == NULL) {
|
||||||
|
dev_tint(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Fill in the ethernet header. */
|
||||||
|
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
|
||||||
|
skb->dev = dev;
|
||||||
|
arp_queue (skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
{
|
||||||
|
int length = skb->len;
|
||||||
|
int output_page;
|
||||||
|
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
|
||||||
|
if (length <= 0)
|
||||||
|
return 0;
|
||||||
|
/* Turn off interrupts so that we can put the packet out safely. */
|
||||||
|
outb(0x00, e8390_base + EN0_IMR);
|
||||||
|
if (ei_local->tx1 == 0) {
|
||||||
|
output_page = ei_local->tx_start_page;
|
||||||
|
ei_local->tx1 = send_length;
|
||||||
|
if (ei_debug && ei_local->tx2 > 0)
|
||||||
|
printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
|
||||||
|
dev->name, ei_local->tx2, lasttx, ei_local->txing);
|
||||||
|
} else if (ei_local->tx2 == 0) {
|
||||||
|
output_page = ei_local->tx_start_page + 6;
|
||||||
|
ei_local->tx2 = send_length;
|
||||||
|
if (ei_debug && ei_local->tx1 > 0)
|
||||||
|
printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
|
||||||
|
dev->name, ei_local->tx1, lasttx, ei_local->txing);
|
||||||
|
} else {
|
||||||
|
/* We can get to here if we get an rx interrupt and queued
|
||||||
|
a tx packet just before masking 8390 irqs above. */
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk("%s: No packet buffer space for ping-pong use.\n",
|
||||||
|
dev->name);
|
||||||
|
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dev->trans_start = jiffies;
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1), output_page);
|
||||||
|
if (! ei_local->txing) {
|
||||||
|
NS8390_trigger_send(dev, send_length, output_page);
|
||||||
|
if (output_page == ei_local->tx_start_page)
|
||||||
|
ei_local->tx1 = -1, lasttx = -1;
|
||||||
|
else
|
||||||
|
ei_local->tx2 = -1, lasttx = -2;
|
||||||
|
ei_local->txing = 1;
|
||||||
|
} else
|
||||||
|
ei_local->txqueue++;
|
||||||
|
if (ei_local->tx1 && ei_local->tx2)
|
||||||
|
dev->tbusy = 1;
|
||||||
|
/* Turn 8390 interrupts back on. */
|
||||||
|
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
{
|
||||||
|
int length = skb->len;
|
||||||
|
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
|
||||||
|
if (length <= 0)
|
||||||
|
return 0;
|
||||||
|
dev->trans_start = jiffies;
|
||||||
|
/* Turn off interrupts so that we can put the packet out safely. */
|
||||||
|
outb(0x00, e8390_base + EN0_IMR);
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1), ei_local->tx_start_page);
|
||||||
|
NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
|
||||||
|
dev->tbusy = 1;
|
||||||
|
/* Turn 8390 interrupts back on. */
|
||||||
|
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||||
|
}
|
||||||
|
#endif /* PINGPONG */
|
||||||
|
if (skb->free)
|
||||||
|
kfree_skb (skb, FREE_WRITE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The typical workload of the driver:
|
||||||
|
Handle the ether interface interrupts. */
|
||||||
|
void
|
||||||
|
ei_interrupt(int reg_ptr)
|
||||||
|
{
|
||||||
|
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
|
||||||
|
struct device *dev = irq2dev_map[irq];
|
||||||
|
int e8390_base;
|
||||||
|
int interrupts, boguscount = 0;
|
||||||
|
|
||||||
|
if (dev == NULL) {
|
||||||
|
printk ("net_interrupt(): irq %d for unknown device.\n", irq);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dev->interrupt = 1;
|
||||||
|
sti(); /* Allow other interrupts. */
|
||||||
|
|
||||||
|
e8390_base = dev->base_addr;
|
||||||
|
|
||||||
|
/* Change to page 0 and read the intr status reg. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
|
||||||
|
if (ei_debug > 3)
|
||||||
|
printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
|
||||||
|
inb_p(e8390_base + EN0_ISR));
|
||||||
|
|
||||||
|
/* !!Assumption!! -- we stay in page 0. Don't break this. */
|
||||||
|
while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
|
||||||
|
&& ++boguscount < 20) {
|
||||||
|
if (interrupts & ENISR_RDC) {
|
||||||
|
/* Ack meaningless DMA complete. */
|
||||||
|
outb_p(ENISR_RDC, e8390_base + EN0_ISR);
|
||||||
|
}
|
||||||
|
if (interrupts & ENISR_OVER) {
|
||||||
|
ei_rx_overrun(dev);
|
||||||
|
} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
|
||||||
|
/* Got a good (?) packet. */
|
||||||
|
ei_receive(dev);
|
||||||
|
}
|
||||||
|
/* Push the next to-transmit packet through. */
|
||||||
|
if (interrupts & ENISR_TX) {
|
||||||
|
ei_tx_intr(dev);
|
||||||
|
} else if (interrupts & ENISR_COUNTERS) {
|
||||||
|
struct ei_device *ei_local = dev->private;
|
||||||
|
ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER0);
|
||||||
|
ei_local->soft_rx_errors += inb_p(e8390_base + EN0_COUNTER1);
|
||||||
|
ei_local->missed_packets += inb_p(e8390_base + EN0_COUNTER2);
|
||||||
|
outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore the transmit errs and reset intr for now. */
|
||||||
|
if (interrupts & ENISR_TX_ERR) {
|
||||||
|
outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
|
||||||
|
}
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupts && ei_debug) {
|
||||||
|
printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
|
||||||
|
outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
|
||||||
|
}
|
||||||
|
dev->interrupt = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have finished a transmit: check for errors and then trigger the next
|
||||||
|
packet to be sent. */
|
||||||
|
static void
|
||||||
|
ei_tx_intr(struct device *dev)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
int status = inb(e8390_base + EN0_TSR);
|
||||||
|
struct ei_device *ei_local = dev->private;
|
||||||
|
|
||||||
|
outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
|
||||||
|
if ((status & ENTSR_PTX) == 0)
|
||||||
|
ei_local->tx_errors++;
|
||||||
|
else
|
||||||
|
ei_local->tx_packets++;
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
ei_local->txqueue--;
|
||||||
|
if (ei_local->tx1 < 0) {
|
||||||
|
if (lasttx != 1 && lasttx != -1)
|
||||||
|
printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
|
||||||
|
ei_local->name, lasttx, ei_local->tx1);
|
||||||
|
ei_local->tx1 = 0;
|
||||||
|
dev->tbusy = 0;
|
||||||
|
if (ei_local->tx2 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
|
||||||
|
ei_local->txing = 1;
|
||||||
|
ei_local->tx2 = -1,
|
||||||
|
lasttx = 2;
|
||||||
|
} else
|
||||||
|
lasttx = 20, ei_local->txing = 0;
|
||||||
|
} else if (ei_local->tx2 < 0) {
|
||||||
|
if (lasttx != 2 && lasttx != -2)
|
||||||
|
printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
|
||||||
|
ei_local->name, lasttx, ei_local->tx2);
|
||||||
|
ei_local->tx2 = 0;
|
||||||
|
dev->tbusy = 0;
|
||||||
|
if (ei_local->tx1 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
|
||||||
|
ei_local->txing = 1;
|
||||||
|
ei_local->tx1 = -1;
|
||||||
|
lasttx = 1;
|
||||||
|
} else
|
||||||
|
lasttx = 10, ei_local->txing = 0;
|
||||||
|
} else
|
||||||
|
printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
|
||||||
|
dev->name, lasttx);
|
||||||
|
#else
|
||||||
|
ei_local->txing = 0;
|
||||||
|
dev->tbusy = 0;
|
||||||
|
#endif
|
||||||
|
mark_bh (INET_BH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have a good packet(s), get it/them out of the buffers. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
ei_receive(struct device *dev)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
struct ei_device *ei_local = dev->private;
|
||||||
|
int rxing_page, this_frame, next_frame, current_offset;
|
||||||
|
int boguscount = 0;
|
||||||
|
struct e8390_pkt_hdr rx_frame;
|
||||||
|
int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
|
||||||
|
|
||||||
|
while (++boguscount < 10) {
|
||||||
|
int size;
|
||||||
|
|
||||||
|
/* Get the rx page (incoming packet pointer). */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
|
||||||
|
rxing_page = inb_p(e8390_base + EN1_CURPAG);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
|
||||||
|
|
||||||
|
/* Remove one frame from the ring. Boundary is alway a page behind. */
|
||||||
|
this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
|
||||||
|
if (this_frame >= ei_local->stop_page)
|
||||||
|
this_frame = ei_local->rx_start_page;
|
||||||
|
|
||||||
|
/* Someday we'll omit the previous step, iff we never get this message.*/
|
||||||
|
if (ei_debug > 0 && this_frame != ei_local->current_page)
|
||||||
|
printk("%s: mismatched read page pointers %2x vs %2x.\n",
|
||||||
|
dev->name, this_frame, ei_local->current_page);
|
||||||
|
|
||||||
|
if (this_frame == rxing_page) /* Read all the frames? */
|
||||||
|
break; /* Done for now */
|
||||||
|
|
||||||
|
current_offset = this_frame << 8;
|
||||||
|
ei_block_input(dev, sizeof(rx_frame), (void *)&rx_frame,
|
||||||
|
current_offset);
|
||||||
|
|
||||||
|
size = rx_frame.count - sizeof(rx_frame);
|
||||||
|
|
||||||
|
next_frame = this_frame + 1 + ((size+4)>>8);
|
||||||
|
|
||||||
|
/* Check for bogosity warned by 3c503 book: the status byte is never
|
||||||
|
written. This happened a lot during testing! This code should be
|
||||||
|
cleaned up someday, and the printk()s should be PRINTK()s. */
|
||||||
|
if ( rx_frame.next != next_frame
|
||||||
|
&& rx_frame.next != next_frame + 1
|
||||||
|
&& rx_frame.next != next_frame - num_rx_pages
|
||||||
|
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
|
||||||
|
#ifndef EI_DEBUG
|
||||||
|
ei_local->current_page = rxing_page;
|
||||||
|
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
|
||||||
|
continue;
|
||||||
|
#else
|
||||||
|
static int last_rx_bogosity = -1;
|
||||||
|
printk("%s: bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
|
||||||
|
dev->name, rx_frame.status, rx_frame.next, rx_frame.count,
|
||||||
|
current_offset);
|
||||||
|
|
||||||
|
if (rx_packets != last_rx_bogosity) {
|
||||||
|
/* Maybe we can avoid resetting the chip... empty the packet ring. */
|
||||||
|
ei_local->current_page = rxing_page;
|
||||||
|
printk("%s: setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
|
||||||
|
dev->name, ei_local->current_page, next_frame,
|
||||||
|
rx_frame.next, rx_frame.status);
|
||||||
|
last_rx_bogosity = rx_packets;
|
||||||
|
outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* Oh no Mr Bill! Last ditch error recovery. */
|
||||||
|
printk("%s: recovery failed, resetting at packet #%d..",
|
||||||
|
dev->name, rx_packets);
|
||||||
|
sti();
|
||||||
|
ei_reset_8390(dev);
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
printk("restarting.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif /* EI8390_NOCHECK */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size < 32 || size > 1535) && ei_debug)
|
||||||
|
printk("%s: bogus packet size, status=%#2x nxpg=%#2x size=%#x\n",
|
||||||
|
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
|
||||||
|
if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
|
||||||
|
int sksize = sizeof(struct sk_buff) + size;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
skb = kmalloc(sksize, GFP_ATOMIC);
|
||||||
|
if (skb != NULL) {
|
||||||
|
skb->lock = 0;
|
||||||
|
skb->mem_len = sksize;
|
||||||
|
skb->mem_addr = skb;
|
||||||
|
/* 'skb+1' points to the start of sk_buff data area. */
|
||||||
|
ei_block_input(dev, size, (void *)(skb+1),
|
||||||
|
current_offset + sizeof(rx_frame));
|
||||||
|
if (dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
|
||||||
|
printk("%s: receive buffers full.\n", dev->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (ei_debug) {
|
||||||
|
printk("%s: Couldn't allocate a sk_buff of size %d.\n",
|
||||||
|
dev->name, sksize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ei_local->rx_packets++;
|
||||||
|
} else {
|
||||||
|
if (ei_debug)
|
||||||
|
printk("%s: bogus packet, status=%#2x nxpg=%#2x size=%d\n",
|
||||||
|
dev->name, rx_frame.status, rx_frame.next, rx_frame.count);
|
||||||
|
ei_local->soft_rx_err_bits |= rx_frame.status,
|
||||||
|
ei_local->soft_rx_errors++;
|
||||||
|
}
|
||||||
|
next_frame = rx_frame.next;
|
||||||
|
|
||||||
|
/* This should never happen, it's here for debugging. */
|
||||||
|
if (next_frame >= ei_local->stop_page) {
|
||||||
|
printk("%s: next frame inconsistency, %#2x..", dev->name, next_frame);
|
||||||
|
next_frame = ei_local->rx_start_page;
|
||||||
|
}
|
||||||
|
ei_local->current_page += 1 + ((size+4)>>8);
|
||||||
|
ei_local->current_page = next_frame;
|
||||||
|
outb(next_frame-1, e8390_base+EN0_BOUNDARY);
|
||||||
|
}
|
||||||
|
/* If any worth-while packets have been received, dev_rint()
|
||||||
|
has done a mark_bh(INET_BH) for us and will work on them
|
||||||
|
when we get to the bottom-half routine. */
|
||||||
|
|
||||||
|
/* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
|
||||||
|
outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, e8390_base+EN0_ISR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have a receiver overrun: we have to kick the 8390 to get it started
|
||||||
|
again.*/
|
||||||
|
static void
|
||||||
|
ei_rx_overrun(struct device *dev)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
struct ei_device *ei_local = dev->private;
|
||||||
|
|
||||||
|
/* We should already be stopped and in page0. Remove after testing. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
|
||||||
|
|
||||||
|
if (ei_debug)
|
||||||
|
printk("%s: Receiver overrun.\n", dev->name);
|
||||||
|
ei_local->rx_overruns++;
|
||||||
|
|
||||||
|
/* The we.c driver does dummy = inb_p( RBCR[01] ); at this point.
|
||||||
|
It might mean something -- magic to speed up a reset? A 8390 bug?*/
|
||||||
|
|
||||||
|
/* Wait for reset in case the NIC is doing a tx or rx. This could take up to
|
||||||
|
1.5msec, but we have no way of timing something in that range. The 'jiffies'
|
||||||
|
are just a sanity check. */
|
||||||
|
while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 1) {
|
||||||
|
printk("%s: reset did not complete at ei_rx_overrun.\n",
|
||||||
|
dev->name);
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
int old_rx_packets = ei_local->rx_packets;
|
||||||
|
/* Remove packets right away. */
|
||||||
|
ei_receive(dev);
|
||||||
|
ei_local->rx_overrun_packets +=
|
||||||
|
(ei_local->rx_packets - old_rx_packets);
|
||||||
|
}
|
||||||
|
outb_p(0xff, e8390_base+EN0_ISR);
|
||||||
|
/* Generic 8390 insns to start up again, same as in open_8390(). */
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
|
||||||
|
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ethif_init(struct device *dev)
|
||||||
|
{
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
/* The open call may be overridden by the card-specific code. */
|
||||||
|
dev->open = &ei_open;
|
||||||
|
|
||||||
|
/* Make up a ei_local structure. */
|
||||||
|
dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
|
||||||
|
memset(dev->private, 0, sizeof(struct ei_device));
|
||||||
|
|
||||||
|
if (1
|
||||||
|
#ifdef WD80x3
|
||||||
|
&& ! wdprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef EL2
|
||||||
|
&& ! el2autoprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef NE2000
|
||||||
|
&& ! neprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef HPLAN
|
||||||
|
&& ! hpprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
&& 1 ) {
|
||||||
|
printk("No ethernet device found.\n");
|
||||||
|
kfree(dev->private);
|
||||||
|
dev->private = NULL;
|
||||||
|
return 1; /* ENODEV or EAGAIN would be more accurate. */
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethdev_init(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the rest of the device structure. */
|
||||||
|
int
|
||||||
|
ethdev_init(struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < DEV_NUMBUFFS; i++)
|
||||||
|
dev->buffs[i] = NULL;
|
||||||
|
|
||||||
|
dev->hard_header = eth_hard_header;
|
||||||
|
dev->add_arp = eth_add_arp;
|
||||||
|
dev->queue_xmit = dev_queue_xmit;
|
||||||
|
dev->rebuild_header = eth_rebuild_header;
|
||||||
|
dev->type_trans = eth_type_trans;
|
||||||
|
|
||||||
|
if (dev->private == NULL) {
|
||||||
|
dev->private = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
|
||||||
|
memset(dev->private, 0, sizeof(struct ei_device));
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->send_packet = 0;
|
||||||
|
dev->hard_start_xmit = &ei_start_xmit;
|
||||||
|
|
||||||
|
dev->type = ETHER_TYPE;
|
||||||
|
dev->hard_header_len = sizeof (struct enet_header);
|
||||||
|
dev->mtu = 1500; /* eth_mtu */
|
||||||
|
dev->addr_len = ETHER_ADDR_LEN;
|
||||||
|
for (i = 0; i < dev->addr_len; i++) {
|
||||||
|
dev->broadcast[i]=0xff;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This page of functions should be 8390 generic */
|
||||||
|
/* Follow National Semi's recommendations for initializing the "NIC". */
|
||||||
|
void NS8390_init(struct device *dev, int startp)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
struct ei_device *ei_local = dev->private;
|
||||||
|
int i;
|
||||||
|
int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
|
||||||
|
|
||||||
|
/* Follow National Semi's recommendations for initing the DP83902. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
|
||||||
|
outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
|
||||||
|
/* Clear the remote byte count registers. */
|
||||||
|
outb_p(0x00, e8390_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, e8390_base + EN0_RCNTHI);
|
||||||
|
/* Set to monitor and loopback mode -- this is vital!. */
|
||||||
|
outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
|
||||||
|
outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
|
||||||
|
/* Set the transmit page and receive ring. */
|
||||||
|
outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
|
||||||
|
ei_local->tx1 = ei_local->tx2 = 0;
|
||||||
|
outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
|
||||||
|
outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
|
||||||
|
ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
|
||||||
|
outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
|
||||||
|
/* Clear the pending interrupts and mask. */
|
||||||
|
outb_p(0xFF, e8390_base + EN0_ISR);
|
||||||
|
outb_p(0x00, e8390_base + EN0_IMR);
|
||||||
|
|
||||||
|
/* Copy the station address into the DS8390 registers,
|
||||||
|
and set the multicast hash bitmap to receive all multicasts. */
|
||||||
|
cli();
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
|
||||||
|
for(i = 0; i < 6; i++) {
|
||||||
|
outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
|
||||||
|
}
|
||||||
|
for(i = 0; i < 8; i++)
|
||||||
|
outb_p(0xff, e8390_base + EN1_MULT + i);
|
||||||
|
|
||||||
|
outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
|
||||||
|
sti();
|
||||||
|
if (startp) {
|
||||||
|
outb_p(0xff, e8390_base + EN0_ISR);
|
||||||
|
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
|
||||||
|
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
|
||||||
|
/* 3c503 TechMan says rxconfig only after the NIC is started. */
|
||||||
|
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trigger a transmit start, assuming the length is valid. */
|
||||||
|
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||||
|
int start_page)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
|
||||||
|
ei_status.txing = 1;
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
|
||||||
|
|
||||||
|
if (inb_p(e8390_base) & E8390_TRANS) {
|
||||||
|
printk("%s: trigger_send() called with the transmitter busy.\n",
|
||||||
|
dev->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
|
||||||
|
outb_p(length >> 8, e8390_base + EN0_TCNTHI);
|
||||||
|
outb_p(start_page, e8390_base + EN0_TPSR);
|
||||||
|
outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -DPINGPONG -I/usr/src/linux/net/tcp -c 8390.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
148
net/tcpip/newether-pingpong/8390.h
Normal file
148
net/tcpip/newether-pingpong/8390.h
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/* Generic NS8390 register definitions. */
|
||||||
|
/* This file is part of Donald Becker's 8390 drivers.
|
||||||
|
This file is distributed under the Linux GPL.
|
||||||
|
Some of these names and comments are from the Crynwr packet drivers. */
|
||||||
|
|
||||||
|
#ifndef e8390_h
|
||||||
|
#define e8390_h
|
||||||
|
#define PINGPONG
|
||||||
|
#ifdef PINGPONG
|
||||||
|
#define TX_PAGES 12
|
||||||
|
#else
|
||||||
|
#define TX_PAGES 6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ETHER_ADDR_LEN 6
|
||||||
|
|
||||||
|
/* From 8390.c */
|
||||||
|
void ei_interrupt(int reg_ptr);
|
||||||
|
/* From auto_irq.c */
|
||||||
|
extern void autoirq_setup(int waittime);
|
||||||
|
extern int autoirq_report(int waittime);
|
||||||
|
|
||||||
|
/* Most of these entries should be in 'struct device' (or most of the
|
||||||
|
things in there should be here!) */
|
||||||
|
/* You have one of these per-board */
|
||||||
|
struct ei_device {
|
||||||
|
char *name;
|
||||||
|
int open:1;
|
||||||
|
int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
|
||||||
|
int txing:1; /* Transmit Active */
|
||||||
|
void (*reset_8390)(struct device *);
|
||||||
|
void (*block_output)(struct device *, int, const unsigned char *, int);
|
||||||
|
int (*block_input)(struct device *, int, char *, int);
|
||||||
|
unsigned char tx_start_page, rx_start_page, stop_page;
|
||||||
|
unsigned char current_page; /* Read pointer in buffer */
|
||||||
|
unsigned char thin_bit; /* Value to write to the 3c503 E33G_CNTRL */
|
||||||
|
unsigned char txqueue; /* Tx Packet buffer queue length. */
|
||||||
|
unsigned char in_interrupt;
|
||||||
|
short tx1, tx2; /* Packet lengths for ping-pong tx. */
|
||||||
|
/* The statistics: */
|
||||||
|
int tx_packets;
|
||||||
|
int tx_errors;
|
||||||
|
int rx_packets;
|
||||||
|
int soft_rx_errors;
|
||||||
|
int soft_rx_err_bits;
|
||||||
|
int missed_packets;
|
||||||
|
int rx_overruns;
|
||||||
|
int rx_overrun_packets;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ei_status (*(struct ei_device *)(dev->private))
|
||||||
|
|
||||||
|
/* Some generic ethernet register configurations. */
|
||||||
|
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
|
||||||
|
#define E8390_RX_IRQ_MASK 0x5
|
||||||
|
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
|
||||||
|
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
|
||||||
|
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
|
||||||
|
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
|
||||||
|
|
||||||
|
/* Register accessed at EN_CMD, the 8390 base addr. */
|
||||||
|
#define E8390_STOP 0x01 /* Stop and reset the chip */
|
||||||
|
#define E8390_START 0x02 /* Start the chip, clear reset */
|
||||||
|
#define E8390_TRANS 0x04 /* Transmit a frame */
|
||||||
|
#define E8390_RREAD 0x08 /* Remote read */
|
||||||
|
#define E8390_RWRITE 0x10 /* Remote write */
|
||||||
|
#define E8390_NODMA 0x20 /* Remote DMA */
|
||||||
|
#define E8390_PAGE0 0x00 /* Select page chip registers */
|
||||||
|
#define E8390_PAGE1 0x40 /* using the two high-order bits */
|
||||||
|
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
|
||||||
|
|
||||||
|
#define E8390_CMD 0x00 /* The command register (for all pages) */
|
||||||
|
/* Page 0 register offsets. */
|
||||||
|
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
|
||||||
|
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
|
||||||
|
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
|
||||||
|
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
|
||||||
|
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
|
||||||
|
#define EN0_TSR 0x04 /* Transmit status reg RD */
|
||||||
|
#define EN0_TPSR 0x04 /* Transmit starting page WR */
|
||||||
|
#define EN0_NCR 0x05 /* Number of collision reg RD */
|
||||||
|
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
|
||||||
|
#define EN0_FIFO 0x06 /* FIFO RD */
|
||||||
|
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
|
||||||
|
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
|
||||||
|
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
|
||||||
|
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
|
||||||
|
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
|
||||||
|
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
|
||||||
|
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
|
||||||
|
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
|
||||||
|
#define EN0_RSR 0x0c /* rx status reg RD */
|
||||||
|
#define EN0_RXCR 0x0c /* RX configuration reg WR */
|
||||||
|
#define EN0_TXCR 0x0d /* TX configuration reg WR */
|
||||||
|
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
|
||||||
|
#define EN0_DCFG 0x0e /* Data configuration reg WR */
|
||||||
|
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
|
||||||
|
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
|
||||||
|
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
|
||||||
|
|
||||||
|
/* Bits in EN0_ISR - Interrupt status register */
|
||||||
|
#define ENISR_RX 0x01 /* Receiver, no error */
|
||||||
|
#define ENISR_TX 0x02 /* Transmitter, no error */
|
||||||
|
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
|
||||||
|
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
|
||||||
|
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
|
||||||
|
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
|
||||||
|
#define ENISR_RDC 0x40 /* remote dma complete */
|
||||||
|
#define ENISR_RESET 0x80 /* Reset completed */
|
||||||
|
#define ENISR_ALL 0x3f /* Interrupts we will enable */
|
||||||
|
|
||||||
|
/* Bits in EN0_DCFG - Data config register */
|
||||||
|
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
|
||||||
|
|
||||||
|
/* Page 1 register offsets. */
|
||||||
|
#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
|
||||||
|
#define EN1_CURPAG 0x07 /* Current memory page RD WR */
|
||||||
|
#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
|
||||||
|
|
||||||
|
/* Bits in received packet status byte and EN0_RSR*/
|
||||||
|
#define ENRSR_RXOK 0x01 /* Received a good packet */
|
||||||
|
#define ENRSR_CRC 0x02 /* CRC error */
|
||||||
|
#define ENRSR_FAE 0x04 /* frame alignment error */
|
||||||
|
#define ENRSR_FO 0x08 /* FIFO overrun */
|
||||||
|
#define ENRSR_MPA 0x10 /* missed pkt */
|
||||||
|
#define ENRSR_PHY 0x20 /* physical/multicase address */
|
||||||
|
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
|
||||||
|
#define ENRSR_DEF 0x80 /* deferring */
|
||||||
|
|
||||||
|
/* Transmitted packet status, EN0_TSR. */
|
||||||
|
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
|
||||||
|
/* The other bits in the TX status register mean:
|
||||||
|
0x02 The transmit wasn't deferred.
|
||||||
|
0x04 The transmit collided at least once.
|
||||||
|
0x08 The transmit collided 16 times, and was deferred.
|
||||||
|
0x10 The carrier sense was lost (from the ethernet transceiver)
|
||||||
|
0x20 A "FIFO underrun" (internal error) occured during transmit.
|
||||||
|
0x40 The collision detect "heartbeat" signal was lost.
|
||||||
|
0x80 There was an out-of-window collision.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The per-packet-header format. */
|
||||||
|
struct e8390_pkt_hdr {
|
||||||
|
unsigned char status; /* status */
|
||||||
|
unsigned char next; /* pointer to next packet. */
|
||||||
|
unsigned short count; /* header + packet lenght in bytes */
|
||||||
|
};
|
||||||
|
#endif /* e8390_h */
|
||||||
44
net/tcpip/newether-pingpong/Announce
Normal file
44
net/tcpip/newether-pingpong/Announce
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
Subject: Enhanced Ethercard driver available for alpha test.
|
||||||
|
|
||||||
|
My "8390" Linux ethercard drivers are now available from usra.edu and
|
||||||
|
super.org in ~ftp/pub/linux/ethercards/*. They'll be at tsx-11 and
|
||||||
|
sunsite RSN.
|
||||||
|
|
||||||
|
These drivers support all common 8390-based ethernet boards. Currently
|
||||||
|
"common" is defined as:
|
||||||
|
|
||||||
|
3Com Products:
|
||||||
|
* 3Com 3c503 Board loaned by Chance Reschke, USRA.edu (thanks!)
|
||||||
|
3Com 3c503/16 and excellent documentation provided by 3Com.
|
||||||
|
|
||||||
|
Clones-n-things
|
||||||
|
NE1000 Novell and Eagle are useless for documentation,
|
||||||
|
* NE2000 but copied the designs directly from NatSemi;->.
|
||||||
|
* Alta Combo(NE2000 clone)
|
||||||
|
Aritsoft LANtastic AE-2 (NE2000 clone w/ extra memory)
|
||||||
|
D-Link Ethernet II
|
||||||
|
|
||||||
|
Cabletron products:
|
||||||
|
E1010 No ID PROM and sketchy info from Ctron means you'll
|
||||||
|
E1010-x have to compile-in information about your board.
|
||||||
|
E2010
|
||||||
|
E2010-x
|
||||||
|
|
||||||
|
WD/SMC products
|
||||||
|
WD8003
|
||||||
|
* WD8013 Board loaned by Russ Nelson, Crynwr Software. Thanks!
|
||||||
|
|
||||||
|
* I've seen it work myself!
|
||||||
|
|
||||||
|
There is support for the following boards, but since I've only been
|
||||||
|
able to borrow a thinnet of an HP ethercard I haven't been able to test it:
|
||||||
|
|
||||||
|
HP LAN adaptors
|
||||||
|
** HP27245
|
||||||
|
** HP27247
|
||||||
|
** HP27250
|
||||||
|
|
||||||
|
Thanks are due to the dozens of alpha testers, and special thanks to Chance Reschke <@usra.edu> and Russ Nelson <@crynwr.com> for loaning me ethercards.
|
||||||
|
|
||||||
|
|
||||||
33
net/tcpip/newether-pingpong/GNUmakefile
Normal file
33
net/tcpip/newether-pingpong/GNUmakefile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# This file must be named 'GNUmakefile'. When it has that name it
|
||||||
|
# loaded in preference to the regular 'Makefile' (which it include
|
||||||
|
# right here) so this has the effect of appending lines to the Makefile.
|
||||||
|
|
||||||
|
include Makefile
|
||||||
|
|
||||||
|
# Add a few files to tcpip.a.
|
||||||
|
newobjs = 8390.o 3c503.o ne.o wd.o hp.o auto_irq.o
|
||||||
|
OBJS := $(OBJS) $(newobjs)
|
||||||
|
tcpip.a: $(newobjs)
|
||||||
|
|
||||||
|
# Set the address and IRQ here. The ne.c and 3c503 driver will autoprobe
|
||||||
|
# if you set the address or IRQ to zero, so we do that by default.
|
||||||
|
#
|
||||||
|
# Add -DEI_NAME="eth0" if you want to be exactly compatible with the default
|
||||||
|
# driver. This will only work if you don't use the distributed 'we' driver!
|
||||||
|
#
|
||||||
|
ether_options := -DEI_NAME=\"eth0\" -DEI8390=0 -DEI8390_IRQ=0
|
||||||
|
Space.o: Space.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) $(ether_options) -c Space.c -o $@
|
||||||
|
|
||||||
|
# Change this to define the set of ethercards your kernel will support.
|
||||||
|
8390.o: 8390.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -DNE2000 -DWD80x3 -DHPLAN -DEL2 -c 8390.c -o $@
|
||||||
|
|
||||||
|
# Change this to -DEL2_AUI if you use the AUI port.
|
||||||
|
3c503.o: 3c503.c 3c503reg.h GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -UEL2_AUI -c 3c503.c -o $@
|
||||||
|
|
||||||
|
# Change this to -DSHMEM=0xd0000 and -DFORCE_8BIT if you have an old
|
||||||
|
# (non-EEPROM) wd8003.
|
||||||
|
wd.o: wd.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -UWD_SHMEM -UFORCE_8BIT -c wd.c -o $@
|
||||||
14
net/tcpip/newether-pingpong/README
Normal file
14
net/tcpip/newether-pingpong/README
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
This is a new version of the ethercard drivers.
|
||||||
|
|
||||||
|
The new features are:
|
||||||
|
|
||||||
|
PING-PONG transmit! This can almost double the transmit rate in some
|
||||||
|
cases. 1MB/sec is now possible!!!
|
||||||
|
|
||||||
|
The 'ne' driver autoselects between the NE1000 and NE2000 modes.
|
||||||
|
There is a less-intrusive 3c503 probe.
|
||||||
|
The 3c503 can now be used in non-shared-memory mode.
|
||||||
|
The HP-LAN non-autoIRQ mode should now work.
|
||||||
|
The WD driver should now work with some older WD8003 cards, but this
|
||||||
|
is untested.
|
||||||
|
|
||||||
110
net/tcpip/newether-pingpong/auto_irq.c
Normal file
110
net/tcpip/newether-pingpong/auto_irq.c
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/* auto_irq.c: Auto-configure IRQ lines for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
|
||||||
|
This code is a general-purpose IRQ line detector for devices with
|
||||||
|
jumpered IRQ lines. If you can make the device raise an IRQ (and
|
||||||
|
that IRQ line isn't already being used), these routines will tell
|
||||||
|
you what IRQ line it's using -- perfect for those oh-so-cool boot-time
|
||||||
|
device probes!
|
||||||
|
|
||||||
|
To use this, first call autoirq_setup(timeout). TIMEOUT is how many
|
||||||
|
'jiffies' (1/18 sec.) to detect other devices that have active IRQ lines,
|
||||||
|
and can usually be zero at boot. 'autoirq_setup()' returns the bit
|
||||||
|
vector of nominally-available IRQ lines (lines may be physically in-use,
|
||||||
|
but not yet registered to a device).
|
||||||
|
Next, set up your device to trigger an interrupt.
|
||||||
|
Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
|
||||||
|
most recently active. The TIMEOUT should usually be zero, but may
|
||||||
|
be set to the number of jiffies to wait for a slow device to raise an IRQ.
|
||||||
|
|
||||||
|
The idea of using the setup timeout to filter out bogus IRQs came from
|
||||||
|
the serial driver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef version
|
||||||
|
static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*#include <linux/config.h>*/
|
||||||
|
/*#include <linux/kernel.h>*/
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/bitops.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
/*#include <asm/system.h>*/
|
||||||
|
|
||||||
|
int irqs_busy = 0x01; /* The set of fixed IRQs always enabled */
|
||||||
|
int irqs_used = 0x01; /* The set of fixed IRQs sometimes enabled. */
|
||||||
|
int irqs_reserved = 0x00; /* An advisory "reserved" table. */
|
||||||
|
int irqs_shared = 0x00; /* IRQ lines "shared" among conforming cards.*/
|
||||||
|
|
||||||
|
static volatile int irq_number; /* The latest irq number we actually found. */
|
||||||
|
static volatile int irq_bitmap; /* The irqs we actually found. */
|
||||||
|
static int irq_handled; /* The irq lines we have a handler on. */
|
||||||
|
|
||||||
|
static void autoirq_probe(int irq)
|
||||||
|
{
|
||||||
|
irq_number = irq;
|
||||||
|
set_bit(irq, (void *)&irq_bitmap); /* irq_bitmap |= 1 << irq; */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct sigaction autoirq_sigaction = { autoirq_probe, 0, SA_INTERRUPT, NULL};
|
||||||
|
|
||||||
|
int autoirq_setup(int waittime)
|
||||||
|
{
|
||||||
|
int i, mask;
|
||||||
|
int timeout = jiffies+waittime;
|
||||||
|
|
||||||
|
irq_number = 0;
|
||||||
|
irq_bitmap = 0;
|
||||||
|
irq_handled = 0;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (!irqaction(i, &autoirq_sigaction))
|
||||||
|
set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
|
||||||
|
}
|
||||||
|
/* Update our USED lists. */
|
||||||
|
irqs_used |= ~irq_handled;
|
||||||
|
|
||||||
|
/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
|
||||||
|
while (timeout >= jiffies)
|
||||||
|
;
|
||||||
|
|
||||||
|
for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
|
||||||
|
if (irq_bitmap & irq_handled & mask) {
|
||||||
|
irq_handled &= ~mask;
|
||||||
|
printk(" Spurious interrupt on IRQ %d\n", i);
|
||||||
|
free_irq(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return irq_handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
int autoirq_report(int waittime)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int timeout = jiffies+waittime;
|
||||||
|
|
||||||
|
/* Hang out at least <waittime> jiffies waiting for the IRQ. */
|
||||||
|
while (timeout >= jiffies)
|
||||||
|
if (irq_number)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Retract the irq handlers that we installed. */
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (test_bit(i, (void *)&irq_handled))
|
||||||
|
free_irq(i);
|
||||||
|
}
|
||||||
|
return irq_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
319
net/tcpip/newether-pingpong/hp.c
Normal file
319
net/tcpip/newether-pingpong/hp.c
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
/* hp.c: A HP LAN ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This is a driver for the HP LAN adaptors.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version = "hp.c:v0.67 3/8/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||||
|
#define port_read(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define port_read_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
|
||||||
|
#define HP_ID 0x07
|
||||||
|
#define HP_CONFIGURE 0x08 /* Configuration register. */
|
||||||
|
#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
|
||||||
|
#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
|
||||||
|
#define HP_DATAON 0x10 /* Turn on dataport */
|
||||||
|
#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
|
||||||
|
|
||||||
|
#define HP_START_PG 0x00 /* First page of TX buffer */
|
||||||
|
#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
|
||||||
|
#define HP_16BSTOP_PG 0xFF /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
|
||||||
|
int hpprobe(int ioaddr, struct device *dev);
|
||||||
|
int hpprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void hp_reset_8390(struct device *dev);
|
||||||
|
static int hp_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void hp_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static void hp_init_card(struct device *dev);
|
||||||
|
|
||||||
|
/* The map from IRQ number to HP_CONFIGURE register setting. */
|
||||||
|
/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
|
||||||
|
static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
|
||||||
|
|
||||||
|
|
||||||
|
/* Probe for an HP LAN adaptor.
|
||||||
|
Also initialize the card and fill in STATION_ADDR with the station
|
||||||
|
address. */
|
||||||
|
|
||||||
|
int hpprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return hpprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && hpprobe1(*port, dev))
|
||||||
|
return dev->base_addr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hpprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
unsigned char SA_prom[6];
|
||||||
|
int tmp;
|
||||||
|
int hplan;
|
||||||
|
|
||||||
|
printk("HP-LAN ethercard probe at %#3x:", ioaddr);
|
||||||
|
tmp = inb_p(ioaddr);
|
||||||
|
if (tmp == 0xFF) {
|
||||||
|
printk(" not found (nothing there).\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < sizeof(SA_prom); i++) {
|
||||||
|
SA_prom[i] = inb(ioaddr + i);
|
||||||
|
if (i < ETHER_ADDR_LEN && station_addr) {
|
||||||
|
printk(" %2.2x", SA_prom[i]);
|
||||||
|
station_addr[i] = SA_prom[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hplan = (SA_prom[0] == 0x08 && SA_prom[1] == 0x00 && SA_prom[2] == 0x09);
|
||||||
|
if (hplan == 0) {
|
||||||
|
printk(" not found (invalid station address prefix).\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_status.tx_start_page = HP_START_PG;
|
||||||
|
ei_status.rx_start_page = HP_START_PG + TX_PAGES;
|
||||||
|
/* Set up the rest of the parameters. */
|
||||||
|
if ((tmp = inb_p(HP_ID)) & 0x80) {
|
||||||
|
ei_status.name = "HP27247";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */
|
||||||
|
} else {
|
||||||
|
ei_status.name = "HP27250";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the base address to point to the NIC! */
|
||||||
|
dev->base_addr = ioaddr + NIC_OFFSET;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
|
||||||
|
int irq_8list[] = { 7, 5, 3, 4, 9, 0};
|
||||||
|
int *irqp = ei_status.word16 ? irq_16list : irq_8list;
|
||||||
|
do {
|
||||||
|
if (request_irq (dev->irq = *irqp, NULL) != -EBUSY) {
|
||||||
|
autoirq_setup(0);
|
||||||
|
/* Twinkle the interrupt, and check if it's seen. */
|
||||||
|
outb_p(irqmap[dev->irq] | HP_RUN, ioaddr + HP_CONFIGURE);
|
||||||
|
outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
|
||||||
|
if (dev->irq == autoirq_report(0) /* It's a good IRQ line! */
|
||||||
|
&& request_irq (dev->irq, &ei_interrupt) == 0) {
|
||||||
|
printk(" got IRQ %d", dev->irq);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
printk(" IRQ%d busy..", dev->irq);
|
||||||
|
}
|
||||||
|
} while (*++irqp);
|
||||||
|
if (*irqp == 0) {
|
||||||
|
printk(" unable to find an free IRQ line.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dev->irq == 2)
|
||||||
|
dev->irq = 9;
|
||||||
|
if (irqaction(dev->irq, &ei_sigaction)) {
|
||||||
|
printk (" unable to get IRQ %d.\n", dev->irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("\n%s: %s using IRQ %d.\n", dev->name, ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
ei_status.reset_8390 = &hp_reset_8390;
|
||||||
|
ei_status.block_input = &hp_block_input;
|
||||||
|
ei_status.block_output = &hp_block_output;
|
||||||
|
hp_init_card(dev);
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hp_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int hp_base = dev->base_addr - NIC_OFFSET;
|
||||||
|
int saved_config = inb_p(hp_base + HP_CONFIGURE);
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
if (ei_debug > 1) printk("resetting the 8390 time=%d...", jiffies);
|
||||||
|
outb_p(0x00, hp_base + HP_CONFIGURE);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb_p(saved_config, hp_base + HP_CONFIGURE);
|
||||||
|
while ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk("%s: hp_reset_8390() did not complete.\n", dev->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ei_debug > 1) printk("8390 reset done.", jiffies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||||
|
porting to a new ethercard look at the packet driver source for hints.
|
||||||
|
The HP LAN doesn't use shared memory -- we put the packet
|
||||||
|
out through the "remote DMA" dataport. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
hp_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
int xfer_count = count;
|
||||||
|
|
||||||
|
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, nic_base);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_read(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
|
||||||
|
if (count & 0x01)
|
||||||
|
buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
|
||||||
|
} else {
|
||||||
|
port_read_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
/* Check only the lower 8 bits so we can ignore ring wrap. */
|
||||||
|
if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
|
||||||
|
printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
|
||||||
|
dev->name, ring_offset + xfer_count, addr);
|
||||||
|
}
|
||||||
|
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hp_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page)
|
||||||
|
{
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
|
||||||
|
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
/* Round the count up for word writes. Do we need to do this?
|
||||||
|
What effect will an odd byte count have on the 8390?
|
||||||
|
I should check someday. */
|
||||||
|
if (ei_status.word16 && (count & 0x01))
|
||||||
|
count++;
|
||||||
|
/* We should already be in page 0, but to be safe... */
|
||||||
|
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
|
||||||
|
|
||||||
|
#ifdef ei8390_bug
|
||||||
|
/* Handle the read-before-write bug the same way as the
|
||||||
|
Crynwr packet driver -- the NatSemi method doesn't work. */
|
||||||
|
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
/* Make certain that the dummy read has occured. */
|
||||||
|
inb_p(0x61);
|
||||||
|
inb_p(0x61);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||||
|
|
||||||
|
outb_p(E8390_RWRITE+E8390_START, nic_base);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
/* Use the 'rep' sequence for 16 bit boards. */
|
||||||
|
port_write(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
|
||||||
|
} else {
|
||||||
|
port_write_b(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
|
||||||
|
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
if ((start_page << 8) + count != addr)
|
||||||
|
printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
|
||||||
|
dev->name, (start_page << 8) + count, addr);
|
||||||
|
}
|
||||||
|
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static void
|
||||||
|
hp_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
int irq = dev->irq;
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
outb_p(irqmap[irq&0x0f] | HP_RUN,
|
||||||
|
dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c hp.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
379
net/tcpip/newether-pingpong/ne.c
Normal file
379
net/tcpip/newether-pingpong/ne.c
Normal file
@@ -0,0 +1,379 @@
|
|||||||
|
/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with many 8390-based ethernet boards. Currently
|
||||||
|
it support the NE1000, NE2000 (and clones), and some Cabletron products.
|
||||||
|
8-bit ethercard support is enabled with #define EI_8BIT
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Routines for the NatSemi-based designs (NE[12]000). */
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"ne.c:v0.50 2/19/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||||
|
#define port_read(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define port_read_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define EN_CMD (dev->base_addr)
|
||||||
|
#define NE_BASE (dev->base_addr)
|
||||||
|
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
|
||||||
|
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
|
||||||
|
|
||||||
|
#define NE1SM_START_PG 0x20 /* First page of TX buffer */
|
||||||
|
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||||
|
#define NESM_START_PG 0x40 /* First page of TX buffer */
|
||||||
|
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
|
||||||
|
int neprobe(int ioaddr, struct device *dev);
|
||||||
|
static int neprobe1(int ioaddr, struct device *dev, int verbose);
|
||||||
|
|
||||||
|
static void ne_reset_8390(struct device *dev);
|
||||||
|
static int ne_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void ne_block_output(struct device *dev, const int count,
|
||||||
|
const unsigned char *buf, const int start_page);
|
||||||
|
|
||||||
|
|
||||||
|
/* Probe for various non-shared-memory ethercards.
|
||||||
|
|
||||||
|
NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
|
||||||
|
buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
|
||||||
|
the SAPROM, while other supposed NE2000 clones must be detected by their
|
||||||
|
SA prefix.
|
||||||
|
|
||||||
|
Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
|
||||||
|
mode results in doubled values, which can be detected and compansated for.
|
||||||
|
|
||||||
|
The probe is also responsible for initializing the card and filling
|
||||||
|
in the 'dev' and 'ei_status' structures.
|
||||||
|
|
||||||
|
We use the minimum memory size for some ethercard product lines, iff we can't
|
||||||
|
distinguish models. You can increase the packet buffer size by setting
|
||||||
|
PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
|
||||||
|
E1010 starts at 0x100 and ends at 0x2000.
|
||||||
|
E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
|
||||||
|
E2010 starts at 0x100 and ends at 0x4000.
|
||||||
|
E2010-x starts at 0x100 and ends at 0xffff. */
|
||||||
|
|
||||||
|
int neprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x280, 0x320, 0x340, 0x360, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return neprobe1(ioaddr, dev, 1);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && neprobe1(*port, dev, 0))
|
||||||
|
return dev->base_addr = *port;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int neprobe1(int ioaddr, struct device *dev, int verbose)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char SA_prom[32];
|
||||||
|
int wordlength = 2;
|
||||||
|
int neX000, ctron, dlink;
|
||||||
|
|
||||||
|
|
||||||
|
if ( inb_p(ioaddr) == 0xFF) {
|
||||||
|
if (verbose) printk("8390 ethercard probe at %#3x failed.\n", ioaddr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("8390 ethercard probe at %#3x:", ioaddr);
|
||||||
|
|
||||||
|
/* Read the 16 bytes of station address prom, returning 1 for
|
||||||
|
an eight-bit interface and 2 for a 16-bit interface.
|
||||||
|
We must first initialize registers, similar to NS8390_init(eifdev, 0).
|
||||||
|
We can't reliably read the SAPROM address without this.
|
||||||
|
(I learned the hard way!). */
|
||||||
|
{
|
||||||
|
struct {char value, offset; } program_seq[] = {
|
||||||
|
{E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD}, /* Select page 0 */
|
||||||
|
{0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
|
||||||
|
{0x00, EN0_RCNTLO}, /* Clear the count regs. */
|
||||||
|
{0x00, EN0_RCNTHI},
|
||||||
|
{0x00, EN0_IMR}, /* Mask completion irq. */
|
||||||
|
{0xFF, EN0_ISR},
|
||||||
|
{E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
|
||||||
|
{E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
|
||||||
|
{32, EN0_RCNTLO},
|
||||||
|
{0x00, EN0_RCNTHI},
|
||||||
|
{0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
|
||||||
|
{0x00, EN0_RSARHI},
|
||||||
|
{E8390_RREAD+E8390_START, EN_CMD},
|
||||||
|
};
|
||||||
|
for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
|
||||||
|
outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
|
||||||
|
}
|
||||||
|
for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
|
||||||
|
SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
|
||||||
|
SA_prom[i+1] = inb_p(ioaddr + NE_DATAPORT);
|
||||||
|
if (SA_prom[i] != SA_prom[i+1])
|
||||||
|
wordlength = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wordlength == 2) {
|
||||||
|
/* We must set the 8390 for word mode, AND RESET IT. */
|
||||||
|
int tmp;
|
||||||
|
outb_p(0x49, ioaddr + EN0_DCFG);
|
||||||
|
tmp = inb_p(NE_BASE + NE_RESET);
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
outb(tmp, NE_BASE + NE_RESET);
|
||||||
|
/* Un-double the SA_prom values. */
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
SA_prom[i] = SA_prom[i+i];
|
||||||
|
} else
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
|
||||||
|
#if defined(show_all_SAPROM)
|
||||||
|
/* If your ethercard isn't detected define this to see the SA_PROM. */
|
||||||
|
for(i = 0; i < sizeof(SA_prom); i++)
|
||||||
|
printk(" %2.2x", SA_prom[i]);
|
||||||
|
#else
|
||||||
|
for(i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||||
|
dev->dev_addr[i] = SA_prom[i];
|
||||||
|
printk(" %2.2x", SA_prom[i]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
|
||||||
|
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
|
||||||
|
dlink = (SA_prom[0] == 0x00 && SA_prom[1] == 0xDE && SA_prom[2] == 0x01);
|
||||||
|
|
||||||
|
/* Set up the rest of the parameters. */
|
||||||
|
if (neX000 && wordlength == 2) {
|
||||||
|
ei_status.name = "NE2000";
|
||||||
|
ei_status.tx_start_page = NESM_START_PG;
|
||||||
|
ei_status.stop_page = NESM_STOP_PG;
|
||||||
|
} else if (neX000 || dlink) {
|
||||||
|
ei_status.name = neX000 ? "NE1000" : "D-Link";
|
||||||
|
ei_status.tx_start_page = NE1SM_START_PG;
|
||||||
|
ei_status.stop_page = NE1SM_STOP_PG;
|
||||||
|
} else if (ctron) {
|
||||||
|
ei_status.name = "Cabletron";
|
||||||
|
ei_status.tx_start_page = 0x01;
|
||||||
|
ei_status.stop_page = (wordlength == 2) ? 0x40 : 0x20;
|
||||||
|
} else {
|
||||||
|
printk(" not found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES;
|
||||||
|
#ifdef PACKETBUF_MEMSIZE
|
||||||
|
/* Allow the packet buffer size to be overridden by know-it-alls. */
|
||||||
|
ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
|
||||||
|
#endif
|
||||||
|
dev->base_addr = ioaddr;
|
||||||
|
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
autoirq_setup(0);
|
||||||
|
outb_p(0x50, nic_base + EN0_IMR); /* Enable one interrupt. */
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */
|
||||||
|
outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
|
||||||
|
dev->irq = autoirq_report(0);
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk(" autoirq is %d", dev->irq);
|
||||||
|
} else if (dev->irq == 2)
|
||||||
|
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||||
|
or don't know which one to set. */
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||||
|
if (irqval) {
|
||||||
|
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("\n%s: %s found, using IRQ %d.\n",
|
||||||
|
dev->name, ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
ei_status.reset_8390 = &ne_reset_8390;
|
||||||
|
ei_status.block_input = &ne_block_input;
|
||||||
|
ei_status.block_output = &ne_block_output;
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ne_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int tmp = inb_p(NE_BASE + NE_RESET);
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
if (ei_debug > 1) printk("resetting the 8390 t=%d...", jiffies);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
outb_p(tmp, NE_BASE + NE_RESET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb_p(tmp, NE_BASE + NE_RESET);
|
||||||
|
while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk("%s: ne_reset_8390() did not complete.\n", dev->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||||
|
porting to a new ethercard look at the packet driver source for hints.
|
||||||
|
The NEx000 doesn't share it on-board packet memory -- you have to put
|
||||||
|
the packet out through the "remote DMA" dataport using outb. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int xfer_count = count;
|
||||||
|
int nic_base = NE_BASE;
|
||||||
|
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_read(NE_BASE + NE_DATAPORT,buf,count>>1);
|
||||||
|
if (count & 0x01)
|
||||||
|
buf[count-1] = inb(NE_BASE + NE_DATAPORT), xfer_count++;
|
||||||
|
} else {
|
||||||
|
port_read_b(NE_BASE + NE_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This was for the ALPHA version only, but enough people have
|
||||||
|
encountering problems that it is still here. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int addr, tries = 10;
|
||||||
|
do {
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
|
||||||
|
-- it's broken! Check the "DMA" address instead. */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
addr = (high << 8) + low;
|
||||||
|
if (((ring_offset + xfer_count) & 0xff) == low)
|
||||||
|
return ring_offset + count;
|
||||||
|
} while (--tries > 0);
|
||||||
|
printk("%s: RX transfer address mismatch, %#4.4x (should be) vs. %#4.4x (actual).\n",
|
||||||
|
dev->name, ring_offset + xfer_count, addr);
|
||||||
|
}
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ne_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const int start_page)
|
||||||
|
{
|
||||||
|
int retries = 0;
|
||||||
|
int nic_base = NE_BASE;
|
||||||
|
|
||||||
|
/* Round the count up for word writes. Do we need to do this?
|
||||||
|
What effect will an odd byte count have on the 8390?
|
||||||
|
I should check someday. */
|
||||||
|
if (ei_status.word16 && (count & 0x01))
|
||||||
|
count++;
|
||||||
|
/* We should already be in page 0, but to be safe... */
|
||||||
|
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, EN_CMD);
|
||||||
|
|
||||||
|
retry:
|
||||||
|
#if defined(rw_bugfix)
|
||||||
|
/* Handle the read-before-write bug the same way as the
|
||||||
|
Crynwr packet driver -- the NatSemi method doesn't work.
|
||||||
|
Actually this doesn't aways work either, but if you have
|
||||||
|
problems with your NEx000 this is better than nothing! */
|
||||||
|
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x42, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
/* Make certain that the dummy read has occured. */
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
#endif /* rw_bugfix */
|
||||||
|
|
||||||
|
/* Now the normal output. */
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||||
|
|
||||||
|
outb_p(E8390_RWRITE+E8390_START, EN_CMD);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
|
||||||
|
} else {
|
||||||
|
port_write_b(NE_BASE + NE_DATAPORT, buf, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This was for the ALPHA version only, but enough people have
|
||||||
|
encountering problems that it is still here. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int addr, tries = 10;
|
||||||
|
do {
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
|
||||||
|
-- it's broken! Check the "DMA" address instead. */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
addr = (high << 8) + low;
|
||||||
|
if ((start_page << 8) + count == addr)
|
||||||
|
return;
|
||||||
|
} while (--tries > 0);
|
||||||
|
printk("%s: Packet buffer transfer address mismatch on TX, %#4.4x vs. %#4.4x.\n",
|
||||||
|
dev->name, (start_page << 8) + count, addr);
|
||||||
|
if (retries++ == 0)
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
334
net/tcpip/newether-pingpong/wd.c
Normal file
334
net/tcpip/newether-pingpong/wd.c
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
/* wd.c: A WD80x3 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This is a driver for the WD8003 and WD8013 ethercards.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
|
||||||
|
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"wd.c:v0.66 3/7/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
|
||||||
|
int wdprobe(int ioaddr, struct device *dev);
|
||||||
|
int wdprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void wd_reset_8390(struct device *dev);
|
||||||
|
static int wd_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void wd_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static int wd_close_card(struct device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
#define WD_START_PG 0x00 /* First page of TX buffer */
|
||||||
|
#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
|
||||||
|
#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
#define WD_CMDREG 0 /* Offset to ASIC command register. */
|
||||||
|
#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
|
||||||
|
#define WD_MEMENB 0x40 /* Enable the shared memory. */
|
||||||
|
#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
|
||||||
|
#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
|
||||||
|
#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
|
||||||
|
#define WD_NIC_OFFSET 16 /* Offset to the 8390 NIC from the base_addr. */
|
||||||
|
|
||||||
|
/* Probe for the WD8003 and WD8013. These cards have the station
|
||||||
|
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
|
||||||
|
following. The routine also initializes the card and fills the
|
||||||
|
station address field. */
|
||||||
|
|
||||||
|
int wdprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x280, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return wdprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb(*port) != 0xff && wdprobe1(*port, dev))
|
||||||
|
return *port;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wdprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
int checksum = 0;
|
||||||
|
int ancient = 0; /* An old card without config registers. */
|
||||||
|
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||||
|
printk("WD80x3 ethercard at %#3x:", ioaddr);
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printk(" %2.2X", inb(ioaddr+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
printk("WD80x3 ethercard at %#3x:", ioaddr+i);
|
||||||
|
for (;i < 33; i++) {
|
||||||
|
printk(" %2.2X", inb(ioaddr+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
#endif
|
||||||
|
printk("WD80x3 ethercard probe at %#3x:", ioaddr);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
int inval = inb(ioaddr + 8 + i);
|
||||||
|
checksum += inval;
|
||||||
|
if (i < 6)
|
||||||
|
printk(" %2.2X", (station_addr[i] = inval));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((checksum & 0xff) != 0xFF) {
|
||||||
|
printk(" not found (%#2.2x).\n", checksum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_status.name = "WD8003";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
|
||||||
|
/* This method of checking for a 16-bit board is borrowed from the
|
||||||
|
we.c driver. A simpler method is just to look in ASIC reg. 0x03.
|
||||||
|
I'm comparing the two method in alpha test to make certain they
|
||||||
|
return the same result. */
|
||||||
|
#ifndef FORCE_8BIT /* Same define as we.c. */
|
||||||
|
/* check for 16 bit board - it doesn't have register 0/8 aliasing */
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
if (inb(ioaddr+i) != inb(ioaddr+8+i))
|
||||||
|
break;
|
||||||
|
if (i != 8) {
|
||||||
|
int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
|
||||||
|
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
|
||||||
|
if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
|
||||||
|
&& (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
|
||||||
|
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
|
||||||
|
/* Magic to set ASIC to word-wide mode. */
|
||||||
|
outb( ISA16 | NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
|
||||||
|
outb(tmp, ioaddr+1);
|
||||||
|
ei_status.name = "WD8013";
|
||||||
|
ei_status.word16 = 1; /* We have a 16bit board here! */
|
||||||
|
}
|
||||||
|
outb(tmp, ioaddr+1); /* Restore original reg1 value. */
|
||||||
|
} else
|
||||||
|
ancient = 1;
|
||||||
|
#endif /* not FORCE_8BIT */
|
||||||
|
|
||||||
|
#ifndef final_version
|
||||||
|
if ( !ancient && (inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01))
|
||||||
|
printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
|
||||||
|
ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
|
||||||
|
/* Allow an override. */
|
||||||
|
dev->mem_start = WD_SHMEM;
|
||||||
|
#else
|
||||||
|
if (dev->mem_start == 0) {
|
||||||
|
dev->mem_start = ((inb(ioaddr)&0x3f) << 13) +
|
||||||
|
(ei_status.word16 ? (inb(ioaddr+WD_CMDREG5)&0x1f)<<19 : 0x80000);
|
||||||
|
if (dev->mem_start < 0xc0000) /* Sanity and old 8003 check */
|
||||||
|
dev->mem_start = 0xd0000;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The 8390 isn't at the base address -- the ASIC regs are there! */
|
||||||
|
dev->base_addr = ioaddr+WD_NIC_OFFSET;
|
||||||
|
|
||||||
|
ei_status.tx_start_page = WD_START_PG;
|
||||||
|
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = ei_status.word16 ? WD13_STOP_PG : WD03_STOP_PG;
|
||||||
|
|
||||||
|
dev->rmem_start = dev->mem_start + TX_PAGES*256;
|
||||||
|
dev->mem_end = dev->rmem_end
|
||||||
|
= dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 3
|
||||||
|
memset((void*)dev->mem_start, 0x42052465,
|
||||||
|
(ei_status.stop_page - WD_START_PG)*256);
|
||||||
|
#endif
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int irqmap[] = {9,3,5,7,10,11,15,4};
|
||||||
|
dev->irq = irqmap[((inb(ioaddr+4) >> 5) & 0x03)
|
||||||
|
+ (inb(ioaddr+1) & 0x04)];
|
||||||
|
} else if (dev->irq == 2)
|
||||||
|
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||||
|
or don't know which one to set. */
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||||
|
if (irqval) {
|
||||||
|
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("\n%s: %s using IRQ %d with shared memory at %#x-%#x.\n",
|
||||||
|
dev->name, ei_status.name, dev->irq, dev->mem_start, dev->mem_end-1);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk("%s: Address read from register is %#x, setting address %#x\n",
|
||||||
|
ei_status.name,
|
||||||
|
((inb(ioaddr+WD_CMDREG5)&0x1f)<<19) + ((inb(ioaddr)&0x3f) << 13),
|
||||||
|
dev->mem_start);
|
||||||
|
/* Map in the shared memory. This is a little risky, since using
|
||||||
|
the stuff the user supplied is probably a bad idea. */
|
||||||
|
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
|
||||||
|
if (ei_status.word16)
|
||||||
|
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
|
||||||
|
|
||||||
|
ei_status.reset_8390 = &wd_reset_8390;
|
||||||
|
ei_status.block_input = &wd_block_input;
|
||||||
|
ei_status.block_output = &wd_block_output;
|
||||||
|
dev->stop = &wd_close_card;
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wd_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
outb(WD_RESET, wd_cmd_port);
|
||||||
|
if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(0x00, wd_cmd_port);
|
||||||
|
while ((inb(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk("%s: wd_reset_8390() did not complete.\n", dev->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printk("WD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||||
|
}
|
||||||
|
printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||||
|
for (;i < 33; i++) {
|
||||||
|
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* Set up the ASIC registers, just in case something changed them. */
|
||||||
|
outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); /* WD_CMDREG */
|
||||||
|
if (ei_status.word16)
|
||||||
|
outb( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output are easy on shared memory ethercards, and trivial
|
||||||
|
on the Western digital card where there is no choice of how to do it. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
void *xfer_start = (void *)(dev->mem_start + ring_offset - (WD_START_PG<<8));
|
||||||
|
#ifdef mapout
|
||||||
|
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
int mem_val = inb(wd_cmdreg);
|
||||||
|
/* Map in the shared memory. */
|
||||||
|
outb(mem_val|WD_MEMENB, wd_cmdreg);
|
||||||
|
#endif
|
||||||
|
if (xfer_start + count > (void*) dev->rmem_end) {
|
||||||
|
/* We must wrap the input move. */
|
||||||
|
int semi_count = (void*)dev->rmem_end - xfer_start;
|
||||||
|
memcpy(buf, xfer_start, semi_count);
|
||||||
|
count -= semi_count;
|
||||||
|
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||||
|
return dev->rmem_start + count;
|
||||||
|
}
|
||||||
|
memcpy(buf, xfer_start, count);
|
||||||
|
if (ei_debug > 4) {
|
||||||
|
unsigned short *board = xfer_start;
|
||||||
|
printk("%s: wd8013 block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
|
||||||
|
dev->name, count, ring_offset, xfer_start,
|
||||||
|
board[-1], board[0], board[1]);
|
||||||
|
}
|
||||||
|
#ifdef mapout
|
||||||
|
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||||
|
#endif
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This could only be outputting to the transmit buffer. The
|
||||||
|
ping-pong transmit setup doesn't work with this yet. */
|
||||||
|
static void
|
||||||
|
wd_block_output(struct device *dev, int count, const unsigned char *buf, int start_page)
|
||||||
|
{
|
||||||
|
unsigned char *shmem = (void *)dev->mem_start + ((start_page - WD_START_PG)<<8);
|
||||||
|
#ifdef mapout
|
||||||
|
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
int mem_val = inb(wd_cmdreg);
|
||||||
|
/* Map in the shared memory. */
|
||||||
|
outb(mem_val|WD_MEMENB, wd_cmdreg);
|
||||||
|
#endif
|
||||||
|
memcpy(shmem, buf, count);
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("%s: wd80*3 block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n",
|
||||||
|
shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count));
|
||||||
|
#ifdef mapout
|
||||||
|
outb(mem_val & ~WD_MEMENB, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static int
|
||||||
|
wd_close_card(struct device *dev)
|
||||||
|
{
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk("%s: Shutting down ethercard.\n", dev->name);
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
/* Turn off the shared memory. */
|
||||||
|
outb((((dev->mem_start>>13) & 0x3f)),
|
||||||
|
dev->base_addr-WD_NIC_OFFSET); /* WD_CMDREG */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
BIN
net/tcpip/newether.taz
Normal file
BIN
net/tcpip/newether.taz
Normal file
Binary file not shown.
340
net/tcpip/newether/3c503.c
Normal file
340
net/tcpip/newether/3c503.c
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with the 3c503 and 3c503/16. It must be used
|
||||||
|
in shared memory mode.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version = "3c503.c:v0.28 1/28/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
|
||||||
|
#include "8390.h"
|
||||||
|
#include "3c503reg.h"
|
||||||
|
#define EL2AUTOPROBE
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int etherlink2 = 0;
|
||||||
|
|
||||||
|
int el2autoprobe(int ioaddr, struct device *dev);
|
||||||
|
int el2probe(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void el2_reset_8390(struct device *dev);
|
||||||
|
static void el2_init_card(struct device *dev);
|
||||||
|
static void el2_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static int el2_block_input(struct device *dev, int count, char *buf,
|
||||||
|
int ring_offset);
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
el2autoprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *addr, addrs[] = { 0xddfff, 0xd9fff, 0xcdfff, 0xc9fff, 0};
|
||||||
|
int ports[] = {0, 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2a0, 0x2e0, 0};
|
||||||
|
|
||||||
|
/* Non-autoprobe case first: */
|
||||||
|
if (ioaddr > 0)
|
||||||
|
if (el2probe(ioaddr, dev))
|
||||||
|
return dev->base_addr = ioaddr;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* We check for a memory-mapped 3c503 board by looking at the
|
||||||
|
end of boot PROM space (works even if a PROM isn't there). */
|
||||||
|
for (addr = addrs; *addr; addr++) {
|
||||||
|
unsigned int base_bits = *(unsigned char *)*addr, i;
|
||||||
|
/* Find first set bit. */
|
||||||
|
for(i = 8; i; i--, base_bits >>= 1)
|
||||||
|
if (base_bits & 0x1)
|
||||||
|
break;
|
||||||
|
if (base_bits == 1 && el2probe(ports[i], dev))
|
||||||
|
return dev->base_addr = ports[i];
|
||||||
|
}
|
||||||
|
#ifdef notdef
|
||||||
|
/* If it's not memory mapped, we don't care to find it. I haven't
|
||||||
|
tested the non-memory-mapped code. */
|
||||||
|
/* It's not memory mapped -- try all of the locations that aren't
|
||||||
|
obviously empty. */
|
||||||
|
{ int *port;
|
||||||
|
for (port = &ports[1]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && el2probe(*port, dev))
|
||||||
|
return dev->base_addr = *port;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Probe for the Etherlink II card at I/O port base IOADDR,
|
||||||
|
returning non-zero on sucess. If found, set the station
|
||||||
|
address and memory parameters in DEVICE. */
|
||||||
|
int
|
||||||
|
el2probe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i, found, mem_jumpers;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
|
||||||
|
/* We verify that it's a 3C503 board by checking the first three octets
|
||||||
|
of its ethernet address. */
|
||||||
|
printk("3c503 probe at %#3x:", ioaddr);
|
||||||
|
outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
|
||||||
|
outb_p(ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
/* Map the station addr PROM into the lower I/O ports. */
|
||||||
|
outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
||||||
|
printk(" %2.2X", (station_addr[i] = inb(ioaddr + i)));
|
||||||
|
}
|
||||||
|
/* Map the 8390 back into the window. */
|
||||||
|
outb(ECNTRL_THIN, ioaddr + 0x406);
|
||||||
|
found =( station_addr[0] == 0x02
|
||||||
|
&& station_addr[1] == 0x60
|
||||||
|
&& station_addr[2] == 0x8c);
|
||||||
|
if (! found) {
|
||||||
|
printk(" 3C503 not found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Probe for, turn on and clear the board's shared memory. */
|
||||||
|
mem_jumpers = inb(ioaddr + 0x404); /* E33G_ROMBASE */
|
||||||
|
if (ei_debug > 2) printk(" memory jumpers %2.2x ", mem_jumpers);
|
||||||
|
outb(EGACFR_IRQOFF, E33G_GACFR); /* Enable RAM */
|
||||||
|
if ((mem_jumpers & 0xf0) == 0) {
|
||||||
|
dev->mem_start = 0;
|
||||||
|
if (ei_debug > 1) printk(" no shared memory ");
|
||||||
|
} else {
|
||||||
|
dev->mem_start = ((mem_jumpers & 0xc0) ? 0xD8000 : 0xC8000) +
|
||||||
|
((mem_jumpers & 0xA0) ? 0x4000 : 0);
|
||||||
|
|
||||||
|
#define EL2_MEMSIZE (EL2SM_STOP_PG - EL2SM_START_PG)*256
|
||||||
|
#ifdef EL2MEMTEST
|
||||||
|
{ /* Check the card's memory. */
|
||||||
|
int *mem_base = (int *)dev->mem_start;
|
||||||
|
int memtest_value = 0xbbadf00d;
|
||||||
|
mem_base[0] = 0xba5eba5e;
|
||||||
|
for (i = 1; i < EL2_MEMSIZE/sizeof(mem_base[0]); i++) {
|
||||||
|
mem_base[i] = memtest_value;
|
||||||
|
if (mem_base[0] != 0xba5eba5e
|
||||||
|
|| mem_base[i] != memtest_value) {
|
||||||
|
printk(" memory failure or memory address conflict.\n");
|
||||||
|
dev->mem_start = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memtest_value += 0x55555555;
|
||||||
|
mem_base[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* EL2MEMTEST */
|
||||||
|
/* Divide the on-board memory into a single maximum-sized transmit
|
||||||
|
(double-sized for ping-pong transmit) buffer at the base, and
|
||||||
|
use the rest as a receive ring. */
|
||||||
|
dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
|
||||||
|
dev->rmem_start = TX_PAGES*256 + dev->mem_start;
|
||||||
|
}
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk("\n3c503: memory params start=%#5x rstart=%#5x end=%#5x rend=%#5x.\n",
|
||||||
|
dev->mem_start, dev->rmem_start, dev->mem_end, dev->rmem_end);
|
||||||
|
|
||||||
|
/* Finish setting the board's parameters. */
|
||||||
|
etherlink2 = 1;
|
||||||
|
ei_status.tx_start_page = EL2SM_START_PG;
|
||||||
|
ei_status.rx_start_page = EL2SM_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = EL2SM_STOP_PG;
|
||||||
|
ei_status.reset_8390 = &el2_reset_8390;
|
||||||
|
ei_status.block_input = &el2_block_input;
|
||||||
|
ei_status.block_output = &el2_block_output;
|
||||||
|
/* This should be probed for (or set via an ioctl()) at run-time someday. */
|
||||||
|
#ifdef EI8390_THICK
|
||||||
|
ei_status.thin_bit = 0;
|
||||||
|
#else
|
||||||
|
ei_status.thin_bit = ECNTRL_THIN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (dev->irq <= 0)
|
||||||
|
dev->irq = 5; /* Autoprobe guess... */
|
||||||
|
if (dev->irq == 2)
|
||||||
|
dev->irq = 9;
|
||||||
|
else if (dev->irq != 9 && (dev->irq > 5 || dev->irq < 3)) {
|
||||||
|
printk("\n3c503: configured interrupt number %d out of range.\n",
|
||||||
|
dev->irq);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (irqaction (dev->irq, &ei_sigaction)) {
|
||||||
|
printk ("\n3c503: Unable to get IRQ%d.\n", dev->irq);
|
||||||
|
/* We could look for other free interrupts here, or something... */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
el2_init_card(dev);
|
||||||
|
|
||||||
|
if (dev->mem_start)
|
||||||
|
printk("3c503 found, memory at %#6x, IRQ %d\n",
|
||||||
|
dev->mem_start, dev->irq);
|
||||||
|
else
|
||||||
|
printk(" 3c503 found, no shared memory, IRQ %d\n", dev->irq);
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
return ioaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called whenever we have a unrecoverable failure:
|
||||||
|
transmit timeout
|
||||||
|
Bad ring buffer packet header
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
el2_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
if (ei_debug > 1) printk("3c503: Resetting the board...");
|
||||||
|
outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
el2_init_card(dev);
|
||||||
|
if (ei_debug > 1) printk("done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the 3c503 GA registers after a reset. */
|
||||||
|
static void
|
||||||
|
el2_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
/* Unmap the station PROM and select the DIX or BNC connector. */
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* Set ASIC copy of rx's first and last+1 buffer pages */
|
||||||
|
/* These must be the same as in the 8390. */
|
||||||
|
outb(ei_status.rx_start_page, E33G_STARTPG);
|
||||||
|
outb(ei_status.stop_page, E33G_STOPPG);
|
||||||
|
|
||||||
|
/* Point the vector pointer registers somewhere ?harmless?. */
|
||||||
|
outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
|
||||||
|
outb(0xff, E33G_VP1);
|
||||||
|
outb(0x00, E33G_VP0);
|
||||||
|
/* Turn off all interrupts until we're opened. */
|
||||||
|
outb_p(0x00, dev->base_addr + EN0_IMR);
|
||||||
|
outb_p(EGACFR_IRQOFF, E33G_GACFR);
|
||||||
|
|
||||||
|
/* Set the interrupt line. */
|
||||||
|
outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
|
||||||
|
outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
|
||||||
|
outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
|
||||||
|
outb_p(0x00, E33G_DMAAL);
|
||||||
|
return; /* We always succeed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either use the shared memory (if enabled on the board) or put the packet
|
||||||
|
out through the ASIC FIFO. The latter is probably much slower. */
|
||||||
|
static void
|
||||||
|
el2_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page)
|
||||||
|
{
|
||||||
|
int i; /* Buffer index */
|
||||||
|
int boguscount = 0; /* timeout counter */
|
||||||
|
if (dev->mem_start) { /* Shared memory transfer */
|
||||||
|
void *dest_addr = (void *)(dev->mem_start +
|
||||||
|
((start_page - ei_status.tx_start_page) << 8));
|
||||||
|
outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM */
|
||||||
|
memcpy(dest_addr, buf, count);
|
||||||
|
if (ei_debug > 2 && memcmp(dest_addr, buf, count))
|
||||||
|
printk("3c503: send_packet() bad memory copy @ %#5x.\n",
|
||||||
|
dest_addr);
|
||||||
|
else if (ei_debug > 4)
|
||||||
|
printk("3c503: send_packet() good memory copy @ %#5x.\n",
|
||||||
|
dest_addr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* Set up then start the internal memory transfer to Tx Start Page */
|
||||||
|
outb(0x00, E33G_DMAAL);
|
||||||
|
outb(start_page, E33G_DMAAH);
|
||||||
|
outb(ei_status.thin_bit | ECNTRL_OUTPUT | ECNTRL_START, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* This is the byte copy loop: it should probably be tuned for
|
||||||
|
for speed once everything is working. I think it is possible
|
||||||
|
to output 8 bytes between each check of the status bit. */
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
if (count % 8 == 7)
|
||||||
|
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||||
|
if (++boguscount > 32) {
|
||||||
|
printk(EI_NAME": fifo blocked in el2_block_output.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outb(buf[i], E33G_FIFOH);
|
||||||
|
}
|
||||||
|
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the new ring pointer. */
|
||||||
|
static int
|
||||||
|
el2_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int boguscount = 0;
|
||||||
|
int end_of_ring = dev->rmem_end;
|
||||||
|
ring_offset -= (EL2SM_START_PG<<8);
|
||||||
|
|
||||||
|
/* Maybe enable shared memory just be to be safe... nahh.*/
|
||||||
|
if (dev->mem_start) { /* Use the shared memory. */
|
||||||
|
if (dev->mem_start + ring_offset + count > end_of_ring) {
|
||||||
|
/* We must wrap the input move. */
|
||||||
|
int semi_count = end_of_ring - (dev->mem_start + ring_offset);
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("3c503: block_input() @ %#5x+%x=%5x.\n",
|
||||||
|
dev->mem_start, ring_offset,
|
||||||
|
(char *)dev->mem_start + ring_offset);
|
||||||
|
memcpy(buf, (char *)dev->mem_start + ring_offset, semi_count);
|
||||||
|
count -= semi_count;
|
||||||
|
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||||
|
return dev->rmem_start + count;
|
||||||
|
}
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("3c503: block_input() @ %#5x+%x=%5x.\n",
|
||||||
|
dev->mem_start, ring_offset,
|
||||||
|
(char *)dev->mem_start + ring_offset);
|
||||||
|
memcpy(buf, (char *)dev->mem_start + ring_offset, count);
|
||||||
|
return ring_offset + count;
|
||||||
|
} else { /* No shared memory, use the fifo. */
|
||||||
|
int i;
|
||||||
|
outb(ring_offset & 0xff, E33G_DMAAL);
|
||||||
|
outb((ring_offset >> 8) & 0xff, E33G_DMAAH);
|
||||||
|
outb(ei_status.thin_bit | ECNTRL_INPUT | ECNTRL_START, E33G_CNTRL);
|
||||||
|
|
||||||
|
/* This is the byte copy loop: it should probably be tuned for
|
||||||
|
for speed once everything is working. */
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
if (count % 8 == 7)
|
||||||
|
while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
|
||||||
|
if (++boguscount > 32) {
|
||||||
|
printk(EI_NAME": fifo blocked in el2_block_input().\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
buf[i] = inb(E33G_FIFOH);
|
||||||
|
}
|
||||||
|
outb(ei_status.thin_bit, E33G_CNTRL);
|
||||||
|
ring_offset += count;
|
||||||
|
if (ring_offset >= end_of_ring)
|
||||||
|
ring_offset = dev->rmem_start + ring_offset - end_of_ring;
|
||||||
|
return ring_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c 3c503.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
59
net/tcpip/newether/3c503reg.h
Normal file
59
net/tcpip/newether/3c503reg.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/* Definitions for the 3Com 3c503 Etherlink 2. */
|
||||||
|
/* This file is part of Donald Becker's 8390 drivers.
|
||||||
|
This file is distributed under the Linux GPL.
|
||||||
|
Some of these names and comments are from the Crynwr packet drivers. */
|
||||||
|
|
||||||
|
#define EL2H (dev->base_addr + 0x400)
|
||||||
|
#define EL2L (dev->base_addr)
|
||||||
|
|
||||||
|
/* Shared memory management parameters */
|
||||||
|
|
||||||
|
#define EL2SM_START_PG (0x20) /* First page of TX buffer */
|
||||||
|
#define EL2SM_STOP_PG (0x40) /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
/* 3Com 3c503 ASIC registers */
|
||||||
|
#define E33G_STARTPG (EL2H+0) /* Start page, must match EN0_STARTPG */
|
||||||
|
#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
|
||||||
|
#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
|
||||||
|
#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
|
||||||
|
/* (non-useful, but it also appears at the end of EPROM space) */
|
||||||
|
#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
|
||||||
|
#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
|
||||||
|
#define E33G_CNTRL (EL2H+6) /* Board's main control register */
|
||||||
|
#define E33G_STATUS (EL2H+7) /* Status on completions. */
|
||||||
|
#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
|
||||||
|
/* (Which IRQ to assert, DMA chan to use) */
|
||||||
|
#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
|
||||||
|
#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
|
||||||
|
/* "Vector pointer" - if this address matches a read, the EPROM (rather than
|
||||||
|
shared RAM) is mapped into memory space. */
|
||||||
|
#define E33G_VP2 (EL2H+11)
|
||||||
|
#define E33G_VP1 (EL2H+12)
|
||||||
|
#define E33G_VP0 (EL2H+13)
|
||||||
|
#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
|
||||||
|
#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
|
||||||
|
|
||||||
|
/* Bits in E33G_CNTRL register: */
|
||||||
|
|
||||||
|
#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
|
||||||
|
#define ECNTRL_THIN (0x02) /* Onboard thin-net xcvr enable */
|
||||||
|
#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
|
||||||
|
#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
|
||||||
|
#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
|
||||||
|
#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
|
||||||
|
#define ECNTRL_START (0x80) /* Start the DMA logic */
|
||||||
|
|
||||||
|
/* Bits in E33G_STATUS register: */
|
||||||
|
|
||||||
|
#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
|
||||||
|
#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
|
||||||
|
#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
|
||||||
|
#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
|
||||||
|
#define ESTAT_DIP (0x08) /* DMA In Progress */
|
||||||
|
|
||||||
|
/* Bits in E33G_GACFR register: */
|
||||||
|
|
||||||
|
#define EGACFR_NORM (0x49) /* Enable 8K shared mem, no DMA TC int */
|
||||||
|
#define EGACFR_IRQOFF (0xc9) /* Above, and disable 8390 IRQ line */
|
||||||
|
|
||||||
|
/* End of 3C503 parameter definitions */
|
||||||
712
net/tcpip/newether/8390.c
Normal file
712
net/tcpip/newether/8390.c
Normal file
@@ -0,0 +1,712 @@
|
|||||||
|
/* 8390.c: A general NS8390 ethernet driver core for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with many 8390-based ethernet adaptors.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"8390.c:v0.27 1/27/93 Donald Becker (becker@super.org)\n";
|
||||||
|
#include <linux/config.h>
|
||||||
|
#if !defined(EL2) && !defined(NE2000) && !defined(WD80x3) && !defined(HPLAN)
|
||||||
|
/* They don't know what they want -- give it all to them! */
|
||||||
|
#define EL2
|
||||||
|
#define NE2000
|
||||||
|
#define WD80x3
|
||||||
|
#define HPLAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Braindamage remaining:
|
||||||
|
|
||||||
|
Ethernet devices should use a chr_drv device interface, with ioctl()s to
|
||||||
|
configure the card, bring the interface up or down, allow access to
|
||||||
|
statistics, and maybe read() and write() access to raw packets.
|
||||||
|
This won't be done until after Linux 1.00.
|
||||||
|
|
||||||
|
This driver should support multiple, diverse boards simultaneousely.
|
||||||
|
This won't be done until after Linux 1.00.
|
||||||
|
|
||||||
|
Sources:
|
||||||
|
The National Semiconductor LAN Databook, and 3Com databooks, both companies
|
||||||
|
provided information readily. The NE* info came from the Crynwr packet
|
||||||
|
driver, and figuring out that the those boards are similar to the NatSemi
|
||||||
|
evaluation board described in AN-729. Thanks NS, no thanks to Novell/Eagle.
|
||||||
|
Cabletron provided only info I had already gotten from other sources -- hiss.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/tty.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include <asm/segment.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <linux/fcntl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "eth.h"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "ip.h"
|
||||||
|
#include "tcp.h"
|
||||||
|
#include "sock.h"
|
||||||
|
#include "arp.h"
|
||||||
|
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
#define ei_reset_8390 (ei_status.reset_8390)
|
||||||
|
#define ei_block_output (ei_status.block_output)
|
||||||
|
#define ei_block_input (ei_status.block_input)
|
||||||
|
|
||||||
|
#define EN_CMD (e8390_base)
|
||||||
|
#define E8390_BASE (e8390_base)
|
||||||
|
|
||||||
|
/* use 0 for production, 1 for verification, >2 for debug */
|
||||||
|
#ifdef EI_DEBUG
|
||||||
|
int ei_debug = EI_DEBUG;
|
||||||
|
#else
|
||||||
|
int ei_debug = 2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int e8390_base;
|
||||||
|
|
||||||
|
static struct device *eifdev; /* For single-board consistency checking. */
|
||||||
|
extern int etherlink2;
|
||||||
|
|
||||||
|
struct ei_device ei_status = { EI_NAME, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
/* The statistics, perhaps these should be in the above structure. */
|
||||||
|
static int tx_packets = 0;
|
||||||
|
static int rx_packets = 0;
|
||||||
|
static int tx_errors = 0;
|
||||||
|
static int soft_rx_errors = 0;
|
||||||
|
static int soft_rx_err_bits = 0;
|
||||||
|
static int missed_packets = 0;
|
||||||
|
|
||||||
|
/* Index to functions. */
|
||||||
|
/* Put in the device structure. */
|
||||||
|
static int ei_open(struct device *dev);
|
||||||
|
static void ei_send_packet(struct sk_buff *skb, struct device *dev);
|
||||||
|
/* Dispatch from interrupts. */
|
||||||
|
static void ei_interrupt(int reg_ptr);
|
||||||
|
static void ei_tx_intr(struct device *dev);
|
||||||
|
static void ei_receive(struct device *dev);
|
||||||
|
static void ei_rx_overrun(struct device *dev);
|
||||||
|
|
||||||
|
/* Routines generic to NS8390-based boards. */
|
||||||
|
void NS8390_init(struct device *dev, int startp);
|
||||||
|
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||||
|
int start_page);
|
||||||
|
|
||||||
|
extern int el2autoprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int el2probe(int ioaddr, struct device *dev);
|
||||||
|
extern int neprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int wdprobe(int ioaddr, struct device *dev);
|
||||||
|
extern int hpprobe(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
struct sigaction ei_sigaction = { ei_interrupt, 0, 0, NULL, };
|
||||||
|
|
||||||
|
/* Open/initialize the board. This routine goes all-out, setting everything
|
||||||
|
up anew at each open, even though many of these registers should only
|
||||||
|
need to be set once at boot.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ei_open(struct device *dev)
|
||||||
|
{
|
||||||
|
if ( ! ei_status.exists) {
|
||||||
|
printk(EI_NAME ": Opening a non-existent physical device\n");
|
||||||
|
return 1; /* ENXIO would be more accurate. */
|
||||||
|
}
|
||||||
|
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
|
||||||
|
ei_status.txing = 0;
|
||||||
|
ei_status.in_interrupt = 0;
|
||||||
|
ei_status.open = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ei_start_xmit(struct sk_buff *skb, struct device *dev)
|
||||||
|
{
|
||||||
|
if ( ! ei_status.exists)
|
||||||
|
return 0; /* We should be able to do ENODEV, but nooo. */
|
||||||
|
|
||||||
|
if (ei_status.txing) { /* Do timeouts, just like the 8003 driver. */
|
||||||
|
int txsr = inb(E8390_BASE+EN0_TSR);
|
||||||
|
int tickssofar = jiffies - dev->trans_start;
|
||||||
|
if (tickssofar < 5 || (tickssofar < 15 && ! (txsr & ENTSR_PTX))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printk(EI_NAME": transmit timed out, TX status %#2x, ISR %#2x.\n",
|
||||||
|
txsr, inb(E8390_BASE+EN0_ISR));
|
||||||
|
/* It's possible to check for an IRQ conflict here.
|
||||||
|
I may have to do that someday. */
|
||||||
|
if ((txsr & ~0x02) == ENTSR_PTX) /* Strip an undefined bit. */
|
||||||
|
printk(EI_NAME": Possible IRQ conflict?\n");
|
||||||
|
else
|
||||||
|
printk(EI_NAME": Possible network cable problem?\n");
|
||||||
|
/* It futile, but try to restart it anyway. */
|
||||||
|
ei_reset_8390(dev);
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is new: it means some higher layer thinks we've missed an
|
||||||
|
tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
|
||||||
|
itself. */
|
||||||
|
if (skb == NULL) {
|
||||||
|
/* Alternative is ei_tx_intr(dev); */
|
||||||
|
ei_status.txing = 1;
|
||||||
|
if (dev_tint(NULL, dev) == 0)
|
||||||
|
ei_status.txing = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Fill in the ethernet header. */
|
||||||
|
if (!skb->arp && dev->rebuild_header(skb+1, dev)) {
|
||||||
|
skb->dev = dev;
|
||||||
|
arp_queue (skb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->trans_start = jiffies;
|
||||||
|
cli();
|
||||||
|
ei_status.txing = 1;
|
||||||
|
#ifdef PINGPONG
|
||||||
|
ei_tx_intr(dev);
|
||||||
|
#endif
|
||||||
|
ei_send_packet(skb, dev);
|
||||||
|
sti();
|
||||||
|
if (skb->free)
|
||||||
|
#ifdef notdef /* 0.98.5 and beyond use kfree_skb(). */
|
||||||
|
free_skb (skb, FREE_WRITE);
|
||||||
|
#else
|
||||||
|
kfree_skb (skb, FREE_WRITE);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The typical workload of the driver:
|
||||||
|
Handle the ether interface interrupts. */
|
||||||
|
static void
|
||||||
|
ei_interrupt(int reg_ptr)
|
||||||
|
{
|
||||||
|
int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
|
||||||
|
struct device *dev;
|
||||||
|
int interrupts, boguscount = 0;
|
||||||
|
|
||||||
|
/* We do the same thing as the 8013 driver, but this is mostly bogus. */
|
||||||
|
for (dev = dev_base; dev != NULL; dev = dev->next) {
|
||||||
|
if (dev->irq == irq) break;
|
||||||
|
}
|
||||||
|
ei_status.in_interrupt++;
|
||||||
|
sti(); /* Allow other interrupts. */
|
||||||
|
|
||||||
|
#ifdef notneeded
|
||||||
|
/* If we a getting a reset-complete interrupt the 8390 might not be
|
||||||
|
mapped in for the 3c503. */
|
||||||
|
if (etherlink2)
|
||||||
|
outb_p(ei_status.thin_bit, E33G_CNTRL),
|
||||||
|
outb_p(0x00, E33G_STATUS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Change to page 0 and read the intr status reg. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, EN_CMD);
|
||||||
|
if (ei_debug > 3)
|
||||||
|
printk(EI_NAME": interrupt(isr=%#2.2x).\n",
|
||||||
|
inb_p(E8390_BASE + EN0_ISR));
|
||||||
|
|
||||||
|
if (ei_status.in_interrupt > 1)
|
||||||
|
printk(EI_NAME ": Reentering the interrupt driver!\n");
|
||||||
|
if (dev == NULL) {
|
||||||
|
printk (EI_NAME ": irq %d for unknown device\n", irq);
|
||||||
|
ei_status.in_interrupt--;
|
||||||
|
return;
|
||||||
|
} else if (ei_debug > 0 && eifdev != dev) {
|
||||||
|
printk (EI_NAME": device mismatch on irq %d.\n", irq);
|
||||||
|
dev = eifdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* !!Assumption!! -- we stay in page 0. Don't break this. */
|
||||||
|
while ((interrupts = inb_p(E8390_BASE + EN0_ISR)) != 0
|
||||||
|
&& ++boguscount < 20) {
|
||||||
|
/* The reset interrupt is the most important... */
|
||||||
|
if (interrupts & ENISR_RDC) {
|
||||||
|
outb_p(ENISR_RDC, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
}
|
||||||
|
if (interrupts & ENISR_OVER) {
|
||||||
|
ei_status.overruns++;
|
||||||
|
ei_rx_overrun(dev);
|
||||||
|
} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
|
||||||
|
/* Got a good (?) packet. */
|
||||||
|
ei_receive(dev);
|
||||||
|
}
|
||||||
|
/* Push the next to-transmit packet through. */
|
||||||
|
if (interrupts & ENISR_TX) {
|
||||||
|
ei_tx_intr(dev);
|
||||||
|
} else if (interrupts & ENISR_COUNTERS) {
|
||||||
|
/* Gotta read the counter to clear the irq, even if we
|
||||||
|
don't care about their values. */
|
||||||
|
inb_p(E8390_BASE + EN0_COUNTER0);
|
||||||
|
inb_p(E8390_BASE + EN0_COUNTER1);
|
||||||
|
missed_packets += inb_p(E8390_BASE + EN0_COUNTER2);
|
||||||
|
outb_p(ENISR_COUNTERS, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, EN_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore the transmit errs and reset intr for now. */
|
||||||
|
if (interrupts & ENISR_TX_ERR) {
|
||||||
|
outb_p(ENISR_TX_ERR, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
}
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, EN_CMD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interrupts && ei_debug) {
|
||||||
|
printk(EI_NAME ": unknown interrupt %#2x\n", interrupts);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
|
||||||
|
outb_p(0xff, E8390_BASE + EN0_ISR); /* Ack. all intrs. */
|
||||||
|
}
|
||||||
|
ei_status.in_interrupt--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
static int lasttx = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is stuffed into the dev struct to be called by dev.c:dev_tint().
|
||||||
|
Evenually this should be replaced by the block_output() routines. */
|
||||||
|
static void
|
||||||
|
ei_send_packet(struct sk_buff *skb, struct device *dev)
|
||||||
|
{
|
||||||
|
int length = skb->len;
|
||||||
|
int send_length = ETHER_MIN_LEN < length ? length : ETHER_MIN_LEN;
|
||||||
|
|
||||||
|
if (length <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
if (ei_status.tx1 == 0) { /* First buffer empty */
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1),
|
||||||
|
ei_status.tx_start_page);
|
||||||
|
ei_status.tx1 = send_length;
|
||||||
|
} else if (ei_status.tx2 == 0) { /* Second buffer empty */
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1),
|
||||||
|
ei_status.tx_start_page+6);
|
||||||
|
ei_status.tx2 = send_length;
|
||||||
|
} else {
|
||||||
|
printk("%s: Internal error, no transmit buffer space tx1=%d tx2=%d lasttx=%d.\n",
|
||||||
|
ei_status.name, ei_status.tx1, ei_status.tx2, lasttx);
|
||||||
|
}
|
||||||
|
ei_status.txqueue++;
|
||||||
|
/* The following should be merged with ei_tx_intr(). */
|
||||||
|
if (lasttx = 0) {
|
||||||
|
if (ei_status.tx1 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx1,
|
||||||
|
ei_status.tx_start_page + 6),
|
||||||
|
ei_status.tx1 = -1,
|
||||||
|
lasttx = 1;
|
||||||
|
} else if (ei_status.tx2 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx2,
|
||||||
|
ei_status.tx_start_page + 6),
|
||||||
|
ei_status.tx2 = -1,
|
||||||
|
lasttx = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ei_block_output(dev, length, (void*)(skb+1), ei_status.tx_start_page);
|
||||||
|
NS8390_trigger_send(dev, send_length, ei_status.tx_start_page);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have finished a transmit: check for errors and then trigger the next
|
||||||
|
packet to be sent. */
|
||||||
|
static void
|
||||||
|
ei_tx_intr(struct device *dev)
|
||||||
|
{
|
||||||
|
int status = inb(E8390_BASE + EN0_TSR);
|
||||||
|
outb_p(ENISR_TX, E8390_BASE + EN0_ISR); /* Ack intr. */
|
||||||
|
if ((status & ENTSR_PTX) == 0)
|
||||||
|
tx_errors++;
|
||||||
|
else
|
||||||
|
tx_packets++;
|
||||||
|
|
||||||
|
#ifdef PINGPONG
|
||||||
|
{
|
||||||
|
ei_status.txqueue--;
|
||||||
|
if (ei_status.tx1 < 0) {
|
||||||
|
if (lasttx != 1)
|
||||||
|
printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
|
||||||
|
ei_status.name, lasttx, ei_status.tx1);
|
||||||
|
ei_status.tx1 = 0;
|
||||||
|
lasttx = 0;
|
||||||
|
if (ei_status.tx2 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx2,
|
||||||
|
ei_status.tx_start_page + 6),
|
||||||
|
ei_status.tx2 = -1,
|
||||||
|
lasttx = 2;
|
||||||
|
}
|
||||||
|
} else if (ei_status.tx2 < 0) {
|
||||||
|
if (lasttx != 2)
|
||||||
|
printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
|
||||||
|
ei_status.name, lasttx, ei_status.tx2);
|
||||||
|
ei_status.tx2 = 0;
|
||||||
|
lasttx = 0;
|
||||||
|
if (ei_status.tx1 > 0) {
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx1,
|
||||||
|
ei_status.tx_start_page),
|
||||||
|
ei_status.tx1 = -1;
|
||||||
|
lasttx = 1;
|
||||||
|
}
|
||||||
|
} /*else
|
||||||
|
printk(EI_NAME": unexpected TX interrupt.\n");*/
|
||||||
|
while ((ei_status.tx1 == 0) || (ei_status.tx2 == 0))
|
||||||
|
if (dev_tint(NULL, dev) == 0) {
|
||||||
|
ei_status.txing = 0;
|
||||||
|
return;
|
||||||
|
} else if (lasttx == 0) {
|
||||||
|
if (ei_status.tx1 == 0 || ei_status.tx2 != 0)
|
||||||
|
printk(EI_NAME": Unexpected tx buffer busy tx1=%d tx2=%d.\n",
|
||||||
|
ei_status.tx1, ei_status.tx2);
|
||||||
|
NS8390_trigger_send(dev, ei_status.tx1,
|
||||||
|
ei_status.tx_start_page),
|
||||||
|
ei_status.tx1 = -1;
|
||||||
|
lasttx = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (dev_tint(NULL, dev) == 0)
|
||||||
|
ei_status.txing = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have a good packet(s), get it/them out of the buffers. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
ei_receive(struct device *dev)
|
||||||
|
{
|
||||||
|
int rxing_page, this_frame, next_frame, current_offset;
|
||||||
|
int boguscount = 0;
|
||||||
|
struct e8390_pkt_hdr rx_frame;
|
||||||
|
int num_rx_pages = ei_status.stop_page-ei_status.rx_start_page;
|
||||||
|
|
||||||
|
while (++boguscount < 10) {
|
||||||
|
int size;
|
||||||
|
|
||||||
|
cli();
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE1, EN_CMD); /* Get the rec. page. */
|
||||||
|
rxing_page = inb_p(E8390_BASE+EN1_CURPAG);/* (Incoming packet pointer).*/
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, EN_CMD);
|
||||||
|
sti();
|
||||||
|
|
||||||
|
/* Remove one frame from the ring. Boundary is alway a page behind. */
|
||||||
|
this_frame = inb_p(E8390_BASE + EN0_BOUNDARY) + 1;
|
||||||
|
if (this_frame >= ei_status.stop_page)
|
||||||
|
this_frame = ei_status.rx_start_page;
|
||||||
|
|
||||||
|
/* Someday we'll omit the previous step, iff we never get this message.*/
|
||||||
|
if (ei_debug > 0 && this_frame != ei_status.current_page)
|
||||||
|
printk(EI_NAME": mismatched read page pointers %2x vs %2x.\n",
|
||||||
|
this_frame, ei_status.current_page);
|
||||||
|
|
||||||
|
if (this_frame == rxing_page) /* Read all the frames? */
|
||||||
|
break; /* Done for now */
|
||||||
|
|
||||||
|
current_offset = this_frame << 8;
|
||||||
|
ei_block_input(dev, sizeof(rx_frame), (void *)&rx_frame,
|
||||||
|
current_offset);
|
||||||
|
|
||||||
|
size = rx_frame.count - sizeof(rx_frame);
|
||||||
|
|
||||||
|
next_frame = this_frame + 1 + ((size+4)>>8);
|
||||||
|
|
||||||
|
/* Check for bogosity warned by 3c503 book: the status byte is never
|
||||||
|
written. This happened a lot during testing! This code should be
|
||||||
|
cleaned up someday, and the printk()s should be PRINTK()s. */
|
||||||
|
if ( rx_frame.next != next_frame
|
||||||
|
&& rx_frame.next != next_frame + 1
|
||||||
|
&& rx_frame.next != next_frame - num_rx_pages
|
||||||
|
&& rx_frame.next != next_frame + 1 - num_rx_pages) {
|
||||||
|
#ifndef EI_DEBUG
|
||||||
|
ei_status.current_page = rxing_page;
|
||||||
|
outb(ei_status.current_page-1, E8390_BASE+EN0_BOUNDARY);
|
||||||
|
continue;
|
||||||
|
#else
|
||||||
|
static int last_rx_bogosity = -1;
|
||||||
|
printk(EI_NAME": bogus packet header, status=%#2x nxpg=%#2x sz=%#x (at %#4x)\n",
|
||||||
|
rx_frame.status, rx_frame.next, rx_frame.count, current_offset);
|
||||||
|
|
||||||
|
if (rx_packets != last_rx_bogosity) {
|
||||||
|
/* Maybe we can avoid resetting the chip... empty the packet ring. */
|
||||||
|
ei_status.current_page = rxing_page;
|
||||||
|
printk(EI_NAME": setting next frame to %#2x (nxt=%#2x, rx_frm.nx=%#2x rx_frm.stat=%#2x).\n",
|
||||||
|
ei_status.current_page, next_frame,
|
||||||
|
rx_frame.next, rx_frame.status);
|
||||||
|
last_rx_bogosity = rx_packets;
|
||||||
|
outb(ei_status.current_page-1, E8390_BASE+EN0_BOUNDARY);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
/* Oh no Mr Bill! Last ditch error recovery. */
|
||||||
|
printk(EI_NAME": multiple sequential lossage, resetting at packet #%d.",
|
||||||
|
rx_packets);
|
||||||
|
sti();
|
||||||
|
ei_reset_8390(dev);
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
printk("restarting.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif /* EI8390_NOCHECK */
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size < 32 || size > 1535) && ei_debug)
|
||||||
|
printk(EI_NAME": bogus big packet, status=%#2x nxpg=%#2x size=%#x\n",
|
||||||
|
rx_frame.status, rx_frame.next, rx_frame.count);
|
||||||
|
if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
|
||||||
|
int sksize = sizeof(struct sk_buff) + size;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
skb = kmalloc(sksize, GFP_ATOMIC);
|
||||||
|
if (skb != NULL) {
|
||||||
|
skb->lock = 0;
|
||||||
|
skb->mem_len = sksize;
|
||||||
|
skb->mem_addr = skb;
|
||||||
|
/* 'skb+1' points to the start of sk_buff data area. */
|
||||||
|
ei_block_input(dev, size, (void *)(skb+1),
|
||||||
|
current_offset + sizeof(rx_frame));
|
||||||
|
if(dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
|
||||||
|
printk(EI_NAME": receive buffers full.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (ei_debug) {
|
||||||
|
printk(EI_NAME": Couldn't allocate a sk_buff of size %d.\n", sksize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rx_packets++;
|
||||||
|
} else {
|
||||||
|
if (ei_debug)
|
||||||
|
printk(EI_NAME": bogus packet, status=%#2x nxpg=%#2x size=%d\n",
|
||||||
|
rx_frame.status, rx_frame.next, rx_frame.count);
|
||||||
|
soft_rx_err_bits |= rx_frame.status,
|
||||||
|
soft_rx_errors++;
|
||||||
|
}
|
||||||
|
next_frame = rx_frame.next;
|
||||||
|
|
||||||
|
/* This should never happen, it's here for debugging. */
|
||||||
|
if (next_frame >= ei_status.stop_page) {
|
||||||
|
printk(EI_NAME": next frame inconsistency, %#2x..", next_frame);
|
||||||
|
next_frame = ei_status.rx_start_page;
|
||||||
|
}
|
||||||
|
ei_status.current_page += 1 + ((size+4)>>8);
|
||||||
|
#ifdef notdef
|
||||||
|
if (ei_status.current_page > ei_status.stop_page)
|
||||||
|
ei_status.current_page -= ei_status.stop_page-ei_status.rx_start_page;
|
||||||
|
if (ei_status.current_page != next_frame) {
|
||||||
|
printk(EI_NAME": inconsistency in next_frame %#2x!=%#2x.\n",
|
||||||
|
this_frame, next_frame);
|
||||||
|
/* Assume this packet frame is scrogged by the NIC, use magic to
|
||||||
|
skip to the next frame. Actually we should stop and restart.*/
|
||||||
|
next_frame = size > 1535 ? rx_frame.status : rx_frame.next;
|
||||||
|
ei_status.current_page = next_frame;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ei_status.current_page = next_frame;
|
||||||
|
outb(next_frame-1, E8390_BASE+EN0_BOUNDARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tell the upper levels we're done. */
|
||||||
|
while (dev_rint(NULL, 0, 0, dev) == 1
|
||||||
|
&& ++boguscount < 20)
|
||||||
|
;
|
||||||
|
/* Bug alert! Reset ENISR_OVER to avoid spurious overruns! */
|
||||||
|
outb_p(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, E8390_BASE+EN0_ISR); /* Ack intr. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have a receiver overrun: we have to kick the 8390 to get it started
|
||||||
|
again. Overruns are detected on a per-256byte-page basis. */
|
||||||
|
static void
|
||||||
|
ei_rx_overrun(struct device *dev)
|
||||||
|
{
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
/* We should already be stopped and in page0. Remove after testing. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, EN_CMD);
|
||||||
|
|
||||||
|
if (ei_debug)
|
||||||
|
printk(EI_NAME ": Receiver overrun.\n");
|
||||||
|
|
||||||
|
/* The we.c driver does dummy = inb_p( RBCR[01] ); at this point.
|
||||||
|
It might mean something -- magic to speed up a reset? A 8390 bug?*/
|
||||||
|
|
||||||
|
/* Wait for reset in case the NIC is doing a tx or rx. This could take up to
|
||||||
|
1.5msec, but we have no way of timing something in that range. The 'jiffies'
|
||||||
|
are just a sanity check. */
|
||||||
|
while ((inb_p(E8390_BASE+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 1) {
|
||||||
|
printk(EI_NAME": reset did not complete at ei_rx_overrun.\n");
|
||||||
|
NS8390_init(dev, 1);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Remove packets right away. */
|
||||||
|
ei_debug += 5;
|
||||||
|
ei_receive(dev);
|
||||||
|
ei_debug -= 5;
|
||||||
|
outb_p(0xff, E8390_BASE+EN0_ISR);
|
||||||
|
/* Generic 8390 insns to start up again, same as in open_8390(). */
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, EN_CMD);
|
||||||
|
outb_p(E8390_TXCONFIG, E8390_BASE + EN0_TXCR); /* xmit on. */
|
||||||
|
#ifdef notneeded
|
||||||
|
outb_p(E8390_RXCONFIG, E8390_BASE + EN0_RXCR); /* rx on, */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ethif_init(struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
eifdev = dev; /* Store for debugging. */
|
||||||
|
|
||||||
|
if (ei_debug > 3)
|
||||||
|
printk(version);
|
||||||
|
if (1
|
||||||
|
#ifdef WD80x3
|
||||||
|
&& ! wdprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef EL2
|
||||||
|
&& ! el2autoprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef NE2000
|
||||||
|
&& ! neprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
#ifdef HPLAN
|
||||||
|
&& ! hpprobe(dev->base_addr, dev)
|
||||||
|
#endif
|
||||||
|
&& 1 ) {
|
||||||
|
dev->open = &ei_open;
|
||||||
|
printk("No ethernet device found.\n");
|
||||||
|
ei_status.exists = 0;
|
||||||
|
return 1; /* ENODEV or EAGAIN would be more accurate. */
|
||||||
|
}
|
||||||
|
|
||||||
|
e8390_base = dev->base_addr;
|
||||||
|
|
||||||
|
/* Initialize the rest of the device structure. Many of these could
|
||||||
|
be in Space.c. */
|
||||||
|
for (i = 0; i < DEV_NUMBUFFS; i++)
|
||||||
|
dev->buffs[i] = NULL;
|
||||||
|
|
||||||
|
ei_status.exists = 1;
|
||||||
|
dev->hard_header = eth_hard_header;
|
||||||
|
dev->add_arp = eth_add_arp;
|
||||||
|
dev->queue_xmit = dev_queue_xmit;
|
||||||
|
dev->rebuild_header = eth_rebuild_header;
|
||||||
|
dev->type_trans = eth_type_trans;
|
||||||
|
|
||||||
|
dev->send_packet = &ei_send_packet;
|
||||||
|
dev->open = &ei_open;
|
||||||
|
dev->hard_start_xmit = &ei_start_xmit;
|
||||||
|
|
||||||
|
dev->type = ETHER_TYPE;
|
||||||
|
dev->hard_header_len = sizeof (struct enet_header);
|
||||||
|
dev->mtu = 1500; /* eth_mtu */
|
||||||
|
dev->addr_len = ETHER_ADDR_LEN;
|
||||||
|
for (i = 0; i < dev->addr_len; i++) {
|
||||||
|
dev->broadcast[i]=0xff;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This page of functions should be 8390 generic */
|
||||||
|
/* Follow National Semi's recommendations for initializing the "NIC". */
|
||||||
|
void NS8390_init(struct device *dev, int startp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int endcfg = ei_status.word16 ? (0x48 | ENDCFG_WTS) : 0x48;
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
|
||||||
|
/* Follow National Semi's recommendations for initing the DP83902. */
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
|
||||||
|
outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
|
||||||
|
/* Clear the remote byte count registers. */
|
||||||
|
outb_p(0x00, e8390_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, e8390_base + EN0_RCNTHI);
|
||||||
|
/* Set to monitor and loopback mode -- this is vital!. */
|
||||||
|
outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
|
||||||
|
outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
|
||||||
|
/* Set the transmit page and receive ring. */
|
||||||
|
outb_p(ei_status.tx_start_page, e8390_base + EN0_TPSR);
|
||||||
|
ei_status.tx1 = ei_status.tx2 = 0;
|
||||||
|
outb_p(ei_status.rx_start_page, e8390_base + EN0_STARTPG);
|
||||||
|
outb_p(ei_status.stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
|
||||||
|
ei_status.current_page = ei_status.rx_start_page; /* assert boundary+1 */
|
||||||
|
outb_p(ei_status.stop_page, e8390_base + EN0_STOPPG);
|
||||||
|
/* Clear the pending interrupts and mask. */
|
||||||
|
outb_p(0xFF, e8390_base + EN0_ISR);
|
||||||
|
outb_p(0x00, e8390_base + EN0_IMR);
|
||||||
|
|
||||||
|
/* Copy the station address into the DS8390 registers,
|
||||||
|
and set the multicast hash bitmap to receive all multicasts. */
|
||||||
|
cli();
|
||||||
|
outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
|
||||||
|
for(i = 0; i < 6; i++) {
|
||||||
|
outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
|
||||||
|
}
|
||||||
|
for(i = 0; i < 8; i++)
|
||||||
|
outb_p(0xff, e8390_base + EN1_MULT + i);
|
||||||
|
|
||||||
|
outb_p(ei_status.rx_start_page, e8390_base + EN1_CURPAG);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
|
||||||
|
sti();
|
||||||
|
if (startp) {
|
||||||
|
outb_p(0xff, e8390_base + EN0_ISR);
|
||||||
|
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
|
||||||
|
outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
|
||||||
|
/* 3c503 TechMan says rxconfig only after the NIC is started. */
|
||||||
|
outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trigger a transmit start, assuming the length is valid. */
|
||||||
|
static void NS8390_trigger_send(struct device *dev, unsigned int length,
|
||||||
|
int start_page)
|
||||||
|
{
|
||||||
|
int e8390_base = dev->base_addr;
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
|
||||||
|
|
||||||
|
if (inb_p(EN_CMD) & E8390_TRANS) {
|
||||||
|
printk(EI_NAME": trigger_send() called with the transmitter busy.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
|
||||||
|
outb_p(length >> 8, e8390_base + EN0_TCNTHI);
|
||||||
|
outb_p(start_page, e8390_base + EN0_TPSR);
|
||||||
|
outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
|
||||||
|
outb_p(ENISR_RDC, e8390_base + EN0_ISR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -DPINGPONG -I/usr/src/linux/net/tcp -c 8390.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
129
net/tcpip/newether/8390.h
Normal file
129
net/tcpip/newether/8390.h
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/* Generic NS8390 register definitions. */
|
||||||
|
/* This file is part of Donald Becker's 8390 drivers.
|
||||||
|
This file is distributed under the Linux GPL.
|
||||||
|
Some of these names and comments are from the Crynwr packet drivers. */
|
||||||
|
|
||||||
|
#ifndef e8390_h
|
||||||
|
#define e8390_h
|
||||||
|
#ifndef EI_NAME
|
||||||
|
#define EI_NAME "eth_if"
|
||||||
|
#endif
|
||||||
|
/*#define PINGPONG*/
|
||||||
|
#ifdef PINGPONG
|
||||||
|
#define TX_PAGES 12
|
||||||
|
#else
|
||||||
|
#define TX_PAGES 6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ETHER_ADDR_LEN 6
|
||||||
|
|
||||||
|
/* From auto_irq.c */
|
||||||
|
extern void autoirq_setup(int waittime);
|
||||||
|
extern int autoirq_report(int waittime);
|
||||||
|
|
||||||
|
/* Most of these entries should be in 'struct device' (or most
|
||||||
|
things in there should be here!) */
|
||||||
|
struct ei_device { /* These should be stored per-board */
|
||||||
|
char *name;
|
||||||
|
int exists:1; /* perhaps in dev->private. */
|
||||||
|
int open:1;
|
||||||
|
int word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
|
||||||
|
void (*reset_8390)(struct device *);
|
||||||
|
void (*block_output)(struct device *, int, const unsigned char *, int);
|
||||||
|
int (*block_input)(struct device *, int, char *, int);
|
||||||
|
unsigned char tx_start_page, rx_start_page, stop_page;
|
||||||
|
unsigned char current_page; /* Read pointer in buffer */
|
||||||
|
unsigned char thin_bit; /* Value to write to the 3c503 E33G_CNTRL */
|
||||||
|
unsigned char txing; /* Transmit Active, don't confuse the upper levels. */
|
||||||
|
unsigned char txqueue; /* Tx Packet buffer queue length. */
|
||||||
|
unsigned char in_interrupt;
|
||||||
|
short tx1, tx2; /* Packet lengths for ping-pong tx. */
|
||||||
|
int overruns; /* Rx overruns. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Some generic ethernet register configurations. */
|
||||||
|
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
|
||||||
|
#define E8390_RX_IRQ_MASK 0x5
|
||||||
|
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
|
||||||
|
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
|
||||||
|
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
|
||||||
|
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
|
||||||
|
|
||||||
|
/* Register accessed at EN_CMD, the 8390 base addr. */
|
||||||
|
#define E8390_STOP 0x01 /* Stop and reset the chip */
|
||||||
|
#define E8390_START 0x02 /* Start the chip, clear reset */
|
||||||
|
#define E8390_TRANS 0x04 /* Transmit a frame */
|
||||||
|
#define E8390_RREAD 0x08 /* Remote read */
|
||||||
|
#define E8390_RWRITE 0x10 /* Remote write */
|
||||||
|
#define E8390_NODMA 0x20 /* Remote DMA */
|
||||||
|
#define E8390_PAGE0 0x00 /* Select page chip registers */
|
||||||
|
#define E8390_PAGE1 0x40 /* using the two high-order bits */
|
||||||
|
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
|
||||||
|
|
||||||
|
/* Page 0 register offsets. */
|
||||||
|
#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
|
||||||
|
#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
|
||||||
|
#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
|
||||||
|
#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
|
||||||
|
#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
|
||||||
|
#define EN0_TSR 0x04 /* Transmit status reg RD */
|
||||||
|
#define EN0_TPSR 0x04 /* Transmit starting page WR */
|
||||||
|
#define EN0_NCR 0x05 /* Number of collision reg RD */
|
||||||
|
#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
|
||||||
|
#define EN0_FIFO 0x06 /* FIFO RD */
|
||||||
|
#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
|
||||||
|
#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
|
||||||
|
#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
|
||||||
|
#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
|
||||||
|
#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
|
||||||
|
#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
|
||||||
|
#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
|
||||||
|
#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
|
||||||
|
#define EN0_RSR 0x0c /* rx status reg RD */
|
||||||
|
#define EN0_RXCR 0x0c /* RX configuration reg WR */
|
||||||
|
#define EN0_TXCR 0x0d /* TX configuration reg WR */
|
||||||
|
#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
|
||||||
|
#define EN0_DCFG 0x0e /* Data configuration reg WR */
|
||||||
|
#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
|
||||||
|
#define EN0_IMR 0x0f /* Interrupt mask reg WR */
|
||||||
|
#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
|
||||||
|
|
||||||
|
/* Bits in EN0_ISR - Interrupt status register */
|
||||||
|
#define ENISR_RX 0x01 /* Receiver, no error */
|
||||||
|
#define ENISR_TX 0x02 /* Transmitter, no error */
|
||||||
|
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
|
||||||
|
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
|
||||||
|
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
|
||||||
|
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
|
||||||
|
#define ENISR_RDC 0x40 /* remote dma complete */
|
||||||
|
#define ENISR_RESET 0x80 /* Reset completed */
|
||||||
|
#define ENISR_ALL 0x3f /* Interrupts we will enable */
|
||||||
|
|
||||||
|
/* Bits in EN0_DCFG - Data config register */
|
||||||
|
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
|
||||||
|
|
||||||
|
/* Page 1 register offsets. */
|
||||||
|
#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
|
||||||
|
#define EN1_CURPAG 0x07 /* Current memory page RD WR */
|
||||||
|
#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
|
||||||
|
|
||||||
|
/* Bits in received packet status byte and EN0_RSR*/
|
||||||
|
#define ENRSR_RXOK 0x01 /* Received a good packet */
|
||||||
|
#define ENRSR_CRC 0x02 /* CRC error */
|
||||||
|
#define ENRSR_FAE 0x04 /* frame alignment error */
|
||||||
|
#define ENRSR_FO 0x08 /* FIFO overrun */
|
||||||
|
#define ENRSR_MPA 0x10 /* missed pkt */
|
||||||
|
#define ENRSR_PHY 0x20 /* physical/multicase address */
|
||||||
|
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
|
||||||
|
#define ENRSR_DEF 0x80 /* deferring */
|
||||||
|
|
||||||
|
/* Transmitted packet status, EN0_TSR. */
|
||||||
|
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
|
||||||
|
|
||||||
|
/* The per-packet-header format. */
|
||||||
|
struct e8390_pkt_hdr {
|
||||||
|
unsigned char status; /* status */
|
||||||
|
unsigned char next; /* pointer to next packet. */
|
||||||
|
unsigned short count; /* header + packet lenght in bytes */
|
||||||
|
};
|
||||||
|
#endif /* e8390_h */
|
||||||
27
net/tcpip/newether/GNUmakefile
Normal file
27
net/tcpip/newether/GNUmakefile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# This has the effect of appending lines to the Makefile.
|
||||||
|
include Makefile
|
||||||
|
|
||||||
|
# Add a few files to tcpip.a.
|
||||||
|
newobjs = 8390.o 3c503.o ne.o wd.o hp.o auto_irq.o
|
||||||
|
OBJS := $(OBJS) $(newobjs)
|
||||||
|
tcpip.a: $(newobjs)
|
||||||
|
|
||||||
|
# Set the address and IRQ here. The ne.c and 3c503 driver will autoprobe
|
||||||
|
# if you set the address or IRQ to zero, so we do that by default.
|
||||||
|
#
|
||||||
|
# Add -DEI_NAME=eth0 if you want to be exactly compatible with the default
|
||||||
|
# driver. This will only work if you don't use the distributed 'we' driver!
|
||||||
|
#
|
||||||
|
Space.o: Space.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -DEI8390=0 -DEI8390_IRQ=0 -c Space.c -o $@
|
||||||
|
|
||||||
|
# Change this to define the set of ethercards your kernel will support.
|
||||||
|
8390.o: 8390.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -DNE2000 -DWD80x3 -DHPLAN -DEL2 -c 8390.c -o $@
|
||||||
|
|
||||||
|
# And set any special compile-time options here.
|
||||||
|
ne.o: ne.c GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -UEI_8BIT -c ne.c -o $@
|
||||||
|
|
||||||
|
3c503.o: 3c503.c 3c503reg.h GNUmakefile
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) -UEI8390_THICK -c 3c503.c -o $@
|
||||||
77
net/tcpip/newether/INSTALL
Normal file
77
net/tcpip/newether/INSTALL
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
Installation Directions:
|
||||||
|
|
||||||
|
EMail me (becker@super.org) telling me you've gotten the "experimental
|
||||||
|
drivers". I need to know how many people have tried, succeeded and
|
||||||
|
failed before this is released as part of the official Linux.
|
||||||
|
|
||||||
|
Use Linux 0.99.2 or later. Make certain you can make a working kernel
|
||||||
|
_before_ you install the ethercard driver.
|
||||||
|
|
||||||
|
Put the all of the files into linux/net/tcp/. You'll need
|
||||||
|
GNUmakefile 8390.c 8390.h Space.c wd.c ne.c 3c503.c 3c503reg.h and hp.c.
|
||||||
|
Space.c is the only tricky one -- it overwrites the old Space.c.
|
||||||
|
Stock versions of Space.c leave the "we" driver enabled and will not work.
|
||||||
|
|
||||||
|
Change the GNUmakefile to reflect your configuration. Use the guide at
|
||||||
|
the end of these instructions.
|
||||||
|
|
||||||
|
Make and install your new kernel.
|
||||||
|
|
||||||
|
To actually use this driver you must get the TCP/IP package and edit
|
||||||
|
your /usr/etc/inet/rc.net file to config "eth_if" instead of
|
||||||
|
"eth0" (the WD8003). (Alternately you can edit the GNUmakefile to use the
|
||||||
|
"eth0" name.)
|
||||||
|
|
||||||
|
If you try to 'config' an interface that doesn't exist your kernel
|
||||||
|
will report "invalid ioctl()" for anthing that tries to use the card.
|
||||||
|
Note that the ethercard devices aren't (yet) real '/dev/eth_if' devices --
|
||||||
|
they only exist in the socket namespace and thus you don't need to
|
||||||
|
'mknode' them.
|
||||||
|
|
||||||
|
________________
|
||||||
|
Important defines
|
||||||
|
|
||||||
|
For Space.c
|
||||||
|
#define EI8390 0x300 /* The base address of your ethercard. */
|
||||||
|
#define EI8390_IRQ 5 /* and the interrupt you want to use. */
|
||||||
|
|
||||||
|
For 8390.c
|
||||||
|
#define EI_DEBUG 2 /* Use '0' for no messages. */
|
||||||
|
#define EL2 /* For the 3c503 driver. */
|
||||||
|
#define NE2000 /* For the NE1000/NE2000/Ctron driver. */
|
||||||
|
#define WD80x3 /* For the WD8003/WD8013 driver. */
|
||||||
|
#define HPLAN /* For the HP27xxx driver. */
|
||||||
|
|
||||||
|
For the individual drivers
|
||||||
|
#undef EI_8BIT /* Define for ne.c iff you have an 8 bit card.*/
|
||||||
|
#undef EI8390_THICK /* Define for 3c503 AUI/DIX transceiver.*/
|
||||||
|
|
||||||
|
EI8390 Define (probably in autoconf.h or config.site.h) this to the base
|
||||||
|
address of your ethernet card.
|
||||||
|
EI8390_IRQ Define (probably in autoconf.h or config.site.h) this to the
|
||||||
|
IRQ line of your ethernet card. Most drivers convert a IRQ2 to an
|
||||||
|
IRQ9 for you, so don't be surprised.
|
||||||
|
EI_DEBUG Set to the desired numeric debugging level. Use 3 or
|
||||||
|
greater when actively debugging a problem, '1' for a
|
||||||
|
casual interest in what's going on, and '0' for normal
|
||||||
|
use. (Most of the debugging stuff has been taken out recently,
|
||||||
|
so this won't have much effect.)
|
||||||
|
EI_PINGPONG
|
||||||
|
Not included or broken the alpha version. Define this if you want
|
||||||
|
ping-pong transmit buffers.
|
||||||
|
EI_8BIT
|
||||||
|
If you are using the ne.c driver and have an 8-bit card (NE1000 or
|
||||||
|
E1xxx Cabletron) you must define this. It's not needed for the other
|
||||||
|
drivers, and I hope to find a way to clean this up in the future.
|
||||||
|
EI8390_THICK
|
||||||
|
Define for this if you are using the 3c503 and use the AUI/DIX
|
||||||
|
connector rather than the built-in thin-net transceiver.
|
||||||
|
|
||||||
|
If you have a Cabletron ethercard you might want to look at ne.c:neprobe()
|
||||||
|
for info on how to enable more packet buffer space.
|
||||||
|
|
||||||
|
ETHERLINK1_IRQ
|
||||||
|
ETHERLINK1 Define these to the base address and IRQ of a 3c501 (NOT 3c503)
|
||||||
|
card. Refer to net/tcp/Space.c.
|
||||||
|
|
||||||
|
|
||||||
80
net/tcpip/newether/README
Normal file
80
net/tcpip/newether/README
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
This is the experimental version of the 8390 ethercard driver.
|
||||||
|
|
||||||
|
You should consult the list below for the specific #define for your
|
||||||
|
ethercard. If you don't have one specific #define the driver will
|
||||||
|
probe for all supported ethercard types.
|
||||||
|
|
||||||
|
Source Header #define to get Supported cards
|
||||||
|
8390.c 8390.h EI8390 (generic, needed for all)
|
||||||
|
3c503.c 3c503reg.h EL2 3c503, 3c503/16
|
||||||
|
ne.c NE2000 NE1000, NE2000, NatSemi, Cabletron
|
||||||
|
wd.c wd.h WD80x3 WD8003, WD8013, SMC elite
|
||||||
|
hp.c HPLAN HP LAN adaptors
|
||||||
|
|
||||||
|
Notes on each package
|
||||||
|
|
||||||
|
3c503.c
|
||||||
|
o You'll need to #define EI8390_THICK if you are using the AUI port instead
|
||||||
|
of the thinnet connector. Russ Nelson <nelson@crynwr.com> sent me an
|
||||||
|
run-time method of selecting between the two, but I haven't put it in yet.
|
||||||
|
This _may_ generate a spurious error message when transmitting the first
|
||||||
|
packet, I haven't yet tracked down this bug report.
|
||||||
|
o If you want to check the shared memory, #define EL2MEMTEST. I don't think
|
||||||
|
the memory check is worth the effort -- it's missed obvious problems.
|
||||||
|
o You must jumper the board to use shared memory -- I don't test the
|
||||||
|
non-shared-memory mode and it's probably broken.
|
||||||
|
o Thanks to Chance Reschke at USRA <creschke@usra.edu> for providing the
|
||||||
|
3c503 and anonymous FTP space.
|
||||||
|
|
||||||
|
ne.c
|
||||||
|
o You'll need to #define EI_8BIT if you are using an 8-bit ethercard
|
||||||
|
such as the NE1000 or Cabletron E10xx. I might someday make a run-time
|
||||||
|
selection between the NE1000 and NE2000 (right now it screws up the
|
||||||
|
non-default bus-width) but I don't know how to distinguish the Cabletron
|
||||||
|
ethercards. I'm hoping to find a general way to identify the board's bus
|
||||||
|
width, but don't hold your breath.
|
||||||
|
|
||||||
|
wd.c
|
||||||
|
o Thanks to Russ Nelson (nelson@crynwr.com) for loaning me a WD8013.
|
||||||
|
o The 8013 doesn't work if it's probed by some other driver first.
|
||||||
|
Make it either first in the probe list or the only driver you compile in.
|
||||||
|
Maybe I should reset the board before looking at it to fix the probe problem.
|
||||||
|
o You machine may fail to do a soft reboot if the packet buffer shared memory is
|
||||||
|
mapped in -- the machine might think its a boot PROM (since it intentionally
|
||||||
|
shares the same memory space as the optional on-board boot PROM).
|
||||||
|
|
||||||
|
hp.c
|
||||||
|
o This has only been tested with a 27245A, 27247A, and 27250. It doesn't yet
|
||||||
|
work with the new 27247B!
|
||||||
|
o Thanks to Chance Reschke at USRA <creschke@usra.edu> for loaning me the
|
||||||
|
27247A ethercard.
|
||||||
|
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
|
||||||
|
The "bogus packet header" is a bug in the 8390. I drop all of
|
||||||
|
the packets in the ring (usually just one) when this happens.
|
||||||
|
If you get this more than just occasionally please report it to me.
|
||||||
|
|
||||||
|
I don't yet distinguish between the problems with IRQ conflicts and
|
||||||
|
problems with the ethernet cable not being connected. If you get
|
||||||
|
a "transmit timed out" message check both! Your ethercard
|
||||||
|
probably will NOT work without proper termination.
|
||||||
|
|
||||||
|
A small number of people report continuous "RX transfer address mismatch"
|
||||||
|
errors with the NE2000. This is a bug in the 8390, and the reason
|
||||||
|
most designers use shared memory or design their own packet buffer access
|
||||||
|
circuitry rather than use what's provided by the 8390. An occasional
|
||||||
|
"mismatch" message (say, once a week) won't impact performance.
|
||||||
|
|
||||||
|
I've fixed most of the spurious "receiver overrun" messages. It
|
||||||
|
was a bug in the 8390 -- the overrun flag is sometimes set
|
||||||
|
when the boundary pointer is written. If you still get
|
||||||
|
isolated overrun errors please send me email.
|
||||||
|
|
||||||
|
The 3c501 driver isn't complete. This card is severely brain-damaged
|
||||||
|
anyway -- you won't notice the performance difference between working
|
||||||
|
and non-working versions anyway. If this source code is here, it is
|
||||||
|
only provided for a few people that wanted to write code for their 3c501
|
||||||
|
cards. Don't send me email about it unless it's a bug fix.
|
||||||
|
|
||||||
134
net/tcpip/newether/Space.c
Normal file
134
net/tcpip/newether/Space.c
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/* Space.c */
|
||||||
|
/* Holds initial configuration information for devices. */
|
||||||
|
#include "dev.h"
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/config.h>
|
||||||
|
|
||||||
|
#define NEXT_DEV NULL
|
||||||
|
|
||||||
|
#ifdef ETHERLINK1
|
||||||
|
extern int etherlink_init(struct device *);
|
||||||
|
|
||||||
|
#ifndef ETHERLINK1_IRQ
|
||||||
|
#define ETHERLINK1_IRQ 9
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct device el_dev = {
|
||||||
|
"if3c501",
|
||||||
|
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
|
||||||
|
ETHERLINK1, ETHERLINK1_IRQ, /* base i/o address, irq. */
|
||||||
|
0,0,0,0,0,
|
||||||
|
NEXT_DEV,
|
||||||
|
etherlink_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (&el_dev)
|
||||||
|
|
||||||
|
#endif /* ETHERLINK1 */
|
||||||
|
|
||||||
|
#if defined(EI8390) || defined(EL2) || defined(NE2000) \
|
||||||
|
|| defined(WD80x3) || defined(HPLAN)
|
||||||
|
extern int ethif_init(struct device *);
|
||||||
|
|
||||||
|
#ifndef EI8390_IRQ
|
||||||
|
#define EI8390_IRQ 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct device ei8390_dev = {
|
||||||
|
#ifdef EI_NAME
|
||||||
|
EI_NAME,
|
||||||
|
#else
|
||||||
|
"eth_if",
|
||||||
|
#endif
|
||||||
|
0, 0, 0, 0, /* memory rx_end, rx_start, end, start are autoconfiged. */
|
||||||
|
EI8390, EI8390_IRQ, 0,0,0,0,0, /* base i/o address, irq, and flags. */
|
||||||
|
NEXT_DEV,
|
||||||
|
ethif_init, 0, {NULL}, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||||
|
NULL, NULL, NULL, NULL, 0, 0, 0, {0,}, {0,}, 0
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (&ei8390_dev)
|
||||||
|
|
||||||
|
#endif /* The EI8390 drivers. */
|
||||||
|
|
||||||
|
#ifdef WD8003
|
||||||
|
extern int wd8003_init(struct device *);
|
||||||
|
|
||||||
|
static struct device wd8003_dev =
|
||||||
|
{
|
||||||
|
"eth0",
|
||||||
|
0xd2000, /* recv memory end. */
|
||||||
|
0xd0600, /* recv memory start. */
|
||||||
|
0xd2000, /* memory end. */
|
||||||
|
0xd0000, /* memory start. */
|
||||||
|
0x280, /* base i/o address. */
|
||||||
|
5, /* irq */
|
||||||
|
0,0,0,0,0, /* flags */
|
||||||
|
NEXT_DEV,
|
||||||
|
wd8003_init,
|
||||||
|
/* wd8003_init should set up the rest. */
|
||||||
|
0, /* trans start. */
|
||||||
|
{NULL}, /* buffs */
|
||||||
|
NULL, /* backlog */
|
||||||
|
NULL, /* open */
|
||||||
|
NULL, /* stop */
|
||||||
|
NULL, /* hard_start_xmit */
|
||||||
|
NULL, /* hard_header */
|
||||||
|
NULL, /* add arp */
|
||||||
|
NULL, /* queue xmit */
|
||||||
|
NULL, /* rebuild header */
|
||||||
|
NULL, /* type_trans */
|
||||||
|
NULL, /* send_packet */
|
||||||
|
NULL, /* private */
|
||||||
|
0, /* type. */
|
||||||
|
0, /* hard_header_len */
|
||||||
|
0, /* mtu */
|
||||||
|
{0,}, /* broadcast address */
|
||||||
|
{0,}, /* device address */
|
||||||
|
0 /* addr len */
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (&wd8003_dev)
|
||||||
|
|
||||||
|
#endif /* WD8003 */
|
||||||
|
|
||||||
|
extern int loopback_init(struct device *dev);
|
||||||
|
|
||||||
|
static struct device loopback_dev =
|
||||||
|
{
|
||||||
|
"loopback",
|
||||||
|
-1, /* recv memory end. */
|
||||||
|
0x0, /* recv memory start. */
|
||||||
|
-1, /* memory end. */
|
||||||
|
0, /* memory start. */
|
||||||
|
0, /* base i/o address. */
|
||||||
|
0, /* irq */
|
||||||
|
0,0,1,0,0, /* flags */
|
||||||
|
NEXT_DEV, /* next device */
|
||||||
|
loopback_init,
|
||||||
|
/* loopback_init should set up the rest. */
|
||||||
|
0, /* trans start. */
|
||||||
|
{NULL}, /* buffs */
|
||||||
|
NULL, /* backlog */
|
||||||
|
NULL, /* open */
|
||||||
|
NULL, /* stop */
|
||||||
|
NULL, /* hard_start_xmit */
|
||||||
|
NULL, /* hard_header */
|
||||||
|
NULL, /* add arp */
|
||||||
|
NULL, /* queue xmit */
|
||||||
|
NULL, /* rebuild header */
|
||||||
|
NULL, /* type_trans */
|
||||||
|
NULL, /* send_packet */
|
||||||
|
NULL, /* private */
|
||||||
|
0, /* type. */
|
||||||
|
0, /* hard_header_len */
|
||||||
|
0, /* mtu */
|
||||||
|
{0,}, /* broadcast address */
|
||||||
|
{0,}, /* device address */
|
||||||
|
0 /* addr len */
|
||||||
|
};
|
||||||
|
#undef NEXT_DEV
|
||||||
|
#define NEXT_DEV (error no next device &loopback_dev)
|
||||||
|
|
||||||
|
struct device *dev_base = &loopback_dev;
|
||||||
100
net/tcpip/newether/auto_irq.c
Normal file
100
net/tcpip/newether/auto_irq.c
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
/* auto_irq.c: Auto-configure IRQ lines for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
|
||||||
|
This code is a general-purpose IRQ line detector for devices with
|
||||||
|
jumpered IRQ lines. If you can make the device raise an IRQ (and
|
||||||
|
that IRQ line isn't already being used), these routines will tell
|
||||||
|
you what IRQ line it's using -- perfect for those oh-so-cool boot-time
|
||||||
|
device probes!
|
||||||
|
|
||||||
|
To use this, first call autoirq_setup(timeout). TIMEOUT is how many
|
||||||
|
'jiffies' (1/18 sec.) to detect other devices that have active IRQ lines,
|
||||||
|
and can usually be zero at boot.
|
||||||
|
Next, set up your device to trigger an interrupt.
|
||||||
|
Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
|
||||||
|
most recently active. The TIMEOUT should usually be zero, but may
|
||||||
|
be set to the number of jiffies to wait for an active IRQ.
|
||||||
|
|
||||||
|
The idea of using the setup timeout to filter out bogus IRQs came from
|
||||||
|
the serial driver.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef version
|
||||||
|
static char *version="auto_irq.c:v0.01 1993 Donald Becker (becker@super.org)";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*#include <linux/config.h>*/
|
||||||
|
/*#include <linux/kernel.h>*/
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/bitops.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
/*#include <asm/system.h>*/
|
||||||
|
|
||||||
|
static volatile int irq_number; /* The latest irq number we actually found. */
|
||||||
|
static volatile int irq_bitmap; /* The irqs we actually found. */
|
||||||
|
static int irq_handled; /* The irq lines we have a handler on. */
|
||||||
|
|
||||||
|
static void autoirq_probe(int irq)
|
||||||
|
{
|
||||||
|
irq_number = irq;
|
||||||
|
set_bit(irq, (void *)&irq_bitmap);
|
||||||
|
/*irq_bitmap |= 1 << irq;*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct sigaction autoirq_sigaction = { autoirq_probe, 0, SA_INTERRUPT, NULL};
|
||||||
|
|
||||||
|
void autoirq_setup(int waittime)
|
||||||
|
{
|
||||||
|
int i, mask;
|
||||||
|
int timeout = jiffies+waittime;
|
||||||
|
|
||||||
|
irq_number = 0;
|
||||||
|
irq_bitmap = 0;
|
||||||
|
irq_handled = 0;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (!irqaction(i, &autoirq_sigaction))
|
||||||
|
set_bit(i, (void *)&irq_handled); /* irq_handled |= 1 << i;*/
|
||||||
|
}
|
||||||
|
/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
|
||||||
|
while (timeout >= jiffies)
|
||||||
|
;
|
||||||
|
|
||||||
|
for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
|
||||||
|
if (irq_bitmap & irq_handled & mask) {
|
||||||
|
irq_handled &= ~mask;
|
||||||
|
printk(" Spurious interrupt on IRQ %d\n", i);
|
||||||
|
free_irq(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int autoirq_report(int waittime)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int timeout = jiffies+waittime;
|
||||||
|
|
||||||
|
/* Hang out at least <waittime> jiffies waiting for the IRQ. */
|
||||||
|
while (timeout >= jiffies)
|
||||||
|
if (irq_number)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Retract the irq handlers that we installed. */
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
if (test_bit(i, (void *)&irq_handled))
|
||||||
|
free_irq(i);
|
||||||
|
}
|
||||||
|
return irq_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c auto_irq.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
312
net/tcpip/newether/hp.c
Normal file
312
net/tcpip/newether/hp.c
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/* hp.c: A HP LAN ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This is a driver for the HP LAN adaptors.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version = "hp.c:v0.28 1/28/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||||
|
#define port_read(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define port_read_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
|
||||||
|
#define HP_ID 0x07
|
||||||
|
#define HP_CONFIGURE 0x08 /* Configuration register. */
|
||||||
|
#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
|
||||||
|
#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
|
||||||
|
#define HP_DATAON 0x10 /* Turn on dataport */
|
||||||
|
#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
|
||||||
|
|
||||||
|
#define HP_START_PG 0x00 /* First page of TX buffer */
|
||||||
|
#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
|
||||||
|
#define HP_16BSTOP_PG 0xFF /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int hpprobe(int ioaddr, struct device *dev);
|
||||||
|
int hpprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void hp_reset_8390(struct device *dev);
|
||||||
|
static int hp_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void hp_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static void hp_init_card(struct device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
/* Probe for an HP LAN adaptor.
|
||||||
|
Also initialize the card and fill in STATION_ADDR with the station
|
||||||
|
address. */
|
||||||
|
|
||||||
|
int hpprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return hpprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && hpprobe1(*port, dev))
|
||||||
|
return dev->base_addr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hpprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
unsigned char SA_prom[6];
|
||||||
|
int tmp;
|
||||||
|
int hplan;
|
||||||
|
|
||||||
|
printk("HP-LAN ethercard probe at %#3x:", ioaddr);
|
||||||
|
tmp = inb_p(ioaddr);
|
||||||
|
if (tmp == 0xFF) {
|
||||||
|
printk(" not found (nothing there).\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < sizeof(SA_prom); i++) {
|
||||||
|
SA_prom[i] = inb(ioaddr + i);
|
||||||
|
if (i < ETHER_ADDR_LEN && station_addr) {
|
||||||
|
printk(" %2.2x", SA_prom[i]);
|
||||||
|
station_addr[i] = SA_prom[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hplan = (SA_prom[0] == 0x08 && SA_prom[1] == 0x00 && SA_prom[2] == 0x09);
|
||||||
|
if (hplan == 0) {
|
||||||
|
printk(" not found (invalid station address prefix).\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_status.tx_start_page = HP_START_PG;
|
||||||
|
ei_status.rx_start_page = HP_START_PG + TX_PAGES;
|
||||||
|
/* Set up the rest of the parameters. */
|
||||||
|
if ((tmp = inb_p(HP_ID)) & 0x80) {
|
||||||
|
ei_status.name = "HP27247";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
ei_status.stop_page = HP_16BSTOP_PG; /* Safe for now */
|
||||||
|
} else {
|
||||||
|
ei_status.name = "HP27250";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
ei_status.stop_page = HP_8BSTOP_PG; /* Safe for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the base address to point to the NIC! */
|
||||||
|
dev->base_addr = ioaddr + NIC_OFFSET;
|
||||||
|
|
||||||
|
if (dev->irq == 2)
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int *irq, irq_list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
|
||||||
|
for (irq = irq_list; *irq; irq++) {
|
||||||
|
if (irqaction(*irq, &ei_sigaction) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*irq == 0) {
|
||||||
|
printk (" unable to get an IRQ.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dev->irq = *irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(" %s found, using IRQ %d.\n", ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
ei_status.reset_8390 = &hp_reset_8390;
|
||||||
|
ei_status.block_input = &hp_block_input;
|
||||||
|
ei_status.block_output = &hp_block_output;
|
||||||
|
hp_init_card(dev);
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hp_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int hp_base = dev->base_addr - NIC_OFFSET;
|
||||||
|
int saved_config = inb_p(hp_base + HP_CONFIGURE);
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
if (ei_debug > 1) printk("resetting the 8390 time=%d...", jiffies);
|
||||||
|
outb_p(0x00, hp_base + HP_CONFIGURE);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb_p(saved_config, hp_base + HP_CONFIGURE);
|
||||||
|
while ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk(EI_NAME": hp_reset_8390() did not complete.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ei_debug > 1) printk("8390 reset done.", jiffies);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||||
|
porting to a new ethercard look at the packet driver source for hints.
|
||||||
|
The HP LAN doesn't use shared memory -- we put the packet
|
||||||
|
out through the "remote DMA" dataport. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
hp_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
int xfer_count = count;
|
||||||
|
|
||||||
|
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, nic_base);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_read(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
|
||||||
|
if (count & 0x01)
|
||||||
|
buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
|
||||||
|
} else {
|
||||||
|
/* Input the bytes with a slow 8-bit loop. Someday change this to:
|
||||||
|
port_read_b(nic_base - NIC_OFFSET + HP_DATA_PORT,buf, count); */
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
buf[i] = inb_p(nic_base - NIC_OFFSET + HP_DATAPORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
/* Check only the lower 8 bits so we can ignore ring wrap. */
|
||||||
|
if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
|
||||||
|
printk(EI_NAME": RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
|
||||||
|
ring_offset + xfer_count, addr);
|
||||||
|
}
|
||||||
|
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hp_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page)
|
||||||
|
{
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
/* Round the count up for word writes. Do we need to do this?
|
||||||
|
What effect will an odd byte count have on the 8390?
|
||||||
|
I should check someday. */
|
||||||
|
if (ei_status.word16 && (count & 0x01))
|
||||||
|
count++;
|
||||||
|
/* We should already be in page 0, but to be safe... */
|
||||||
|
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
|
||||||
|
|
||||||
|
#ifdef notdef
|
||||||
|
/* Handle the read-before-write bug the same way as the
|
||||||
|
Crynwr packet driver -- the NatSemi method doesn't work. */
|
||||||
|
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
/* Make certain that the dummy read has occured. */
|
||||||
|
inb_p(0x61);
|
||||||
|
inb_p(0x61);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||||
|
|
||||||
|
outb_p(E8390_RWRITE+E8390_START, nic_base);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
/* Use the 'rep' sequence for 16 bit boards. */
|
||||||
|
port_write(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
|
||||||
|
} else {
|
||||||
|
/* Output the bytes with a slow 8-bit loop. This actually is almost
|
||||||
|
as fast as possible, but it does tie up the processor. We could
|
||||||
|
also use a 'rep' sequnce here. */
|
||||||
|
for(i = 0; i < count; i++)
|
||||||
|
outb_p(buf[i], nic_base - NIC_OFFSET + HP_DATAPORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
|
||||||
|
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
if ((start_page << 8) + count != addr)
|
||||||
|
printk(EI_NAME": TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
|
||||||
|
(start_page << 8) + count, addr);
|
||||||
|
}
|
||||||
|
outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static void
|
||||||
|
hp_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
int irq = dev->irq;
|
||||||
|
/* default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
|
||||||
|
static char irqval[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
outb_p(irqval[irq&0x0f] | HP_RUN,
|
||||||
|
dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c hp.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
372
net/tcpip/newether/ne.c
Normal file
372
net/tcpip/newether/ne.c
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1992,1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This driver should work with many 8390-based ethernet boards. Currently
|
||||||
|
it support the NE1000, NE2000 (and clones), and some Cabletron products.
|
||||||
|
8-bit ethercard support is enabled with #define EI_8BIT
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Routines for the NatSemi-based designs (NE[12]000). */
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"ne.c:v0.27 1/27/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
/* These should be in <asm/io.h> someday, borrowed from blk_drv/hd.c. */
|
||||||
|
#define port_read(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define port_read_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;insb"::"d" (port),"D" (buf),"c" (nr):"cx","di")
|
||||||
|
#define port_write_b(port,buf,nr) \
|
||||||
|
__asm__("cld;rep;outsb"::"d" (port),"S" (buf),"c" (nr):"cx","si")
|
||||||
|
|
||||||
|
#define EN_CMD (dev->base_addr)
|
||||||
|
#define NE_BASE (dev->base_addr)
|
||||||
|
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
|
||||||
|
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
|
||||||
|
|
||||||
|
#define NE1SM_START_PG 0x20 /* First page of TX buffer */
|
||||||
|
#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||||
|
#define NESM_START_PG 0x40 /* First page of TX buffer */
|
||||||
|
#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int neprobe(int ioaddr, struct device *dev);
|
||||||
|
static int neprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void ne_reset_8390(struct device *dev);
|
||||||
|
static int ne_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void ne_block_output(struct device *dev, const int count,
|
||||||
|
const unsigned char *buf, const int start_page);
|
||||||
|
static void ne_init_card(struct device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
/* Probe for the NE1000 and NE2000. NEx000-like boards have 0x57,0x57 in
|
||||||
|
bytes 0x0e,0x0f of the SAPROM, but if we read by 16 bit words the NE1000
|
||||||
|
appears to have 0x00, 0x42. */
|
||||||
|
/* Also initialize the card and fill in STATION_ADDR with the station
|
||||||
|
address. The station address (and other data) is stored in the
|
||||||
|
packet buffer memory space, 32 bytes starting at remote DMA address 0. */
|
||||||
|
|
||||||
|
int neprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x320, 0x340, 0x360, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return neprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && neprobe1(*port, dev))
|
||||||
|
return dev->base_addr = *port;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int neprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
unsigned char SA_prom[32];
|
||||||
|
int cmdreg;
|
||||||
|
int ne2000 = 0, ne1000 = 0, ctron = 0;
|
||||||
|
|
||||||
|
printk("8390 ethercard probe at %#3x:", ioaddr);
|
||||||
|
|
||||||
|
cmdreg = inb_p(ioaddr);
|
||||||
|
if (cmdreg == 0xFF) {
|
||||||
|
printk(" not found (%#2.2x).\n", cmdreg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Ooops, we must first initialize registers -- we can't just read the PROM
|
||||||
|
address right away. (Learned the hard way.) */
|
||||||
|
/* NS8390_init(eifdev, 0);*/
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, ioaddr);
|
||||||
|
#ifdef EI_8BIT
|
||||||
|
outb_p(0x48, ioaddr + EN0_DCFG); /* Set byte-wide for probe. */
|
||||||
|
#else
|
||||||
|
outb_p(0x49, ioaddr + EN0_DCFG); /* Set word-wide for probe. */
|
||||||
|
#endif
|
||||||
|
/* Even though we'll set them soon, we must clear them! */
|
||||||
|
outb_p(0x00, ioaddr + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, ioaddr + EN0_RCNTHI);
|
||||||
|
|
||||||
|
outb_p(0x00, ioaddr + EN0_IMR); /* Mask completion irq. */
|
||||||
|
outb_p(0xFF, ioaddr + EN0_ISR);
|
||||||
|
|
||||||
|
/* Set to monitor and loopback mode. */
|
||||||
|
outb_p(E8390_RXOFF, ioaddr + EN0_RXCR); /* 0x20 */
|
||||||
|
outb_p(E8390_TXOFF, ioaddr + EN0_TXCR); /* 0x02 */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef EI_8BIT
|
||||||
|
outb_p(sizeof(SA_prom), ioaddr + EN0_RCNTLO);
|
||||||
|
#else
|
||||||
|
/* Double count 0x20 words, the SA PROM is only byte wide. */
|
||||||
|
outb_p(2*sizeof(SA_prom), ioaddr + EN0_RCNTLO);
|
||||||
|
#endif
|
||||||
|
outb_p(0x00, ioaddr + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, ioaddr + EN0_RSARLO); /* DMA starting at 0x0000. */
|
||||||
|
outb_p(0x00, ioaddr + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, ioaddr);
|
||||||
|
for(i = 0; i < sizeof(SA_prom); i++) {
|
||||||
|
SA_prom[i] = inb_p(ioaddr + NE_DATAPORT);
|
||||||
|
if (i < ETHER_ADDR_LEN && station_addr) {
|
||||||
|
printk(" %2.2x", SA_prom[i]);
|
||||||
|
station_addr[i] = SA_prom[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef EI_8BIT
|
||||||
|
ne1000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
|
||||||
|
#else
|
||||||
|
ne2000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
|
||||||
|
ne1000 = (SA_prom[14] == 0x00 && SA_prom[15] == 0x42);
|
||||||
|
ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Set up the rest of the parameters. */
|
||||||
|
if (ne1000) {
|
||||||
|
ei_status.name = "NE1000";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
ei_status.tx_start_page = NE1SM_START_PG;
|
||||||
|
ei_status.rx_start_page = NE1SM_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = NE1SM_STOP_PG;
|
||||||
|
} else if (ne2000) {
|
||||||
|
ei_status.name = "NE2000";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
ei_status.tx_start_page = NESM_START_PG;
|
||||||
|
ei_status.rx_start_page = NESM_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = NESM_STOP_PG;
|
||||||
|
} else if (ctron) {
|
||||||
|
/* You'll have to set these yourself, but this info might be useful.
|
||||||
|
Cabletron packet buffer locations:
|
||||||
|
E1010 starts at 0x100 and ends at 0x2000.
|
||||||
|
E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
|
||||||
|
E2010 starts at 0x100 and ends at 0x4000.
|
||||||
|
E2010-x starts at 0x100 and ends at 0xffff. */
|
||||||
|
ei_status.name = "Cabletron";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
ei_status.tx_start_page = 0x01;
|
||||||
|
ei_status.rx_start_page = 0x01 + TX_PAGES;
|
||||||
|
#ifndef CTRON_MEMSIZE
|
||||||
|
#define CTRON_MEMSIZE 0x20 /* Extra safe... */
|
||||||
|
#endif
|
||||||
|
ei_status.stop_page = CTRON_MEMSIZE;
|
||||||
|
} else {
|
||||||
|
printk(" not found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->base_addr = ioaddr;
|
||||||
|
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
autoirq_setup(0);
|
||||||
|
outb_p(0x50, nic_base + EN0_IMR); /* Enable one interrupt. */
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */
|
||||||
|
outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
|
||||||
|
dev->irq = autoirq_report(0);
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk(" autoirq is %d", dev->irq);
|
||||||
|
} else if (dev->irq == 2)
|
||||||
|
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||||
|
or don't know which one to set. */
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||||
|
if (irqval) {
|
||||||
|
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(" %s found, using IRQ %d.\n", ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
ei_status.reset_8390 = &ne_reset_8390;
|
||||||
|
ei_status.block_input = &ne_block_input;
|
||||||
|
ei_status.block_output = &ne_block_output;
|
||||||
|
ne_init_card(dev);
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ne_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int tmp = inb_p(NE_BASE + NE_RESET);
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
if (ei_debug > 1) printk("resetting the 8390 t=%d...", jiffies);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb_p(tmp, NE_BASE + NE_RESET);
|
||||||
|
while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk(EI_NAME": ne_reset_8390() did not complete.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output, similar to the Crynwr packet driver. If you
|
||||||
|
porting to a new ethercard look at the packet driver source for hints.
|
||||||
|
The NEx000 doesn't have shared memory on the board -- put the packet
|
||||||
|
out through the "remote DMA" dataport. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
ne_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
int xfer_count = count;
|
||||||
|
int nic_base = NE_BASE;
|
||||||
|
|
||||||
|
outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, EN_CMD);
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
port_read(NE_BASE + NE_DATAPORT,buf,count>>1);
|
||||||
|
if (count & 0x01)
|
||||||
|
buf[count-1] = inb(NE_BASE + NE_DATAPORT), xfer_count++;
|
||||||
|
} else {
|
||||||
|
/* Input the bytes with a slow 8-bit loop. Someday change this to:
|
||||||
|
port_read_b(NE_BASE + NE_DATAPORT, buf, count); */
|
||||||
|
int i;
|
||||||
|
/* Input the bytes with a slow 8-bit loop. Tune this someday. */
|
||||||
|
for(i = 0; i < count; i++) {
|
||||||
|
buf[i] = inb_p(NE_BASE + NE_DATAPORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
/* Check only the lower 8 bits so we can ignore ring wrap. */
|
||||||
|
if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
|
||||||
|
printk(EI_NAME": RX transfer address mismatch, %#4.4x (should be) vs. %#4.4x (actual).\n",
|
||||||
|
ring_offset + xfer_count, addr);
|
||||||
|
}
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ne_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const int start_page)
|
||||||
|
{
|
||||||
|
int i, retries = 0;
|
||||||
|
int nic_base = NE_BASE;
|
||||||
|
|
||||||
|
/* Round the count up for word writes. Do we need to do this?
|
||||||
|
What effect will an odd byte count have on the 8390?
|
||||||
|
I should check someday. */
|
||||||
|
if (ei_status.word16 && (count & 0x01))
|
||||||
|
count++;
|
||||||
|
/* We should already be in page 0, but to be safe... */
|
||||||
|
outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, EN_CMD);
|
||||||
|
|
||||||
|
retry:
|
||||||
|
#if !defined(no_rw_bugfix) && 0
|
||||||
|
/* Handle the read-before-write bug the same way as the
|
||||||
|
Crynwr packet driver -- the NatSemi method doesn't work.
|
||||||
|
Actually this doesn't aways work either, but if you have
|
||||||
|
problems with your NEx000 this is better than nothing! */
|
||||||
|
outb_p(0x42, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x42, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, EN_CMD);
|
||||||
|
/* Make certain that the dummy read has occured. */
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
SLOW_DOWN_IO;
|
||||||
|
#endif /* no_rw_bugfix */
|
||||||
|
|
||||||
|
/* Now the normal output. */
|
||||||
|
outb_p(count & 0xff, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(count >> 8, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(0x00, nic_base + EN0_RSARLO);
|
||||||
|
outb_p(start_page, nic_base + EN0_RSARHI);
|
||||||
|
|
||||||
|
outb_p(E8390_RWRITE+E8390_START, EN_CMD);
|
||||||
|
if (ei_status.word16) {
|
||||||
|
/* Use the 'rep' sequence for 16 bit boards. */
|
||||||
|
port_write(NE_BASE + NE_DATAPORT, buf, count>>1);
|
||||||
|
} else {
|
||||||
|
/* Output the bytes with a slow 8-bit loop. Someday change this to:
|
||||||
|
port_write_b(NE_BASE + NE_DATAPORT, buf, count); */
|
||||||
|
/* Output the bytes with a slow 8-bit loop. This actually is almost
|
||||||
|
as fast as possible, but it does tie up the processor. We could
|
||||||
|
also use a 'rep' sequnce here. */
|
||||||
|
for(i = 0; i < count; i++)
|
||||||
|
outb_p(buf[i], NE_BASE + NE_DATAPORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
|
||||||
|
|
||||||
|
/* This is for the ALPHA version only, remove for later releases. */
|
||||||
|
if (ei_debug > 0) { /* DMA termination address check... */
|
||||||
|
int high = inb_p(nic_base + EN0_RSARHI);
|
||||||
|
int low = inb_p(nic_base + EN0_RSARLO);
|
||||||
|
int addr = (high << 8) + low;
|
||||||
|
if ((start_page << 8) + count != addr)
|
||||||
|
printk(EI_NAME": Packet buffer transfer address mismatch on TX, %#4.4x vs. %#4.4x.\n",
|
||||||
|
(start_page << 8) + count, addr);
|
||||||
|
if (retries++ == 0)
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static void
|
||||||
|
ne_init_card(struct device *dev)
|
||||||
|
{
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
352
net/tcpip/newether/wd.c
Normal file
352
net/tcpip/newether/wd.c
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
/* wd.c: A WD80x3 ethernet driver for linux. */
|
||||||
|
/*
|
||||||
|
Written 1993 by Donald Becker. This is alpha test code.
|
||||||
|
This is a extension to the Linux operating system, and is covered by
|
||||||
|
same Gnu Public License that covers that work.
|
||||||
|
|
||||||
|
This is a driver for the WD8003 and WD8013 ethercards.
|
||||||
|
|
||||||
|
The Author may be reached as becker@super.org or
|
||||||
|
C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
|
||||||
|
|
||||||
|
Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static char *version =
|
||||||
|
"wd.c:v0.28a 1/28/93 Donald Becker (becker@super.org)\n";
|
||||||
|
|
||||||
|
#include <linux/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
#include <memory.h>
|
||||||
|
|
||||||
|
#include "dev.h"
|
||||||
|
#include "8390.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern void NS8390_init(struct device *dev, int startp);
|
||||||
|
extern int ei_debug;
|
||||||
|
extern struct sigaction ei_sigaction;
|
||||||
|
extern struct ei_device ei_status;
|
||||||
|
|
||||||
|
int wdprobe(int ioaddr, struct device *dev);
|
||||||
|
int wdprobe1(int ioaddr, struct device *dev);
|
||||||
|
|
||||||
|
static void wd_reset_8390(struct device *dev);
|
||||||
|
static int wd_block_input(struct device *dev, int count,
|
||||||
|
char *buf, int ring_offset);
|
||||||
|
static void wd_block_output(struct device *dev, int count,
|
||||||
|
const unsigned char *buf, const start_page);
|
||||||
|
static int wd_close_card(struct device *dev);
|
||||||
|
|
||||||
|
|
||||||
|
/* The stop page doesn't use the whole packet buffer RAM for the
|
||||||
|
16 bit versions. This could be changed for the final version. */
|
||||||
|
#define WD_START_PG 0x00 /* First page of TX buffer */
|
||||||
|
#define WD_STOP_PG 0x40 /* Last page +1 of RX ring */
|
||||||
|
|
||||||
|
#define WD_CMDREG 0 /* Offset to ASIC command register. */
|
||||||
|
#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
|
||||||
|
#define WD_MEMENB 0x40 /* Enable the shared memory. */
|
||||||
|
#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
|
||||||
|
#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
|
||||||
|
#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
|
||||||
|
#define WD_NIC_OFFSET 16 /* Offset to the 8390 NIC from the base_addr. */
|
||||||
|
|
||||||
|
/* Probe for the WD8003 and WD8013. These cards have the station
|
||||||
|
address PROM at I/O ports <base>+8 to <base>+13, with a checksum
|
||||||
|
following. The routine also initializes the card and fills the
|
||||||
|
station address field. */
|
||||||
|
|
||||||
|
int wdprobe(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int *port, ports[] = {0x300, 0x280, 0};
|
||||||
|
|
||||||
|
if (ioaddr > 0x100)
|
||||||
|
return wdprobe1(ioaddr, dev);
|
||||||
|
|
||||||
|
for (port = &ports[0]; *port; port++)
|
||||||
|
if (inb_p(*port) != 0xff && wdprobe1(*port, dev))
|
||||||
|
return dev->base_addr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wdprobe1(int ioaddr, struct device *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *station_addr = dev->dev_addr;
|
||||||
|
int checksum = 0;
|
||||||
|
int bits16 = 0;
|
||||||
|
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||||
|
printk("WD80x3 ethercard at %#3x:", ioaddr);
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printk(" %2.2X", inb(ioaddr+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
printk("WD80x3 ethercard at %#3x:", ioaddr+i);
|
||||||
|
for (;i < 33; i++) {
|
||||||
|
printk(" %2.2X", inb(ioaddr+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
#endif
|
||||||
|
printk("WD80x3 ethercard probe at %#3x:", ioaddr);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
int inval = inb(ioaddr + 8 + i);
|
||||||
|
checksum += inval;
|
||||||
|
if (i < 6)
|
||||||
|
printk(" %2.2X", (station_addr[i] = inval));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((checksum & 0xff) != 0xFF) {
|
||||||
|
printk(" not found (%#2.2x).\n", checksum);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ei_status.name = "WD8003";
|
||||||
|
ei_status.word16 = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* This method of checking for a 16-bit board is borrowed from the
|
||||||
|
we.c driver. A simpler method is just to look in ASIC reg. 0x03.
|
||||||
|
I'm comparing the two method in alpha test to make certain they
|
||||||
|
return the same result. */
|
||||||
|
#ifndef FORCE_8BIT /* Same define as we.c. */
|
||||||
|
/* check for 16 bit board - it doesn't have register 0/8 aliasing */
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
int tmp;
|
||||||
|
if( inb_p(ioaddr+8+i) != inb_p(ioaddr+i) ){
|
||||||
|
tmp = inb_p(ioaddr+1); /* fiddle with 16bit bit */
|
||||||
|
outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
|
||||||
|
if ((tmp & 0x01) == (inb_p( ioaddr+1) & 0x01)) {
|
||||||
|
int asic_reg5 = inb(ioaddr+WD_CMDREG5);
|
||||||
|
bits16 = 1; /* use word mode of operation */
|
||||||
|
/* Magic to set ASIC to word-wide mode. */
|
||||||
|
outb( ISA16 | NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
|
||||||
|
outb(tmp, ioaddr+1);
|
||||||
|
ei_status.name = "WD8013";
|
||||||
|
ei_status.word16 = 1;
|
||||||
|
break; /* We have a 16bit board here! */
|
||||||
|
}
|
||||||
|
outb(tmp, ioaddr+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
bits8 = 1;
|
||||||
|
#endif /* FORCE_8BIT */
|
||||||
|
|
||||||
|
#ifndef final_version
|
||||||
|
if ((inb(ioaddr+1) & 0x01) != (ei_status.word16 & 0x01))
|
||||||
|
printk("\nWD80x3: Bus width conflict, %d (probe) != %d (reg report).\n",
|
||||||
|
ei_status.word16 ? 16:8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ei_status.tx_start_page = WD_START_PG;
|
||||||
|
ei_status.rx_start_page = WD_START_PG + TX_PAGES;
|
||||||
|
ei_status.stop_page = WD_STOP_PG;
|
||||||
|
|
||||||
|
#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
|
||||||
|
/* Allow an override for alpha testing. */
|
||||||
|
dev->mem_start = WD_SHMEM;
|
||||||
|
#else
|
||||||
|
if (dev->mem_start == 0) {
|
||||||
|
dev->mem_start = ((inb(ioaddr)&0x3f) << 13) +
|
||||||
|
(ei_status.word16 ? (inb(ioaddr+WD_CMDREG5)&0x1f)<<19 : 0x80000);
|
||||||
|
printk(" address %#x,", dev->mem_start);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
dev->rmem_start = dev->mem_start + TX_PAGES*256;
|
||||||
|
dev->mem_end = dev->rmem_end
|
||||||
|
= dev->mem_start + (WD_STOP_PG - WD_START_PG)*256;
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 3
|
||||||
|
memset((void*)dev->mem_start, 0x42424242, (WD_STOP_PG - WD_START_PG)*256);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The 8390 isn't at the base address -- the ASIC regs are there! */
|
||||||
|
dev->base_addr = ioaddr+WD_NIC_OFFSET;
|
||||||
|
|
||||||
|
if (dev->irq < 2) {
|
||||||
|
int nic_base = dev->base_addr;
|
||||||
|
outb(WD_RESET, ioaddr);
|
||||||
|
autoirq_setup(1);
|
||||||
|
outb_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
|
||||||
|
if (ei_status.word16)
|
||||||
|
outb_p( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
|
||||||
|
/* This doesn't reliably generate an interrupt! */
|
||||||
|
outb_p(0xff, nic_base + EN0_ISR); /* Ack. all intrs. */
|
||||||
|
outb_p(0x50, nic_base + EN0_IMR); /* Enable "DMA complete" interrupt. */
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTLO);
|
||||||
|
outb_p(0x00, nic_base + EN0_RCNTHI);
|
||||||
|
outb_p(E8390_RREAD+E8390_START, nic_base); /* Trigger it... */
|
||||||
|
outb_p(E8390_RWRITE+E8390_START, nic_base); /* Trigger it again... */
|
||||||
|
dev->irq = autoirq_report(1);
|
||||||
|
outb_p(0x00, nic_base + EN0_IMR); /* Mask it again. */
|
||||||
|
if (ei_debug > 2)
|
||||||
|
printk(" autoIRQ %d", dev->irq);
|
||||||
|
if (dev->irq == 0) {
|
||||||
|
printk(" autoIRQ failed, isr=%02x", inb(nic_base + EN0_ISR));
|
||||||
|
dev->irq = 10;
|
||||||
|
}
|
||||||
|
} else if (dev->irq == 2)
|
||||||
|
/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
|
||||||
|
or don't know which one to set. */
|
||||||
|
dev->irq = 9;
|
||||||
|
|
||||||
|
/* Snarf the interrupt now. There's no point in waiting since we cannot
|
||||||
|
share and the board will usually be enabled. */
|
||||||
|
{ int irqval = irqaction (dev->irq, &ei_sigaction);
|
||||||
|
if (irqval) {
|
||||||
|
printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(" %s found, using IRQ %d.\n", ei_status.name, dev->irq);
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk(version);
|
||||||
|
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk("%s: Address read from register is %#x, setting address %#x\n",
|
||||||
|
ei_status.name,
|
||||||
|
((inb(ioaddr+WD_CMDREG5)&0x1f)<<19) + ((inb(ioaddr)&0x3f) << 13),
|
||||||
|
dev->mem_start);
|
||||||
|
/* Map in the shared memory. This is a little risky, since using
|
||||||
|
the stuff the user supplied is probably a bad idea. */
|
||||||
|
outb_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), ioaddr); /* WD_CMDREG */
|
||||||
|
if (ei_status.word16)
|
||||||
|
outb_p( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), ioaddr+WD_CMDREG5);
|
||||||
|
|
||||||
|
ei_status.reset_8390 = &wd_reset_8390;
|
||||||
|
ei_status.block_input = &wd_block_input;
|
||||||
|
ei_status.block_output = &wd_block_output;
|
||||||
|
dev->stop = &wd_close_card;
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
|
||||||
|
return dev->base_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wd_reset_8390(struct device *dev)
|
||||||
|
{
|
||||||
|
int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
int reset_start_time = jiffies;
|
||||||
|
|
||||||
|
outb(WD_RESET, wd_cmd_port);
|
||||||
|
if (ei_debug > 1) printk("resetting the WD80x3 t=%d...", jiffies);
|
||||||
|
ei_status.txing = 0;
|
||||||
|
|
||||||
|
sti();
|
||||||
|
/* We shouldn't use the boguscount for timing, but this hasn't been
|
||||||
|
checked yet, and you could hang your machine if jiffies break... */
|
||||||
|
{
|
||||||
|
int boguscount = 150000;
|
||||||
|
while(jiffies - reset_start_time < 2)
|
||||||
|
if (boguscount-- < 0) {
|
||||||
|
printk("jiffy failure (t=%d)...", jiffies);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(0x00, wd_cmd_port);
|
||||||
|
while ((inb_p(dev->base_addr+EN0_ISR) & ENISR_RESET) == 0)
|
||||||
|
if (jiffies - reset_start_time > 2) {
|
||||||
|
printk(EI_NAME": wd_reset_8390() did not complete.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined(EI_DEBUG) && EI_DEBUG > 2
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printk("WD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||||
|
}
|
||||||
|
printk("\nWD80x3 ethercard at %#3x:", wd_cmd_port);
|
||||||
|
for (;i < 33; i++) {
|
||||||
|
printk(" %2.2X", inb(wd_cmd_port+i));
|
||||||
|
}
|
||||||
|
printk("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* Set up the ASIC registers, just in case something changed them. */
|
||||||
|
outb_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port); /* WD_CMDREG */
|
||||||
|
if (ei_status.word16)
|
||||||
|
outb_p( ISA16 | NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block input and output are easy on shared memory ethercards, and trivial
|
||||||
|
on the Western digital card where there is no choice of how to do it. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
wd_block_input(struct device *dev, int count, char *buf, int ring_offset)
|
||||||
|
{
|
||||||
|
void *xfer_start = (void *)(dev->mem_start + ring_offset - (WD_START_PG<<8));
|
||||||
|
#ifdef mapout
|
||||||
|
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
/* Map in the shared memory. */
|
||||||
|
outb_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmdreg);
|
||||||
|
#endif
|
||||||
|
if (xfer_start + count > (void*) dev->rmem_end) {
|
||||||
|
/* We must wrap the input move. */
|
||||||
|
int semi_count = (void*)dev->rmem_end - xfer_start;
|
||||||
|
memcpy(buf, xfer_start, semi_count);
|
||||||
|
count -= semi_count;
|
||||||
|
memcpy(buf + semi_count, (char *)dev->rmem_start, count);
|
||||||
|
return dev->rmem_start + count;
|
||||||
|
}
|
||||||
|
memcpy(buf, xfer_start, count);
|
||||||
|
if (ei_debug > 4) {
|
||||||
|
unsigned short *board = xfer_start;
|
||||||
|
printk("wd8013: wd_block_input(cnt=%d offset=%3x addr=%#x) = %2x %2x %2x...\n",
|
||||||
|
count, ring_offset, xfer_start, board[-1], board[0], board[1]);
|
||||||
|
}
|
||||||
|
#ifdef mapout
|
||||||
|
outb(0, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||||
|
#endif
|
||||||
|
return ring_offset + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This could only be outputting to the transmit buffer. The
|
||||||
|
ping-pong transmit setup doesn't work with this yet. */
|
||||||
|
static void
|
||||||
|
wd_block_output(struct device *dev, int count, const unsigned char *buf, int start_page)
|
||||||
|
{
|
||||||
|
unsigned char *shmem = (void *)dev->mem_start + ((start_page - WD_START_PG)<<8);
|
||||||
|
#ifdef mapout
|
||||||
|
int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
|
||||||
|
/* Map in the shared memory. */
|
||||||
|
outb_p((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmdreg);
|
||||||
|
#endif
|
||||||
|
memcpy(shmem, buf, count);
|
||||||
|
if (ei_debug > 4)
|
||||||
|
printk("wd8013: wd_block_output(addr=%#x cnt=%d) -> %2x=%2x %2x=%2x %d...\n",
|
||||||
|
shmem, count, shmem[23], buf[23], shmem[24], buf[24], memcmp(shmem,buf,count));
|
||||||
|
#ifdef mapout
|
||||||
|
outb(0, wd_cmdreg); /* WD_CMDREG: Map out the shared memory. */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function resets the ethercard if something screws up. */
|
||||||
|
static int
|
||||||
|
wd_close_card(struct device *dev)
|
||||||
|
{
|
||||||
|
if (ei_debug > 1)
|
||||||
|
printk("%s: shutting down ethercard.\n", ei_status.name);
|
||||||
|
NS8390_init(dev, 0);
|
||||||
|
/* Turn off the shared memory. */
|
||||||
|
outb_p((((dev->mem_start>>13) & 0x3f)),
|
||||||
|
dev->base_addr-WD_NIC_OFFSET); /* WD_CMDREG */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local variables:
|
||||||
|
* compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c wd.c"
|
||||||
|
* version-control: t
|
||||||
|
* kept-new-versions: 5
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
5
net/tcpip/ping.README
Normal file
5
net/tcpip/ping.README
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
This is a ping binary for Linux .98pl5 + the 3 tcp patches. It should also
|
||||||
|
work with any kernel > .98pl5. It needs to be installed suid root.
|
||||||
|
|
||||||
|
The source has a message saying it's public domain, but it also claims
|
||||||
|
to be Copyright by Berkeley with the standard Berkeley license.
|
||||||
116
net/tcpip/tcp5.diff
Normal file
116
net/tcpip/tcp5.diff
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
From owner-linux-activists@joker.cs.hut.fi Wed Aug 5 03:51:40 1992
|
||||||
|
Received: from funet.fi by lazy.qt.IPA.FhG.de with SMTP
|
||||||
|
(5.61+/IDA-1.2.8/gandalf.2) id AA03065; Wed, 5 Aug 92 03:51:37 +0200
|
||||||
|
Received: from santra.hut.fi by funet.fi with SMTP (PP) id <02221-0@funet.fi>;
|
||||||
|
Wed, 5 Aug 1992 04:50:24 +0300
|
||||||
|
Received: from joker.cs.hut.fi by santra.hut.fi (5.65c/8.0/TeKoLa) id AA17553;
|
||||||
|
Wed, 5 Aug 1992 04:50:12 +0300
|
||||||
|
Received: by joker.cs.hut.fi (5.65b/6.8/S-TeKoLa) id AA13438;
|
||||||
|
Wed, 5 Aug 92 04:49:56 +0259
|
||||||
|
Received: from sunlight.Stanford.EDU by joker.cs.hut.fi (5.65b/6.8/S-TeKoLa)
|
||||||
|
id AA13417; Wed, 5 Aug 92 04:49:12 +0300
|
||||||
|
Received: from elaine12.Stanford.EDU by sunlight.Stanford.EDU (4.1/AIR-1.0)
|
||||||
|
id AA18390; Tue, 4 Aug 92 18:49:17 PDT
|
||||||
|
From: bir7@leland.stanford.edu
|
||||||
|
Message-Id: <9208050149.AA18390@sunlight.Stanford.EDU>
|
||||||
|
Subject: Re: tcp/ip diffs
|
||||||
|
Sender: owner-linux-activists@niksula.hut.fi
|
||||||
|
To: linux-activists@niksula.hut.fi
|
||||||
|
X-Note1: Remember to put 'X-Mn-Key: normal' to your mail body or header
|
||||||
|
Date: Tue, 4 Aug 92 18:49:17 PDT
|
||||||
|
Cc: linux-activists@joker.cs.hut.fi (Linux Activists), arl@cs.hut.fi
|
||||||
|
In-Reply-To: <9208050130.AA02024@klaava.Helsinki.FI>; from "Linus Torvalds" at Aug 5, 92 4:30 am
|
||||||
|
X-Mailer: ELM [version 2.3 PL11]
|
||||||
|
|
||||||
|
X-Mn-Key: NET
|
||||||
|
>
|
||||||
|
> bir7@leland.stanford.edu: "tcp/ip diffs" (Aug 4, 16:44):
|
||||||
|
> > Here are diffs against .97 (+ profile patches) that are required for
|
||||||
|
> > all the tcp/ip code. It includes 4 files, one for vhangup, one for
|
||||||
|
> > the tty's, one for fcntl, and one for the net directory. I would
|
||||||
|
> > appretiate it if you would include all these diffs in the next
|
||||||
|
> > release.
|
||||||
|
>
|
||||||
|
> Your next message indicated there were problems with the patch, so I
|
||||||
|
> won't put it in yet. Other than that, I think I'm ready for the tcp/ip
|
||||||
|
> code.
|
||||||
|
Some diffs to fix the problem are at the end of the message.
|
||||||
|
For the net/tcp directory, you should probably get it from nic.funet.fi
|
||||||
|
Linux/testing/beta/NET, as it's very big.
|
||||||
|
>
|
||||||
|
> One thing I've been wondering about: does the tcp/ip code use 386bsd
|
||||||
|
> code heavily? I've had a few people asking about it, as they don't like
|
||||||
|
> the USL suit against BSDI. I'd rather be NET/2 free (which the current
|
||||||
|
> kernel should be), although I personally think the USL suit will fail
|
||||||
|
> (but it may take some time)
|
||||||
|
>
|
||||||
|
All the clients and servers are from BIND. The only kernel
|
||||||
|
code that is remotely related to anything is the wd8003 driver. I used
|
||||||
|
the 386bsd driver as an example when I wrote the code. But basically
|
||||||
|
everything is different. The basic tcp/ip code was written completely
|
||||||
|
by me. The only thing I've gotten from anyone else is some icmp code
|
||||||
|
which is probably not from net2, as it has to interface with everything
|
||||||
|
I did which is not remotely like anything in net2 (Actually I don't know
|
||||||
|
this since I haven't look at the net2 code, but the way the tcp/ip code
|
||||||
|
evolved it couldn't be like anything else.)
|
||||||
|
|
||||||
|
Everyone Please note at the end of this message are some diffs which must
|
||||||
|
be applied for unix domain sockets to work (after applying my sock.diff
|
||||||
|
which screws them up.)
|
||||||
|
|
||||||
|
[Ari, Please add them to the ftp site either in the tar file or next to it.]
|
||||||
|
|
||||||
|
Ross Biro bir7@leland.stanford.edu
|
||||||
|
Member League for Programming Freedom (LPF)
|
||||||
|
mail league@prep.ai.mit.edu for information
|
||||||
|
|
||||||
|
ps. I'm using X right now, so the diffs work.
|
||||||
|
---- important diffs -----
|
||||||
|
*** ../linux/net/unix.c Tue Aug 4 12:26:38 1992
|
||||||
|
--- linux/net/unix.c Tue Aug 4 17:30:29 1992
|
||||||
|
***************
|
||||||
|
*** 431,441 ****
|
||||||
|
{
|
||||||
|
struct socket *clientsock;
|
||||||
|
|
||||||
|
PRINTK("unix_proto_accept: socket 0x%x accepted via socket 0x%x\n",
|
||||||
|
sock, newsock);
|
||||||
|
- unix_data_ref(UN_DATA(newsock->conn));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if there aren't any sockets awaiting connection, then wait for
|
||||||
|
* one, unless nonblocking
|
||||||
|
*/
|
||||||
|
--- 431,440 ----
|
||||||
|
***************
|
||||||
|
*** 446,456 ****
|
||||||
|
if (current->signal & ~current->blocked) {
|
||||||
|
PRINTK("sys_accept: sleep was interrupted\n");
|
||||||
|
return -ERESTARTSYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- UN_DATA(newsock)->peerupd = UN_DATA(newsock->conn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* great. finish the connection relative to server and client,
|
||||||
|
* wake up the client and return the new fd to the server
|
||||||
|
*/
|
||||||
|
--- 445,454 ----
|
||||||
|
***************
|
||||||
|
*** 459,468 ****
|
||||||
|
--- 457,470 ----
|
||||||
|
newsock->conn = clientsock;
|
||||||
|
clientsock->conn = newsock;
|
||||||
|
clientsock->state = SS_CONNECTED;
|
||||||
|
newsock->state = SS_CONNECTED;
|
||||||
|
wake_up(clientsock->wait);
|
||||||
|
+
|
||||||
|
+ unix_data_ref (UN_DATA(newsock->conn));
|
||||||
|
+ UN_DATA(newsock)->peerupd = UN_DATA(newsock->conn);
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gets the current name or the name of the connected socket.
|
||||||
|
|
||||||
82
net/tcpip/tcp5.patch
Normal file
82
net/tcpip/tcp5.patch
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
From owner-linux-activists@joker.cs.hut.fi Wed Aug 5 19:54:00 1992
|
||||||
|
Received: from funet.fi by lazy.qt.IPA.FhG.de with SMTP
|
||||||
|
(5.61+/IDA-1.2.8/gandalf.2) id AA03798; Wed, 5 Aug 92 19:53:55 +0200
|
||||||
|
Received: from santra.hut.fi by funet.fi with SMTP (PP) id <12734-0@funet.fi>;
|
||||||
|
Wed, 5 Aug 1992 20:44:54 +0300
|
||||||
|
Received: from joker.cs.hut.fi by santra.hut.fi (5.65c/8.0/TeKoLa) id AA23193;
|
||||||
|
Wed, 5 Aug 1992 20:44:44 +0300
|
||||||
|
Received: by joker.cs.hut.fi (5.65b/6.8/S-TeKoLa) id AA23011;
|
||||||
|
Wed, 5 Aug 92 20:44:27 +0300
|
||||||
|
Received: from sunlight.Stanford.EDU by joker.cs.hut.fi (5.65b/6.8/S-TeKoLa)
|
||||||
|
id AA23002; Wed, 5 Aug 92 20:43:45 +0300
|
||||||
|
Received: from elaine29.Stanford.EDU by sunlight.Stanford.EDU (4.1/AIR-1.0)
|
||||||
|
id AA23764; Wed, 5 Aug 92 10:43:45 PDT
|
||||||
|
From: bir7@leland.stanford.edu
|
||||||
|
Message-Id: <9208051743.AA23764@sunlight.Stanford.EDU>
|
||||||
|
Subject: Re: No Problem with tcpip diffs - alpha 5
|
||||||
|
Sender: owner-linux-activists@niksula.hut.fi
|
||||||
|
To: linux-activists@niksula.hut.fi
|
||||||
|
X-Note1: Remember to put 'X-Mn-Key: normal' to your mail body or header
|
||||||
|
Date: Wed, 5 Aug 92 10:43:45 PDT
|
||||||
|
Cc: linux-activists@joker.cs.hut.fi (Linux Activists),
|
||||||
|
torvalds@kruuna.helsinki.fi (Linus Torvalds)
|
||||||
|
In-Reply-To: <9208051731.AA11359@sparta.com>; from "Robert T. Harris" at Aug 5, 92 1:31 pm
|
||||||
|
X-Mailer: ELM [version 2.3 PL11]
|
||||||
|
|
||||||
|
X-Mn-Key: NET
|
||||||
|
|
||||||
|
> When you are wrong - you are wrong - I was wrong - sorry!
|
||||||
|
>
|
||||||
|
> I posted a message describing problems with the tcpip alpha 5 release diffs,
|
||||||
|
> my problem turned out to be trying to patch using the alpha 4 release diffs. I
|
||||||
|
> left the old alpha release stuff in /usr/src/inet and thought that it had come
|
||||||
|
> from alpha 5 - I wondered why I had a duplicate set of diffs at /usr/src!
|
||||||
|
>
|
||||||
|
|
||||||
|
Actually I think there may be problems with the diffs (Coming from
|
||||||
|
me you should expect that.) They may have part of the profiling code in
|
||||||
|
them. It seems that they are against a stock .97 kernel without the
|
||||||
|
profiling code, but the "new" kernel had the profiling code in. If
|
||||||
|
something strange happens, it might be do to that. Now on to the bugs.
|
||||||
|
|
||||||
|
First You will only be able to connect via the loopback interface
|
||||||
|
for 2 reasons.
|
||||||
|
|
||||||
|
1) in line 107 of sock.h in the definition of struct sock:
|
||||||
|
unsigned long retransmits should be long retransmits
|
||||||
|
(connect sets it to negative to give more time to make the
|
||||||
|
connection.)
|
||||||
|
|
||||||
|
2) ip.c: ip_compute_csum
|
||||||
|
unsigned short sum = 0;
|
||||||
|
should be
|
||||||
|
unsigned long sum = 0;
|
||||||
|
|
||||||
|
(around line 604)
|
||||||
|
shrl $16, %%eax
|
||||||
|
adcw %%ax, %%bx
|
||||||
|
adcw $0, %%bx
|
||||||
|
should be
|
||||||
|
shrl $16, %%eax
|
||||||
|
addw %%ax, %%bx <- changed the 'c' to a 'd'
|
||||||
|
adcw $0, %%bx
|
||||||
|
|
||||||
|
|
||||||
|
And (this one shouldn't matter.)
|
||||||
|
(around line 622)
|
||||||
|
lodsb
|
||||||
|
movb $0,%%al
|
||||||
|
should be
|
||||||
|
lodsb
|
||||||
|
movb $0, %%ah <-- change the 'l' to a 'h'
|
||||||
|
|
||||||
|
With this I can connect using other than the loopback interface.
|
||||||
|
(It's what I get for rushing and only testing it with itself when
|
||||||
|
I've made some major changes.)
|
||||||
|
|
||||||
|
I'm sorry for the inconvenience.
|
||||||
|
|
||||||
|
Ross Biro bir7@leland.stanford.edu
|
||||||
|
Member League for Programming Freedom (LPF)
|
||||||
|
mail league@prep.ai.mit.edu for information
|
||||||
|
|
||||||
BIN
net/tcpip/tcp8p1.tar.Z.uue
Normal file
BIN
net/tcpip/tcp8p1.tar.Z.uue
Normal file
Binary file not shown.
BIN
net/tcpip/tcp8p2-against-0.98p2.uue
Normal file
BIN
net/tcpip/tcp8p2-against-0.98p2.uue
Normal file
Binary file not shown.
2
net/tcpip/tcp98pl5-p3.README
Normal file
2
net/tcpip/tcp98pl5-p3.README
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
This is the third tcp patch to 0.98pl5. All three go in without any problem.
|
||||||
|
This one fixes raw sockets and a few other minor problems.
|
||||||
4
net/tcpip/tcpip.README
Normal file
4
net/tcpip/tcpip.README
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
tcpip.tar.Z contains a working config (and source) as well as
|
||||||
|
services protocols and the install script. Get the Linux TCP/IP
|
||||||
|
FAQ for more information on how to set things up.
|
||||||
|
|
||||||
23
net/tcpip/tcpip.iafa
Normal file
23
net/tcpip/tcpip.iafa
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
Package-Name: tcpip.8.tar.Z
|
||||||
|
|
||||||
|
Title: Linux tcpip configuration and support programs
|
||||||
|
|
||||||
|
Version: 1.0
|
||||||
|
|
||||||
|
Description: configuration program for Inet sockets for Linux
|
||||||
|
|
||||||
|
Author: Ross Biro bir7@leland.stanford.edu
|
||||||
|
|
||||||
|
Maintained-by: Author
|
||||||
|
|
||||||
|
Maintained-at: Check tsx-11.mit.edu and nic.funet.fi
|
||||||
|
|
||||||
|
Platforms: Linux .98pl5+(4 meg of ram may be needed.)
|
||||||
|
|
||||||
|
Copying-Policy: Copyrighted Liscensed under GPL
|
||||||
|
|
||||||
|
Keywords: tcp ip tcp/ip network ethernet sockets internet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
net/tcpip/tcpip.tar.Z.A5
Normal file
BIN
net/tcpip/tcpip.tar.Z.A5
Normal file
Binary file not shown.
BIN
net/tcpip/tcpip.tar.Z.A6
Normal file
BIN
net/tcpip/tcpip.tar.Z.A6
Normal file
Binary file not shown.
BIN
net/tcpip/tcpip.tar.Z.A7
Normal file
BIN
net/tcpip/tcpip.tar.Z.A7
Normal file
Binary file not shown.
BIN
net/tcpip/tcpip082-udp_rcv-patch
Normal file
BIN
net/tcpip/tcpip082-udp_rcv-patch
Normal file
Binary file not shown.
Reference in New Issue
Block a user