From 22d7dffa657b99d814718d6d6af2c05128f48a37 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:11:29 -0500 Subject: [PATCH] Import 2.0.31pre5 --- CREDITS | 22 +- Documentation/Changes | 8 +- Documentation/memory-tuning.txt | 48 + Documentation/networking/alias.txt | 73 +- Documentation/networking/ipx.txt | 19 + Documentation/networking/so_bindtodevice.txt | 69 + Documentation/networking/z8530drv.txt | 24 +- Documentation/specialix.txt | 334 +++ MAINTAINERS | 27 +- arch/i386/defconfig | 2 +- arch/i386/mm/init.c | 2 + drivers/block/loop.c | 35 +- drivers/char/Config.in | 4 + drivers/char/Makefile | 8 + drivers/char/README.scc | 26 +- drivers/char/cd1865.h | 263 ++ drivers/char/console.c | 19 + drivers/char/misc.c | 5 +- drivers/char/scc.c | 2815 ++++++++++++------ drivers/char/specialix.c | 2330 +++++++++++++++ drivers/char/specialix_io8.h | 146 + drivers/char/tty_io.c | 3 + drivers/net/3c509.c | 137 +- drivers/net/atp.c | 402 ++- drivers/net/atp.h | 24 +- drivers/net/de4x5.c | 1742 ++++++++--- drivers/net/de4x5.h | 329 +- drivers/net/eql.c | 71 +- drivers/net/ibmtr.c | 7 +- drivers/net/new_tunnel.c | 4 +- drivers/net/ppp.c | 20 +- drivers/pci/pci.c | 72 +- drivers/scsi/in2000.readme | 155 + drivers/sound/sequencer.c | 2 +- fs/binfmt_elf.c | 4 +- fs/isofs/inode.c | 28 + fs/isofs/namei.c | 12 +- include/asm-alpha/socket.h | 2 + include/asm-i386/pgtable.h | 6 +- include/asm-i386/socket.h | 2 + include/linux/if.h | 6 + include/linux/interrupt.h | 1 + include/linux/major.h | 3 + include/linux/net_alias.h | 70 +- include/linux/pci.h | 95 +- include/linux/proc_fs.h | 1 + include/linux/route.h | 1 + include/linux/scc.h | 199 +- include/linux/sysctl.h | 2 + include/linux/tty.h | 1 + include/net/ip.h | 3 + include/net/ipx.h | 2 + include/net/route.h | 20 +- include/net/sock.h | 2 + include/net/tcp.h | 20 +- init/main.c | 6 + kernel/fork.c | 1 + net/Config.in | 3 - net/core/dev.c | 17 +- net/core/net_alias.c | 219 +- net/core/sock.c | 38 +- net/core/sysctl_net_core.c | 10 + net/ipv4/arp.c | 4 +- net/ipv4/icmp.c | 17 +- net/ipv4/ip_alias.c | 2 +- net/ipv4/ip_forward.c | 4 +- net/ipv4/ip_fragment.c | 2 +- net/ipv4/ip_fw.c | 6 +- net/ipv4/ip_output.c | 80 +- net/ipv4/ip_sockglue.c | 5 +- net/ipv4/packet.c | 15 +- net/ipv4/rarp.c | 2 +- net/ipv4/route.c | 50 +- net/ipv4/sysctl_net_ipv4.c | 5 +- net/ipv4/tcp.c | 126 +- net/ipv4/tcp_input.c | 202 +- net/ipv4/tcp_output.c | 76 +- net/ipv4/tcp_timer.c | 4 +- net/ipv4/udp.c | 105 +- net/netsyms.c | 1 + 80 files changed, 8560 insertions(+), 2167 deletions(-) create mode 100644 Documentation/memory-tuning.txt create mode 100644 Documentation/networking/ipx.txt create mode 100644 Documentation/networking/so_bindtodevice.txt create mode 100644 Documentation/specialix.txt create mode 100644 drivers/char/cd1865.h create mode 100644 drivers/char/specialix.c create mode 100644 drivers/char/specialix_io8.h create mode 100644 drivers/scsi/in2000.readme diff --git a/CREDITS b/CREDITS index ad0d3271df61..9949277cbe65 100644 --- a/CREDITS +++ b/CREDITS @@ -240,7 +240,7 @@ S: USA N: Juan Jose Ciarlante E: jjciarla@raiz.uncu.edu.ar -E: juanjo@irriga.uncu.edu.ar +E: irriga@impsat1.com.ar D: Network driver alias support D: IP masq hashing and app modules S: Las Cuevas 2385 - Bo Guemes @@ -862,8 +862,9 @@ S: Australia N: Martin Mares E: mj@k332.feld.cvut.cz +W: http://atrey.karlin.mff.cuni.cz/~mj/ D: BIOS video mode handling code -D: Miscellaneous kernel fixes +D: Miscellaneous kernel fixes and hacks D: MOXA C-218 serial board driver D: BOOTP support S: Kankovskeho 1241 @@ -1124,6 +1125,12 @@ S: Rebmannsweg 34h S: 79539 Loerrach S: Germany +N: Joerg Reuter +E: jreuter@poboxes.com +E: dl1bke@db0pra.ampr.org (amateur radio) +W: http://www.rat.de/jr +D: Z8530 SCC driver and DAMA Slave for AX.25 + N: William E. Roadcap E: roadcapw@titus.org W: http://www.cfw.com/~roadcapw @@ -1178,14 +1185,14 @@ S: 00980 Helsinki S: Finland N: Eric Schenk -E: schenk@cs.toronto.edu +E: Eric.Schenk@dna.lth.se D: Random kernel debugging. D: SYSV Semaphore code rewrite. D: Network layer debugging. D: Dial on demand facility (diald). -S: 7 Borden Street -S: Toronto, Ontario -S: Canada M5S 2M8 +S: Dag Hammerskjolds v. 3E +S: S-226 64 LUND +S: Sweden N: Peter De Schrijver E: stud11@cc4.kuleuven.ac.be @@ -1524,8 +1531,9 @@ S: Fin-00150 Helsinki S: Finland N: Roger E. Wolff -E: wolff@dutecai.et.tudelft.nl +E: R.E.Wolff@BitWizard.nl D: Written kmalloc/kfree +D: Written Specialix IO8+ driver S: Oosterstraat 23 S: 2611 TT Delft S: The Netherlands diff --git a/Documentation/Changes b/Documentation/Changes index 9ccdff91c85d..19533d0cac49 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -295,7 +295,7 @@ Mount currently at release 2.5. Some may find, especially when using the loop or xiafs file system, NFS, or automounting, that they need to upgrade to the latest release of mount, available from -ftp://ftp.win.tue.nl/pub/linux/util/mount-2.5p.tar.gz. +ftp://ftp.win.tue.nl/pub/linux/util/mount-2.6g.tar.gz. Console ======= @@ -679,12 +679,14 @@ ftp://sunsite.unc.edu/pub/Linux/system/Daemons/gpm-1.10.tar.gz SysVinit utilities ================== -ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.64.tar.gz +ftp://sunsite.unc.edu/pub/Linux/system/daemons/init/sysvinit-2.64.tar.gz +or for the very latest: +ftp://ftp.debian.org/debian/unstable/source/base/sysvinit_2.71-1.tar.gz Util-linux ========== -ftp://sunsite.unc.edu/pub/Linux/system/Misc/util-linux-2.5.tar.gz +ftp://sunsite.unc.edu/pub/Linux/system/misc/util-linux-2.6.tar.gz Mtools ====== diff --git a/Documentation/memory-tuning.txt b/Documentation/memory-tuning.txt new file mode 100644 index 000000000000..04c67d650e90 --- /dev/null +++ b/Documentation/memory-tuning.txt @@ -0,0 +1,48 @@ +There are several files in /proc/sys/vm you can use to tune the +memory system with. + +You inspect them with 'cat', and set them with 'echo'. For example, +/proc/sys/vm/freepages: + +'# cat /proc/sys/vm/freepages' may yield: +64 96 128 + +These three numbers are: min_free_pages, free_pages_low and +free_pages_high. + +You can adjust these with a command such as: + +# echo 128 256 512 > /proc/sys/vm/freepages + +Free memory never goes down below min_free_pages except for atomic +allocation. Background swapping is started if the number of free +pages falls below free_pages_high, and intensive swapping is started +below free_pages_low. A "page" is 4 kB. + +The values selected as boot defaults are the following: For a +machine with n>=8 Megabytes of memory, set min_free_pages = n*2, +free_pages_low = n*3 and free_pages_high = n*4. Machines with less +than 8 Megabytes or less as if they had 8 Megabytes. + +If "out of memory" errors sometimes occur, or if your machine does lots +of networking, increasing min_free_pages to 64 or more may be a good +idea. + +free_pages_low should probably be about double of min_free_pages. + +After a period of inactivity, the difference between free_pages_high and +free_pages low is immediately available for any program you want to +start up, without any need to swap out anything else. If your memory +is large enough (e.g. > 16 Meg), keeping 2 or 3 megabytes of memory +ready for this purpose is probably a good idea. + +I've found that + +# echo 128 256 1024 > /proc/sys/vm/freepages + +gives good performance for a 32 Meg system used as a small server and +personal workstation. + +The other three files in /proc/sys/vm are undocumented, as yet. + +Thomas Koenig, ig25@rz.uni-karlsruhe.de diff --git a/Documentation/networking/alias.txt b/Documentation/networking/alias.txt index 6b742170ab25..4889c683bf66 100644 --- a/Documentation/networking/alias.txt +++ b/Documentation/networking/alias.txt @@ -1,9 +1,11 @@ -NET_ALIAS device aliasing v0.4x +NET_ALIAS device aliasing v0.5x =============================== The main step taken in versions 0.40+ is the implementation of a device aliasing mechanism that creates *actual* devices. This development includes NET_ALIAS (generic aliasing) plus IP_ALIAS (specific IP) support. + From version 0.50, dynamic configuration of max alias per device and + tx/rx stats for aliases added. Features -------- @@ -13,6 +15,10 @@ o AF_INET optimized o hashed alias address lookup o net_alias_type objs registration/unreg., module-ables. o /proc/net/aliases & /proc/net/alias_types entries +o /proc/sys/net/core/net_alias_max entry (affects hash table size + also) +o tx/rx stats + o IP alias implementation: static or runtime module. @@ -23,10 +29,14 @@ Usage (IP aliasing) # cat /proc/net/alias* For IP aliasing you must have IP_ALIAS support included by - static linking ('y' to 2nd question above), or runtime module - insertion ('m' to 2nd q. above): - # insmod /usr/src/linux/modules/ip_alias.o (1.3.xx) - # insmod /usr/src/ip_alias/ip_alias.o (1.2.xx) see above. + static linking ('y' to CONFIG_IP_ALIAS? question), or runtime module + insertion ('m'): + # insmod /usr/src/linux/modules/ip_alias.o (2.0.xx) or + # modprobe ip_alias.o + + Also, dynamic loading is supported (kerneld). + You should have the following line in /etc/conf.modules: + alias net_alias-2 ip_alias o Alias creation. Alias creation is done by 'magic' iface naming: eg. to create a @@ -49,11 +59,11 @@ o Alias deletion. alias device is closed before deletion, so all network stuff that points to it (routes, arp entries, ...) will be released. -Alias (re-)configuring +o Alias (re-)configuring Aliases *are* devices, so you configure and refer to them as usual (ifconfig, route, etc). -o Procfs entries +o PROCfs entries 2 entries are added to help fetching alias runtime configuration: a) /proc/net/alias_types Will show you alias_types registered (ie. address families that @@ -70,7 +80,50 @@ o Procfs entries # cat /proc/net/aliases device family address eth0:0 2 200.1.1.1 + +o PROCfs dynamic configuration + You can now change the max aliases per device limit via + /proc/sys/net/core/net_alias_max entry + # cat /proc/sys/net/core/net_alias_max + 256 + # echo 1000 > /proc/sys/net/core/net_alias_max + # cat /proc/sys/net/core/net_alias_max + 1000 + # _ + + With this funcionality you can disable net_alias creation from now on + # echo 0 > /proc/sys/net/core/net_alias_max + The new aliasing limit is considered (grabbed) when creating the + FIRST alias for the main device. + Eg: + # echo 10 > /proc/sys/net/core/net_alias_max + # ifconfig eth0:0 xx.xx.xx.xx (first alias creation for eth0, + eth0 will 'remember' max==10) + # echo 1000 > /proc/sys/net/core/net_alias_max + # ifconfig eth0:999 xx.xx.xx.xx + SIOCIFSADDR: No such device + Of course these semantics can be changed, please let me know. + + Configuration changes get logged as usual (klogd -> /var/log/messages) + +o Alias devices rx/tx stats + Fake rx/tx stats are accounted: + - TX + When the packet is ``switched'' from logical alias device to + physical device, tx counter gets incr. + - RX + When an incoming packet's address equals alias device's addr it + gets ``switched'' from physical to logical device, rx counter gets + incr. + + Please NOTE that for ``same'' network alias devices you usually have + one net-route through physical device (eg. eth0), so output pkts + will NOT pass down via alias device (so, no tx++ will occur). + + Also NOTE that currently ifconfig does not handle the ``:'' of alias devices + names, a little patch (attached) solves the problem. + Relationship with main device ----------------------------- - On main device closing, all aliases will be closed and freed. @@ -78,12 +131,12 @@ Relationship with main device main device (aliases get 'stacked' after main_dev), eg: lo->eth0->eth0:0->eth0:2->eth1->0 If eth0 is unregistered, all it aliases will also be: - lo->eth1->0 + lo->eth1->0 Contact ------- -Please finger or e-mail me: - Juan Jose Ciarlante +Please e-mail me: + Juan Jose Ciarlante or ; local variables: diff --git a/Documentation/networking/ipx.txt b/Documentation/networking/ipx.txt new file mode 100644 index 000000000000..c120e022715a --- /dev/null +++ b/Documentation/networking/ipx.txt @@ -0,0 +1,19 @@ +The IPX support in the Linux kernel has two modes of operation: +With and without the full internal IPX network. For all normal +operations, you do not need the full internal IPX network. + +The full internal IPX network enables you to allocate sockets on +different virtual nodes of the internal network. This is done by +evaluating the field sipx_node of the socket address given to the bind +call. So applications should always initialize the node field to 0 +when binding a socket on the primary network. In this case the socket +is assigned the default node that has been given to the kernel when +the internal network was created. +By enabling the full internal IPX network the cross-forwarding of +packets targeted at 'special' sockets to sockets listening on the +primary network is disabled. This might break existing applications, +especially RIP/SAP daemons. A RIP/SAP daemon that works well with +the full internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs. + +If you want the full internal network, please uncomment the correspondig +#define in line 19 of include/net/ipx.h diff --git a/Documentation/networking/so_bindtodevice.txt b/Documentation/networking/so_bindtodevice.txt new file mode 100644 index 000000000000..d0a3c2dc1485 --- /dev/null +++ b/Documentation/networking/so_bindtodevice.txt @@ -0,0 +1,69 @@ +SO_BINDTODEVICE socket option for Linux 2.0.30+ +by Elliot Poger (elliot@poger.com) +of Stanford's MosquitoNet project (http://mosquitonet.stanford.edu) + +Using the SO_BINDTODEVICE socket option allows your user-level Berkeley +sockets code to explicitly select which network interface is used for +both input and output on a per-socket basis. I originally wrote it to +allow the Internet Software Consortium DHCP server +(http://www.fugue.com/dhcp/) to run on Linux machines with multiple +interfaces. It has been tested with UDP and TCP sockets. + +Usage is as follows: + + + int skfd; + struct ifreq interface; + + skfd = socket(AF_INET, SOCK_DGRAM, 0); + strncpy(interface.ifr_ifrn.ifrn_name, "eth1", IFNAMSIZ); + if (setsockopt(skfd, SOL_SOCKET, SO_BINDTODEVICE, + (char *)&interface, sizeof(interface)) < 0) { + perror("sendpacket: setting SO_BINDTODEVICE"); + exit(1); + } + + +Once the BINDTODEVICE socket option has been set for a socket, as above, +any data sent over this socket is guaranteed to go out of the "eth1" +interface, and any data received through the socket is guaranteed to +have arrived on eth1. If you want to send and receive over multiple +interfaces, keeping them separate, you can open several sockets and bind +each one to a different interface with SO_BINDTODEVICE. (You _can_ call +BINDTODEVICE more than once for a socket to change the interface it's +bound to, but results may be unpredictable because of caching effects +in the kernel...) + +Note that the routing table is still consulted when packets are transmitted. +Basically, routing proceeds as usual, except that any routes which go +through a network interface other than the one specified in the BINDTODEVICE +call are ignored. If you attempt to send a packet to a certain IP address +through an interface which provides no route to that IP address, you'll get +a "network unreachable" error. Here is an example of a routing table which +will allow you to send packets to any IP address through either eth0 or +eth1: + +Destination Gateway Genmask Flags Metric Ref Use Iface +171.64.69.0 0.0.0.0 255.255.255.192 U 0 0 37 eth0 +171.64.69.192 0.0.0.0 255.255.255.192 U 0 0 677 eth1 +127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 4 lo +0.0.0.0 171.64.69.1 0.0.0.0 UG 0 0 45 eth0 +0.0.0.0 171.64.69.193 0.0.0.0 UG 1 0 5 eth1 + +Note that there are actually TWO default routes. The routing table is +searched from top to bottom, so every time you send out a packet, the first +(uppermost) matching route which the kernel routing function finds which +matches the destination IP address is used. In this case, packets sent to +the IP address 152.2.128.159 will normally be sent through eth0 and gateway +171.64.69.1; if the socket is bound to the eth1 device, the packets will be +sent through eth1 and gateway 171.64.69.193; if the socket is bound to some +other device, you will get a "network unreachable" error. + +By the way, you can add multiple default routes and set the order of +preference as follows: + +route add default gateway 171.64.69.1 +route add default gateway 171.64.69.193 metric 1 + +Routes with a higher "metric" are put lower in the table and thus have a +lower preference. diff --git a/Documentation/networking/z8530drv.txt b/Documentation/networking/z8530drv.txt index f7df36efbfce..2982c3a57dba 100644 --- a/Documentation/networking/z8530drv.txt +++ b/Documentation/networking/z8530drv.txt @@ -1,18 +1,8 @@ This is a subset of the documentation. To use this driver you MUST have the -full package from: - -Internet: -========= - -ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.0.dl1bke.real.tar.gz - -[ - if you can't find it there, try: - .../tcpip/linux/z8530drv-2.0.dl1bke.tar.gz - -] - -and various mirrors (i.e. nic.switch.ch) +full package "z8530drv-2.4c.dl1bke.tar.gz" from either ftp.pspt.fi, +sunsite.unc.edu or db0bm.automation.fh-aachen.de. Do not try to use the +utilities from z8530drv-utils-3.0 as they will not work with the 2.4 series +of the driver! --------------------------------------------------------------------------- @@ -21,7 +11,7 @@ and various mirrors (i.e. nic.switch.ch) ******************************************************************** - (c) 1993,1995 by Joerg Reuter DL1BKE + (c) 1993,1997 by Joerg Reuter DL1BKE portions (c) 1993 Guido ten Dolle PE1NNZ @@ -787,5 +777,5 @@ Many thanks to Linus Torvalds and Alan Cox for including the driver in the Linux standard distribution and their support. Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.tng.oche.de + WWW : http://www.rat.de/jr + Internet: jreuter@poboxes.com diff --git a/Documentation/specialix.txt b/Documentation/specialix.txt new file mode 100644 index 000000000000..cb4eeb7cb742 --- /dev/null +++ b/Documentation/specialix.txt @@ -0,0 +1,334 @@ + + specialix.txt -- specialix IO8+ multiport serial driver readme. + + + + Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + + Specialix pays for the development and support of this driver. + Please DO contact io8-linux@specialix.co.uk if you require + support. + + This driver was developed in the BitWizard linux device + driver service. If you require a linux device driver for your + product, please contact devices@BitWizard.nl for a quote. + + This code is firmly based on the riscom/8 serial driver, + written by Dmitry Gorodchanin. The specialix IO8+ card + programming information was obtained from the CL-CD1865 Data + Book, and Specialix document number 6200059: IO8+ Hardware + Functional Specification. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + USA. + + +Intro +===== + + +This file contains some random information, that I like to have online +instead of in a manual that can get lost. Ever misplace your Linux +kernel sources? And the manual of one of the boards in your computer? + + +Adresses and interrupts +======================= + +Addres dip switch settings: +The dip switch sets bits 2-9 of the IO address. + + 9 8 7 6 5 4 3 2 + +-----------------+ + 0 | X X X X X X X | + | | = IoBase = 0x100 + 1 | X | + +-----------------+ ------ RS232 connectors ----> + + | | | + edge connector + | | | + V V V + +Base address 0x100 caused a conflict in one of my computers once. I +haven't the foggiest why. My Specialix card is now at 0x180. My +other computer runs just fine with the Specialix card at 0x100.... +The card occupies 4 addresses, but actually only two are really used. + +The driver now still autoprobes at 0x100, 0x180, 0x250 and 0x260. If +that causes trouble for you, please report that. I'll remove +autoprobing then. + +The driver will tell the card what IRQ to use, so you don't have to +change any jumpers to change the IRQ. Just use a command line +argument (irq=xx) to the insmod program to set the interrupt. + +If your specialix cards are not at the default locations, you can use +the kernel command line argument "specialix=io0,irq0,io1,irq1...". +Here "io0" is the io address for the first card, and "irq0" is the +irq line that the first card should use. And so on. + +Examples. + +You use the driver as a module and have three cards at 0x100, 0x250 +and 0x180. And some way or another you want them detected in that +order. Moreover irq 12 is taken (e.g. by your PS/2 mouse). + + insmod specialix.o iobase=0x100,0x250,0x180 irq=9,11,15 + +The same three cards, but now in the kernel would require you to +add + + specialix=0x100,9,0x250,11,0x180,15 + +to the command line. This would become + + append="specialix=0x100,9,0x250,11,0x180,15" + +in your /etc/lilo.conf file if you use lilo. + + +Baud rates +========== + +The rev 1.2 and below boards use a CL-CD1864. These chips can only +do 64kbit. The rev 1.3 and newer boards use a CL-CD1865. These chips +are officially capable of 115k2. + +The Specialix card uses a 25MHz crystal (in times two mode, which in +fact is a divided by two mode). This is not enough to reach the rated +115k2 on all ports at the same time. With this clock rate you can only +do 37% of this rate. This means that at 115k2 on all ports you are +going to loose characters (The chip cannot handle that many incoming +bits at this clock rate.) (Yes, you read that correctly: there is a +limit to the number of -=bits=- per second that the chip can handle.) + +If you near the "limit" you will first start to see a graceful +degradation in that the chip cannot keep the transmitter busy at all +times. However with a central clock this slow, you can also get it to +miss incoming characters. + +The specialix card cannot reliably do 115k2. If you use it, you have +to do "extensive testing" (*) to verify if it actually works. + +When "mgetty" communicates with my modem at 115k2 it reports: +got: +++[0d]ATQ0V1H0[0d][0d][8a]O[cb][0d][8a] + ^^^^ ^^^^ ^^^^ + +The three characters that have the "^^^" under them have suffered a +bit error in the highest bit. In conclusion: I've tested it, and found +that it simply DOESN"T work for me. I also suspect that this is also +caused by the baud rate being just a little bit out of tune. + + +(*) Cirrus logic CD1864 databook, page 40. + + +Cables for the Specialix IO8+ +============================= + +The pinout of the connectors on the IO8+ is: + + pin short direction long name + name + Pin 1 DCD input Data Carrier Detect + Pin 2 RXD input Receive + Pin 3 DTR/RTS output Data Terminal Ready/Ready To Send + Pin 4 GND - Ground + Pin 5 TXD output Transmit + Pin 6 CTS input Clear To Send + + + -- 6 5 4 3 2 1 -- + | | + | | + | | + | | + +----- -----+ + |__________| + clip + + Front view of an RJ12 connector. Cable moves "into" the paper. + (the plug is ready to plug into your mouth this way...) + + + NULL cable. I don't know who is going to use these except for + testing purposes, but I tested the cards with this cable. (It + took quite a while to figure out, so I'm not going to delete + it. So there! :-) + + + This end goes This end needs + straight into the some twists in + RJ12 plug. the wiring. + IO8+ RJ12 IO8+ RJ12 + 1 DCD white - + - - 1 DCD + 2 RXD black 5 TXD + 3 DTR/RTS red 6 CTS + 4 GND green 4 GND + 5 TXD yellow 2 RXD + 6 CTS blue 3 DTR/RTS + + + Same NULL cable, but now sorted on the second column. + + 1 DCD white - + - - 1 DCD + 5 TXD yellow 2 RXD + 6 CTS blue 3 DTR/RTS + 4 GND green 4 GND + 2 RXD black 5 TXD + 3 DTR/RTS red 6 CTS + + + + This is a modem cable usable for hardware handshaking: + RJ12 DB25 DB9 + 1 DCD white 8 DCD 1 DCD + 2 RXD black 3 RXD 2 RXD + 3 DTR/RTS red 4 RTS 7 RTS + 4 GND green 7 GND 5 GND + 5 TXD yellow 2 TXD 3 TXD + 6 CTS blue 5 CTS 8 CTS + +---- 6 DSR 6 DSR + +---- 20 DTR 4 DTR + + This is a modem cable usable for software handshaking: + It allows you to reset the modem using the DTR ioctls. + I (REW) have never tested this, "but xxxxxxxxxxxxx + says that it works." If you test this, please + tell me and I'll fill in your name on the xxx's. + + RJ12 DB25 DB9 + 1 DCD white 8 DCD 1 DCD + 2 RXD black 3 RXD 2 RXD + 3 DTR/RTS red 20 DTR 4 DTR + 4 GND green 7 GND 5 GND + 5 TXD yellow 2 TXD 3 TXD + 6 CTS blue 5 CTS 8 CTS + +---- 6 DSR 6 DSR + +---- 4 RTS 7 RTS + + I bought a 6 wire flat cable. It was colored as indicated. + Check that yours is the same before you trust me on this. + + +Hardware handshaking issues. +============================ + +The driver can be compiled in two different ways. The default +("Specialix DTR/RTS pin is RTS" is off) the pin behaves as DTR when +hardware handshaking is off. It behaves as the RTS hardware +handshaking signal when hardware handshaking is selected. + +When you use this, you have to use the appropriate cable. The +cable will either be compatible with hardware handshaking or with +software handshaking. So switching on the fly is not really an +option. + +I actually prefer to use the "Specialix DTR/RTS pin is RTS" option. +This makes the DTR/RTS pin always an RTS pin, and ioctls to +change DTR are always ignored. I have a cable that is configured +for this. + + +Ports and devices +================= + +Port 0 is the one furthest from the ISA connector. + +Devices: + +You should make the devices as follows: + +bash +cd /dev +for i in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \ + 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 +do + echo -n "$i " + mknod /dev/ttyW$i c 75 $i + mknod /dev/cuw$i c 76 $i +done +echo "" + + +You cannot have more than 4 boards in one computer. The card only +supports 4 different interrupts. If you really want this, contact me +about this and I'll give you a few tips (requires soldering iron).... + + +------------------------------------------------------------------------ + + + Fixed bugs and restrictions: + - During intialization, interrupts are blindly turned on. + Having a shadow variable would cause an extra memory + access on every IO instruction. + - The interrupt (on the card) should be disabled when we + don't allocate the Linux end of the interrupt. This allows + a different driver/card to use it while all ports are not in + use..... (a la standard serial port) + == An extra _off variant of the sx_in and sx_out macros are + now available. They don't set the interrupt enable bit. + These are used during initialization. Normal operation uses + the old variant which enables the interrupt line. + - RTS/DTR issue needs to be implemented according to + specialix' spec. + I kind of like the "determinism" of the current + implementation. Compile time flag? + == Ok. Compile time flag! Default is how Specialix likes it. + == Now a config time flag! Gets saved in your config file. Neat! + - Can you set the IO address from the lilo command line? + If you need this, bug me about it, I'll make it. + == Hah! No bugging needed. Fixed! :-) + - Cirrus logic hasn't gotten back to me yet why the CD1865 can + and the CD1864 can't do 115k2. I suspect that this is + because the CD1864 is not rated for 33MHz operation. + Therefore the CD1864 versions of the card can't do 115k2 on + all ports just like the CD1865 versions. The driver does + not block 115k2 on CD1864 cards. + == I called the Cirrus Logic representative here in Holland. + The CD1864 databook is identical to the CD1865 databook, + except for an extra warning at the end. Similar Bit errors + have been observed in testing at 115k2 on both an 1865 and + a 1864 chip. I see no reason why I would prohibit 115k2 on + 1864 chips and not do it on 1865 chips. Actually there is + reason to prohibit it on BOTH chips. I print a warning. + If you use 115k2, you're on your own. + - A spiky CD may send spurious HUPs. Also in CLOCAL??? + -- A fix for this turned out to be counter productive. + Different fix? Current behaviour is acceptable? + -- Maybe the current implementation is correct. If anybody + gets bitten by this, please report, and it will get fixed. + + -- Testing revealed that when in CLOCAL, the problem doesn't + occur. As warned for in the CD1865 manual, the chip may + send modem intr's on a spike. We could filter those out, + but that would be a cludge anyway (You'd still risk getting + a spurious HUP when two spikes occur.)..... + + + + Bugs & restrictions: + - This is a difficult card to autoprobe. + You have to WRITE to the address register to even + read-probe a CD186x register. Disable autodetection? + -- Specialix: any suggestions? + - Arbitrary baud rates are not implemented yet. + If you need this, bug me about it. + + diff --git a/MAINTAINERS b/MAINTAINERS index 497f2382dfd9..7621c0e142ac 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -110,6 +110,12 @@ P: Jean Tourrilhes M: jt@hplb.hpl.hp.com S: Maintained +TOKEN-RING NETWORK DRIVER +P: Paul Norton +M: pnorton@cts.com +L: linux-net@vger.rutgers.edu +S: Maintained + APM DRIVER P: Rik Faith & Stephen Rothwell M: faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au @@ -134,6 +140,18 @@ M: Nigel.Metheringham@ThePLAnet.net L: masq@indyramp.com S: Maintained +AX.25 DAMA SLAVE +P: Joerg Reuter +M: jreuter@poboxes.com +L: linux-hams@vger.rutgers.edu +S: Maintained + +Z8530 SCC DRIVER FOR AX.25 +P: Joerg Reuter +M: jreuter@poboxes.com +L: linux-hams@vger.rutgers.edu +S: Maintained + BUSLOGIC SCSI DRIVER P: Leonard N. Zubkoff M: Leonard N. Zubkoff @@ -306,7 +324,7 @@ S: Maintained SVGA HANDLING: P: Martin Mares M: mj@k332.feld.cvut.cz -L: linux-kernel@vger.rutgers.edu +L: linux-video@atrey.karlin.mff.cuni.cz S: Maintained VFAT FILESYSTEM: @@ -328,6 +346,13 @@ M: begemot@bgm.rosprint.net L: linux-kernel@vger.rutgers.edu S: Maintained +SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER +P: Roger Wolff +M: R.E.Wolff@BitWizard.nl +M: io8-linux@specialix.co.uk +L: linux-kernel@vger.rutgers.edu ? +S: Supported + MOUSE AND MISC DEVICES [GENERAL] P: Alessandro Rubini M: rubini@ipvvis.unipv.it diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 74dbc3f86961..b568a45b0e85 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -124,7 +124,6 @@ CONFIG_EL3=y # ISDN subsystem # # CONFIG_ISDN is not set -CONFIG_HISAX_EURO=y # # CD-ROM drivers (not for SCSI or IDE/ATAPI drives) @@ -161,6 +160,7 @@ CONFIG_SERIAL=y # CONFIG_STALDRV is not set # CONFIG_RISCOM8 is not set # CONFIG_PRINTER is not set +# CONFIG_SPECIALIX is not set # CONFIG_MOUSE is not set # CONFIG_UMISC is not set # CONFIG_QIC02_TAPE is not set diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index edcb6b0fbd70..997bd1e31d0f 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -35,6 +35,8 @@ #endif #endif +const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; + extern void die_if_kernel(char *,struct pt_regs *,long); extern void show_net_buffers(void); diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 8c557cda045a..228c0049f18b 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -12,6 +12,8 @@ * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994 * * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996 + * + * Fixed do_loop_request() re-entrancy - Mar 20, 1997 */ #include @@ -181,12 +183,15 @@ static void do_lo_request(void) char *dest_addr; struct loop_device *lo; struct buffer_head *bh; + struct request *current_request; repeat: INIT_REQUEST; - if (MINOR(CURRENT->rq_dev) >= MAX_LOOP) + current_request=CURRENT; + CURRENT=current_request->next; + if (MINOR(current_request->rq_dev) >= MAX_LOOP) goto error_out; - lo = &loop_dev[MINOR(CURRENT->rq_dev)]; + lo = &loop_dev[MINOR(current_request->rq_dev)]; if (!lo->lo_inode || !lo->transfer) goto error_out; @@ -197,14 +202,14 @@ repeat: blksize = BLOCK_SIZE; } - dest_addr = CURRENT->buffer; + dest_addr = current_request->buffer; if (blksize < 512) { - block = CURRENT->sector * (512/blksize); + block = current_request->sector * (512/blksize); offset = 0; } else { - block = CURRENT->sector / (blksize >> 9); - offset = (CURRENT->sector % (blksize >> 9)) << 9; + block = current_request->sector / (blksize >> 9); + offset = (current_request->sector % (blksize >> 9)) << 9; } block += lo->lo_offset / blksize; offset += lo->lo_offset % blksize; @@ -212,13 +217,13 @@ repeat: block++; offset -= blksize; } - len = CURRENT->current_nr_sectors << 9; + len = current_request->current_nr_sectors << 9; - if (CURRENT->cmd == WRITE) { + if (current_request->cmd == WRITE) { if (lo->lo_flags & LO_FLAGS_READ_ONLY) goto error_out; - } else if (CURRENT->cmd != READ) { - printk("unknown loop device command (%d)?!?", CURRENT->cmd); + } else if (current_request->cmd != READ) { + printk("unknown loop device command (%d)?!?", current_request->cmd); goto error_out; } while (len > 0) { @@ -237,7 +242,7 @@ repeat: block, blksize); goto error_out; } - if (!buffer_uptodate(bh) && ((CURRENT->cmd == READ) || + if (!buffer_uptodate(bh) && ((current_request->cmd == READ) || (offset || (len < blksize)))) { ll_rw_block(READ, 1, &bh); wait_on_buffer(bh); @@ -250,13 +255,13 @@ repeat: if (size > len) size = len; - if ((lo->transfer)(lo, CURRENT->cmd, bh->b_data + offset, + if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset, dest_addr, size)) { printk("loop: transfer error block %d\n", block); brelse(bh); goto error_out; } - if (CURRENT->cmd == WRITE) { + if (current_request->cmd == WRITE) { mark_buffer_uptodate(bh, 1); mark_buffer_dirty(bh, 1); } @@ -266,9 +271,13 @@ repeat: offset = 0; block++; } + current_request->next=CURRENT; + CURRENT=current_request; end_request(1); goto repeat; error_out: + current_request->next=CURRENT; + CURRENT=current_request; end_request(0); goto repeat; } diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 5d97997c5b44..a339033498a9 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -14,6 +14,10 @@ if [ "$CONFIG_STALDRV" = "y" ]; then fi tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8 tristate 'Parallel printer support' CONFIG_PRINTER +tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX +if [ "$CONFIG_SPECIALIX" = "y" -o "$CONFIG_SPECIALIX" = "m" ]; then + bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS +fi bool 'Mouse Support (not serial mice)' CONFIG_MOUSE diff --git a/drivers/char/Makefile b/drivers/char/Makefile index b8f8ed7d0695..205a78428bdb 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -78,6 +78,14 @@ else endif endif +ifeq ($(CONFIG_SPECIALIX),y) +L_OBJS += specialix.o +else + ifeq ($(CONFIG_SPECIALIX),m) + M_OBJS += specialix.o + endif +endif + ifeq ($(CONFIG_ATIXL_BUSMOUSE),y) M = y L_OBJS += atixlmouse.o diff --git a/drivers/char/README.scc b/drivers/char/README.scc index 71b0c5e4148c..d5d517ef7861 100644 --- a/drivers/char/README.scc +++ b/drivers/char/README.scc @@ -1,24 +1,14 @@ - -You will find subset of the documentation in +You will find a subset of the documentation in linux/Documentation/networking/z8530drv.txt +To use this driver you MUST have the full package "z8530drv-2.4c.dl1bke.tar.gz" +from either ftp.pspt.fi, sunsite.unc.edu or db0bm.automation.fh-aachen.de. +The package includes the utilities necessary to initialize and control the driver. -To use this driver you MUST have the full package from - -ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.01.dl1bke.real.tar.gz - -[ - if you can't find it there, try: - .../tcpip/linux/z8530drv-2.01.dl1bke.tar.gz - -] - -and various mirrors (i.e. nic.switch.ch) - -The package includes the utilities necessary to initialize and -control the driver. +Do not try to use the utilities from z8530drv-utils-3.0 as they will not work +with the 2.4 series of the driver! Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.tng.oche.de + WWW : http://www.rat.de/jr + Internet: jreuter@poboxes.com diff --git a/drivers/char/cd1865.h b/drivers/char/cd1865.h new file mode 100644 index 000000000000..e7407f4b85c8 --- /dev/null +++ b/drivers/char/cd1865.h @@ -0,0 +1,263 @@ +/* + * linux/drivers/char/cd1865.h -- Definitions relating to the CD1865 + * for the Specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + */ + +/* + * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards. + */ + + +/* Values of choice for Interrupt ACKs */ +/* These values are "obligatory" if you use the register based + * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865 + * databook */ +#define SX_ACK_MINT 0x75 /* goes to PILR1 */ +#define SX_ACK_TINT 0x76 /* goes to PILR2 */ +#define SX_ACK_RINT 0x77 /* goes to PILR3 */ + +/* Chip ID (is used when chips ar daisy chained.) */ +#define SX_ID 0x10 + +/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */ + +#define CD186x_NCH 8 /* Total number of channels */ +#define CD186x_TPC 16 /* Ticks per character */ +#define CD186x_NFIFO 8 /* TX FIFO size */ + + +/* Global registers */ + +#define CD186x_GIVR 0x40 /* Global Interrupt Vector Register */ +#define CD186x_GICR 0x41 /* Global Interrupting Channel Register */ +#define CD186x_PILR1 0x61 /* Priority Interrupt Level Register 1 */ +#define CD186x_PILR2 0x62 /* Priority Interrupt Level Register 2 */ +#define CD186x_PILR3 0x63 /* Priority Interrupt Level Register 3 */ +#define CD186x_CAR 0x64 /* Channel Access Register */ +#define CD186x_SRSR 0x65 /* Channel Access Register */ +#define CD186x_GFRCR 0x6b /* Global Firmware Revision Code Register */ +#define CD186x_PPRH 0x70 /* Prescaler Period Register High */ +#define CD186x_PPRL 0x71 /* Prescaler Period Register Low */ +#define CD186x_RDR 0x78 /* Receiver Data Register */ +#define CD186x_RCSR 0x7a /* Receiver Character Status Register */ +#define CD186x_TDR 0x7b /* Transmit Data Register */ +#define CD186x_EOIR 0x7f /* End of Interrupt Register */ +#define CD186x_MRAR 0x75 /* Modem Request Acknowlege register */ +#define CD186x_TRAR 0x76 /* Transmit Request Acknowlege register */ +#define CD186x_RRAR 0x77 /* Recieve Request Acknowlege register */ +#define CD186x_SRCR 0x66 /* Service Request Configuration register */ + +/* Channel Registers */ + +#define CD186x_CCR 0x01 /* Channel Command Register */ +#define CD186x_IER 0x02 /* Interrupt Enable Register */ +#define CD186x_COR1 0x03 /* Channel Option Register 1 */ +#define CD186x_COR2 0x04 /* Channel Option Register 2 */ +#define CD186x_COR3 0x05 /* Channel Option Register 3 */ +#define CD186x_CCSR 0x06 /* Channel Control Status Register */ +#define CD186x_RDCR 0x07 /* Receive Data Count Register */ +#define CD186x_SCHR1 0x09 /* Special Character Register 1 */ +#define CD186x_SCHR2 0x0a /* Special Character Register 2 */ +#define CD186x_SCHR3 0x0b /* Special Character Register 3 */ +#define CD186x_SCHR4 0x0c /* Special Character Register 4 */ +#define CD186x_MCOR1 0x10 /* Modem Change Option 1 Register */ +#define CD186x_MCOR2 0x11 /* Modem Change Option 2 Register */ +#define CD186x_MCR 0x12 /* Modem Change Register */ +#define CD186x_RTPR 0x18 /* Receive Timeout Period Register */ +#define CD186x_MSVR 0x28 /* Modem Signal Value Register */ +#define CD186x_MSVRTS 0x29 /* Modem Signal Value Register */ +#define CD186x_MSVDTR 0x2a /* Modem Signal Value Register */ +#define CD186x_RBPRH 0x31 /* Receive Baud Rate Period Register High */ +#define CD186x_RBPRL 0x32 /* Receive Baud Rate Period Register Low */ +#define CD186x_TBPRH 0x39 /* Transmit Baud Rate Period Register High */ +#define CD186x_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */ + + +/* Global Interrupt Vector Register (R/W) */ + +#define GIVR_ITMASK 0x07 /* Interrupt type mask */ +#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */ +#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */ +#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */ +#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */ + + +/* Global Interrupt Channel Register (R/W) */ + +#define GICR_CHAN 0x1c /* Channel Number Mask */ +#define GICR_CHAN_OFF 2 /* Channel Number shift */ + + +/* Channel Address Register (R/W) */ + +#define CAR_CHAN 0x07 /* Channel Number Mask */ +#define CAR_A7 0x08 /* A7 Address Extension (unused) */ + + +/* Receive Character Status Register (R/O) */ + +#define RCSR_TOUT 0x80 /* Rx Timeout */ +#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ +#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ +#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ +#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ +#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ +#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ +#define RCSR_BREAK 0x08 /* Break has been detected */ +#define RCSR_PE 0x04 /* Parity Error */ +#define RCSR_FE 0x02 /* Frame Error */ +#define RCSR_OE 0x01 /* Overrun Error */ + + +/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ + +#define CCR_HARDRESET 0x81 /* Reset the chip */ + +#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ + +#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ +#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ +#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ + +#define CCR_SSCH1 0x21 /* Send Special Character 1 */ + +#define CCR_SSCH2 0x22 /* Send Special Character 2 */ + +#define CCR_SSCH3 0x23 /* Send Special Character 3 */ + +#define CCR_SSCH4 0x24 /* Send Special Character 4 */ + +#define CCR_TXEN 0x18 /* Enable Transmitter */ +#define CCR_RXEN 0x12 /* Enable Receiver */ + +#define CCR_TXDIS 0x14 /* Disable Transmitter */ +#define CCR_RXDIS 0x11 /* Disable Receiver */ + + +/* Interrupt Enable Register (R/W) */ + +#define IER_DSR 0x80 /* Enable interrupt on DSR change */ +#define IER_CD 0x40 /* Enable interrupt on CD change */ +#define IER_CTS 0x20 /* Enable interrupt on CTS change */ +#define IER_RXD 0x10 /* Enable interrupt on Receive Data */ +#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ +#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ +#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ +#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ + + +/* Channel Option Register 1 (R/W) */ + +#define COR1_ODDP 0x80 /* Odd Parity */ +#define COR1_PARMODE 0x60 /* Parity Mode mask */ +#define COR1_NOPAR 0x00 /* No Parity */ +#define COR1_FORCEPAR 0x20 /* Force Parity */ +#define COR1_NORMPAR 0x40 /* Normal Parity */ +#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ +#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ +#define COR1_1SB 0x00 /* 1 Stop Bit */ +#define COR1_15SB 0x04 /* 1.5 Stop Bits */ +#define COR1_2SB 0x08 /* 2 Stop Bits */ +#define COR1_CHARLEN 0x03 /* Character Length */ +#define COR1_5BITS 0x00 /* 5 bits */ +#define COR1_6BITS 0x01 /* 6 bits */ +#define COR1_7BITS 0x02 /* 7 bits */ +#define COR1_8BITS 0x03 /* 8 bits */ + + +/* Channel Option Register 2 (R/W) */ + +#define COR2_IXM 0x80 /* Implied XON mode */ +#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ +#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ +#define COR2_LLM 0x10 /* Local Loopback Mode */ +#define COR2_RLM 0x08 /* Remote Loopback Mode */ +#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ +#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ +#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ + + +/* Channel Option Register 3 (R/W) */ + +#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ +#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ +#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ +#define COR3_SCDE 0x10 /* Special Character Detection Enable */ +#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ + + +/* Channel Control Status Register (R/O) */ + +#define CCSR_RXEN 0x80 /* Receiver Enabled */ +#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ +#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ +#define CCSR_TXEN 0x08 /* Transmitter Enabled */ +#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ +#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ + + +/* Modem Change Option Register 1 (R/W) */ + +#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ +#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ +#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ +#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ +#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ + + +/* Modem Change Option Register 2 (R/W) */ + +#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ +#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ +#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ + +/* Modem Change Register (R/W) */ + +#define MCR_DSRCHG 0x80 /* DSR Changed */ +#define MCR_CDCHG 0x40 /* CD Changed */ +#define MCR_CTSCHG 0x20 /* CTS Changed */ + + +/* Modem Signal Value Register (R/W) */ + +#define MSVR_DSR 0x80 /* Current state of DSR input */ +#define MSVR_CD 0x40 /* Current state of CD input */ +#define MSVR_CTS 0x20 /* Current state of CTS input */ +#define MSVR_DTR 0x02 /* Current state of DTR output */ +#define MSVR_RTS 0x01 /* Current state of RTS output */ + + +/* Escape characters */ + +#define CD186x_C_ESC 0x00 /* Escape character */ +#define CD186x_C_SBRK 0x81 /* Start sending BREAK */ +#define CD186x_C_DELAY 0x82 /* Delay output */ +#define CD186x_C_EBRK 0x83 /* Stop sending BREAK */ + +#define SRSR_RREQint 0x10 /* This chip wants "rec" serviced */ +#define SRSR_TREQint 0x04 /* This chip wants "transmit" serviced */ +#define SRSR_MREQint 0x01 /* This chip wants "mdm change" serviced */ + + + +#define SRCR_PKGTYPE 0x80 +#define SRCR_REGACKEN 0x40 +#define SRCR_DAISYEN 0x20 +#define SRCR_GLOBPRI 0x10 +#define SRCR_UNFAIR 0x08 +#define SRCR_AUTOPRI 0x02 +#define SRCR_PRISEL 0x01 + + diff --git a/drivers/char/console.c b/drivers/char/console.c index 6df46a8f5d83..e4e65b9f6719 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -2320,3 +2320,22 @@ int con_get_font (char *arg) { return set_get_font (arg,0,video_mode_512ch); } + +/* + * Report the current status of the vc. This is exported to modules (ARub) + */ + +int con_get_info(int *mode, int *shift, int *col, int *row, + struct tty_struct **tty) +{ + extern int shift_state; + extern struct tty_driver console_driver; + struct tty_struct **console_table=console_driver.table; + + if (mode) *mode = vt_cons[fg_console]->vc_mode; + if (shift) *shift = shift_state; + if (col) *col = video_num_columns; + if (row) *row = video_num_lines; + if (tty) *tty = console_table[fg_console]; + return fg_console; +} diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 82f729fc3bb6..dc91564f164b 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -57,7 +57,7 @@ static struct miscdevice misc_list = { 0, "head", NULL, &misc_list, &misc_list } /* * Assigned numbers, used for dynamic minors */ -#define DYNAMIC_MINORS 64 /* like dynamic majors */ +#define DYNAMIC_MINORS 64 /* like dynamic majors used to do */ static unsigned char misc_minors[DYNAMIC_MINORS / 8]; #ifndef MODULE @@ -70,6 +70,8 @@ extern void watchdog_init(void); extern void wdt_init(void); extern void pcwatchdog_init(void); extern int rtc_init(void); +extern int con_get_info(int *mode, int *shift, int *col, int *row, + struct tty_struct **tty); #ifdef CONFIG_PROC_FS static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused) @@ -181,6 +183,7 @@ static struct symbol_table misc_syms = { #ifndef MODULE X(set_selection), /* used by the kmouse module, can only */ X(paste_selection), /* be exported if misc.c is in linked in */ + X(con_get_info), #endif #include }; diff --git a/drivers/char/scc.c b/drivers/char/scc.c index 486f52653539..fbc3fd9e4c72 100644 --- a/drivers/char/scc.c +++ b/drivers/char/scc.c @@ -1,6 +1,6 @@ -#define RCS_ID "$Id: scc.c,v 1.41 1995/12/17 22:36:40 jreuter Exp jreuter $" +#define RCS_ID "$Id: scc.c,v 1.67 1997/04/10 15:24:41 jreuter Exp jreuter $" -#define BANNER "Z8530 SCC driver version 2.01.dl1bke (alpha) by DL1BKE\n" +#define BANNER "Z8530 SCC driver version 2.4c.dl1bke (experimental) by DL1BKE\n" /* @@ -11,7 +11,7 @@ ******************************************************************** - Copyright (c) 1993, 1995 Joerg Reuter DL1BKE + Copyright (c) 1993, 1997 Joerg Reuter DL1BKE portions (c) 1993 Guido ten Dolle PE1NNZ @@ -48,16 +48,6 @@ ******************************************************************** - - ...If you find any portions of the code that are copyrighted to you, - and you don't want to see in here, please send me a private (!) - message to my internet site. I will change it as soon as possible. - Please don't flame to the tcp-group or anywhere else. Thanks! - - Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org - AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU - Internet: jreuter@lykos.tng.oche.de - Incomplete history of z8530drv: ------------------------------- @@ -74,89 +64,105 @@ 950131 - changed copyright notice to GPL without limitations. . - . + . . - 950922 - using kernel timer chain - - 951002 - splitting timer routine, adding support for throttle/ - unthrottle calls, removed typo in kiss decoder, - corrected termios settings. - - 951011 - at last found one (the?) problem which caused the - driver to mess up buffers on heavy load pe1ayx was - complaining about. Quite simple to reproduce: - set a 9k6 port into audio loopback, add a route - to 44.99.99.99 on that route and do a - ping -f 44.99.99.99 + 951114 - rewrote memory management, took first steps to allow + compilation as a module. Well, it looks like a whole + new driver now. + + 960413 - Heiko Eissfeld fixed missing verify_area() calls + + 960507 - the driver may be used as a network driver now. + (for kernel AX.25). + + 960512 - added scc_net_ioctl(), restructured ioctl routines + added /proc filesystem support - 951013 - it still does not survive a flood ping... + 960517 - fixed fullduplex mode 2 operation and waittime bug - 951107 - axattach w/o "-s" option (or setting an invalid - baudrate) does not produce "division by zero" faults - anymore. + 960518 - added fullduplex mode 3 to allow direct hardware + access via protocol layer. Removed kiss.not_slip, + I do not think someone really needed it. - 951114 - rewrote memory management, took first steps to allow - compilation as a module. Well, it looks like a whole - new driver now. BTW: It d o e s survive the flood - ping at last... - - 951116 - scc_config.h is gone -- the driver will be initialized - through ioctl-commands. Use sccinit.c for this purpose. + 960607 - switching off receiver while transmission in halfduplex + mode with external MODEM clock. - 951117 - Well --- what should I say: You can compile it as a - module now. And I solved the problem with slip.c... + 960715 - rewrote interrupt service routine for "polling" mode, + fixed bug in grouping algorithm. - 951120 - most ioctl() routines may be called w/o suser() - permissions, check if you've set the permissions of - /dev/scc* right! NOT 0666, you know it's evil ;-) + 960719 - New transmit timer routines (let's see what it breaks), + some improvements regarding DCD and SYNC interrupts, + clean-up of printk(). - 951217 - found silly bug in init_channel(), some bugfixes - for the "standalone" module + 960725 - Fixed Maxkeyup problems. Will generate HDLC abort and + recover now. + 960808 - Maxkeyup will set dev->tbusy until all remaining frames + are sent. + + 970115 - Fixed return values in scc_net_tx(), added missing + scc_enqueue_buffer() + + 970410 - Fixed problem with new *_timer() code in sched.h for + kernel 2.0.30. Thanks to: ---------- - PE1CHL Rob - for a lot of good ideas from his SCC driver for DOS - PE1NNZ Guido - for his port of the original driver to Linux - KA9Q Phil - from whom we stole the mbuf-structure - PA3AYX Hans - for some useful changes - DL8MBT Flori - for support - DG0FT Rene - for the BayCom USCC support - PA3AOU Harry - for ESCC testing, information supply and support - + PE1CHL Rob - for a lot of good ideas from his SCC driver for DOS + PE1NNZ Guido - for his port of the original driver to Linux + KA9Q Phil - from whom we stole the mbuf-structure + PA3AYX Hans - for some useful changes + DL8MBT Flori - for support + DG0FT Rene - for the BayCom USCC support + PA3AOU Harry - for ESCC testing, information supply and support + Heiko Eissfeld - verify_area() clean-up + DL3KHB Klaus - fullduplex mode 2 bug-hunter + PA3GCU Richard - for support and finding various bugs. + PE1KOX Rob, DG1RTF Thomas, ON5QK Roland, G4XYW Andy, Linus, - EI9GL Paul, - + GW4PTS Alan, EI9GL Paul, G0DZB Peter, G8IMB Martin + and all who sent me bug reports and ideas... NB -- if you find errors, change something, please let me know first before you distribute it... And please don't touch the version number. Just replace my callsign in - "v2.01.dl1bke" with your own. Just to avoid confusion... - + "v2.4c.dl1bke" with your own. Just to avoid confusion... + If you want to add your modification to the linux distribution please (!) contact me first. - - Jörg Reuter DL1BKE - + + New versions of the driver will be announced on the linux-hams + mailing list on vger.rutgers.edu. To subscribe send an e-mail + to majordomo@vger.rutgers.edu with the following line in + the body of the mail: + + subscribe linux-hams + + The content of the "Subject" field will be ignored. + + vy 73, + Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org + AX-25 : DL1BKE @ DB0ACH.#NRW.DEU.EU + Internet: jreuter@poboxes.com */ /* ----------------------------------------------------------------------- */ -#define DEBUG_BUFFERS /* keep defined unless it is really stable... */ - -#undef SCC_DELAY /* perhaps a 486DX2 is a *bit* too fast */ -#undef SCC_LDELAY 5 /* slow it even a bit more down */ +#undef SCC_DELAY /* perhaps your ISA bus is a *bit* too fast? */ +#undef SCC_LDELAY 1 /* slow it even a bit more down */ #undef DONT_CHECK /* don't look if the SCCs you specified are available */ #define MAXSCC 4 /* number of max. supported chips */ #define RXBUFFERS 8 /* default number of RX buffers */ #define TXBUFFERS 8 /* default number of TX buffers */ #define BUFSIZE 384 /* must not exceed 4096-sizeof(mbuf) */ -#define TPS 25 /* scc_tx_timer(): Ticks Per Second */ +#undef DISABLE_ALL_INTS /* use cli()/sti() in ISR instead of */ + /* enable_irq()/disable_irq() */ +#undef SCC_DEBUG #define DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */ @@ -177,12 +183,26 @@ #include #include #include +#include #include #include #include #include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_SCC_STANDALONE +#include "scc.h" +#else #include +#endif +#include +#include #include #include #include @@ -193,6 +213,7 @@ #include #include #include +#include #ifdef MODULE int init_module(void); @@ -203,6 +224,15 @@ void cleanup_module(void); #define Z8530_MAJOR 34 #endif +#ifndef PROC_NET_Z8530 +#define PROC_NET_Z8530 PROC_NET_LAST+10 /* sorry... */ +#endif + +#if !defined(CONFIG_SCC_DEV) && !defined(CONFIG_SCC_TTY) +#define CONFIG_SCC_DEV +#define CONFIG_SCC_TTY +#endif + int scc_init(void); static struct mbuf * scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer); @@ -211,6 +241,7 @@ static void alloc_buffer_pool(struct scc_channel *scc); static void free_buffer_pool(struct scc_channel *scc); static struct mbuf * scc_get_buffer(struct scc_channel *scc, char type); +#ifdef CONFIG_SCC_TTY int scc_open(struct tty_struct *tty, struct file *filp); static void scc_close(struct tty_struct *tty, struct file *filp); int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count); @@ -219,81 +250,112 @@ static void scc_flush_chars(struct tty_struct *tty); static int scc_write_room(struct tty_struct *tty); static int scc_chars_in_buffer(struct tty_struct *tty); static void scc_flush_buffer(struct tty_struct *tty); -static int scc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +static int scc_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void scc_set_termios(struct tty_struct *tty, struct termios *old_termios); static void scc_set_ldisc(struct tty_struct *tty); static void scc_throttle(struct tty_struct *tty); static void scc_unthrottle(struct tty_struct *tty); static void scc_start(struct tty_struct *tty); static void scc_stop(struct tty_struct *tty); +static void kiss_encode(struct scc_channel *scc); +static void scc_rx_timer(unsigned long); +static void scc_change_speed(struct scc_channel *scc); +#endif -static void z8530_init(void); +static void t_dwait(unsigned long); +static void t_txdelay(unsigned long); +static void t_tail(unsigned long); +static void t_busy(unsigned long); +static void t_maxkeyup(unsigned long); +static void t_idle(unsigned long); +static void scc_tx_done(struct scc_channel *); +static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long); +static void scc_start_maxkeyup(struct scc_channel *); +static void scc_start_defer(struct scc_channel *); -static void scc_change_speed(struct scc_channel *scc); -static void kiss_encode(struct scc_channel *scc); +static int scc_ioctl(struct scc_channel *scc, unsigned int cmd, void * arg); + +static void z8530_init(void); static void init_channel(struct scc_channel *scc); static void scc_key_trx (struct scc_channel *scc, char tx); -static void scc_txint(register struct scc_channel *scc); -static void scc_exint(register struct scc_channel *scc); -static void scc_rxint(register struct scc_channel *scc); -static void scc_spint(register struct scc_channel *scc); static void scc_isr(int irq, void *dev_id, struct pt_regs *regs); -static void scc_tx_timer(unsigned long); -static void scc_rx_timer(unsigned long); static void scc_init_timer(struct scc_channel *scc); -/* from serial.c */ +#ifdef CONFIG_SCC_DEV +static int scc_net_setup(struct scc_channel *scc, unsigned char *name); +static void scc_net_rx(struct scc_channel *scc, struct mbuf *bp); +#endif + +static unsigned char *SCC_DriverName = "scc"; +#ifdef CONFIG_SCC_TTY static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 0 }; +struct semaphore scc_sem = MUTEX; +static struct termios scc_std_termios; +unsigned char scc_wbuf[BUFSIZE]; struct tty_driver scc_driver; -static int scc_refcount; static struct tty_struct *scc_table[2*MAXSCC]; static struct termios scc_termios[2 * MAXSCC]; static struct termios scc_termios_locked[2 * MAXSCC]; +static int scc_refcount; +#endif + -struct irqflags { unsigned char used : 1; } Ivec[16]; +static struct irqflags { unsigned char used : 1; } Ivec[16]; -struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */ -io_port SCC_ctrl[2 * MAXSCC]; /* Control ports */ +static struct scc_channel SCC_Info[2 * MAXSCC]; /* information per channel */ -unsigned char Random = 0; /* random number for p-persist */ -unsigned char Driver_Initialized = 0; -int Nchips = 0; -io_port Vector_Latch = 0; +static struct scc_ctrl { + io_port chan_A; + io_port chan_B; + int irq; +} SCC_ctrl[MAXSCC+1]; -struct semaphore scc_sem = MUTEX; -unsigned char scc_wbuf[BUFSIZE]; +static unsigned char Driver_Initialized = 0; +static int Nchips = 0; +static io_port Vector_Latch = 0; -static struct termios scc_std_termios; +#define SCC_DEVNAME (scc->stat.is_netdev? scc->dev->name:kdevname(scc->tty->device)) /* ******************************************************************** */ /* * Port Access Functions * */ /* ******************************************************************** */ -static inline unsigned char -InReg(register io_port port, register unsigned char reg) +/* These provide interrupt save 2-step access to the Z8530 registers */ + +static __inline__ unsigned char +InReg(io_port port, unsigned char reg) { + unsigned long flags; + unsigned char r; + + save_flags(flags); + cli(); #ifdef SCC_LDELAY - register unsigned char r; Outb(port, reg); udelay(SCC_LDELAY); r=Inb(port); udelay(SCC_LDELAY); - return r; #else Outb(port, reg); - return Inb(port); + r=Inb(port); #endif + restore_flags(flags); + return r; } -static inline void -OutReg(register io_port port, register unsigned char reg, register unsigned char val) +static __inline__ void +OutReg(io_port port, unsigned char reg, unsigned char val) { + unsigned long flags; + + save_flags(flags); + cli(); #ifdef SCC_LDELAY Outb(port, reg); udelay(SCC_LDELAY); Outb(port, val); udelay(SCC_LDELAY); @@ -301,26 +363,40 @@ OutReg(register io_port port, register unsigned char reg, register unsigned char Outb(port, reg); Outb(port, val); #endif + restore_flags(flags); } -static inline void -wr(register struct scc_channel *scc, register unsigned char reg, register unsigned char val) +static __inline__ void +wr(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); } -static inline void -or(register struct scc_channel *scc, register unsigned char reg, register unsigned char val) +static __inline__ void +or(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); } -static inline void -cl(register struct scc_channel *scc, register unsigned char reg, register unsigned char val) +static __inline__ void +cl(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); } +#ifdef DISABLE_ALL_INTS +static __inline__ void scc_cli(int irq) +{ cli(); } +static __inline__ void scc_sti(int irq) +{ sti(); } +#else +static __inline__ void scc_cli(int irq) +{ disable_irq(irq); } +static __inline__ void scc_sti(int irq) +{ enable_irq(irq); } +#endif + + /* ******************************************************************** */ /* * Memory Buffer Management */ /* ******************************************************************** */ @@ -331,6 +407,9 @@ cl(register struct scc_channel *scc, register unsigned char reg, register unsign * fast. It has the disadvantage to mess things up double if something * is wrong. * + * We could have used the socket buffer routines from skbuff.h instead, + * but our own routines keep the driver portable... + * * Every scc channel has its own buffer pool now with an adjustable * number of buffers for transmit and receive buffers. The buffer * size remains the same for each AX.25 frame, but is (as a semi- @@ -354,23 +433,6 @@ scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer) save_flags(flags); cli(); /* do not disturb! */ -#ifdef DEBUG_BUFFERS - if (queue == NULLBUFP) /* called with illegal parameters, notify the user */ - - { - printk("z8530drv: Pointer to queue anchor is NULL pointer [enq]\n"); - restore_flags(flags); - return NULLBUF; - } - - if (buffer == NULLBUF) - { - printk("z8530drv: can't enqueue a NULL pointer\n"); - restore_flags(flags); - return NULLBUF; - } -#endif - anchor = *queue; /* the anchor is the "start" of the chain */ if (anchor == NULLBUF) /* found an empty list */ @@ -380,22 +442,9 @@ scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer) } else if (anchor->prev == NULLBUF) /* list has one member only */ { -#ifdef DEBUG_BUFFERS - if (anchor->prev != NULLBUF) /* oops?! */ - printk("weird --- anchor->prev is NULL but not anchor->next [enq]\n"); -#endif - anchor->next = anchor->prev = buffer; - buffer->next = buffer->prev = anchor; - } else -#ifdef DEBUG_BUFFERS - if (anchor->next == NULLBUF) /* this has to be an error. Okay, make the best out of it */ - { - printk("z8530drv: weird --- anchor->next is NULL but not anchor->prev [enq]\n"); anchor->next = anchor->prev = buffer; buffer->next = buffer->prev = anchor; - } else -#endif - { /* every other case */ + } else { /* every other case */ buffer->prev = anchor->prev; /* self explaining, isn't it? */ buffer->next = anchor; anchor->prev->next = buffer; @@ -418,28 +467,12 @@ scc_dequeue_buffer(struct mbuf **queue) save_flags(flags); cli(); -#ifdef DEBUG_BUFFERS - if (queue == NULLBUFP) /* called with illegal parameter */ - - { - printk("z8530drv: Pointer to queue anchor is NULL pointer [deq]\n"); - restore_flags(flags); - return NULLBUF; - } -#endif - buffer = *queue; /* head of the chain */ if (buffer != NULLBUF) /* not an empty list? */ { if (buffer->prev != NULLBUF) /* not last buffer? */ { -#ifdef DEBUG_BUFFERS - if (buffer->next == NULLBUF) - { /* what?! */ - printk("z8530drv: weird --- buffer->next is NULL but not buffer->prev [deq]\n"); - } else -#endif if (buffer->prev->next == buffer->prev->prev) { /* only one buffer remaining... */ buffer->next->prev = NULLBUF; @@ -449,10 +482,7 @@ scc_dequeue_buffer(struct mbuf **queue) buffer->prev->next = buffer->next; } } -#ifdef DEBUG_BUFFERS - else if (buffer->next != NULLBUF) - printk("z8530drv: weird --- buffer->prev is NULL but not buffer->next [deq]\n"); -#endif + *queue = buffer->next; /* new head of chain */ buffer->next = NULLBUF; /* for security only... */ @@ -476,6 +506,12 @@ alloc_buffer_pool(struct scc_channel *scc) struct mbuf * bptr; int buflen; + if (scc->mempool) + { + printk(KERN_DEBUG "z8530drv: alloc_buffer_pool() had buffers already allocated\n"); + return; + } + buflen = sizeof(struct mbuf) + scc->stat.bufsize; if (scc->stat.bufsize < 336) scc->stat.bufsize = 336; @@ -487,7 +523,6 @@ alloc_buffer_pool(struct scc_channel *scc) if (scc->stat.rxbuffers < 4) scc->stat.rxbuffers = 4; if (scc->stat.txbuffers < 4) scc->stat.txbuffers = 4; - /* allocate receive buffers for this channel */ @@ -502,7 +537,7 @@ alloc_buffer_pool(struct scc_channel *scc) if (bptr == NULLBUF) { - printk("z8530drv: %s: can't allocate memory for rx buffer pool", kdevname(scc->tty->device)); + printk(KERN_WARNING "z8530drv: %s: can't allocate memory for rx buffer pool", SCC_DEVNAME); break; } @@ -527,7 +562,7 @@ alloc_buffer_pool(struct scc_channel *scc) if (bptr == NULLBUF) { - printk("z8530drv: %s: can't allocate memory for tx buffer pool", kdevname(scc->tty->device)); + printk(KERN_WARNING "z8530drv: %s: can't allocate memory for tx buffer pool", SCC_DEVNAME); break; } @@ -537,6 +572,8 @@ alloc_buffer_pool(struct scc_channel *scc) scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); } + + scc->mempool = 1; } @@ -545,102 +582,89 @@ alloc_buffer_pool(struct scc_channel *scc) static void free_buffer_pool(struct scc_channel *scc) { - struct mbuf * bptr; unsigned long flags; - int cnt; + int rx_cnt, tx_cnt; + + if (!scc->mempool) + return; /* this one is a bit tricky and probably dangerous. */ + rx_cnt = tx_cnt = 0; save_flags(flags); cli(); /* esp. to free the buffers currently in use by ISR */ - bptr = scc->rx_bp; - if (bptr != NULLBUF) + if (scc->rx_bp != NULLBUF) { + kfree(scc->rx_bp); scc->rx_bp = NULLBUF; - scc_enqueue_buffer(&scc->rx_buffer_pool, bptr); + rx_cnt++; } - bptr = scc->tx_bp; - if (bptr != NULLBUF) + if (scc->tx_bp != NULLBUF) { + kfree(scc->tx_bp); scc->tx_bp = NULLBUF; - scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); + tx_cnt++; } - bptr = scc->kiss_decode_bp; - if (bptr != NULLBUF) + if (scc->kiss_decode_bp != NULLBUF) { + kfree(scc->kiss_decode_bp); scc->kiss_decode_bp = NULLBUF; - scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); + tx_cnt++; } - - bptr = scc->kiss_encode_bp; - if (bptr != NULLBUF) + +#ifdef CONFIG_SCC_TTY + if (scc->kiss_encode_bp != NULLBUF) { + kfree(scc->kiss_encode_bp); scc->kiss_encode_bp = NULLBUF; - scc_enqueue_buffer(&scc->rx_buffer_pool, bptr); + rx_cnt++; } +#endif restore_flags(flags); while (scc->rx_queue != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->rx_queue); - scc_enqueue_buffer(&scc->rx_buffer_pool, bptr); + kfree(scc_dequeue_buffer(&scc->rx_queue)); + rx_cnt++; } + scc->stat.rx_queued = 0; while (scc->tx_queue != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->tx_queue); - scc_enqueue_buffer(&scc->tx_buffer_pool, bptr); + kfree(scc_dequeue_buffer(&scc->tx_queue)); + tx_cnt++; } - - /* you want to know why we move every buffer back to the - buffer pool? Well, good question... ;-) - */ - - cnt = 0; - - /* Probably because we just want a central position in the - code were we actually call free()? - */ + scc->stat.tx_queued = 0; while (scc->rx_buffer_pool != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->rx_buffer_pool); - - if (bptr != NULLBUF) - { - cnt++; - kfree(bptr); - } + kfree(scc_dequeue_buffer(&scc->rx_buffer_pool)); + rx_cnt++; } - - if (cnt < scc->stat.rxbuffers) /* hmm... hmm... :-( */ - printk("z8530drv: oops, deallocated only %d of %d rx buffers\n", cnt, scc->stat.rxbuffers); - if (cnt > scc->stat.rxbuffers) /* WHAT?!! */ - printk("z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", cnt, scc->stat.rxbuffers); - - cnt = 0; while (scc->tx_buffer_pool != NULLBUF) { - bptr = scc_dequeue_buffer(&scc->tx_buffer_pool); - - if (bptr != NULLBUF) - { - cnt++; - kfree(bptr); - } + kfree(scc_dequeue_buffer(&scc->tx_buffer_pool)); + tx_cnt++; } - - if (cnt < scc->stat.txbuffers) - printk("z8530drv: oops, deallocated only %d of %d tx buffers\n", cnt, scc->stat.txbuffers); - if (cnt > scc->stat.txbuffers) - printk("z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", cnt, scc->stat.txbuffers); + + if (rx_cnt < scc->stat.rxbuffers) /* hmm... hmm... :-( */ + printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d rx buffers\n", rx_cnt, scc->stat.rxbuffers); + if (rx_cnt > scc->stat.rxbuffers) /* WHAT?!! */ + printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", rx_cnt, scc->stat.rxbuffers); + + if (tx_cnt < scc->stat.txbuffers) + printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d tx buffers\n", tx_cnt, scc->stat.txbuffers); + if (tx_cnt > scc->stat.txbuffers) + printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", tx_cnt, scc->stat.txbuffers); + + scc->mempool = 0; } @@ -666,28 +690,33 @@ scc_get_buffer(struct scc_channel *scc, char type) if (bptr == NULLBUF) { - printk("z8530drv: scc_get_buffer(%s): tx buffer pool empty\n", kdevname(scc->tty->device)); - +#if 0 + printk(KERN_DEBUG "z8530drv: tx buffer pool for %s empty\n", SCC_DEVNAME); +#endif /* use the oldest from the queue instead */ bptr = scc_dequeue_buffer(&scc->tx_queue); + scc->stat.tx_queued--; + scc->stat.nospace++; /* this should never, ever happen... */ if (bptr == NULLBUF) - printk("z8530drv: scc_get_buffer(): panic - even no buffer found in tx queue\n"); + printk(KERN_ERR "z8530drv: panic - all tx buffers for %s gone\n", SCC_DEVNAME); } } else { bptr = scc_dequeue_buffer(&scc->rx_buffer_pool); if (bptr == NULLBUF) { - printk("z8530drv: scc_get_buffer(%s): rx buffer pool empty\n", kdevname(scc->tty->device)); + printk(KERN_DEBUG "z8530drv: rx buffer pool for %s empty\n", SCC_DEVNAME); bptr = scc_dequeue_buffer(&scc->rx_queue); + scc->stat.rx_queued--; + scc->stat.nospace++; if (bptr == NULLBUF) - printk("z8530drv: scc_get_buffer(): panic - even no buffer found in rx queue\n"); + printk(KERN_ERR "z8530drv: panic - all rx buffers for %s gone\n", SCC_DEVNAME); } } @@ -705,153 +734,117 @@ scc_get_buffer(struct scc_channel *scc, char type) /* * Interrupt Service Routines * */ /* ******************************************************************** */ -/* ----> interrupt service routine for the 8530 <---- */ -/* it's recommended to keep this function "inline" ;-) */ - -static inline void -scc_isr_dispatch(register struct scc_channel *scc, register int vector) -{ - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; - default : printk("scc_isr(): unknown interrupt status (addr %4.4x, state %2.2x)\n",scc->ctrl,vector); - } -} +/* ----> subroutines for the interrupt handlers <---- */ +#ifdef CONFIG_SCC_TTY +/* kick rx_timer (try to send received frame or a status message ASAP) */ - -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. +/* of course we could define a "bottom half" routine to do the job, + but since its structures are saved in an array instead of a linked + list we would get in trouble if it clashes with another driver. + IMHO we are fast enough with a timer routine called on the next + timer-INT... Your opinions? */ -static void -scc_isr(int irq, void *dev_id, struct pt_regs *regs) +static __inline__ void +kick_rx_timer(struct scc_channel *scc) { - register unsigned char vector; - register struct scc_channel *scc; - register io_port q; - register io_port *p; - register int k; - + del_timer(&(scc->rx_t)); + scc->rx_t.expires = jiffies + 1; + scc->rx_t.function = scc_rx_timer; + scc->rx_t.data = (unsigned long) scc; + add_timer(&scc->rx_t); +} +#endif - cli(); +static __inline__ void +scc_notify(struct scc_channel *scc, int event) +{ + struct mbuf *bp; - if (Vector_Latch) - { - while(1) /* forever...? */ - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - /* ...not forever! */ - - /* Extract channel number and status from vector. */ - /* Isolate channel number */ - /* Call handler */ - - if (vector & 0x01) break; - - scc=&SCC_Info[(((vector>>1)&0x7c)^0x04) >> 2]; - - if (!scc->tty) break; - - scc_isr_dispatch(scc, vector); - - Outb(scc->ctrl,0x38); /* Reset Highest IUS" opcode to WR0 */ - } - - sti(); + if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) return; - } - - /* Find the SCC generating the interrupt by polling all attached SCCs - * reading RR3A (the interrupt pending register) - */ + bp = scc_get_buffer(scc, BT_RECEIVE); - k = 0; - p = SCC_ctrl; - - while (k++ < Nchips) - { - if (!(q=*p++)) break; - - Outb(q,3); + if (bp == NULLBUF) + return; - if (Inb(q)) - { - if (!(q=*p++)) break; - - Outb(q,2); - vector=Inb(q); /* Read the vector */ - - /* Extract channel number and status from vector. */ - /* Isolate channel number */ - /* Call handler */ - + bp->data[0] = PARAM_HWEVENT; + bp->data[1] = event; + bp->cnt += 2; - if (vector & 1) break; - - scc = &SCC_Info[(((vector >> 1) & 0x7c) ^ 0x04) >> 2]; - - if (!scc->tty) break; - - /* Isolate status info from vector, call handler */ - - scc_isr_dispatch(scc, vector); - - k = 0; - p = SCC_ctrl; + if (scc->stat.is_netdev) + { +#ifdef CONFIG_SCC_DEV + scc_net_rx(scc, bp); + scc_enqueue_buffer(&scc->rx_buffer_pool, bp); +#endif + } else { +#ifdef CONFIG_SCC_TTY + scc_enqueue_buffer(&scc->rx_queue, bp); + scc->stat.rx_queued++; + kick_rx_timer(scc); +#endif + } +} - } else p++; - } +static __inline__ void +flush_rx_FIFO(struct scc_channel *scc) +{ + int k; - sti(); + for (k=0; k<3; k++) + Inb(scc->data); + + if(scc->rx_bp != NULLBUF) /* did we receive something? */ + { + scc->stat.rxerrs++; /* then count it as an error */ + scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp); + + scc->rx_bp = NULLBUF; + } } - /* ----> four different interrupt handlers for Tx, Rx, changing of */ /* DCD/CTS and Rx/Tx errors */ - /* Transmitter interrupt handler */ -static void -scc_txint(register struct scc_channel *scc) +static __inline__ void +scc_txint(struct scc_channel *scc) { - register struct mbuf *bp; + struct mbuf *bp; scc->stat.txints++; bp = scc->tx_bp; + /* send first octet */ + if (bp == NULLBUF) { - do + do { if (bp != NULLBUF) + { scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + scc->stat.tx_queued--; + } bp = scc_dequeue_buffer(&scc->tx_queue); if (bp == NULLBUF) { - scc->stat.tx_state = TXS_BUSY; - scc->t_tail = scc->kiss.tailtime; - + scc_tx_done(scc); Outb(scc->ctrl, RES_Tx_P); /* clear int */ return; } - if ( scc->kiss.not_slip && (bp->cnt > 0) ) + if (bp->cnt > 0) { bp->rw_ptr++; bp->cnt--; @@ -859,57 +852,47 @@ scc_txint(register struct scc_channel *scc) } while (bp->cnt < 1); - - Outb(scc->ctrl, RES_Tx_CRC); /* reset CRC generator */ + scc->tx_bp = bp; + scc->stat.tx_state = TXS_ACTIVE; + + OutReg(scc->ctrl, R0, RES_Tx_CRC); + /* reset CRC generator */ or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*bp->rw_ptr); /* send byte */ + Outb(scc->data,*bp->rw_ptr++); /* send byte */ + if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl, RES_EOM_L); - - scc->tx_bp = bp; - scc->stat.tx_state = TXS_ACTIVE; /* next byte... */ - } else + Outb(scc->ctrl,RES_EOM_L); + + bp->cnt--; + return; + } + + /* End Of Frame... */ + if (bp->cnt <= 0) { - if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0; - Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ cl(scc, R10, ABUNDER); /* send CRC */ scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + scc->stat.tx_queued--; scc->tx_bp = NULLBUF; scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ return; - } else { - Outb(scc->data,*bp->rw_ptr); - } + } + /* send octet */ + + Outb(scc->data,*bp->rw_ptr); bp->rw_ptr++; /* increment pointer */ bp->cnt--; /* decrease byte count */ } -static inline void -flush_FIFO(register struct scc_channel *scc) + +/* External/Status interrupt handler */ +static __inline__ void +scc_exint(struct scc_channel *scc) { - register int k; - - for (k=0; k<3; k++) - Inb(scc->data); - - if(scc->rx_bp != NULLBUF) /* did we receive something? */ - { - scc->stat.rxerrs++; /* then count it as an error */ - scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp); - - scc->rx_bp = NULLBUF; - } -} - - -/* External/Status interrupt handler */ -static void -scc_exint(register struct scc_channel *scc) -{ - register unsigned char status,changes,chg_and_stat; + unsigned char status,changes,chg_and_stat; scc->stat.exints++; @@ -920,9 +903,9 @@ scc_exint(register struct scc_channel *scc) /* ABORT: generated whenever DCD drops while receiving */ if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ - flush_FIFO(scc); - - + flush_rx_FIFO(scc); + + /* DCD: on = start to receive packet, off = ABORT condition */ /* (a successfully received packet generates a special condition int) */ @@ -936,69 +919,67 @@ scc_exint(register struct scc_channel *scc) or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ } else { /* DCD is now OFF */ cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ - flush_FIFO(scc); + flush_rx_FIFO(scc); } + + if (!scc->kiss.softdcd) + scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF); + } + + /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ + + if (changes & SYNC_HUNT) + { + if (scc->kiss.softdcd) + scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON); + else + cl(scc,R15,SYNCIE); /* oops, we were too lazy to disable this? */ } #ifdef notdef - /* CTS: use external TxDelay (what's that good for?!) */ - + /* CTS: use external TxDelay (what's that good for?!) + * Anyway: If we _could_ use it (BayCom USCC uses CTS for + * own purposes) we _should_ use the "autoenable" feature + * of the Z8530 and not this interrupt... + */ + if (chg_and_stat & CTS) /* CTS is now ON */ - { - if (!Running(t_txdel) && scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc->t_txdel = 0; /* kick it! */ - + { + if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ + scc_start_tx_timer(scc, t_txdelay, 0); } #endif - if ( (status & TxEOM) && (scc->stat.tx_state == TXS_ACTIVE) ) + if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) { scc->stat.tx_under++; /* oops, an underrun! count 'em */ - Outb(scc->ctrl, RES_Tx_P); Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ - scc->t_maxk = 1; - + if (scc->tx_bp != NULLBUF) { scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp); + scc->stat.tx_queued--; scc->tx_bp = NULLBUF; } - if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0; or(scc,R10,ABUNDER); + scc_start_tx_timer(scc, t_txdelay, 1); /* restart transmission */ } - if (status & ZCOUNT) /* Oops? */ - { - scc->stat.tx_under = 9999; /* errr... yes. */ - Outb(scc->ctrl, RES_Tx_P); /* just to be sure */ - scc->t_maxk = 1; - - if (scc->tx_bp != NULLBUF) - { - scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp); - scc->tx_bp = NULLBUF; - } - - if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0; - scc->kiss.tx_inhibit = 1; /* don't try it again! */ - } - - scc->status = status; Outb(scc->ctrl,RES_EXT_INT); } /* Receiver interrupt handler */ -static void -scc_rxint(register struct scc_channel *scc) +static __inline__ void +scc_rxint(struct scc_channel *scc) { - register struct mbuf *bp; + struct mbuf *bp; scc->stat.rxints++; - if( Running(t_maxk) && !(scc->kiss.fulldup)) + if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) { Inb(scc->data); /* discard char */ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ @@ -1012,20 +993,22 @@ scc_rxint(register struct scc_channel *scc) bp = scc_get_buffer(scc, BT_RECEIVE); if (bp == NULLBUF) { - printk("scc_rxint(): panic --- cannot get a buffer\n"); Inb(scc->data); or(scc, R3, ENT_HM); - scc->stat.nospace++; return; } scc->rx_bp = bp; + + *bp->rw_ptr=0; /* KISS data */ + bp->rw_ptr++; + bp->cnt++; } if (bp->cnt > scc->stat.bufsize) { #ifdef notdef - printk("scc_rxint(): oops, received huge frame...\n"); + printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); #endif scc_enqueue_buffer(&scc->rx_buffer_pool, bp); scc->rx_bp = NULLBUF; @@ -1040,33 +1023,13 @@ scc_rxint(register struct scc_channel *scc) bp->cnt++; } -/* kick rx_timer (try to send received frame or part of it ASAP) */ - -/* of course we could define a "bottom half" routine to do the job, - but since its structures are saved in an array instead of a linked - list we would get in trouble if it clashes with another driver or - when we try to modularize the driver. IMHO we are fast enough - with a timer routine called on the next timer-INT... Your opinions? - */ - -static inline void -kick_rx_timer(register struct scc_channel *scc) -{ - if (scc->rx_t.next) - del_timer(&(scc->rx_t)); - - scc->rx_t.expires = jiffies + 1; - scc->rx_t.function = scc_rx_timer; - scc->rx_t.data = (unsigned long) scc; - add_timer(&scc->rx_t); -} /* Receive Special Condition interrupt handler */ -static void -scc_spint(register struct scc_channel *scc) +static __inline__ void +scc_spint(struct scc_channel *scc) { - register unsigned char status; - register struct mbuf *bp; + unsigned char status; + struct mbuf *bp; scc->stat.spints++; @@ -1083,7 +1046,7 @@ scc_spint(register struct scc_channel *scc) if (bp) scc_enqueue_buffer(&scc->rx_buffer_pool, bp); scc->rx_bp = NULLBUF; } - + if(status & END_FR && bp != NULLBUF) /* end of frame */ { /* CRC okay, frame ends on 8 bit boundary and received something ? */ @@ -1092,23 +1055,141 @@ scc_spint(register struct scc_channel *scc) { /* ignore last received byte (first of the CRC bytes) */ bp->cnt--; - - scc_enqueue_buffer(&scc->rx_queue, bp); + + if (scc->stat.is_netdev) + { +#ifdef CONFIG_SCC_DEV + scc_net_rx(scc, bp); + scc_enqueue_buffer(&scc->rx_buffer_pool, bp); +#endif + } else { +#ifdef CONFIG_SCC_TTY + scc_enqueue_buffer(&scc->rx_queue, bp); + scc->stat.rx_queued++; + kick_rx_timer(scc); +#endif + } + scc->rx_bp = NULLBUF; scc->stat.rxframes++; - scc->stat.rx_queued++; - kick_rx_timer(scc); } else { /* a bad frame */ scc_enqueue_buffer(&scc->rx_buffer_pool, bp); scc->rx_bp = NULLBUF; scc->stat.rxerrs++; } + } + + Outb(scc->ctrl,ERR_RES); +} + + +/* ----> interrupt service routine for the Z8530 <---- */ + +static void +scc_isr_dispatch(struct scc_channel *scc, int vector) +{ + switch (vector & VECTOR_MASK) + { + case TXINT: scc_txint(scc); break; + case EXINT: scc_exint(scc); break; + case RXINT: scc_rxint(scc); break; + case SPINT: scc_spint(scc); break; } +} + +/* If the card has a latch for the interrupt vector (like the PA0HZP card) + use it to get the number of the chip that generated the int. + If not: poll all defined chips. + */ + +#define SCC_IRQTIMEOUT 30000 + +static void +scc_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char vector; + struct scc_channel *scc; + struct scc_ctrl *ctrl; + int k; - Outb(scc->ctrl,ERR_RES); + scc_cli(irq); + + if (Vector_Latch) + { + for(k=0; k < SCC_IRQTIMEOUT; k++) + { + Outb(Vector_Latch, 0); /* Generate INTACK */ + + /* Read the vector */ + if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; + if (vector & 0x01) break; + + scc=&SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->tty && !scc->dev) break; + + scc_isr_dispatch(scc, vector); + + OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ + } + scc_sti(irq); + + if (k == SCC_IRQTIMEOUT) + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); + + return; + } + + /* Find the SCC generating the interrupt by polling all attached SCCs + * reading RR3A (the interrupt pending register) + */ + + ctrl = SCC_ctrl; + while (ctrl->chan_A) + { + if (ctrl->irq != irq) + { + ctrl++; + continue; + } + + scc = NULL; + for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) + { + vector=InReg(ctrl->chan_B,R2); /* Read the vector */ + if (vector & 0x01) break; + + scc = &SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->tty && !scc->dev) break; + + scc_isr_dispatch(scc, vector); + } + + if (k == SCC_IRQTIMEOUT) + { + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); + break; + } + + /* This looks wierd and it is. At least the BayCom USCC doesn't + * use the Interrupt Daisy Chain, thus we'll have to start + * all over again to be sure not to miss an interrupt from + * (any of) the other chip(s)... + * Honestly, the situation *is* braindamaged... + */ + + if (scc != NULL) + { + OutReg(scc->ctrl,R0,RES_H_IUS); + ctrl = SCC_ctrl; + } else + ctrl++; + } + + scc_sti(irq); } + /* ******************************************************************** */ /* * Init Channel */ /* ******************************************************************** */ @@ -1116,30 +1197,31 @@ scc_spint(register struct scc_channel *scc) /* ----> set SCC channel speed <---- */ -static inline void set_brg(register struct scc_channel *scc, unsigned int tc) +static __inline__ void +set_brg(struct scc_channel *scc, unsigned int tc) { - unsigned long flags; - - save_flags(flags); cli(); /* 2-step register accesses... */ - cl(scc,R14,BRENABL); /* disable baudrate generator */ wr(scc,R12,tc & 255); /* brg rate LOW */ wr(scc,R13,tc >> 8); /* brg rate HIGH */ or(scc,R14,BRENABL); /* enable baudrate generator */ - - restore_flags(flags); } -static inline void set_speed(register struct scc_channel *scc) +static __inline__ void +set_speed(struct scc_channel *scc) { + disable_irq(scc->irq); + if (scc->modem.speed > 0) /* paranoia... */ set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); + + enable_irq(scc->irq); } /* ----> initialize a SCC channel <---- */ -static inline void init_brg(register struct scc_channel *scc) +static __inline__ void +init_brg(struct scc_channel *scc) { wr(scc, R14, BRSRC); /* BRG source = PCLK */ OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ @@ -1192,14 +1274,17 @@ static inline void init_brg(register struct scc_channel *scc) */ static void -init_channel(register struct scc_channel *scc) +init_channel(struct scc_channel *scc) { - unsigned long flags; + disable_irq(scc->irq); - if (scc->rx_t.next) del_timer(&(scc->rx_t)); - if (scc->tx_t.next) del_timer(&(scc->tx_t)); +#ifdef CONFIG_SCC_TTY + del_timer(&scc->rx_t); +#endif + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); + - save_flags(flags); cli(); wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ wr(scc,R1,0); /* no W/REQ operation */ @@ -1273,9 +1358,12 @@ init_channel(register struct scc_channel *scc) or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ } - /* enable CTS (not for Baycom), ABORT & DCD interrupts */ - wr(scc,R15,((scc->brand & BAYCOM) ? 0 : CTSIE)|BRKIE|DCDIE|TxUIE); - + /* enable ABORT, DCD & SYNC/HUNT interrupts */ + + wr(scc,R15, BRKIE|TxUIE|DCDIE); + if (scc->kiss.softdcd) + or(scc,R15, SYNCIE); + Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ @@ -1287,7 +1375,7 @@ init_channel(register struct scc_channel *scc) scc_init_timer(scc); - restore_flags(flags); + enable_irq(scc->irq); } @@ -1305,21 +1393,29 @@ static void scc_key_trx(struct scc_channel *scc, char tx) { unsigned int time_const; - - if (scc->modem.speed < baud_table[1]) - scc->modem.speed = 1200; if (scc->brand & PRIMUS) Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); - + + if (scc->modem.speed < 300) + scc->modem.speed = 1200; + time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; - + + disable_irq(scc->irq); + + if (tx) + { + or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ + or(scc, R15, TxUIE); + } + if (scc->modem.clocksrc == CLK_DPLL) - { /* simplex operation */ + { /* force simplex operation */ if (tx) { - cl(scc,R3,RxENABLE|ENT_HM); /* then switch off receiver */ - + cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ + cl(scc, R15, DCDIE); /* No DCD changes, please */ set_brg(scc, time_const); /* reprogram baudrate generator */ /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ @@ -1335,21 +1431,140 @@ scc_key_trx(struct scc_channel *scc, char tx) wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); or(scc,R3,RxENABLE|ENT_HM); + or(scc,R15, DCDIE); } } else { if (tx) + { + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + cl(scc, R3, RxENABLE); + cl(scc, R15, DCDIE); + } + + or(scc,R5,RTS|TxENAB); /* enable tx */ - else + } else { cl(scc,R5,RTS|TxENAB); /* disable tx */ + + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + { + or(scc, R3, RxENABLE|ENT_HM); + or(scc, R15, DCDIE); + } + } + } + + enable_irq(scc->irq); +} + + +/* ----> SCC timer interrupt handler and friends. <---- */ + +static void +scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_t); + + if (when == 0) + { + handler((unsigned long) scc); + } else + if (when != TIMER_OFF) + { + scc->tx_t.data = (unsigned long) scc; + scc->tx_t.function = handler; + scc->tx_t.expires = jiffies + (when*HZ)/100; + add_timer(&scc->tx_t); + } + + restore_flags(flags); +} + +static void +scc_start_defer(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + + if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) + { + scc->tx_wdog.data = (unsigned long) scc; + scc->tx_wdog.function = t_busy; + scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; + add_timer(&scc->tx_wdog); + } + restore_flags(flags); +} + +static void +scc_start_maxkeyup(struct scc_channel *scc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + del_timer(&scc->tx_wdog); + + if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) + { + scc->tx_wdog.data = (unsigned long) scc; + scc->tx_wdog.function = t_maxkeyup; + scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; + add_timer(&scc->tx_wdog); } + + restore_flags(flags); } +/* + * This is called from scc_txint() when there are no more frames to send. + * Not exactly a timer function, but it is a close friend of the family... + */ + +static void +scc_tx_done(struct scc_channel *scc) +{ + /* + * trx remains keyed in fulldup mode 2 until t_idle expires. + */ + + switch (scc->kiss.fulldup) + { + case KISS_DUPLEX_LINK: + scc->stat.tx_state = TXS_IDLE2; + if (scc->kiss.idletime != TIMER_OFF) + scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100); + break; + case KISS_DUPLEX_OPTIMA: + scc_notify(scc, HWEV_ALL_SENT); + break; + default: + scc->stat.tx_state = TXS_BUSY; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); + } + +#ifdef CONFIG_SCC_DEV + if (scc->stat.is_netdev) + scc->dev->tbusy = 0; +#endif +} -/* ----> SCC timer interrupt handler and friends. Will be called every 1/TPS s <---- */ static unsigned char Rand = 17; -static inline int is_grouped(register struct scc_channel *scc) +static __inline__ int +is_grouped(struct scc_channel *scc) { int k; struct scc_channel *scc2; @@ -1362,8 +1577,8 @@ static inline int is_grouped(register struct scc_channel *scc) scc2 = &SCC_Info[k]; grp2 = scc2->kiss.group; - if (scc2 == scc || !(scc2->tty && grp2)) - return 0; + if (scc2 == scc || !(scc2->tty && scc2->dev && grp2)) + continue; if ((grp1 & 0x3f) == (grp2 & 0x3f)) { @@ -1376,205 +1591,244 @@ static inline int is_grouped(register struct scc_channel *scc) } return 0; } - -static inline void dw_slot_timeout(register struct scc_channel *scc) +/* DWAIT and SLOTTIME expired + * + * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer + * else key trx and start txdelay + * fulldup == 1: key trx and start txdelay + * fulldup == 2: mintime expired, reset status or key trx and start txdelay + */ + +static void +t_dwait(unsigned long channel) { - scc->t_dwait = TIMER_STOPPED; - scc->t_slot = TIMER_STOPPED; + struct scc_channel *scc = (struct scc_channel *) channel; - if (!scc->kiss.fulldup) + if (scc->stat.tx_state == TXS_WAIT) + { + if (scc->tx_queue == NULL) + { + scc->stat.tx_state = TXS_IDLE; + return; + } + + scc->stat.tx_state = TXS_BUSY; + } + + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) { Rand = Rand * 17 + 31; if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)) || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) { - if (scc->t_mbusy == TIMER_STOPPED) - scc->t_mbusy = TPS * scc->kiss.maxdefer; - - scc->t_slot = scc->kiss.slottime; + scc_start_defer(scc); + scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); return ; - } } - + if ( !(scc->wreg[R5] & RTS) ) { - scc->t_txdel = scc->kiss.txdelay; scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); } else { - scc->t_txdel = 0; + scc_start_tx_timer(scc, t_txdelay, 0); } } +/* TXDELAY expired + * + * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. + */ - -static inline void txdel_timeout(register struct scc_channel *scc) +static void +t_txdelay(unsigned long channel) { - scc->t_txdel = TIMER_STOPPED; - - scc->t_maxk = TPS * scc->kiss.maxkeyup; - + struct scc_channel *scc = (struct scc_channel *) channel; + + scc_start_maxkeyup(scc); + if (scc->tx_bp == NULLBUF) + { + disable_irq(scc->irq); scc_txint(scc); + enable_irq(scc->irq); + } } +/* TAILTIME expired + * + * switch off transmitter. If we were stopped by Maxkeyup restart + * transmission after 'mintime' seconds + */ -static inline void tail_timeout(register struct scc_channel *scc) +static void +t_tail(unsigned long channel) { - scc->t_tail = TIMER_STOPPED; + struct scc_channel *scc = (struct scc_channel *) channel; - /* when fulldup is 0 or 1, switch off the transmitter. - * when frames are still queued (because of transmit time limit), - * restart the procedure to get the channel after MINTIME. - * when fulldup is 2, the transmitter remains keyed and we - * continue sending after waiting for waittime. IDLETIME is an - * idle timeout in this case. - */ - - if (scc->kiss.fulldup < 2) - { - if (scc->tx_bp) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_BUSY; - scc->t_dwait = TPS * scc->kiss.mintime; /* try again */ - } - - scc->stat.tx_state = TXS_IDLE; - scc->t_maxk = TIMER_STOPPED; - scc_key_trx(scc, TX_OFF); - return; - } - - if (scc->tx_bp) /* maxkeyup expired */ /* ?! */ - { - scc->stat.tx_state = TXS_BUSY; - scc->t_txdel = TPS * scc->kiss.waittime; - } - else - - scc->t_idle = TPS * scc->kiss.idletime; + del_timer(&scc->tx_wdog); + scc_key_trx(scc, TX_OFF); + + if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ + { + scc->stat.tx_state = TXS_WAIT; + + if (scc->tx_bp != NULLBUF) + scc_enqueue_buffer(&scc->tx_queue, scc->tx_bp); + + scc->tx_bp = NULLBUF; + + if (scc->kiss.mintime != TIMER_OFF) /* try it again */ + scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); + else + scc_start_tx_timer(scc, t_dwait, 0); + return; + } + + scc->stat.tx_state = TXS_IDLE; + +#ifdef CONFIG_SCC_DEV + if (scc->stat.is_netdev) + scc->dev->tbusy = 0; +#endif } -static inline void busy_timeout(register struct scc_channel *scc) +/* BUSY timeout + * + * throw away send buffers if DCD remains active too long. + */ + +static void +t_busy(unsigned long channel) { -#ifdef THROW_AWAY_AFTER_BUSY_TIMEOUT - register struct mbuf *bp; /* not tested */ + struct scc_channel *scc = (struct scc_channel *) channel; + struct mbuf *bp; + + del_timer(&scc->tx_t); - while (bp = scc_dequeue_buffer(&scc->tx_queue)) + while ((bp = scc_dequeue_buffer(&scc->tx_queue)) != NULLBUF) scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + + scc->stat.tx_queued = 0; + scc->stat.txerrs++; scc->tx_queue = NULLBUF; scc->stat.tx_state = TXS_IDLE; - -#else - scc->t_txdel = scc->kiss.txdelay; /* brute force ... */ + +#ifdef CONFIG_SCC_DEV + if (scc->stat.is_netdev) + scc->dev->tbusy = 0; #endif - scc->t_mbusy = TIMER_STOPPED; - } +/* MAXKEYUP timeout + * + * this is our watchdog. + */ -static inline void maxk_idle_timeout(register struct scc_channel *scc) -{ - scc->t_maxk = TIMER_STOPPED; - scc->t_idle = TIMER_STOPPED; - - scc->stat.tx_state = TXS_BUSY; - scc->t_tail = scc->kiss.tailtime; -} - -static void -scc_tx_timer(unsigned long channel) +static void +t_maxkeyup(unsigned long channel) { - register struct scc_channel *scc; + struct scc_channel *scc = (struct scc_channel *) channel; unsigned long flags; - - scc = (struct scc_channel *) channel; - - if (scc->tty && scc->init) - { - save_flags(flags); cli(); - - /* KISS-TNC emulation */ - - if (Expired(t_dwait)) dw_slot_timeout(scc) ; else - if (Expired(t_slot)) dw_slot_timeout(scc) ; else - if (Expired(t_txdel)) txdel_timeout(scc) ; else - if (Expired(t_tail)) tail_timeout(scc) ; - - /* watchdogs */ - - if (Expired(t_mbusy)) busy_timeout(scc); - if (Expired(t_maxk)) maxk_idle_timeout(scc); - if (Expired(t_idle)) maxk_idle_timeout(scc); - - restore_flags(flags); + save_flags(flags); + cli(); + + if (scc->stat.is_netdev) + { +#ifdef CONFIG_SCC_DEV + /* + * let things settle down before we start to + * accept new data. + */ + scc->dev->tbusy = 1; + scc->dev->trans_start = jiffies; +#endif } + + del_timer(&scc->tx_t); + + cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ + cl(scc, R15, TxUIE); /* count it. */ + OutReg(scc->ctrl, R0, RES_Tx_P); + + restore_flags(flags); + + scc->stat.txerrs++; + scc->stat.tx_state = TXS_TIMEOUT; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); +} + +/* IDLE timeout + * + * in fulldup mode 2 it keys down the transmitter after 'idle' seconds + * of inactivity. We will not restart transmission before 'mintime' + * expires. + */ + +static void +t_idle(unsigned long channel) +{ + struct scc_channel *scc = (struct scc_channel *) channel; - scc->tx_t.expires = jiffies + HZ/TPS; - add_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); + scc_key_trx(scc, TX_OFF); + + if (scc->kiss.mintime != TIMER_OFF) + scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); + scc->stat.tx_state = TXS_WAIT; } - + +#ifdef CONFIG_SCC_TTY static void scc_rx_timer(unsigned long channel) { - register struct scc_channel *scc; + struct scc_channel *scc; scc = (struct scc_channel *) channel; if (scc->rx_queue && scc->throttled) { - scc->rx_t.expires = jiffies + HZ/TPS; + scc->rx_t.expires = jiffies + HZ/25; /* 40 msec */ add_timer(&scc->rx_t); return; } kiss_encode(scc); - + if (scc->rx_queue && !scc->throttled) { - printk("z8530drv: warning: %s should be throttled\n", - kdevname(scc->tty->device)); - - scc->rx_t.expires = jiffies + HZ/TPS; + printk(KERN_DEBUG "z8530drv: warning, %s should be throttled\n", + SCC_DEVNAME); + + scc->rx_t.expires = jiffies + HZ/25; /* 40 msec */ add_timer(&scc->rx_t); } } +#endif static void scc_init_timer(struct scc_channel *scc) { unsigned long flags; - save_flags(flags); cli(); - - Stop_Timer(t_dwait); - Stop_Timer(t_slot); - Stop_Timer(t_txdel); - Stop_Timer(t_tail); - Stop_Timer(t_mbusy); - Stop_Timer(t_maxk); - Stop_Timer(t_idle); + save_flags(flags); + cli(); scc->stat.tx_state = TXS_IDLE; - if (scc->tx_t.next) - del_timer(&scc->tx_t); - - scc->tx_t.data = (unsigned long) scc; - scc->tx_t.function = scc_tx_timer; - scc->tx_t.expires = jiffies + HZ/TPS; - add_timer(&scc->tx_t); - +#ifdef CONFIG_SCC_TTY scc->rx_t.data = (unsigned long) scc; scc->rx_t.function = scc_rx_timer; +#endif restore_flags(flags); } @@ -1586,54 +1840,111 @@ scc_init_timer(struct scc_channel *scc) /* - * this will set the "kiss" parameters through kiss itself + * this will set the "kiss" parameters through KISS commands or ioctl() */ + +#define CAST(x) (unsigned long)(x) + +static unsigned int +kiss_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) +{ + int dcd; + + switch (cmd) + { + case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; + case PARAM_PERSIST: scc->kiss.persist=arg; break; + case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; + case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; + case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; + case PARAM_DTR: break; /* does someone need this? */ + case PARAM_GROUP: scc->kiss.group=arg; break; + case PARAM_IDLE: scc->kiss.idletime=arg; break; + case PARAM_MIN: scc->kiss.mintime=arg; break; + case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; + case PARAM_WAIT: scc->kiss.waittime=arg; break; + case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; + case PARAM_TX: scc->kiss.tx_inhibit=arg; break; + + case PARAM_SOFTDCD: + scc->kiss.softdcd=arg; + if (arg) + or(scc, R15, SYNCIE); + else + cl(scc, R15, SYNCIE); + break; + + case PARAM_SPEED: + if (arg < 256) + scc->modem.speed=arg*100; + else + scc->modem.speed=arg; + + if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ + set_speed(scc); + break; + + case PARAM_RTS: + if ( !(scc->wreg[R5] & RTS) ) + { + if (arg != TX_OFF) + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); + } else { + if (arg == TX_OFF) + { + scc->stat.tx_state = TXS_BUSY; + scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); + } + } + break; + + case PARAM_HWEVENT: + dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD)); + scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF); + break; + + default: return -EINVAL; + } + + return 0; +} + + -static void -kiss_set_param(struct scc_channel *scc,char cmd, unsigned int val) -{ - -#define VAL val=val*TPS/100 -#define SVAL val? val:TIMER_STOPPED - - switch(cmd){ - case PARAM_TXDELAY: - scc->kiss.txdelay = VAL; break; - case PARAM_PERSIST: - scc->kiss.persist = val; break; - case PARAM_SLOTTIME: - scc->kiss.slottime = VAL; break; - case PARAM_TXTAIL: - scc->kiss.tailtime = VAL; break; - case PARAM_FULLDUP: - scc->kiss.fulldup = val; break; - case PARAM_WAIT: - scc->kiss.waittime = VAL; break; - case PARAM_MAXKEY: - scc->kiss.maxkeyup = SVAL; break; - case PARAM_MIN: - scc->kiss.mintime = SVAL; break; - case PARAM_IDLE: - scc->kiss.idletime = val; break; - case PARAM_MAXDEFER: - scc->kiss.maxdefer = SVAL; break; - case PARAM_GROUP: - scc->kiss.group = val; break; - case PARAM_TX: - scc->kiss.tx_inhibit = val; - case PARAM_SOFTDCD: - scc->kiss.softdcd = val; +static unsigned long +kiss_get_param(struct scc_channel *scc, unsigned int cmd) +{ + switch (cmd) + { + case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); + case PARAM_PERSIST: return CAST(scc->kiss.persist); + case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); + case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); + case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); + case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); + case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); + case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); + case PARAM_SPEED: return CAST(scc->modem.speed); + case PARAM_GROUP: return CAST(scc->kiss.group); + case PARAM_IDLE: return CAST(scc->kiss.idletime); + case PARAM_MIN: return CAST(scc->kiss.mintime); + case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); + case PARAM_WAIT: return CAST(scc->kiss.waittime); + case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); + case PARAM_TX: return CAST(scc->kiss.tx_inhibit); + default: return NO_SUCH_PARAM; } - return; -#undef VAL -#undef SVAL } +#undef CAST +#undef SVAL /* interpret frame: strip CRC and decode KISS */ -static void kiss_interpret_frame(struct scc_channel * scc) +static void +kiss_interpret_frame(struct scc_channel * scc) { unsigned char kisscmd; unsigned long flags; @@ -1642,14 +1953,6 @@ static void kiss_interpret_frame(struct scc_channel * scc) bp = scc->kiss_decode_bp; bp->rw_ptr = bp->data; -#ifdef DEBUG_BUFFERS - if (bp == NULLBUF) - { - printk("kiss_interpret_frame(): weird --- nothing to do.\n"); - return; - } -#endif - if (bp->cnt < 2) { scc_enqueue_buffer(&scc->tx_buffer_pool, bp); @@ -1657,15 +1960,8 @@ static void kiss_interpret_frame(struct scc_channel * scc) return; } - - - if (scc->kiss.not_slip) - { - kisscmd = *bp->rw_ptr; - bp->rw_ptr++; - } else { - kisscmd = 0; - } + kisscmd = *bp->rw_ptr; + bp->rw_ptr++; if (kisscmd & 0xa0) { @@ -1679,10 +1975,8 @@ static void kiss_interpret_frame(struct scc_channel * scc) } } - kisscmd &= 0x1f; - - + if (kisscmd) { kiss_set_param(scc, kisscmd, *bp->rw_ptr); @@ -1692,32 +1986,47 @@ static void kiss_interpret_frame(struct scc_channel * scc) } scc_enqueue_buffer(&scc->tx_queue, bp); /* enqueue frame */ + scc->kiss_decode_bp = NULLBUF; scc->stat.txframes++; scc->stat.tx_queued++; - scc->kiss_decode_bp = NULLBUF; - save_flags(flags); cli(); + save_flags(flags); + cli(); - if(scc->stat.tx_state == TXS_IDLE) - { /* when transmitter is idle */ + /* + * start transmission if the trx state is idle or + * t_idle hasn't expired yet. Use dwait/persistance/slottime + * algorithm for normal halfduplex operation. + */ + + if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) + { scc->stat.tx_state = TXS_BUSY; - scc->t_dwait = (scc->kiss.fulldup? 0:scc->kiss.waittime); + if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); + else + scc_start_tx_timer(scc, t_dwait, 0); } - + restore_flags(flags); } -static inline void kiss_store_byte(struct scc_channel *scc, unsigned char ch) +#ifdef CONFIG_SCC_TTY +static inline void +kiss_store_byte(struct scc_channel *scc, unsigned char ch) { - register struct mbuf *bp = scc->kiss_decode_bp; + struct mbuf *bp = scc->kiss_decode_bp; + static int k = 0; if (bp != NULLBUF) { if (bp->cnt > scc->stat.bufsize) - printk("kiss_decode(): frame too long\n"); - else { + if (!k++) + printk(KERN_NOTICE "z8530drv: KISS frame too long\n"); + } else { + k = 0; *bp->rw_ptr = ch; bp->rw_ptr++; bp->cnt++; @@ -1725,7 +2034,8 @@ static inline void kiss_store_byte(struct scc_channel *scc, unsigned char ch) } } -static inline int kiss_decode(struct scc_channel *scc, unsigned char ch) +static inline int +kiss_decode(struct scc_channel *scc, unsigned char ch) { switch (scc->stat.tx_kiss_state) { @@ -1779,7 +2089,7 @@ static inline int kiss_decode(struct scc_channel *scc, unsigned char ch) /* ----> Encode received data and write it to the flip-buffer <---- */ static void -kiss_encode(register struct scc_channel *scc) +kiss_encode(struct scc_channel *scc) { struct mbuf *bp; struct tty_struct * tty = scc->tty; @@ -1825,10 +2135,6 @@ kiss_encode(register struct scc_channel *scc) if (scc->stat.rx_kiss_state == KISS_IDLE) { tty_insert_flip_char(tty, FEND, 0); - - if (scc->kiss.not_slip) - tty_insert_flip_char(tty, 0, 0); - scc->stat.rx_kiss_state = KISS_RXFRAME; } @@ -1852,6 +2158,7 @@ kiss_encode(register struct scc_channel *scc) queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */ } +#endif /* CONFIG_SCC_TTY */ /* ******************************************************************* */ @@ -1868,7 +2175,7 @@ z8530_init(void) char *flag; - printk("Init Z8530 driver: %u channels, IRQ", Nchips*2); + printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); flag=" "; for (k = 0; k < 16; k++) @@ -1885,9 +2192,7 @@ z8530_init(void) { scc=&SCC_Info[2*chip]; if (!scc->ctrl) continue; - - save_flags(flags); cli(); /* because of 2-step accesses */ - + /* Special SCC cards */ if(scc->brand & EAGLE) /* this is an EAGLE card */ @@ -1901,12 +2206,15 @@ z8530_init(void) /* some general init we can do now */ + save_flags(flags); + cli(); + Outb(scc->ctrl, 0); OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ udelay(100); /* give it 'a bit' more time than required */ wr(scc, R2, chip*16); /* interrupt vector */ wr(scc, R9, VIS); /* vector includes status */ - + restore_flags(flags); } @@ -1920,18 +2228,17 @@ z8530_init(void) /* ******************************************************************** */ - /* scc_paranoia_check(): warn user if something went wrong */ -static inline int scc_paranoia_check(struct scc_channel *scc, kdev_t device, - const char *routine) +static __inline__ int +scc_paranoia_check(struct scc_channel *scc, kdev_t device, const char *routine) { #ifdef SCC_PARANOIA_CHECK static const char *badmagic = - "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; + KERN_ALERT "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; static const char *badinfo = - "Warning: Z8530 not found for (%s) in %s\n"; + KERN_CRIT "Warning: Z8530 not found for (%s) in %s (forgot to run sccinit?)\n"; if (!scc->init) { @@ -1949,10 +2256,11 @@ static const char *badinfo = return 0; } - +#ifdef CONFIG_SCC_TTY /* ----> this one is called whenever you open the device <---- */ -int scc_open(struct tty_struct *tty, struct file * filp) +int +scc_open(struct tty_struct *tty, struct file * filp) { struct scc_channel *scc; int chan; @@ -1976,28 +2284,30 @@ int scc_open(struct tty_struct *tty, struct file * filp) if (scc->magic != SCC_MAGIC) { - printk("ERROR: scc_open(): bad magic number for device (%s)", + printk(KERN_ALERT "z8530drv: scc_open() found bad magic number for device (%s)", kdevname(tty->device)); return -ENODEV; - } - + } + MOD_INC_USE_COUNT; - - if(scc->tty != NULL) + + if(scc->tty != NULL || scc->stat.is_netdev) { scc->tty_opened++; return 0; } + + scc->tty = tty; - scc->tty = tty; + if(!scc->init) + return 0; + alloc_buffer_pool(scc); - - if(!scc->init) return 0; scc->throttled = 0; - scc->stat.tx_kiss_state = KISS_IDLE; /* don't change this... */ - scc->stat.rx_kiss_state = KISS_IDLE; /* ...or this */ + scc->stat.tx_kiss_state = KISS_IDLE; + scc->stat.rx_kiss_state = KISS_IDLE; init_channel(scc); return 0; @@ -2014,10 +2324,11 @@ scc_close(struct tty_struct *tty, struct file * filp) if (!scc || (scc->magic != SCC_MAGIC)) return; + MOD_DEC_USE_COUNT; - if(scc->tty_opened) + if(scc->tty_opened || scc->stat.is_netdev) { scc->tty_opened--; return; @@ -2028,21 +2339,26 @@ scc_close(struct tty_struct *tty, struct file * filp) if (!Driver_Initialized) return; - save_flags(flags); cli(); + save_flags(flags); + cli(); Outb(scc->ctrl,0); /* Make sure pointer is written */ wr(scc,R1,0); /* disable interrupts */ wr(scc,R3,0); scc->tty = NULL; - + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); del_timer(&scc->rx_t); - free_buffer_pool(scc); - restore_flags(flags); + if (!scc->init) + return; + + free_buffer_pool(scc); + scc->throttled = 0; tty->stopped = 0; } @@ -2068,135 +2384,144 @@ scc_change_speed(struct scc_channel * scc) if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ set_speed(scc); } - +#endif /* CONFIG_SCC_TTY */ /* ----> ioctl-routine of the driver <---- */ -/* perform ioctl on SCC (sdlc) channel - * this is used for AX.25 mode, and will set the "kiss" parameters - */ + +/* sub routines to read/set KISS parameters */ -/* TIOCMGET - get modem status arg: (unsigned long *) arg - * TIOCMBIS - set PTT arg: --- - * TIOCMBIC - reset PTT arg: --- - * TIOCMBIC - set PTT arg: --- - * TIOCSCCINI - initialize driver arg: --- - * TIOCCHANINI - initialize channel arg: (struct scc_modem *) arg - * TIOCGKISS - get level 1 parameter arg: (struct ioctl_command *) arg - * TIOCSKISS - set level 1 parameter arg: (struct ioctl_command *) arg - * TIOCSCCSTAT - get driver status arg: (struct scc_stat *) arg +/* generic ioctl() functions for both TTY or network device mode: + * + * TIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg + * TIOCSCCINI - initialize driver arg: --- + * TIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg + * TIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg + * TIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg + * TIOCSCCSTAT - get driver status arg: (struct scc_stat *) arg */ - static int -scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) +scc_ioctl(struct scc_channel *scc, unsigned int cmd, void *arg) { - struct scc_channel * scc = tty->driver_data; - unsigned long flags, r; - unsigned int result; - unsigned int value; - struct ioctl_command kiss_cmd; + unsigned long flags; + struct scc_kiss_cmd kiss_cmd; struct scc_mem_config memcfg; struct scc_hw_config hwcfg; int error, chan; - - if (scc->magic != SCC_MAGIC) - { - printk("ERROR: scc_ioctl(): bad magic number for device %s", - kdevname(tty->device)); - - return -ENODEV; - } - - r = NO_SUCH_PARAM; + unsigned char device_name[10]; if (!Driver_Initialized) { if (cmd == TIOCSCCCFG) { int found = 1; - + if (!suser()) return -EPERM; if (!arg) return -EFAULT; - + + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_hw_config)); + if (error) return error; + if (Nchips >= MAXSCC) return -EINVAL; - - memcpy_fromfs(&hwcfg, (void *) arg, sizeof(hwcfg)); - + + memcpy_fromfs(&hwcfg, arg, sizeof(hwcfg)); + if (hwcfg.irq == 2) hwcfg.irq = 9; - + if (!Ivec[hwcfg.irq].used && hwcfg.irq) { if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) - printk("z8530drv: Warning --- could not get IRQ %d\n", hwcfg.irq); + printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); else Ivec[hwcfg.irq].used = 1; } - + if (hwcfg.vector_latch) Vector_Latch = hwcfg.vector_latch; - + if (hwcfg.clock == 0) hwcfg.clock = DEFAULT_CLOCK; #ifndef DONT_CHECK - save_flags(flags); cli(); - + disable_irq(hwcfg.irq); + check_region(scc->ctrl, 1); Outb(hwcfg.ctrl_a, 0); udelay(5); OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ udelay(5); - - if (InReg(hwcfg.ctrl_a,R13) != 0x55 ) + + if (InReg(hwcfg.ctrl_a,R13) != 0x55) found = 0; - - restore_flags(flags); + + enable_irq(hwcfg.irq); #endif if (found) { SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; SCC_Info[2*Nchips ].data = hwcfg.data_a; + SCC_Info[2*Nchips ].irq = hwcfg.irq; SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; SCC_Info[2*Nchips+1].data = hwcfg.data_b; + SCC_Info[2*Nchips+1].irq = hwcfg.irq; - SCC_ctrl[2*Nchips ] = hwcfg.ctrl_a; - SCC_ctrl[2*Nchips+1] = hwcfg.ctrl_b; + SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; + SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; + SCC_ctrl[Nchips].irq = hwcfg.irq; } - + + for (chan = 0; chan < 2; chan++) { + sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); + SCC_Info[2*Nchips+chan].special = hwcfg.special; SCC_Info[2*Nchips+chan].clock = hwcfg.clock; SCC_Info[2*Nchips+chan].brand = hwcfg.brand; SCC_Info[2*Nchips+chan].option = hwcfg.option; SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; - + #ifdef DONT_CHECK - printk("%s%i: data port = 0x%3.3x control port = 0x%3.3x\n", - scc_driver.name, 2*Nchips+chan, + printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", + device_name, SCC_Info[2*Nchips+chan].data, SCC_Info[2*Nchips+chan].ctrl); #else - printk("%s%i: data port = 0x%3.3x control port = 0x%3.3x -- %s\n", - scc_driver.name, 2*Nchips+chan, + printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x -- %s\n", + device_name, chan? hwcfg.data_b : hwcfg.data_a, chan? hwcfg.ctrl_b : hwcfg.ctrl_a, found? "found" : "missing"); #endif - + if (found) { request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); +#ifdef CONFIG_SCC_DEV + if (Nchips+chan != 0) + scc_net_setup(&SCC_Info[2*Nchips+chan], device_name); +#endif } } if (found) Nchips++; +#ifdef SCC_DEBUG + printk(KERN_INFO "Available modes:" +#ifdef CONFIG_SCC_DEV + " dev" +#endif +#ifdef CONFIG_SCC_TTY + " tty" +#endif + "\n"); +#endif + return 0; } @@ -2209,9 +2534,6 @@ scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned return -EINVAL; z8530_init(); - - scc->tty=tty; - alloc_buffer_pool(scc); return 0; } @@ -2220,263 +2542,268 @@ scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned if (!scc->init) { - if (cmd == TIOCCHANINI) + if (cmd == TIOCSCCCHANINI) { - if (!arg) - return -EFAULT; - - if (!suser()) - return -EPERM; - - memcpy_fromfs(&scc->modem, (void *) arg, sizeof(struct scc_modem)); + if (!suser()) return -EPERM; + if (!arg) return -EINVAL; + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_modem)); + if (error) return error; + + scc->stat.rxbuffers = RXBUFFERS; + scc->stat.txbuffers = TXBUFFERS; + scc->stat.bufsize = BUFSIZE; + + memcpy_fromfs(&scc->modem, arg, sizeof(struct scc_modem)); /* default KISS Params */ if (scc->modem.speed < 4800) { - scc->kiss.txdelay = 36*TPS/100; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16*TPS/100; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50*TPS/100; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.not_slip = 1; /* KISS mode */ - scc->kiss.softdcd = 0; /* hardware dcd */ + scc->kiss.txdelay = 36; /* 360 ms */ + scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ + scc->kiss.slottime = 16; /* 160 ms */ + scc->kiss.tailtime = 4; /* minimal reasonable value */ + scc->kiss.fulldup = 0; /* CSMA */ + scc->kiss.waittime = 50; /* 500 ms */ + scc->kiss.maxkeyup = 10; /* 10 s */ + scc->kiss.mintime = 3; /* 3 s */ + scc->kiss.idletime = 30; /* 30 s */ + scc->kiss.maxdefer = 120; /* 2 min */ + scc->kiss.softdcd = 0; /* hardware dcd */ } else { - scc->kiss.txdelay = 10*TPS/100; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8*TPS/100; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50*TPS/100; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.not_slip = 1; /* KISS mode */ - scc->kiss.softdcd = 0; /* hardware dcd */ + scc->kiss.txdelay = 10; /* 100 ms */ + scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ + scc->kiss.slottime = 8; /* 160 ms */ + scc->kiss.tailtime = 1; /* minimal reasonable value */ + scc->kiss.fulldup = 0; /* CSMA */ + scc->kiss.waittime = 50; /* 500 ms */ + scc->kiss.maxkeyup = 7; /* 7 s */ + scc->kiss.mintime = 3; /* 3 s */ + scc->kiss.idletime = 30; /* 30 s */ + scc->kiss.maxdefer = 120; /* 2 min */ + scc->kiss.softdcd = 0; /* hardware dcd */ } - scc->init = 1; + scc->init = 1; return 0; } return -EINVAL; } + + switch(cmd) + { + case TIOCSCCSMEM: + case TIOCCHANMEM_OLD: + if (!suser()) return -EPERM; + if (!arg) return -EINVAL; + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config)); + if (error) return error; + + memcpy_fromfs(&memcfg, arg, sizeof(struct scc_mem_config)); - switch(cmd){ - case TCSBRK: - return 0; - case TIOCMGET: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *)); - if (error) - return error; + save_flags(flags); + cli(); - save_flags(flags); cli(); + free_buffer_pool(scc); + scc->stat.rxbuffers = memcfg.rxbuffers; + scc->stat.txbuffers = memcfg.txbuffers; + scc->stat.bufsize = memcfg.bufsize; + if (scc->tty || (scc->dev && (scc->dev->flags & IFF_UP))) + alloc_buffer_pool(scc); - result = ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0) - | ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0) - | ((InReg(scc->ctrl,R0) & DCD) ? TIOCM_CAR : 0) - | ((InReg(scc->ctrl,R0) & CTS) ? TIOCM_CTS : 0); + restore_flags(flags); + return 0; - restore_flags(flags); + case TIOCSCCGSTAT: + case TIOCSCCSTAT_OLD: + if (!arg) return -EINVAL; + error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config)); + if (error) return error; - put_user(result,(unsigned int *) arg); - return 0; - case TIOCMBIS: - case TIOCMBIC: - case TIOCMSET: - switch (cmd) { - case TIOCMBIS: - scc->wreg[R5] |= DTR; - scc->wreg[R5] |= RTS; - break; - case TIOCMBIC: - scc->wreg[R5] &= ~DTR; - scc->wreg[R5] &= ~RTS; - break; - case TIOCMSET: - value = get_user((unsigned int *) arg); - - if(value & TIOCM_DTR) - scc->wreg[R5] |= DTR; - else - scc->wreg[R5] &= ~DTR; - if(value & TIOCM_RTS) - scc->wreg[R5] |= RTS; - else - scc->wreg[R5] &= ~RTS; - break; - } + memcpy_tofs(arg, &scc->stat, sizeof(struct scc_stat)); + return 0; - save_flags(flags); cli(); + case TIOCSCCGKISS: + case TIOCGKISS_OLD: + if (!arg) return -EINVAL; + error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config)); + if (error) return error; - if(scc->stat.tx_state == TXS_IDLE && !Running(t_idle)) - maxk_idle_timeout(scc); - - restore_flags(flags); + memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd)); + kiss_cmd.param = kiss_get_param(scc, kiss_cmd.command); + memcpy_tofs(arg, &kiss_cmd, sizeof(struct scc_kiss_cmd)); + return 0; + break; - return 0; + case TIOCSCCSKISS: + case TIOCSKISS_OLD: + if (!suser()) return -EPERM; + if (!arg) return -EINVAL; + error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config)); + if (error) return error; + + memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd)); + return kiss_set_param(scc, kiss_cmd.command, kiss_cmd.param); + break; + + default: + return -ENOIOCTLCMD; - case TCGETS: - error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios)); - if (error) - return error; - if (!arg) - return -EFAULT; + } + + return 0; +} + +#ifdef CONFIG_SCC_TTY +/* addtional ioctl() for TTY driver mode: + * + * TIOCMGET - get modem status arg: (unsigned long *) arg + * TIOCMBIS - set PTT arg: --- + * TIOCMBIC - reset PTT arg: --- + * TIOCMBIC - set PTT arg: --- + */ + +static int +scc_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct scc_channel * scc = tty->driver_data; + unsigned long flags; + unsigned int result; + unsigned int value; + int error; + + if (scc->magic != SCC_MAGIC) + { + printk(KERN_ALERT "z8530drv: scc_ioctl() found bad magic number for device %s", + SCC_DEVNAME); - memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios)); - return 0; - - case TCSETS: - case TCSETSF: /* should flush first, but... */ - case TCSETSW: /* should wait 'till flush, but... */ - if (!arg) - return -EFAULT; - - memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios)); - scc_change_speed(scc); - return 0; - - case TIOCCHANMEM: - if (!arg) - return -EFAULT; + return -ENODEV; + } + + if (!Driver_Initialized && cmd == TIOCSCCINI) + scc->tty=tty; + + switch(cmd) + { + case TIOCSCCCFG: + case TIOCSCCINI: + case TIOCSCCCHANINI: + case TIOCSCCSMEM: + case TIOCSCCSKISS: + case TIOCSCCGKISS: + + case TIOCSCCCFG_OLD: + case TIOCSCCINI_OLD: + case TIOCCHANINI_OLD: + case TIOCCHANMEM_OLD: + case TIOCSKISS_OLD: + case TIOCGKISS_OLD: + case TIOCSCCSTAT_OLD: + return scc_ioctl(scc, cmd, (void *) arg); + + case TCSBRK: + return 0; - memcpy_fromfs(&memcfg, (void *) arg, sizeof(struct scc_mem_config)); - - save_flags(flags); cli(); - - free_buffer_pool(scc); - scc->stat.rxbuffers = memcfg.rxbuffers; - scc->stat.txbuffers = memcfg.txbuffers; - scc->stat.bufsize = memcfg.bufsize; - alloc_buffer_pool(scc); - - restore_flags(flags); - return 0; - + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *)); + if (error) + return error; + + save_flags(flags); + cli(); - case TIOCSCCSTAT: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct scc_stat)); - if (error) - return error; + result = ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0) + | ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0) + | ((InReg(scc->ctrl,R0) & DCD) ? TIOCM_CAR : 0) + | ((InReg(scc->ctrl,R0) & CTS) ? TIOCM_CTS : 0); - if (!arg) - return -EFAULT; + restore_flags(flags); - memcpy_tofs((void *) arg, &scc->stat, sizeof(struct scc_stat)); - return 0; - -#define TICKS (100/TPS) -#define CAST(x) (unsigned long)(x) -#define Val kiss_cmd.param -#define VAL kiss_cmd.param*TPS/100 -#define SVAL kiss_cmd.param? kiss_cmd.param:TIMER_STOPPED - - case TIOCGKISS: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct ioctl_command)); - if (error) - return error; + put_user(result,(unsigned int *) arg); + return 0; - if (!arg) - return -EFAULT; + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + switch (cmd) + { + case TIOCMBIS: + scc->wreg[R5] |= DTR; + scc->wreg[R5] |= RTS; + break; + case TIOCMBIC: + scc->wreg[R5] &= ~DTR; + scc->wreg[R5] &= ~RTS; + break; + case TIOCMSET: + error = verify_area(VERIFY_READ, (void *) arg,sizeof(unsigned int *)); + if (error) + return error; + value = get_user((unsigned int *) arg); - memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command)); - - switch (kiss_cmd.command) - { - case PARAM_TXDELAY: r = CAST(scc->kiss.txdelay*TICKS); break; - case PARAM_PERSIST: r = CAST(scc->kiss.persist); break; - case PARAM_SLOTTIME: r = CAST(scc->kiss.slottime*TICKS); break; - case PARAM_TXTAIL: r = CAST(scc->kiss.tailtime*TICKS); break; - case PARAM_FULLDUP: r = CAST(scc->kiss.fulldup); break; - case PARAM_SOFTDCD: r = CAST(scc->kiss.softdcd); break; - case PARAM_DTR: r = CAST((scc->wreg[R5] & DTR)? 1:0); break; - case PARAM_RTS: r = CAST((scc->wreg[R5] & RTS)? 1:0); break; - case PARAM_SPEED: r = CAST(scc->modem.speed); break; - case PARAM_GROUP: r = CAST(scc->kiss.group); break; - case PARAM_IDLE: r = CAST(scc->kiss.idletime); break; - case PARAM_MIN: r = CAST(scc->kiss.mintime); break; - case PARAM_MAXKEY: r = CAST(scc->kiss.maxkeyup); break; - case PARAM_WAIT: r = CAST(scc->kiss.waittime); break; - case PARAM_MAXDEFER: r = CAST(scc->kiss.maxdefer); break; - case PARAM_TX: r = CAST(scc->kiss.tx_inhibit); break; - case PARAM_SLIP: r = CAST(!scc->kiss.not_slip); break; - default: r = NO_SUCH_PARAM; - } - - kiss_cmd.param = r; - - memcpy_tofs((void *) arg, &kiss_cmd, sizeof(struct ioctl_command)); - return 0; - break; + if(value & TIOCM_DTR) + scc->wreg[R5] |= DTR; + else + scc->wreg[R5] &= ~DTR; + if(value & TIOCM_RTS) + scc->wreg[R5] |= RTS; + else + scc->wreg[R5] &= ~RTS; + break; + } + + OutReg(scc->ctrl, R5, scc->wreg[R5]); + return 0; - case TIOCSKISS: - if (!arg) - return -EFAULT; - - memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command)); + case TCGETS: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios)); + if (error) + return error; + if (!arg) + return -EFAULT; + + memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios)); + return 0; - switch (kiss_cmd.command) - { - case PARAM_TXDELAY: scc->kiss.txdelay=VAL; break; - case PARAM_PERSIST: scc->kiss.persist=Val; break; - case PARAM_SLOTTIME: scc->kiss.slottime=VAL; break; - case PARAM_TXTAIL: scc->kiss.tailtime=VAL; break; - case PARAM_FULLDUP: scc->kiss.fulldup=Val; break; - case PARAM_SOFTDCD: scc->kiss.softdcd=Val; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_RTS: break; /* or this? */ - case PARAM_SPEED: scc->modem.speed=Val; break; - case PARAM_GROUP: scc->kiss.group=Val; break; - case PARAM_IDLE: scc->kiss.idletime=Val; break; - case PARAM_MIN: scc->kiss.mintime=SVAL; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=SVAL; break; - case PARAM_WAIT: scc->kiss.waittime=Val; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=SVAL; break; - case PARAM_TX: scc->kiss.tx_inhibit=Val; break; - case PARAM_SLIP: scc->kiss.not_slip=!Val; break; - default: return -ENOIOCTLCMD; - } + case TCSETS: + case TCSETSF: /* should flush first, but... */ + case TCSETSW: /* should wait 'till flush, but... */ + if (!arg) + return -EFAULT; - return 0; - break; -#undef TICKS -#undef CAST -#undef VAL -#undef SVAL -#undef Val + error = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios)); + if (error) + return error; + memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios)); + scc_change_speed(scc); + return 0; - default: - return -ENOIOCTLCMD; - } + } + + return -ENOIOCTLCMD; } /* ----> tx routine: decode KISS data and scc_enqueue it <---- */ /* send raw frame to SCC. used for AX.25 */ -int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +int +scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) { struct scc_channel * scc = tty->driver_data; unsigned char *p; - unsigned long flags; int cnt, cnt2; - if (!tty) return count; + if (!tty) + return -EINVAL; if (scc_paranoia_check(scc, tty->device, "scc_write")) - return 0; + return -EINVAL; if (scc->kiss.tx_inhibit) return count; - - save_flags(flags); cli(); - + cnt2 = count; while (cnt2) @@ -2493,8 +2820,8 @@ int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, i else memcpy(scc_wbuf, buf, cnt); - /* Strange thing. The timeout of the slip driver is */ - /* very small, thus we'll wake him up now. */ + /* The timeout of the slip driver is very small, */ + /* thus we'll wake him up now. */ if (cnt2 == 0) { @@ -2510,34 +2837,28 @@ int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, i while(cnt--) if (kiss_decode(scc, *p++)) - { - scc->stat.nospace++; - restore_flags(flags); - return 0; - } + return -ENOMEM; } /* while cnt2 */ - restore_flags(flags); - return count; } /* put a single char into the buffer */ -static void scc_put_char(struct tty_struct * tty, unsigned char ch) +static void +scc_put_char(struct tty_struct * tty, unsigned char ch) { struct scc_channel *scc = tty->driver_data; - unsigned char ch2; if (scc_paranoia_check(scc, tty->device, "scc_put_char")) return; - - ch2 = ch; - scc_write(tty, 0, &ch2, 1); /* that's all */ + + kiss_decode(scc, ch); } -static void scc_flush_chars(struct tty_struct * tty) +static void +scc_flush_chars(struct tty_struct * tty) { struct scc_channel *scc = tty->driver_data; @@ -2547,7 +2868,8 @@ static void scc_flush_chars(struct tty_struct * tty) } -static int scc_write_room(struct tty_struct *tty) +static int +scc_write_room(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2557,7 +2879,8 @@ static int scc_write_room(struct tty_struct *tty) return BUFSIZE; } -static int scc_chars_in_buffer(struct tty_struct *tty) +static int +scc_chars_in_buffer(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2567,7 +2890,8 @@ static int scc_chars_in_buffer(struct tty_struct *tty) return 0; } -static void scc_flush_buffer(struct tty_struct *tty) +static void +scc_flush_buffer(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2579,41 +2903,43 @@ static void scc_flush_buffer(struct tty_struct *tty) (tty->ldisc.write_wakeup)(tty); } -static void scc_throttle(struct tty_struct *tty) +static void +scc_throttle(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; if (scc_paranoia_check(scc, tty->device, "scc_throttle")) return; -#ifdef DEBUG - printk("scc: scc_throttle() called for device %d\n", MINOR(tty->device)); +#ifdef SCC_DEBUG + printk(KERN_DEBUG "z8530drv: scc_throttle() called for device %d\n", MINOR(tty->device)); #endif scc->throttled = 1; del_timer(&(scc->rx_t)); - scc->rx_t.expires = jiffies + HZ/TPS; + scc->rx_t.expires = jiffies + HZ/25; add_timer(&scc->rx_t); } -static void scc_unthrottle(struct tty_struct *tty) +static void +scc_unthrottle(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; if (scc_paranoia_check(scc, tty->device, "scc_unthrottle")) return; -#ifdef DEBUG - printk("scc: scc_unthrottle() called for device %d\n", MINOR(tty->device)); +#ifdef SCC_DEBUG + printk(KERN_DEBUG "z8350drv: scc_unthrottle() called for device %d\n", MINOR(tty->device)); #endif scc->throttled = 0; del_timer(&(scc->rx_t)); - scc_tx_timer(scc->rx_t.data); } /* experimental, the easiest way to stop output is a fake scc_throttle */ -static void scc_start(struct tty_struct *tty) +static void +scc_start(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2623,7 +2949,8 @@ static void scc_start(struct tty_struct *tty) scc_unthrottle(tty); } -static void scc_stop(struct tty_struct *tty) +static void +scc_stop(struct tty_struct *tty) { struct scc_channel *scc = tty->driver_data; @@ -2657,20 +2984,521 @@ scc_set_ldisc(struct tty_struct * tty) scc_change_speed(scc); } +#endif /* CONFIG_SCC_TTY */ + +#ifdef CONFIG_SCC_DEV +/* ******************************************************************** */ +/* * Network driver routines * */ +/* ******************************************************************** */ + +static unsigned char ax25_bcast[AX25_ADDR_LEN] = +{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; +static unsigned char ax25_nocall[AX25_ADDR_LEN] = +{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; + +static int +scc_net_init(struct device *dev) +{ + return 0; /* dummy */ +} + +/* ----> open network device <---- */ + +static int +scc_net_open(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -ENODEV; + + if (scc->tty != NULL) + return -EBUSY; + + if (!scc->init) + return -EINVAL; + + scc->stat.is_netdev = 1; + + MOD_INC_USE_COUNT; + + alloc_buffer_pool(scc); + + scc->stat.tx_kiss_state = KISS_IDLE; /* unused */ + scc->stat.rx_kiss_state = KISS_IDLE; /* unused */ + + init_channel(scc); + + dev->tbusy = 0; + dev->start = 1; + + return 0; +} + +/* ----> close network device <---- */ + +static int +scc_net_close(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -ENODEV; + + if (!scc->stat.is_netdev) + return -ENXIO; + + MOD_DEC_USE_COUNT; + + scc->stat.is_netdev = 0; + + save_flags(flags); + cli(); + + Outb(scc->ctrl,0); /* Make sure pointer is written */ + wr(scc,R1,0); /* disable interrupts */ + wr(scc,R3,0); + + del_timer(&scc->tx_t); + del_timer(&scc->tx_wdog); +#ifdef CONFIG_SCC_TTY + del_timer(&scc->rx_t); +#endif + restore_flags(flags); + + free_buffer_pool(scc); + + dev->tbusy = 1; + dev->start = 0; + + return 0; +} + +/* ----> receive frame, called from scc_rxint() <---- */ + +static void +scc_net_rx(struct scc_channel *scc, struct mbuf *bp) +{ + struct sk_buff *skb; + + if ( bp == NULL || bp->cnt == 0 || + scc == NULL || scc->magic != SCC_MAGIC) + return; + + skb = dev_alloc_skb(bp->cnt+1); + + if (skb == NULL) + { + scc->dev_stat.rx_dropped++; + return; + } + + scc->dev_stat.rx_packets++; + + skb->dev = scc->dev; + skb->protocol = htons(ETH_P_AX25); + + memcpy(skb_put(skb, bp->cnt), bp->data, bp->cnt); + + skb->mac.raw = skb->data; + netif_rx(skb); + + return; +} + +/* ----> transmit frame <---- */ + +static int +scc_net_tx(struct sk_buff *skb, struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + struct mbuf *bp; + long ticks; + + /* + * We have our own queues, dev->tbusy is set only by t_maxkeyup + * to avoid running the driver into maxkeup again and again. + * + * Hope I havn't outsmart me _again_ ;-) + */ + + if (dev->tbusy) + { + ticks = (signed) (jiffies - dev->trans_start); + + if (ticks < scc->kiss.maxdefer*HZ || scc == NULL) + return 1; + + /* + * Arrgh... Seems to be a _very_ busy channel. + * throw away transmission queue. + */ + + del_timer(&scc->tx_wdog); + t_busy((unsigned long) scc); + + dev->tbusy=0; + dev->trans_start = jiffies; + } + + if (skb == NULL) + { + dev_tint(dev); + return 0; + } + + if (scc == NULL || scc->magic != SCC_MAGIC) + { + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + scc->dev_stat.tx_packets++; + bp = scc_get_buffer(scc, BT_TRANSMIT); + + if (bp == NULLBUF || skb->len > scc->stat.bufsize || skb->len == 0) + { + scc->dev_stat.tx_dropped++; + if (bp) scc_enqueue_buffer(&scc->tx_buffer_pool, bp); + dev_kfree_skb(skb, FREE_WRITE); + return 0; + } + + memcpy(bp->data, skb->data, skb->len); + bp->cnt = skb->len; + + dev_kfree_skb(skb, FREE_WRITE); + + scc->kiss_decode_bp = bp; + kiss_interpret_frame(scc); + dev->trans_start = jiffies; + return 0; +} + +/* ----> set interface callsign <---- */ + +static int +scc_net_set_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *) addr; + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + return 0; +} + +/* ----> rebuild header <---- */ + +static int +scc_net_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb) +{ + return ax25_rebuild_header(buff, dev, raddr, skb); +} + +/* ----> "hard" header <---- */ + +static int +scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len) +{ + return ax25_encapsulate(skb, dev, type, daddr, saddr, len); +} + +/* ----> get statistics <---- */ + +static struct enet_statistics * +scc_net_get_stats(struct device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return NULL; + + scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; + scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; + scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; + scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; + + return &scc->dev_stat; +} + +/* ----> ioctl for network devices <---- */ + +static int +scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned int ncmd = 0; + int res; + + if (scc == NULL || scc->magic != SCC_MAGIC) + return -EINVAL; + + if (!Driver_Initialized && cmd == SIOCSCCINI) + { + scc->dev = dev; + scc->stat.is_netdev = 1; + } + + switch (cmd) + { + case SIOCSCCRESERVED: return -EINVAL; /* unused */ + case SIOCSCCCFG: ncmd = TIOCSCCCFG; break; + case SIOCSCCINI: ncmd = TIOCSCCINI; break; + case SIOCSCCCHANINI: ncmd = TIOCSCCCHANINI; break; + case SIOCSCCSMEM: ncmd = TIOCSCCSMEM; break; + case SIOCSCCGKISS: ncmd = TIOCSCCGKISS; break; + case SIOCSCCSKISS: ncmd = TIOCSCCSKISS; break; + case SIOCSCCGSTAT: ncmd = TIOCSCCGSTAT; break; + default: return -EINVAL; + } + + res = scc_ioctl(scc, ncmd, (void *) ifr->ifr_data); + +#ifdef CONFIG_SCC_TTY + if (!(dev->flags & IFF_UP)) + scc->stat.is_netdev = 0; +#endif + + return res; + +} + + +/* ----> initialize interface <---- */ + +static int +scc_net_setup(struct scc_channel *scc, unsigned char *name) +{ + int k; + unsigned char *buf; + struct device *dev; + + if (dev_get(name) != NULL) + { + printk(KERN_INFO "Z8530drv: device %s already exists.\n", name); + return -EEXIST; + } + + if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL) + return -ENOMEM; + +#ifndef CONFIG_SCC_TTY + scc->stat.is_netdev = 1; +#endif + dev = scc->dev; + memset(dev, 0, sizeof(struct device)); + + buf = (unsigned char *) kmalloc(10, GFP_KERNEL); + strcpy(buf, name); + + dev->priv = (void *) scc; + dev->name = buf; + dev->init = scc_net_init; + + if (register_netdev(dev) != 0) + { + kfree(dev); + return -EIO; + } + + for (k=0; k < DEV_NUMBUFFS; k++) + skb_queue_head_init(&dev->buffs[k]); + + dev->open = scc_net_open; + dev->stop = scc_net_close; + + dev->hard_start_xmit = scc_net_tx; + dev->hard_header = scc_net_header; + dev->rebuild_header = scc_net_rebuild_header; + dev->set_mac_address = scc_net_set_mac_address; + dev->get_stats = scc_net_get_stats; + dev->do_ioctl = scc_net_ioctl; + + memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); + memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); + + dev->flags = 0; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = 4; + + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; + dev->addr_len = AX25_ADDR_LEN; + + return 0; +} +#endif /* CONFIG_SCC_DEV */ + +/* ******************************************************************** */ +/* * dump statistics to /proc/net/z8530drv * */ +/* ******************************************************************** */ + + +static int +scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +{ + struct scc_channel *scc; + char *txstate, *clksrc, *brand; + struct scc_kiss *kiss; + struct scc_stat *stat; + int len = 0; + off_t pos = 0; + off_t begin = 0; + int k; + if (!Driver_Initialized) + { + len += sprintf(buffer, "Driver not initialized.\n"); + goto done; + } + + if (!Nchips) + { + len += sprintf(buffer, "Z8530 chips not found\n"); + goto done; + } + + for (k = 0; k < Nchips*2; k++) + { + scc = &SCC_Info[k]; + stat = &scc->stat; + kiss = &scc->kiss; + + if (!scc->init) + continue; + + switch(stat->tx_state) + { + case TXS_IDLE: txstate = "idle"; break; + case TXS_BUSY: txstate = "busy"; break; + case TXS_ACTIVE: txstate = "active"; break; + case TXS_NEWFRAME: txstate = "new"; break; + case TXS_IDLE2: txstate = "keyed"; break; + case TXS_WAIT: txstate = "wait"; break; + case TXS_TIMEOUT: txstate = "timeout"; break; + default: txstate = "???"; + } + switch(scc->modem.clocksrc) + { + case CLK_DPLL: clksrc = "dpll"; break; + case CLK_EXTERNAL: clksrc = "ext"; break; + case CLK_DIVIDER: clksrc = "div"; break; + default: clksrc = "???"; + } + switch(scc->brand) + { + case PA0HZP: brand="pa0hzp"; break; + case EAGLE: brand="eagle"; break; + case PC100: brand="pc1100"; break; + case PRIMUS: brand="primus"; break; + case DRSI: brand="drsi"; break; + case BAYCOM: brand="baycom"; break; + default: brand="???"; + } + + len += sprintf(buffer+len, "Device : %s%d\n", SCC_DriverName, k); + len += sprintf(buffer+len, "Hardware : %3.3x %3.3x %d %lu %s %s %3.3x %3.3x %d\n", + scc->data, scc->ctrl, scc->irq, scc->clock, brand, + scc->enhanced? "enh":"norm", Vector_Latch, scc->special, + scc->option); + len += sprintf(buffer+len, "Received : %lu %lu %d\n", + stat->rxframes, stat->rxerrs, stat->rx_over); + len += sprintf(buffer+len, "Transmitted: %lu %lu %d %s\n", + stat->txframes, stat->txerrs, stat->tx_under, txstate); + len += sprintf(buffer+len, "Interrupts : %lu %lu %lu %lu\n", + stat->rxints, stat->txints, stat->exints, stat->spints); + len += sprintf(buffer+len, "Buffers : %d %d/%d %d/%d %d\n", + stat->bufsize, stat->rx_queued, stat->rxbuffers, + stat->tx_queued, stat->txbuffers, stat->nospace); + len += sprintf(buffer+len, "MODEM : %lu %s %s %s\n", + scc->modem.speed, scc->modem.nrz? "nrz":"nrzi", + clksrc, kiss->softdcd? "soft":"hard"); + len += sprintf(buffer+len, "Mode : %s\n", + stat->is_netdev? "dev":"tty"); +#define K(x) kiss->x + len += sprintf(buffer+len, "KISS params: %d %d %d %d %d %d %d %d %d %d %d %d\n", + K(txdelay), K(persist), K(slottime), K(tailtime), + K(fulldup), K(waittime), K(mintime), K(maxkeyup), + K(idletime), K(maxdefer), K(tx_inhibit), K(group)); +#undef K +#ifdef SCC_DEBUG + { + int reg; + len += sprintf(buffer+len, "Z8530 wregs: "); + for (reg = 0; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); + len += sprintf(buffer+len, "\n"); + + len += sprintf(buffer+len, "Z8530 rregs: %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); + for (reg = 3; reg < 8; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "XX "); + for (reg = 9; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "\n"); + } +#endif + len += sprintf(buffer+len, "\n"); + + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + + if (pos > offset + length) + break; + } + +done: + + *start = buffer + (offset - begin); + len -= (offset - begin); + + if (len > length) len = length; + + return len; +} + +#ifdef CONFIG_INET +struct proc_dir_entry scc_proc_dir_entry = +{ + PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, scc_net_get_info +}; + +#define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry); +#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530); +#else +#define scc_net_procfs_init() +#define scc_net_procfs_remove() +#endif + + /* ******************************************************************** */ /* * Init SCC driver * */ /* ******************************************************************** */ -int scc_init (void) +int scc_init (void) { int chip, chan, k; +#ifdef CONFIG_SCC_DEV + char devname[10]; +#endif + printk(KERN_INFO BANNER); + +#ifdef CONFIG_SCC_TTY memset(&scc_std_termios, 0, sizeof(struct termios)); memset(&scc_driver, 0, sizeof(struct tty_driver)); scc_driver.magic = TTY_DRIVER_MAGIC; - scc_driver.name = "scc"; + scc_driver.name = SCC_DriverName; scc_driver.major = Z8530_MAJOR; scc_driver.minor_start = 0; scc_driver.num = MAXSCC*2; @@ -2699,17 +3527,19 @@ int scc_init (void) scc_driver.throttle = scc_throttle; scc_driver.unthrottle = scc_unthrottle; - scc_driver.ioctl = scc_ioctl; + scc_driver.ioctl = scc_tty_ioctl; scc_driver.set_termios = scc_set_termios; scc_driver.set_ldisc = scc_set_ldisc; - printk(BANNER); if (tty_register_driver(&scc_driver)) { - printk("Failed to register Z8530 SCC driver\n"); + printk(KERN_ERR "Failed to register Z8530 SCC driver\n"); return -EIO; } +#endif + + memset(&SCC_ctrl, 0, sizeof(SCC_ctrl)); /* pre-init channel information */ @@ -2719,16 +3549,19 @@ int scc_init (void) memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel)); for (chan = 0; chan < 2; chan++) - { SCC_Info[2*chip+chan].magic = SCC_MAGIC; - SCC_Info[2*chip+chan].stat.rxbuffers = RXBUFFERS; - SCC_Info[2*chip+chan].stat.txbuffers = TXBUFFERS; - SCC_Info[2*chip+chan].stat.bufsize = BUFSIZE; - } } - + for (k = 0; k < 16; k++) Ivec[k].used = 0; +#ifdef CONFIG_SCC_DEV + sprintf(devname,"%s0", SCC_DriverName); + + if (scc_net_setup(SCC_Info, devname)) + printk(KERN_WARNING "z8530drv: cannot setup network device\n"); +#endif + scc_net_procfs_init(); + return 0; } @@ -2743,9 +3576,9 @@ int init_module(void) int result = 0; result = scc_init(); - + if (result == 0) - printk("Copyright 1993,1995 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n"); + printk(KERN_INFO "Copyright 1993,1996 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n"); return result; } @@ -2754,19 +3587,26 @@ void cleanup_module(void) { long flags; io_port ctrl; - int k, errno; + int k; struct scc_channel *scc; - save_flags(flags); cli(); - if ( (errno = tty_unregister_driver(&scc_driver)) ) + save_flags(flags); + cli(); + +#ifdef CONFIG_SCC_TTY + if ( (k = tty_unregister_driver(&scc_driver)) ) { - printk("Failed to unregister Z8530 SCC driver (%d)", -errno); + printk(KERN_ERR "z8530drv: failed to unregister tty driver: (%d)\n", -k); restore_flags(flags); return; } - +#endif + + if (Nchips == 0) + unregister_netdev(SCC_Info[0].dev); + for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k*2]) ) + if ( (ctrl = SCC_ctrl[k].chan_A) ) { Outb(ctrl, 0); OutReg(ctrl,R9,FHWRES); /* force hardware reset */ @@ -2780,6 +3620,11 @@ void cleanup_module(void) { release_region(scc->ctrl, 1); release_region(scc->data, 1); + if (scc->dev) + { + unregister_netdev(scc->dev); + kfree(scc->dev); + } } } @@ -2787,5 +3632,7 @@ void cleanup_module(void) if (Ivec[k].used) free_irq(k, NULL); restore_flags(flags); + + scc_net_procfs_remove(); } #endif diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c new file mode 100644 index 000000000000..3b2292f7767d --- /dev/null +++ b/drivers/char/specialix.c @@ -0,0 +1,2330 @@ +/* + * specialix.c -- specialix IO8+ multiport serial driver. + * + * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl) + * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net) + * + * Specialix pays for the development and support of this driver. + * Please DO contact io8-linux@specialix.co.uk if you require + * support. But please read the documentation (specialix.txt) + * first. + * + * This driver was developped in the BitWizard linux device + * driver service. If you require a linux device driver for your + * product, please contact devices@BitWizard.nl for a quote. + * + * This code is firmly based on the riscom/8 serial driver, + * written by Dmitry Gorodchanin. The specialix IO8+ card + * programming information was obtained from the CL-CD1865 Data + * Book, and Specialix document number 6200059: IO8+ Hardware + * Functional Specification. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * Revision history: + * + * Revision 1.0: April 1st 1997. + * Initial release for alpha testing. + * Revision 1.1: April 14th 1997. + * Incorporated Richard Hudsons suggestions, + * removed some debugging printk's. + * Revision 1.2: April 15th 1997. + * Ported to 2.1.x kernels. + * Revision 1.3: April 17th 1997 + * Backported to 2.0. (Compatibility macros). + * Revision 1.4: April 18th 1997 + * Fixed DTR/RTS bug that caused the card to indicate + * "don't send data" to a modem after the password prompt. + * Fixed bug for premature (fake) interrupts. + * Revision 1.5: April 19th 1997 + * fixed a minor typo in the header file, cleanup a little. + * performance warnings are now MAXed at once per minute. + * Revision 1.6: May 23 1997 + * Changed the specialix=... format to include interrupt. + * Revision 1.7: May 27 1997 + * Made many more debug printk's a compile time option. + * + */ + +#define VERSION "1.7" + + +/* + * There is a bunch of documentation about the card, jumpers, config + * settings, restrictions, cables, device names and numbers in + * ../../Documentation/specialix.txt + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ************************************************************** */ +/* * This section can be removed when 2.0 becomes outdated.... * */ +/* ************************************************************** */ + +#if LINUX_VERSION_CODE < 131328 /* Less than 2.1.0 */ +#define TWO_ZERO +#else +#if LINUX_VERSION_CODE < 131368 /* less than 2.1.40 */ +/* This has not been extensively tested yet. Sorry. */ +#warning "You're on your own between 2.1.0 and 2.1.40.... " +#warning "Please use 2.1.40 or higher." +#endif +#endif + + +#ifdef TWO_ZERO +#define Get_user(a,b) a = get_user(b) +#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +#define queue_task queue_task_irq_off +#else +#define Get_user(a,b) get_user(a,b) +#endif + +/* ************************************************************** */ +/* * End of compatibility section.. * */ +/* ************************************************************** */ + + +#ifndef TWO_ZERO +#include +#endif + +#include "specialix_io8.h" +#include "cd1865.h" + + + +/* Configurable options: */ + +/* Am I paranoid or not ? ;-) */ +#define SPECIALIX_PARANOIA_CHECK + +/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help) + When the IRQ routine leaves the chip in a state that is keeps on + requiring attention, the timer doesn't help either. */ +#undef SPECIALIX_TIMER + +/* + * The following defines are mostly for testing purposes. But if you need + * some nice reporting in your syslog, you can define them also. + */ +#undef SX_REPORT_FIFO +#undef SX_REPORT_OVERRUN + + + +#ifdef CONFIG_SPECIALIX_RTSCTS +#define SX_CRTSCTS(bla) 1 +#else +#define SX_CRTSCTS(tty) C_CRTSCTS(tty) +#endif + + +/* Used to be outb (0xff, 0x80); */ +#define short_pause() udelay (1) + + +#define SPECIALIX_LEGAL_FLAGS \ + (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \ + ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \ + ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP) + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +DECLARE_TASK_QUEUE(tq_specialix); + + + +#define SPECIALIX_TYPE_NORMAL 1 +#define SPECIALIX_TYPE_CALLOUT 2 + +static struct specialix_board * IRQ_to_board[16] = { NULL, } ; +static struct tty_driver specialix_driver, specialix_callout_driver; +static int specialix_refcount = 0; +static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, }; +static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, }; +static unsigned char * tmp_buf = NULL; +static struct semaphore tmp_buf_sem = MUTEX; + +static unsigned long baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 0, +}; + +static struct specialix_board sx_board[SX_NBOARD] = { + { 0, SX_IOBASE1, 9, }, + { 0, SX_IOBASE2, 11, }, + { 0, SX_IOBASE3, 12, }, + { 0, SX_IOBASE4, 15, }, +}; + +static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] = { + { 0, }, +}; + + +#ifdef SPECIALIX_TIMER +static struct timer_list missed_irq_timer; +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs); +#endif + + + +static inline int sx_paranoia_check(struct specialix_port const * port, + kdev_t device, const char *routine) +{ +#ifdef SPECIALIX_PARANOIA_CHECK + static const char *badmagic = + KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n"; + static const char *badinfo = + KERN_ERR "sx: Warning: null specialix port for device %s in %s\n"; + + if (!port) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (port->magic != SPECIALIX_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + + +/* + * + * Service functions for specialix IO8+ driver. + * + */ + +/* Get board number from pointer */ +extern inline int board_No (struct specialix_board * bp) +{ + return bp - sx_board; +} + + +/* Get port number from pointer */ +extern inline int port_No (struct specialix_port const * port) +{ + return SX_PORT(port - sx_port); +} + + +/* Get pointer to board from pointer to port */ +extern inline struct specialix_board * port_Board(struct specialix_port const * port) +{ + return &sx_board[SX_BOARD(port - sx_port)]; +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg | 0x80; + outb (reg | 0x80, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Input Byte from CL CD186x register */ +extern inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + return inb (bp->base + SX_DATA_REG); +} + + +/* Output Byte to CL CD186x register */ +extern inline void sx_out_off(struct specialix_board * bp, unsigned short reg, + unsigned char val) +{ + bp->reg = reg; + outb (reg, bp->base + SX_ADDR_REG); + outb (val, bp->base + SX_DATA_REG); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* Wait for Channel Command Register ready */ +extern inline void sx_wait_CCR_off(struct specialix_board * bp) +{ + unsigned long delay; + + for (delay = SX_CCR_TIMEOUT; delay; delay--) + if (!sx_in_off(bp, CD186x_CCR)) + return; + + printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp)); +} + + +/* + * specialix IO8+ IO range functions. + */ + +extern inline int sx_check_io_range(struct specialix_board * bp) +{ + return check_region (bp->base, SX_IO_SPACE); +} + + +extern inline void sx_request_io_range(struct specialix_board * bp) +{ + request_region(bp->base, SX_IO_SPACE, "specialix IO8+" ); +} + + +extern inline void sx_release_io_range(struct specialix_board * bp) +{ + release_region(bp->base, SX_IO_SPACE); +} + + +/* Must be called with enabled interrupts */ +extern inline void sx_long_delay(unsigned long delay) +{ + unsigned long i; + + for (i = jiffies + delay; i > jiffies; ) ; +} + + + +/* Set the IRQ using the RTS lines that run to the PAL on the board.... */ +int sx_set_irq ( struct specialix_board *bp) +{ + int virq; + int i; + + switch (bp->irq) { + /* In the same order as in the docs... */ + case 15: virq = 0;break; + case 12: virq = 1;break; + case 11: virq = 2;break; + case 9: virq = 3;break; + default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq); + return 0; + } + + for (i=0;i<2;i++) { + sx_out(bp, CD186x_CAR, i); + sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0); + } + return 1; +} + + +/* Reset and setup CD186x chip */ +static int sx_init_CD186x(struct specialix_board * bp) +{ + unsigned long flags; + int scaler; + int rv = 1; + + save_flags(flags); cli(); + + sx_wait_CCR_off(bp); /* Wait for CCR ready */ + sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */ + sti(); + sx_long_delay(HZ/20); /* Delay 0.05 sec */ + cli(); + sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */ + sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */ + sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */ + sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */ + sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */ + /* Set RegAckEn */ + sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN); + + /* Setting up prescaler. We need 4 ticks per 1 ms */ + scaler = SX_OSCFREQ/SPECIALIX_TPS; + + sx_out_off(bp, CD186x_PPRH, scaler >> 8); + sx_out_off(bp, CD186x_PPRL, scaler & 0xff); + + if (!sx_set_irq (bp)) { + /* Figure out how to pass this along... */ + printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq); + rv = 0; + } + + restore_flags(flags); + return rv; +} + + +int read_cross_byte (struct specialix_board *bp, int reg, int bit) +{ + int i; + int t; + + for (i=0, t=0;i<8;i++) { + sx_out_off (bp, CD186x_CAR, i); + if (sx_in_off (bp, reg) & bit) + t |= 1 << i; + } + return t; +} + + +#ifdef SPECIALIX_TIMER +void missed_irq (unsigned long data) +{ + if (sx_in ((struct specialix_board *)data, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)) { + printk (KERN_INFO "Missed interrupt... Calling int from timer. \n"); + sx_interrupt (((struct specialix_board *)data)->irq, + NULL, NULL); + } + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +} +#endif + + + +/* Main probing routine, also sets irq. */ +static int sx_probe(struct specialix_board *bp) +{ + unsigned char val1, val2; +#if 0 + int irqs = 0; + int retries; +#endif + int rev; + int chip; + + if (sx_check_io_range(bp)) + return 1; + + /* Are the I/O ports here ? */ + sx_out_off(bp, CD186x_PPRL, 0x5a); + short_pause (); + val1 = sx_in_off(bp, CD186x_PPRL); + + sx_out_off(bp, CD186x_PPRL, 0xa5); + short_pause (); + val2 = sx_in_off(bp, CD186x_PPRL); + + + if ((val1 != 0x5a) || (val2 != 0xa5)) { + printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + /* Check the DSR lines that Specialix uses as board + identification */ + val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR); + val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS); +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", + board_No(bp), val1, val2); +#endif + if (val1 != 0xb2) { + printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n", + board_No(bp), bp->base); + return 1; + } + + +#if 0 + /* It's time to find IRQ for this board */ + for (retries = 0; retries < 5 && irqs <= 0; retries++) { + irqs = probe_irq_on(); + sx_init_CD186x(bp); /* Reset CD186x chip */ + sx_out(bp, CD186x_CAR, 2); /* Select port 2 */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */ + sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */ + sx_long_delay(HZ/20); + irqs = probe_irq_off(irqs); + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "SRSR = %02x, ", sx_in(bp, CD186x_SRSR)); + printk ( "TRAR = %02x, ", sx_in(bp, CD186x_TRAR)); + printk ( "GIVR = %02x, ", sx_in(bp, CD186x_GIVR)); + printk ( "GICR = %02x, ", sx_in(bp, CD186x_GICR)); + printk ( "\n"); +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + /* Hmmm. This is dead code anyway. */ + } +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", + val1, val2, val3); +#endif + + } + +#if 0 + if (irqs <= 0) { + printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n", + board_No(bp), bp->base); + return 1; + } +#endif + printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs); + if (irqs > 0) + bp->irq = irqs; +#endif + /* Reset CD186x again */ + if (!sx_init_CD186x(bp)) { + return -EIO; + } + + sx_request_io_range(bp); + bp->flags |= SX_BOARD_PRESENT; + + /* Chip revcode pkgtype + GFRCR SRCR bit 7 + CD180 rev B 0x81 0 + CD180 rev C 0x82 0 + CD1864 rev A 0x82 1 + CD1865 rev A 0x83 1 -- Do not use!!! Does not work. + CD1865 rev B 0x84 1 + -- Thanks to Gwen Wang, Cirrus Logic. + */ + + switch (sx_in_off(bp, CD186x_GFRCR)) { + case 0x82:chip = 1864;rev='A';break; + case 0x83:chip = 1865;rev='A';break; + case 0x84:chip = 1865;rev='B';break; + case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */ + default:chip=-1;rev='x'; + } + +#if SPECIALIX_DEBUG > 2 + printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) ); +#endif + +#ifdef SPECIALIX_TIMER + init_timer (&missed_irq_timer); + missed_irq_timer.function = missed_irq; + missed_irq_timer.data = (unsigned long) bp; + missed_irq_timer.expires = jiffies + HZ; + add_timer (&missed_irq_timer); +#endif + + printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n", + board_No(bp), + bp->base, bp->irq, + chip, rev); + + return 0; +} + +/* + * + * Interrupt processing routines. + * */ + +extern inline void sx_mark_event(struct specialix_port * port, int event) +{ + /* + * I'm not quite happy with current scheme all serial + * drivers use their own BH routine. + * It seems this easily can be done with one BH routine + * serving for all serial drivers. + * For now I must introduce another one - SPECIALIX_BH. + * Still hope this will be changed in near future. + * -- Dmitry. + */ + set_bit(event, &port->event); + queue_task(&port->tqueue, &tq_specialix); + mark_bh(SPECIALIX_BH); +} + + +extern inline struct specialix_port * sx_get_port(struct specialix_board * bp, + unsigned char const * what) +{ + unsigned char channel; + struct specialix_port * port; + + channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; + if (channel < CD186x_NCH) { + port = &sx_port[board_No(bp) * SX_NPORT + channel]; + if (port->flags & ASYNC_INITIALIZED) { + return port; + } + } + printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", + board_No(bp), what, channel); + return NULL; +} + + +extern inline void sx_receive_exc(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char status; + unsigned char ch; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + return; + } + +#ifdef SX_REPORT_OVERRUN + status = sx_in(bp, CD186x_RCSR); + if (status & RCSR_OE) { + port->overrun++; +#if SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", + board_No(bp), port_No(port), port->overrun); +#endif + } + status &= port->mark_mask; +#else + status = sx_in(bp, CD186x_RCSR) & port->mark_mask; +#endif + ch = sx_in(bp, CD186x_RDR); + if (!status) { + return; + } + if (status & RCSR_TOUT) { + printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", + board_No(bp), port_No(port)); + return; + + } else if (status & RCSR_BREAK) { +#ifdef SPECIALIX_DEBUG + printk(KERN_DEBUG "sx%d: port %d: Handling break...\n", + board_No(bp), port_No(port)); +#endif + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + if (port->flags & ASYNC_SAK) + do_SAK(tty); + + } else if (status & RCSR_PE) + *tty->flip.flag_buf_ptr++ = TTY_PARITY; + + else if (status & RCSR_FE) + *tty->flip.flag_buf_ptr++ = TTY_FRAME; + + else if (status & RCSR_OE) + *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; + + else + *tty->flip.flag_buf_ptr++ = 0; + + *tty->flip.char_buf_ptr++ = ch; + tty->flip.count++; + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_receive(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + if (!(port = sx_get_port(bp, "Receive"))) + return; + + tty = port->tty; + + count = sx_in(bp, CD186x_RDCR); + +#ifdef SX_REPORT_FIFO + port->hits[count > 8 ? 9 : count]++; +#endif + + while (count--) { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", + board_No(bp), port_No(port)); + break; + } + *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR); + *tty->flip.flag_buf_ptr++ = 0; + tty->flip.count++; + } + queue_task(&tty->flip.tqueue, &tq_timer); +} + + +extern inline void sx_transmit(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char count; + + + if (!(port = sx_get_port(bp, "Transmit"))) + return; + + tty = port->tty; + + if (port->IER & IER_TXEMPTY) { + /* FIFO drained */ + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXEMPTY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if ((port->xmit_cnt <= 0 && !port->break_length) + || tty->stopped || tty->hw_stopped) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + return; + } + + if (port->break_length) { + if (port->break_length > 0) { + if (port->COR2 & COR2_ETC) { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_SBRK); + port->COR2 &= ~COR2_ETC; + } + count = MIN(port->break_length, 0xff); + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_DELAY); + sx_out(bp, CD186x_TDR, count); + if (!(port->break_length -= count)) + port->break_length--; + } else { + sx_out(bp, CD186x_TDR, CD186x_C_ESC); + sx_out(bp, CD186x_TDR, CD186x_C_EBRK); + sx_out(bp, CD186x_COR2, port->COR2); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + port->break_length = 0; + } + return; + } + + count = CD186x_NFIFO; + do { + sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); + port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->xmit_cnt <= 0) + break; + } while (--count > 0); + + if (port->xmit_cnt <= 0) { + sx_out(bp, CD186x_CAR, port_No(port)); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_IER, port->IER); + } + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); +} + + +extern inline void sx_check_modem(struct specialix_board * bp) +{ + struct specialix_port *port; + struct tty_struct *tty; + unsigned char mcr; + +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Modem intr. "); +#endif + if (!(port = sx_get_port(bp, "Modem"))) + return; + + tty = port->tty; + + mcr = sx_in(bp, CD186x_MCR); + printk ("mcr = %02x.\n", mcr); + + if ((mcr & MCR_CDCHG)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "CD just changed... "); +#endif + if (sx_in(bp, CD186x_MSVR) & MSVR_CD) { +#ifdef SPECIALIX_DEBUG + printk ( "Waking up guys in open.\n"); +#endif + wake_up_interruptible(&port->open_wait); + } + else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_CALLOUT_NOHUP))) { +#ifdef SPECIALIX_DEBUG + printk ( "Sending HUP.\n"); +#endif + queue_task(&port->tqueue_hangup, + &tq_scheduler); + } else { +#ifdef SPECIALIX_DEBUG + printk ( "Don't need to send HUP.\n"); +#endif + } + } + +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + if (mcr & MCR_CTSCHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } + if (mcr & MCR_DSSXHG) { + if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { + tty->hw_stopped = 0; + port->IER |= IER_TXRDY; + if (port->xmit_cnt <= port->wakeup_chars) + sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); + } else { + tty->hw_stopped = 1; + port->IER &= ~IER_TXRDY; + } + sx_out(bp, CD186x_IER, port->IER); + } +#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ + + /* Clear change bits */ + sx_out(bp, CD186x_MCR, 0); +} + + +/* The main interrupt processing routine */ +static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs) +{ + unsigned char status; + unsigned char ack; + struct specialix_board *bp; + unsigned long loop = 0; + int saved_reg; + + bp = IRQ_to_board[irq]; + + if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) { +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq); +#endif + return; + } + + saved_reg = bp->reg; + + while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) & + (SRSR_RREQint | + SRSR_TREQint | + SRSR_MREQint)))) { + if (status & SRSR_RREQint) { + ack = sx_in(bp, CD186x_RRAR); + + if (ack == (SX_ID | GIVR_IT_RCV)) + sx_receive(bp); + else if (ack == (SX_ID | GIVR_IT_REXC)) + sx_receive_exc(bp); + else + printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n", + board_No(bp), ack); + + } else if (status & SRSR_TREQint) { + ack = sx_in(bp, CD186x_TRAR); + + if (ack == (SX_ID | GIVR_IT_TX)) + sx_transmit(bp); + else + printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n", + board_No(bp), ack); + } else if (status & SRSR_MREQint) { + ack = sx_in(bp, CD186x_MRAR); + + if (ack == (SX_ID | GIVR_IT_MODEM)) + sx_check_modem(bp); + else + printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n", + board_No(bp), ack); + + } + + sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ + } + bp->reg = saved_reg; + outb (bp->reg, bp->base + SX_ADDR_REG); +} + + +/* + * Routines for open & close processing. + */ + + +/* Called with disabled interrupts */ +extern inline int sx_setup_board(struct specialix_board * bp) +{ + int error; + + if (bp->flags & SX_BOARD_ACTIVE) + return 0; + + error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL); + + if (error) + return error; + + IRQ_to_board[bp->irq] = bp; + (void) sx_in (bp, 0); /* Turn ON interrupts. */ + + bp->flags |= SX_BOARD_ACTIVE; + + MOD_INC_USE_COUNT; + return 0; +} + + +/* Called with disabled interrupts */ +extern inline void sx_shutdown_board(struct specialix_board *bp) +{ + if (!(bp->flags & SX_BOARD_ACTIVE)) + return; + + bp->flags &= ~SX_BOARD_ACTIVE; + + free_irq(bp->irq, NULL); + (void) sx_in_off (bp, 0); /* Turn off interrupts. */ + + IRQ_to_board[bp->irq] = NULL; + + MOD_DEC_USE_COUNT; +} + + +/* + * Setting up port characteristics. + * Must be called with disabled interrupts + */ +static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + unsigned long baud; + long tmp; + unsigned char cor1 = 0, cor3 = 0; + unsigned char mcor1 = 0, mcor2 = 0; + static int again=0; + + if (!(tty = port->tty) || !tty->termios) + return; + + port->IER = 0; + port->COR2 = 0; + /* Select port on the board */ + sx_out(bp, CD186x_CAR, port_No(port)); + + /* The Specialix board doens't implement the RTS lines. + They are used to set the IRQ level. Don't touch them. */ + if (SX_CRTSCTS(tty)) + port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); + else + port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR); +#endif + baud = C_BAUD(tty); + + if (baud & CBAUDEX) { + baud &= ~CBAUDEX; + if (baud < 1 || baud > 2) + port->tty->termios->c_cflag &= ~CBAUDEX; + else + baud += 15; + } + if (baud == 15) { + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + baud ++; + if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + baud += 2; + } + + + if (!baud_table[baud]) { + /* Drop DTR & exit */ +#ifdef SPECIALIX_DEBUG + printk (KERN_DEBUG "Dropping DTR... Hmm....\n"); +#endif + if (!SX_CRTSCTS (tty)) { + port -> MSVR &= ~ MSVR_DTR; + sx_out(bp, CD186x_MSVR, port->MSVR ); + } +#ifdef DEBUG_SPECIALIX + else + printk (KERN_DEBUG "Can't drop DTR: no DTR.\n"); +#endif + return; + } else { + /* Set DTR on */ + if (!SX_CRTSCTS (tty)) { + port ->MSVR |= MSVR_DTR; + } + } + + /* + * Now we must calculate some speed depended things + */ + + /* Set baud rate for port */ + tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + + CD186x_TPC/2) / CD186x_TPC); + if ((tmp < 0x10) && (again < jiffies)) { + again = jiffies + HZ * 60; + /* Page 48 of version 2.0 of the CL-CD1865 databook */ + if (tmp >= 12) { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Performance degradation is possible.\n", + port_No (port), tmp); + } else { + printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" + "Warning: overstressing Cirrus chip. " + "This might not work.\n", + port_No (port), tmp); + } + } + + sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); + sx_out(bp, CD186x_RBPRL, tmp & 0xff); + sx_out(bp, CD186x_TBPRL, tmp & 0xff); + + baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */ + + /* Two timer ticks seems enough to wakeup something like SLIP driver */ + tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; + port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? + SERIAL_XMIT_SIZE - 1 : tmp); + + /* Receiver timeout will be transmission time for 1.5 chars */ + tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud; + tmp = (tmp > 0xff) ? 0xff : tmp; + sx_out(bp, CD186x_RTPR, tmp); + + switch (C_CSIZE(tty)) { + case CS5: + cor1 |= COR1_5BITS; + break; + case CS6: + cor1 |= COR1_6BITS; + break; + case CS7: + cor1 |= COR1_7BITS; + break; + case CS8: + cor1 |= COR1_8BITS; + break; + } + + if (C_CSTOPB(tty)) + cor1 |= COR1_2SB; + + cor1 |= COR1_IGNORE; + if (C_PARENB(tty)) { + cor1 |= COR1_NORMPAR; + if (C_PARODD(tty)) + cor1 |= COR1_ODDP; + if (I_INPCK(tty)) + cor1 &= ~COR1_IGNORE; + } + /* Set marking of some errors */ + port->mark_mask = RCSR_OE | RCSR_TOUT; + if (I_INPCK(tty)) + port->mark_mask |= RCSR_FE | RCSR_PE; + if (I_BRKINT(tty) || I_PARMRK(tty)) + port->mark_mask |= RCSR_BREAK; + if (I_IGNPAR(tty)) + port->mark_mask &= ~(RCSR_FE | RCSR_PE); + if (I_IGNBRK(tty)) { + port->mark_mask &= ~RCSR_BREAK; + if (I_IGNPAR(tty)) + /* Real raw mode. Ignore all */ + port->mark_mask &= ~RCSR_OE; + } + /* Enable Hardware Flow Control */ + if (C_CRTSCTS(tty)) { +#ifdef SPECIALIX_BRAIN_DAMAGED_CTS + port->IER |= IER_DSR | IER_CTS; + mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; + mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; + tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR)); +#else + port->COR2 |= COR2_CTSAE; +#endif + } + /* Enable Software Flow Control. FIXME: I'm not sure about this */ + /* Some people reported that it works, but I still doubt it */ + if (I_IXON(tty)) { + port->COR2 |= COR2_TXIBE; + cor3 |= (COR3_FCT | COR3_SCDE); + if (I_IXANY(tty)) + port->COR2 |= COR2_IXM; + sx_out(bp, CD186x_SCHR1, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); + sx_out(bp, CD186x_SCHR3, START_CHAR(tty)); + sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); + } + if (!C_CLOCAL(tty)) { + /* Enable CD check */ + port->IER |= IER_CD; + mcor1 |= MCOR1_CDZD; + mcor2 |= MCOR2_CDOD; + } + + if (C_CREAD(tty)) + /* Enable receiver */ + port->IER |= IER_RXD; + + /* Set input FIFO size (1-8 bytes) */ + cor3 |= SPECIALIX_RXFIFO; + /* Setting up CD186x channel registers */ + sx_out(bp, CD186x_COR1, cor1); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_COR3, cor3); + /* Make CD186x know about registers change */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); + /* Setting up modem option registers */ +#ifdef DEBUG_SPECIALIX + printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2); +#endif + sx_out(bp, CD186x_MCOR1, mcor1); + sx_out(bp, CD186x_MCOR2, mcor2); + /* Enable CD186x transmitter & receiver */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); + /* Enable interrupts */ + sx_out(bp, CD186x_IER, port->IER); + /* And finally set the modem lines... */ + sx_out(bp, CD186x_MSVR, port->MSVR); +} + + +/* Must be called with interrupts enabled */ +static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port) +{ + unsigned long flags; + + if (port->flags & ASYNC_INITIALIZED) + return 0; + + if (!port->xmit_buf) { + /* We may sleep in get_free_page() */ + unsigned long tmp; + + if (!(tmp = get_free_page(GFP_KERNEL))) + return -ENOMEM; + + if (port->xmit_buf) { + free_page(tmp); + return -ERESTARTSYS; + } + port->xmit_buf = (unsigned char *) tmp; + } + + save_flags(flags); cli(); + + if (port->tty) + clear_bit(TTY_IO_ERROR, &port->tty->flags); + + if (port->count == 1) + bp->count++; + + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + sx_change_speed(bp, port); + port->flags |= ASYNC_INITIALIZED; + + restore_flags(flags); + return 0; +} + + +/* Must be called with interrupts disabled */ +static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port) +{ + struct tty_struct *tty; + + if (!(port->flags & ASYNC_INITIALIZED)) + return; + +#ifdef SX_REPORT_OVERRUN + printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n", + board_No(bp), port_No(port), port->overrun); +#endif +#ifdef SX_REPORT_FIFO + { + int i; + + printk(KERN_INFO "sx%d: port %d: FIFO hits [ ", + board_No(bp), port_No(port)); + for (i = 0; i < 10; i++) { + printk("%ld ", port->hits[i]); + } + printk("].\n"); + } +#endif + if (port->xmit_buf) { + free_page((unsigned long) port->xmit_buf); + port->xmit_buf = NULL; + } + + /* Select port */ + sx_out(bp, CD186x_CAR, port_No(port)); + + if (!(tty = port->tty) || C_HUPCL(tty)) { + /* Drop DTR */ + sx_out(bp, CD186x_MSVDTR, 0); + } + + /* Reset port */ + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SOFTRESET); + /* Disable all interrupts from this port */ + port->IER = 0; + sx_out(bp, CD186x_IER, port->IER); + + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); + port->flags &= ~ASYNC_INITIALIZED; + + if (--bp->count < 0) { + printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n", + board_No(bp), bp->count); + bp->count = 0; + } + + /* + * If this is the last opened port on the board + * shutdown whole board + */ + if (!bp->count) + sx_shutdown_board(bp); +} + + +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct specialix_port *port) +{ + struct wait_queue wait = { current, NULL }; + struct specialix_board *bp = port_Board(port); + int retval; + int do_clocal = 0; + int CD; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { + interruptible_sleep_on(&port->close_wait); + if (port->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) { + if (port->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_SESSION_LOCKOUT) && + (port->session != current->session)) + return -EBUSY; + if ((port->flags & ASYNC_CALLOUT_ACTIVE) && + (port->flags & ASYNC_PGRP_LOCKOUT) && + (port->pgrp != current->pgrp)) + return -EBUSY; + port->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (port->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (port->flags & ASYNC_CALLOUT_ACTIVE) { + if (port->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (C_CLOCAL(tty)) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&port->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + port->count--; + sti(); + port->blocked_open++; + while (1) { + cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; + if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) { + if (SX_CRTSCTS (tty)) { + /* Activate RTS */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } else { + /* Activate DTR */ + port->MSVR |= MSVR_DTR; + sx_out (bp, CD186x_MSVR, port->MSVR); + } + } + sti(); + current->state = TASK_INTERRUPTIBLE; + if (tty_hung_up_p(filp) || + !(port->flags & ASYNC_INITIALIZED)) { + if (port->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && + !(port->flags & ASYNC_CLOSING) && + (do_clocal || CD)) + break; + if (current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&port->open_wait, &wait); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + if (retval) + return retval; + + port->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + + +static int sx_open(struct tty_struct * tty, struct file * filp) +{ + int board; + int error; + struct specialix_port * port; + struct specialix_board * bp; + unsigned long flags; + + board = SX_BOARD(MINOR(tty->device)); + + if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) + return -ENODEV; + + bp = &sx_board[board]; + port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device)); + +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", + board, bp, port, SX_PORT(MINOR(tty->device))); +#endif + + if (sx_paranoia_check(port, tty->device, "sx_open")) + return -ENODEV; + + if ((error = sx_setup_board(bp))) + return error; + + port->count++; + tty->driver_data = port; + port->tty = tty; + + if ((error = sx_setup_port(bp, port))) + return error; + + if ((error = block_til_ready(tty, filp, port))) + return error; + + if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL) + *tty->termios = port->normal_termios; + else + *tty->termios = port->callout_termios; + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + + port->session = current->session; + port->pgrp = current->pgrp; + return 0; +} + + +static void sx_close(struct tty_struct * tty, struct file * filp) +{ + struct specialix_port *port = (struct specialix_port *) tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + unsigned long timeout; + + if (!port || sx_paranoia_check(port, tty->device, "close")) + return; + + save_flags(flags); cli(); + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + + bp = port_Board(port); + if ((tty->count == 1) && (port->count != 1)) { + printk(KERN_ERR "sx%d: sx_close: bad port count;" + " tty->count is 1, port count is %d\n", + board_No(bp), port->count); + port->count = 1; + } + if (--port->count < 0) { + printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n", + board_No(bp), port_No(port), port->count); + port->count = 0; + } + if (port->count) { + restore_flags(flags); + return; + } + port->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (port->flags & ASYNC_NORMAL_ACTIVE) + port->normal_termios = *tty->termios; + if (port->flags & ASYNC_CALLOUT_ACTIVE) + port->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, port->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the receive line status interrupts, and tell the + * interrupt driver to stop checking the data ready bit in the + * line status register. + */ + port->IER &= ~IER_RXD; + if (port->flags & ASYNC_INITIALIZED) { + port->IER &= ~IER_TXRDY; + port->IER |= IER_TXEMPTY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important if there is a transmit FIFO! + */ + timeout = jiffies+HZ; + while(port->IER & IER_TXEMPTY) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->timeout; + schedule(); + if (jiffies > timeout) { + printk (KERN_INFO "Timeout waiting for close\n"); + break; + } + } + + } + sx_shutdown_port(bp, port); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + port->event = 0; + port->tty = 0; + if (port->blocked_open) { + if (port->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + port->close_delay; + schedule(); + } + wake_up_interruptible(&port->open_wait); + } + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&port->close_wait); + restore_flags(flags); +} + + +static int sx_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + int c, total = 0; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_write")) + return 0; + + bp = port_Board(port); + + if (!tty || !port->xmit_buf || !tmp_buf) + return 0; + + if (from_user) + down(&tmp_buf_sem); + + save_flags(flags); + while (1) { + cli(); + c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + if (c <= 0) + break; + + if (from_user) { + copy_from_user(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, + SERIAL_XMIT_SIZE - port->xmit_head)); + memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c); + } else + memcpy(port->xmit_buf + port->xmit_head, buf, c); + port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); + port->xmit_cnt += c; + restore_flags(flags); + buf += c; + count -= c; + total += c; + } + if (from_user) + up(&tmp_buf_sem); + if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && + !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); + return total; +} + + +static void sx_put_char(struct tty_struct * tty, unsigned char ch) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_put_char")) + return; + + if (!tty || !port->xmit_buf) + return; + + save_flags(flags); cli(); + + if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); + return; + } + + port->xmit_buf[port->xmit_head++] = ch; + port->xmit_head &= SERIAL_XMIT_SIZE - 1; + port->xmit_cnt++; + restore_flags(flags); +} + + +static void sx_flush_chars(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_chars")) + return; + + if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !port->xmit_buf) + return; + + save_flags(flags); cli(); + port->IER |= IER_TXRDY; + sx_out(port_Board(port), CD186x_CAR, port_No(port)); + sx_out(port_Board(port), CD186x_IER, port->IER); + restore_flags(flags); +} + + +static int sx_write_room(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int ret; + + if (sx_paranoia_check(port, tty->device, "sx_write_room")) + return 0; + + ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; +} + + +static int sx_chars_in_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + + if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer")) + return 0; + + return port->xmit_cnt; +} + + +static void sx_flush_buffer(struct tty_struct *tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_flush_buffer")) + return; + + save_flags(flags); cli(); + port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + + +static int sx_get_modem_info(struct specialix_port * port, unsigned int *value) +{ + struct specialix_board * bp; + unsigned char status; + unsigned int result; + unsigned long flags; + + bp = port_Board(port); + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + status = sx_in(bp, CD186x_MSVR); + restore_flags(flags); +#ifdef DEBUG_SPECIALIX + printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", + port_No(port), status, sx_in (bp, CD186x_CAR)); + printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port); +#endif + if (SX_CRTSCTS(port->tty)) { + result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_RTS : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } else { + result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ + | ((status & MSVR_DTR) ? TIOCM_DTR : 0) + | ((status & MSVR_CD) ? TIOCM_CAR : 0) + |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */ + | ((status & MSVR_CTS) ? TIOCM_CTS : 0); + } + put_user(result,(unsigned long *) value); + return 0; +} + + +static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + struct specialix_board *bp = port_Board(port); + + error = verify_area(VERIFY_READ, value, sizeof(int)); + if (error) + return error; + + Get_user(arg, (unsigned long *) value); + switch (cmd) { + case TIOCMBIS: + /* if (arg & TIOCM_RTS) + port->MSVR |= MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; */ + + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR |= MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR |= MSVR_DTR; + } + break; + case TIOCMBIC: + /* if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_RTS; */ + /* if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; */ + if (SX_CRTSCTS(port->tty)) { + if (arg & TIOCM_RTS) + port->MSVR &= ~MSVR_DTR; + } else { + if (arg & TIOCM_DTR) + port->MSVR &= ~MSVR_DTR; + } + break; + case TIOCMSET: + /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : + (port->MSVR & ~MSVR_RTS); */ + /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); */ + if (SX_CRTSCTS(port->tty)) { + port->MSVR = (arg & TIOCM_RTS) ? + (port->MSVR | MSVR_DTR) : + (port->MSVR & ~MSVR_DTR); + } else { + port->MSVR = (arg & TIOCM_DTR) ? + (port->MSVR | MSVR_DTR): + (port->MSVR & ~MSVR_DTR); + } + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); + return 0; +} + + +extern inline void sx_send_break(struct specialix_port * port, unsigned long length) +{ + struct specialix_board *bp = port_Board(port); + unsigned long flags; + + save_flags(flags); cli(); + port->break_length = SPECIALIX_TPS / HZ * length; + port->COR2 |= COR2_ETC; + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_COR2, port->COR2); + sx_out(bp, CD186x_IER, port->IER); + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_CORCHG2); + sx_wait_CCR(bp); + restore_flags(flags); +} + + +extern inline int sx_set_serial_info(struct specialix_port * port, + struct serial_struct * newinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int change_speed; + unsigned long flags; + int error; + + error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp)); + if (error) + return error; + + copy_from_user(&tmp, newinfo, sizeof(tmp)); + +#if 0 + if ((tmp.irq != bp->irq) || + (tmp.port != bp->base) || + (tmp.type != PORT_CIRRUS) || + (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) || + (tmp.custom_divisor != 0) || + (tmp.xmit_fifo_size != CD186x_NFIFO) || + (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) + return -EINVAL; +#endif + + change_speed = ((port->flags & ASYNC_SPD_MASK) != + (tmp.flags & ASYNC_SPD_MASK)); + + if (!suser()) { + if ((tmp.close_delay != port->close_delay) || + (tmp.closing_wait != port->closing_wait) || + ((tmp.flags & ~ASYNC_USR_MASK) != + (port->flags & ~ASYNC_USR_MASK))) + return -EPERM; + port->flags = ((port->flags & ~ASYNC_USR_MASK) | + (tmp.flags & ASYNC_USR_MASK)); + } else { + port->flags = ((port->flags & ~ASYNC_FLAGS) | + (tmp.flags & ASYNC_FLAGS)); + port->close_delay = tmp.close_delay; + port->closing_wait = tmp.closing_wait; + } + if (change_speed) { + save_flags(flags); cli(); + sx_change_speed(bp, port); + restore_flags(flags); + } + return 0; +} + + +extern inline int sx_get_serial_info(struct specialix_port * port, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct specialix_board *bp = port_Board(port); + int error; + + error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp)); + if (error) + return error; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = PORT_CIRRUS; + tmp.line = port - sx_port; + tmp.port = bp->base; + tmp.irq = bp->irq; + tmp.flags = port->flags; + tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; + tmp.close_delay = port->close_delay * HZ/100; + tmp.closing_wait = port->closing_wait * HZ/100; + tmp.xmit_fifo_size = CD186x_NFIFO; + copy_to_user(retinfo, &tmp, sizeof(tmp)); + return 0; +} + + +static int sx_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + int error; + int retval; + + if (sx_paranoia_check(port, tty->device, "sx_ioctl")) + return -ENODEV; + + switch (cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (!arg) + sx_send_break(port, HZ/4); /* 1/4 second */ + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + sx_send_break(port, arg ? arg*(HZ/10) : HZ/4); + return 0; + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); + if (error) + return error; + put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + case TIOCSSOFTCAR: + Get_user(arg, (unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; + case TIOCMGET: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(unsigned int)); + if (error) + return error; + return sx_get_modem_info(port, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return sx_set_modem_info(port, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return sx_get_serial_info(port, (struct serial_struct *) arg); + case TIOCSSERIAL: + return sx_set_serial_info(port, (struct serial_struct *) arg); + default: + return -ENOIOCTLCMD; + } + return 0; +} + + +static void sx_throttle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_throttle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + + /* Use DTR instead of RTS ! */ + if (SX_CRTSCTS (tty)) + port->MSVR &= ~MSVR_DTR; + else { + /* Auch!!! I think the system shouldn't call this then. */ + /* Or maybe we're supposed (allowed?) to do our side of hw + handshake anyway, even when hardware handshake is off. + When you see this in your logs, please report.... */ + printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n", + port_No (port)); + } + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH2); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_unthrottle(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_unthrottle")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + /* XXXX Use DTR INSTEAD???? */ + if (SX_CRTSCTS(tty)) { + port->MSVR |= MSVR_DTR; + } /* Else clause: see remark in "sx_throttle"... */ + + sx_out(bp, CD186x_CAR, port_No(port)); + if (I_IXOFF(tty)) { + sx_wait_CCR(bp); + sx_out(bp, CD186x_CCR, CCR_SSCH1); + sx_wait_CCR(bp); + } + sx_out(bp, CD186x_MSVR, port->MSVR); + restore_flags(flags); +} + + +static void sx_stop(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_stop")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + port->IER &= ~IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + restore_flags(flags); +} + + +static void sx_start(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_start")) + return; + + bp = port_Board(port); + + save_flags(flags); cli(); + if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { + port->IER |= IER_TXRDY; + sx_out(bp, CD186x_CAR, port_No(port)); + sx_out(bp, CD186x_IER, port->IER); + } + restore_flags(flags); +} + + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_sx_hangup() -> tty->hangup() -> sx_hangup() + * + */ +static void do_sx_hangup(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + tty = port->tty; + if (!tty) + return; + + tty_hangup(tty); +} + + +static void sx_hangup(struct tty_struct * tty) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + struct specialix_board *bp; + + if (sx_paranoia_check(port, tty->device, "sx_hangup")) + return; + + bp = port_Board(port); + + sx_shutdown_port(bp, port); + port->event = 0; + port->count = 0; + port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + port->tty = 0; + wake_up_interruptible(&port->open_wait); +} + + +static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios) +{ + struct specialix_port *port = (struct specialix_port *)tty->driver_data; + unsigned long flags; + + if (sx_paranoia_check(port, tty->device, "sx_set_termios")) + return; + + if (tty->termios->c_cflag == old_termios->c_cflag && + tty->termios->c_iflag == old_termios->c_iflag) + return; + + save_flags(flags); cli(); + sx_change_speed(port_Board(port), port); + restore_flags(flags); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + sx_start(tty); + } +} + + +static void do_specialix_bh(void) +{ + run_task_queue(&tq_specialix); +} + + +static void do_softint(void *private_) +{ + struct specialix_port *port = (struct specialix_port *) private_; + struct tty_struct *tty; + + if(!(tty = port->tty)) + return; + + if (clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + + +static int sx_init_drivers(void) +{ + int error; + int i; + + + if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) { + printk(KERN_ERR "sx: Couldn't get free page.\n"); + return 1; + } + init_bh(SPECIALIX_BH, do_specialix_bh); + memset(IRQ_to_board, 0, sizeof(IRQ_to_board)); + memset(&specialix_driver, 0, sizeof(specialix_driver)); + specialix_driver.magic = TTY_DRIVER_MAGIC; + specialix_driver.name = "ttyW"; + specialix_driver.major = SPECIALIX_NORMAL_MAJOR; + specialix_driver.num = SX_NBOARD * SX_NPORT; + specialix_driver.type = TTY_DRIVER_TYPE_SERIAL; + specialix_driver.subtype = SPECIALIX_TYPE_NORMAL; + specialix_driver.init_termios = tty_std_termios; + specialix_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + specialix_driver.flags = TTY_DRIVER_REAL_RAW; + specialix_driver.refcount = &specialix_refcount; + specialix_driver.table = specialix_table; + specialix_driver.termios = specialix_termios; + specialix_driver.termios_locked = specialix_termios_locked; + + specialix_driver.open = sx_open; + specialix_driver.close = sx_close; + specialix_driver.write = sx_write; + specialix_driver.put_char = sx_put_char; + specialix_driver.flush_chars = sx_flush_chars; + specialix_driver.write_room = sx_write_room; + specialix_driver.chars_in_buffer = sx_chars_in_buffer; + specialix_driver.flush_buffer = sx_flush_buffer; + specialix_driver.ioctl = sx_ioctl; + specialix_driver.throttle = sx_throttle; + specialix_driver.unthrottle = sx_unthrottle; + specialix_driver.set_termios = sx_set_termios; + specialix_driver.stop = sx_stop; + specialix_driver.start = sx_start; + specialix_driver.hangup = sx_hangup; + + specialix_callout_driver = specialix_driver; + specialix_callout_driver.name = "cuw"; + specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR; + specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT; + + if ((error = tty_register_driver(&specialix_driver))) { + free_page((unsigned long)tmp_buf); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&specialix_callout_driver))) { + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n", + error); + return 1; + } + + memset(sx_port, 0, sizeof(sx_port)); + for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { + sx_port[i].callout_termios = specialix_callout_driver.init_termios; + sx_port[i].normal_termios = specialix_driver.init_termios; + sx_port[i].magic = SPECIALIX_MAGIC; + sx_port[i].tqueue.routine = do_softint; + sx_port[i].tqueue.data = &sx_port[i]; + sx_port[i].tqueue_hangup.routine = do_sx_hangup; + sx_port[i].tqueue_hangup.data = &sx_port[i]; + sx_port[i].close_delay = 50 * HZ/100; + sx_port[i].closing_wait = 3000 * HZ/100; + } + + return 0; +} + + +static void sx_release_drivers(void) +{ + free_page((unsigned long)tmp_buf); + tty_unregister_driver(&specialix_driver); + tty_unregister_driver(&specialix_callout_driver); +} + + +#ifndef MODULE +/* + * Called at boot time. + * + * You can specify IO base for up to SX_NBOARD cards, + * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt. + * Note that there will be no probing at default + * addresses in this case. + * + */ +void specialix_setup(char *str, int * ints) +{ + int i; + + for (i=0;i + +#ifdef __KERNEL__ + +#define SX_NBOARD 4 +/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */ +#define SX_IO_SPACE 4 +/* eight ports per board. */ +#define SX_NPORT 8 +#define SX_BOARD(line) ((line) / SX_NPORT) +#define SX_PORT(line) ((line) & (SX_NPORT - 1)) + + +#define SX_DATA_REG 0 /* Base+0 : Data register */ +#define SX_ADDR_REG 1 /* base+1 : Address register. */ + +#define MHz *1000000 /* I'm ashamed of myself. */ + +/* On-board oscillator frequency */ +#define SX_OSCFREQ (25 MHz/2) +/* There is a 25MHz crystal on the board, but the chip is in /2 mode */ + + +/* Ticks per sec. Used for setting receiver timeout and break length */ +#define SPECIALIX_TPS 4000 + +/* Yeah, after heavy testing I decided it must be 6. + * Sure, You can change it if needed. + */ +#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ + +#define SPECIALIX_MAGIC 0x0907 + +#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto + 10 milliseconds before the internal + processor is available again after + you give it a command */ + +#define SX_IOBASE1 0x100 +#define SX_IOBASE2 0x180 +#define SX_IOBASE3 0x250 +#define SX_IOBASE4 0x260 + +struct specialix_board { + unsigned long flags; + unsigned short base; + unsigned char irq; + signed char count; + unsigned char DTR; + int reg; +}; + +#define SX_BOARD_PRESENT 0x00000001 +#define SX_BOARD_ACTIVE 0x00000002 + + +struct specialix_port { + int magic; + int baud_base; + int flags; + struct tty_struct * tty; + int count; + int blocked_open; + int event; + int timeout; + int close_delay; + long session; + long pgrp; + unsigned char * xmit_buf; + int custom_divisor; + int xmit_head; + int xmit_tail; + int xmit_cnt; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct tq_struct tqueue; + struct tq_struct tqueue_hangup; + short wakeup_chars; + short break_length; + unsigned short closing_wait; + unsigned char mark_mask; + unsigned char IER; + unsigned char MSVR; + unsigned char COR2; +#ifdef SX_REPORT_OVERRUN + unsigned long overrun; +#endif +#ifdef SX_REPORT_FIFO + unsigned long hits[10]; +#endif +}; + +#endif /* __KERNEL__ */ +#endif /* __LINUX_SPECIALIX_H */ + + + + + + + + + diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 8dad84c28bf4..2bccbc6a5f6e 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1930,6 +1930,9 @@ int tty_init(void) #endif #ifdef CONFIG_BAYCOM baycom_init(); +#endif +#ifdef CONFIG_SPECIALIX + specialix_init(); #endif pty_init(); vcs_init(); diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index 90ae03ed9476..33b2b9159f61 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -1,8 +1,8 @@ /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */ /* - Written 1993-1995 by Donald Becker. + Written 1993-1997 by Donald Becker. - Copyright 1994,1995 by Donald Becker. + Copyright 1994-1997 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. This software may be used and distributed according to the terms of the GNU Public License, @@ -31,9 +31,17 @@ what the docs say some people definitely get problems with lower (but in card spec) delays + v1.10 4/21/97 Fixed module code so that multiple cards may be detected, + other cleanups. -djb */ -static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n"; +static char *version = "3c509.c:1.11 4/22/97 becker@cesdis.gsfc.nasa.gov\n"; +/* A few values that may be tweaked. */ + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (400*HZ/1000) +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +#define INTR_WORK 10 #include @@ -64,7 +72,7 @@ int el3_debug = 2; /* To minimize the size of the driver source I only define operating constants if they are used several times. You'll need the manual - if you want to understand driver details. */ + anyway if you want to understand driver details. */ /* Offsets from base I/O address. */ #define EL3_DATA 0x00 #define EL3_CMD 0x0e @@ -115,11 +123,13 @@ enum RxFilter { struct el3_private { struct enet_statistics stats; + struct device *next_dev; /* skb send-queue */ int head, size; struct sk_buff *queue[SKB_QUEUE_SIZE]; }; static int id_port = 0x100; +static struct device *el3_root_dev = NULL; static ushort id_read_eeprom(int index); static ushort read_eeprom(short ioaddr, int index); @@ -130,9 +140,7 @@ static void update_stats(int addr, struct device *dev); static struct enet_statistics *el3_get_stats(struct device *dev); static int el3_rx(struct device *dev); static int el3_close(struct device *dev); -#ifdef HAVE_MULTICAST static void set_multicast_list(struct device *dev); -#endif @@ -140,7 +148,7 @@ int el3_probe(struct device *dev) { short lrs_state = 0xff, i; ushort ioaddr, irq, if_port; - short *phys_addr = (short *)dev->dev_addr; + short phys_addr[3]; static int current_tag = 0; /* First check all slots of the EISA bus. The next slot address to @@ -195,6 +203,8 @@ int el3_probe(struct device *dev) outb(0x02, 0xA79); /* Return to WaitForKey state. */ /* Select an open I/O location at 0x1*0 to do contention select. */ for (id_port = 0x100; id_port < 0x200; id_port += 0x10) { + if (check_region(id_port, 1)) + continue; outb(0x00, id_port); outb(0xff, id_port); if (inb(id_port) & 0x01) @@ -210,13 +220,6 @@ int el3_probe(struct device *dev) on cards as they are found. Cards with their tag set will not respond to subsequent ID sequences. */ - if (check_region(id_port,1)) { - static int once = 1; - if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",id_port); - once = 0; - return -ENODEV; - } - outb(0x00, id_port); outb(0x00, id_port); for(i = 0; i < 255; i++) { @@ -247,13 +250,13 @@ int el3_probe(struct device *dev) if_port = iobase >> 14; ioaddr = 0x200 + ((iobase & 0x1f) << 4); } - if (dev->irq > 1 && dev->irq < 16) + if (dev && dev->irq > 1 && dev->irq < 16) irq = dev->irq; else irq = id_read_eeprom(9) >> 12; - if (dev->base_addr != 0 - && dev->base_addr != (unsigned short)ioaddr) { + if (dev && dev->base_addr != 0 + && dev->base_addr != (unsigned short)ioaddr) { return -ENODEV; } @@ -270,9 +273,14 @@ int el3_probe(struct device *dev) /* Free the interrupt so that some other card can use it. */ outw(0x0f00, ioaddr + WN0_IRQ); found: + if (dev == NULL) { + dev = init_etherdev(dev, sizeof(struct el3_private)); + } + memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr)); dev->base_addr = ioaddr; dev->irq = irq; dev->if_port = if_port; + request_region(dev->base_addr, EL3_IO_EXTENT, "3c509"); { @@ -287,11 +295,15 @@ int el3_probe(struct device *dev) printk(", IRQ %d.\n", dev->irq); /* Make up a EL3-specific-data structure. */ - dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct el3_private)); + ((struct el3_private *)dev->priv)->next_dev = el3_root_dev; + el3_root_dev = dev; + if (el3_debug > 0) printk(version); @@ -300,9 +312,7 @@ int el3_probe(struct device *dev) dev->hard_start_xmit = &el3_start_xmit; dev->stop = &el3_close; dev->get_stats = &el3_get_stats; -#ifdef HAVE_MULTICAST - dev->set_multicast_list = &set_multicast_list; -#endif + dev->set_multicast_list = &set_multicast_list; /* Fill in the generic fields of the device structure. */ ether_setup(dev); @@ -354,7 +364,6 @@ el3_open(struct device *dev) outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD); if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) { - irq2dev_map[dev->irq] = NULL; return -EAGAIN; } @@ -430,7 +439,7 @@ el3_start_xmit(struct sk_buff *skb, struct device *dev) /* Transmitter timeout, serious problems. */ if (dev->tbusy) { int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 40*HZ/100) + if (tickssofar < TX_TIMEOUT) return 1; printk("%s: transmit timed out, Tx_status %2.2x status %4.4x " "Tx FIFO room %d.\n", @@ -444,14 +453,6 @@ el3_start_xmit(struct sk_buff *skb, struct device *dev) dev->tbusy = 0; } - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - if (skb->len <= 0) - return 0; - if (el3_debug > 4) { printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n", dev->name, skb->len, inw(ioaddr + EL3_STATUS)); @@ -483,7 +484,11 @@ el3_start_xmit(struct sk_buff *skb, struct device *dev) outw(skb->len, ioaddr + TX_FIFO); outw(0x00, ioaddr + TX_FIFO); /* ... and the packet rounded to a doubleword. */ +#ifdef __powerpc__ + outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +#else outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2); +#endif dev->trans_start = jiffies; if (inw(ioaddr + TX_FREE) > 1536) { @@ -516,7 +521,7 @@ el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct device *dev = (struct device *)dev_id; int ioaddr, status; - int i = 0; + int i = INTR_WORK; if (dev == NULL) { printk ("el3_interrupt(): irq %d for unknown device.\n", irq); @@ -568,7 +573,7 @@ el3_interrupt(int irq, void *dev_id, struct pt_regs *regs) } } - if (++i > 10) { + if (--i < 0) { printk("%s: Infinite loop in interrupt, status %4.4x.\n", dev->name, status); /* Clear all interrupts. */ @@ -667,13 +672,18 @@ el3_rx(struct device *dev) pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; - skb_reserve(skb,2); /* Align IP on 16 byte */ + skb_reserve(skb, 2); /* Align IP on 16 byte */ /* 'skb->data' points to the start of sk_buff data area. */ - insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len), - (pkt_len + 3) >> 2); +#ifdef __powerpc__ + insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len), + (pkt_len + 3) >> 2); +#else + insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len), + (pkt_len + 3) >> 2); +#endif - skb->protocol=eth_type_trans(skb,dev); + skb->protocol = eth_type_trans(skb,dev); netif_rx(skb); outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ lp->stats.rx_packets++; @@ -692,7 +702,6 @@ el3_rx(struct device *dev) return 0; } -#ifdef HAVE_MULTICAST /* * Set or clear the multicast filter for this adaptor. */ @@ -717,7 +726,6 @@ set_multicast_list(struct device *dev) else outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD); } -#endif static int el3_close(struct device *dev) @@ -758,43 +766,50 @@ el3_close(struct device *dev) } #ifdef MODULE -static char devicename[9] = { 0, }; -static struct device dev_3c509 = { - devicename, /* device name is inserted by linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, - 0, 0, 0, NULL, el3_probe }; - -static int io = 0; -static int irq = 0; +/* Parameter that may be passed into the module. */ +static int debug = -1; +static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1}; +static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1}; int init_module(void) { - dev_3c509.base_addr = io; - dev_3c509.irq = irq; - if (!EISA_bus && !io) { - printk("3c509: WARNING! Module load-time probing works reliably only for EISA bus!!\n"); + int el3_cards = 0; + + if (debug >= 0) + el3_debug = debug; + + el3_root_dev = NULL; + while (el3_probe(0) == 0) { + if (irq[el3_cards] > 1) + el3_root_dev->irq = irq[el3_cards]; + if (xcvr[el3_cards] >= 0) + el3_root_dev->if_port = xcvr[el3_cards]; + el3_cards++; } - if (register_netdev(&dev_3c509) != 0) - return -EIO; - return 0; + + return el3_cards ? 0 : -ENODEV; } void cleanup_module(void) { - unregister_netdev(&dev_3c509); - kfree_s(dev_3c509.priv,sizeof(struct el3_private)); - dev_3c509.priv=NULL; - /* If we don't do this, we can't re-insmod it later. */ - release_region(dev_3c509.base_addr, EL3_IO_EXTENT); + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (el3_root_dev) { + next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev; + unregister_netdev(el3_root_dev); + release_region(el3_root_dev->base_addr, EL3_IO_EXTENT); + kfree(el3_root_dev); + el3_root_dev = next_dev; + } } #endif /* MODULE */ /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 diff --git a/drivers/net/atp.c b/drivers/net/atp.c index 6017a6743c03..a9445eaac197 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -1,9 +1,9 @@ /* atp.c: Attached (pocket) ethernet adapter driver for linux. */ /* - This is a driver for a commonly OEMed pocket (parallel port) - ethernet adapter. + This is a driver for commonly OEMed pocket (parallel port) + ethernet adapters based on the Realtek RTL8002 and RTL8012 chips. - Written 1993,1994,1995 by Donald Becker. + Written 1993-95,1997 by Donald Becker. Copyright 1993 United States Government as represented by the Director, National Security Agency. @@ -19,7 +19,11 @@ */ static const char *version = - "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + "atp.c:v1.08 4/1/97 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n"; + +/* Operational parameters that may be safely changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT ((400*HZ)/1000) /* This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket @@ -33,6 +37,10 @@ static const char *version = description is written based on guesses and writing lots of special-purpose code to test my theorized operation. + In 1997 Realtek made available the documentation for the second generation + RTL8012 chip, which has lead to several driver improvements. + http://www.realtek.com.tw/cn/cn.html + Theory of Operation The RTL8002 adapter seems to be built around a custom spin of the SEEQ @@ -58,6 +66,10 @@ static const char *version = timing and control bits. The data is then read from status port or written to the data port. + Correction: the controller has two banks of 16 registers. The second + bank contains only the multicast filter table (now used) and the EEPROM + access registers. + Since the bulk data transfer of the actual packets through the slow parallel port dominates the driver's running time, four distinct data (non-register) transfer modes are provided by the adapter, two in each @@ -80,6 +92,14 @@ static const char *version = generate reasonable object code. This header file also documents my interpretations of the device registers. */ +#include +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif #include #include @@ -103,6 +123,49 @@ static const char *version = #include "atp.h" +/* Kernel compatibility defines, common to David Hind's PCMCIA package. + This is only in the support-all-kernels source code. */ +#include /* Evil, but neccessary */ + +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) /* What to put in timer->expires. */ +#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC) +#define virt_to_bus(addr) ((unsigned long)addr) +#define bus_to_virt(addr) ((void*)addr) + +#else /* 1.3.0 and later */ +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) +#endif +#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 +#ifdef MODULE +#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) +char kernel_version[] = UTS_RELEASE; +#endif +#else +#undef MOD_INC_USE_COUNT +#define MOD_INC_USE_COUNT +#undef MOD_DEC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif +#endif /* 1.3.38 */ + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#endif + +#ifdef SA_SHIRQ +#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance) +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define FREE_IRQ(irqnum, dev) free_irq(irqnum) +#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n) +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif +/* End of kernel compatibility defines. */ + /* use 0 for production, 1 for verification, >2 for debug */ #ifndef NET_DEBUG #define NET_DEBUG 1 @@ -112,6 +175,9 @@ static unsigned int net_debug = NET_DEBUG; /* The number of low I/O ports used by the ethercard. */ #define ETHERCARD_TOTAL_SIZE 3 +/* Sequence to switch an 8012 from printer mux to ethernet mode. */ +static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,}; + /* This code, written by wwc@super.org, resets the adapter every TIMED_CHECKER ticks. This recovers from an unknown error which hangs the device. */ @@ -119,13 +185,11 @@ static unsigned int net_debug = NET_DEBUG; #ifdef TIMED_CHECKER #include static void atp_timed_checker(unsigned long ignored); -static struct device *atp_timed_dev; -static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker}; #endif /* Index to functions, as function prototypes. */ -extern int atp_probe(struct device *dev); +extern int atp_init(struct device *dev); static int atp_probe1(struct device *dev, short ioaddr); static void get_node_ID(struct device *dev); @@ -135,14 +199,23 @@ static void hardware_init(struct device *dev); static void write_packet(short ioaddr, int length, unsigned char *packet, int mode); static void trigger_send(short ioaddr, int length); static int net_send_packet(struct sk_buff *skb, struct device *dev); -static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void net_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs); static void net_rx(struct device *dev); static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode); static int net_close(struct device *dev); static struct enet_statistics *net_get_stats(struct device *dev); -static void set_multicast_list(struct device *dev); +#ifdef NEW_MULTICAST +static void set_rx_mode_8002(struct device *dev); +static void set_rx_mode_8012(struct device *dev); +#else +static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs); +static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs); +#endif +/* A list of all installed ATP devices, for removing the driver module. */ +static struct device *root_atp_dev = NULL; + /* Check for a network adapter of this type, and return '0' iff one exists. If dev->base_addr == 0, probe all likely locations. If dev->base_addr == 1, always return failure. @@ -153,7 +226,7 @@ int atp_init(struct device *dev) { int *port, ports[] = {0x378, 0x278, 0x3bc, 0}; - int base_addr = dev->base_addr; + int base_addr = dev ? dev->base_addr : 0; if (base_addr > 0x1ff) /* Check a single specified location. */ return atp_probe1(dev, base_addr); @@ -174,7 +247,8 @@ atp_init(struct device *dev) static int atp_probe1(struct device *dev, short ioaddr) { - int saved_ctrl_reg, status; + struct net_local *lp; + int saved_ctrl_reg, status, i; outb(0xff, ioaddr + PAR_DATA); /* Save the original value of the Control register, in case we guessed @@ -182,6 +256,9 @@ static int atp_probe1(struct device *dev, short ioaddr) saved_ctrl_reg = inb(ioaddr + PAR_CONTROL); /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */ outb(0x04, ioaddr + PAR_CONTROL); + /* Turn off the printer multiplexer on the 8012. */ + for (i = 0; i < 8; i++) + outb(mux_8012[i], ioaddr + PAR_DATA); write_reg_high(ioaddr, CMR1, CMR1h_RESET); eeprom_delay(2048); status = read_nibble(ioaddr, CMR1); @@ -196,6 +273,9 @@ static int atp_probe1(struct device *dev, short ioaddr) outb(saved_ctrl_reg, ioaddr + PAR_CONTROL); return 1; } + + dev = init_etherdev(dev, sizeof(struct net_local)); + /* Find the IRQ used by triggering an interrupt. */ write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */ @@ -218,27 +298,29 @@ static int atp_probe1(struct device *dev, short ioaddr) dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); - /* Leave the hardware in a reset state. */ - write_reg_high(ioaddr, CMR1, CMR1h_RESET); + /* Reset the ethernet hardware and activate the printer pass-through. */ + write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); if (net_debug) printk(version); /* Initialize the device structure. */ ether_setup(dev); - dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct net_local)); + lp = (struct net_local *)dev->priv; + lp->chip_type = RTL8002; + lp->addr_mode = CMR2h_Normal; - { - struct net_local *lp = (struct net_local *)dev->priv; - lp->addr_mode = CMR2h_Normal; - } + lp->next_module = root_atp_dev; + root_atp_dev = dev; /* For the ATP adapter the "if_port" is really the data transfer mode. */ - dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4; + dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4; if (dev->mem_end & 0xf) net_debug = dev->mem_end & 7; @@ -246,14 +328,9 @@ static int atp_probe1(struct device *dev, short ioaddr) dev->stop = net_close; dev->hard_start_xmit = net_send_packet; dev->get_stats = net_get_stats; - dev->set_multicast_list = &set_multicast_list; + dev->set_multicast_list = + lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012; -#ifdef TIMED_CHECKER - del_timer(&atp_timer); - atp_timer.expires = jiffies + TIMED_CHECKER; - atp_timed_dev = dev; - add_timer(&atp_timer); -#endif return 0; } @@ -322,18 +399,32 @@ static unsigned short eeprom_op(short ioaddr, unsigned int cmd) */ static int net_open(struct device *dev) { + struct net_local *lp = (struct net_local *)dev->priv; /* The interrupt line is turned off (tri-stated) when the device isn't in use. That's especially important for "attached" interfaces where the port or interrupt may be shared. */ +#ifndef SA_SHIRQ if (irq2dev_map[dev->irq] != 0 || (irq2dev_map[dev->irq] = dev) == 0 - || request_irq(dev->irq, &net_interrupt, 0, "ATP", NULL)) { + || REQUEST_IRQ(dev->irq, &net_interrupt, 0, "ATP", dev)) { return -EAGAIN; } +#else + if (request_irq(dev->irq, &net_interrupt, 0, "ATP Ethernet", dev)) + return -EAGAIN; +#endif + MOD_INC_USE_COUNT; hardware_init(dev); dev->start = 1; + + init_timer(&lp->timer); + lp->timer.expires = RUN_AT(TIMED_CHECKER); + lp->timer.data = (unsigned long)dev; + lp->timer.function = &atp_timed_checker; /* timer handler */ + add_timer(&lp->timer); + return 0; } @@ -345,6 +436,9 @@ static void hardware_init(struct device *dev) int ioaddr = dev->base_addr; int i; + /* Turn off the printer multiplexer on the 8012. */ + for (i = 0; i < 8; i++) + outb(mux_8012[i], ioaddr + PAR_DATA); write_reg_high(ioaddr, CMR1, CMR1h_RESET); for (i = 0; i < 6; i++) @@ -418,11 +512,18 @@ net_send_packet(struct sk_buff *skb, struct device *dev) struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; - if (dev->tbusy) { - /* If we get here, some higher level has decided we are broken. - There should really be a "kick me" function call instead. */ - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 5) +#ifndef final_version + if (skb == NULL || skb->len <= 0) { + printk("%s: Obsolete driver layer request made: skbuff==NULL.\n", + dev->name); + dev_tint(dev); + return 0; + } +#endif + + /* Use transmit-while-tbusy as a crude error timer. */ + if (set_bit(0, (void*)&dev->tbusy) != 0) { + if (jiffies - dev->trans_start < TX_TIMEOUT) return 1; printk("%s: transmit timed out, %s?\n", dev->name, inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem" @@ -432,21 +533,8 @@ net_send_packet(struct sk_buff *skb, struct device *dev) hardware_init(dev); dev->tbusy=0; dev->trans_start = jiffies; - } - - /* If some higher layer thinks we've missed an tx-done interrupt - we are passed NULL. Caution: dev_tint() handles the cli()/sti() - itself. */ - if (skb == NULL) { - dev_tint(dev); - return 0; - } - - /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ - if (set_bit(0, (void*)&dev->tbusy) != 0) - printk("%s: Transmitter access conflict.\n", dev->name); - else { + return 1; + } else { short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; unsigned char *buf = skb->data; int flags; @@ -484,9 +572,13 @@ net_send_packet(struct sk_buff *skb, struct device *dev) /* The typical workload of the driver: Handle the network interface interrupts. */ static void -net_interrupt(int irq, void *dev_id, struct pt_regs * regs) +net_interrupt IRQ(int irq, void *dev_instance, struct pt_regs * regs) { +#ifdef SA_SHIRQ + struct device *dev = (struct device *)dev_instance; +#else struct device *dev = (struct device *)(irq2dev_map[irq]); +#endif struct net_local *lp; int ioaddr, status, boguscount = 20; static int num_tx_since_rx = 0; @@ -585,11 +677,6 @@ net_interrupt(int irq, void *dev_id, struct pt_regs * regs) int i; for (i = 0; i < 6; i++) write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); -#ifdef TIMED_CHECKER - del_timer(&atp_timer); - atp_timer.expires = jiffies + TIMED_CHECKER; - add_timer(&atp_timer); -#endif } /* Tell the adapter that it can go back to using the output line as IRQ. */ @@ -610,17 +697,23 @@ net_interrupt(int irq, void *dev_id, struct pt_regs * regs) #ifdef TIMED_CHECKER /* This following code fixes a rare (and very difficult to track down) problem where the adapter forgets its ethernet address. */ -static void atp_timed_checker(unsigned long ignored) +static void atp_timed_checker(unsigned long data) { - int i; - int ioaddr = atp_timed_dev->base_addr; + struct device *dev = (struct device *)data; + int ioaddr = dev->base_addr; + struct net_local *lp = (struct net_local *)dev->priv; + int tickssofar = jiffies - lp->last_rx_time; + int i; - if (!atp_timed_dev->interrupt) - { - for (i = 0; i < 6; i++) -#if 0 - if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i]) - { + if (tickssofar > 2*HZ && dev->interrupt == 0) { +#if 1 + for (i = 0; i < 6; i++) + write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]); + lp->last_rx_time = jiffies; +#else + for (i = 0; i < 6; i++) + if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i]) + { struct net_local *lp = (struct net_local *)atp_timed_dev->priv; write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); if (i == 2) @@ -632,13 +725,10 @@ static void atp_timed_checker(unsigned long ignored) else lp->stats.rx_errors++; } -#else - write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]); #endif } - del_timer(&atp_timer); - atp_timer.expires = jiffies + TIMED_CHECKER; - add_timer(&atp_timer); + lp->timer.expires = RUN_AT(TIMED_CHECKER); + add_timer(&lp->timer); } #endif @@ -647,11 +737,7 @@ static void net_rx(struct device *dev) { struct net_local *lp = (struct net_local *)dev->priv; int ioaddr = dev->base_addr; -#ifdef notdef - ushort header[4]; -#else struct rx_header rx_head; -#endif /* Process the received packet. */ outb(EOC+MAR, ioaddr + PAR_DATA); @@ -661,18 +747,23 @@ static void net_rx(struct device *dev) rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr); if ((rx_head.rx_status & 0x77) != 0x01) { lp->stats.rx_errors++; - /* Ackkk! I don't have any documentation on what the error bits mean! - The best I can do is slap the device around a bit. */ + if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++; + else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++; if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n", dev->name, rx_head.rx_status); - hardware_init(dev); + if (rx_head.rx_status & 0x0020) { + lp->stats.rx_fifo_errors++; + write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE); + write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); + } else if (rx_head.rx_status & 0x0050) + hardware_init(dev); return; } else { - /* Malloc up new buffer. */ - int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */ + /* Malloc up new buffer. The "-4" is omits the FCS (CRC). */ + int pkt_len = (rx_head.rx_count & 0x7ff) - 4; struct sk_buff *skb; - skb = dev_alloc_skb(pkt_len); + skb = DEV_ALLOC_SKB(pkt_len + 2); if (skb == NULL) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; @@ -680,23 +771,20 @@ static void net_rx(struct device *dev) } skb->dev = dev; +#if LINUX_VERSION_CODE >= 0x10300 + skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port); - - if (net_debug > 6) { - unsigned char *data = skb->data; - printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x" - "%02x%02x%02x %02x%02x..", - data[0], data[1], data[2], data[3], data[4], data[5], - data[6], data[7], data[8], data[9], data[10], data[11], - data[12], data[13]); - } - - skb->protocol=eth_type_trans(skb,dev); + skb->protocol = eth_type_trans(skb, dev); +#else + read_block(ioaddr, pkt_len, skb->data, dev->if_port); + skb->len = pkt_len; +#endif netif_rx(skb); lp->stats.rx_packets++; } done: write_reg(ioaddr, CMR1, CMR1_NextPkt); + lp->last_rx_time = jiffies; return; } @@ -730,17 +818,23 @@ net_close(struct device *dev) dev->tbusy = 1; dev->start = 0; + del_timer(&lp->timer); + /* Flush the Tx and disable Rx here. */ lp->addr_mode = CMR2h_OFF; write_reg_high(ioaddr, CMR2, CMR2h_OFF); /* Free the IRQ line. */ outb(0x00, ioaddr + PAR_CONTROL); - free_irq(dev->irq, NULL); + FREE_IRQ(dev->irq, dev); +#ifndef SA_SHIRQ irq2dev_map[dev->irq] = 0; +#endif - /* Leave the hardware in a reset state. */ - write_reg_high(ioaddr, CMR1, CMR1h_RESET); + /* Reset the ethernet hardware and activate the printer pass-through. */ + write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX); + + MOD_DEC_USE_COUNT; return 0; } @@ -757,29 +851,125 @@ net_get_stats(struct device *dev) /* * Set or clear the multicast filter for this adapter. */ - -static void set_multicast_list(struct device *dev) + +/* The little-endian AUTODIN32 ethernet CRC calculation. + This is common code and should be moved to net/core/crc.c */ +static unsigned const ethernet_polynomial_le = 0xedb88320U; +static inline unsigned ether_crc_le(int length, unsigned char *data) +{ + unsigned int crc = 0xffffffff; /* Initial value. */ + while(--length >= 0) { + unsigned char current_octet = *data++; + int bit; + for (bit = 8; --bit >= 0; current_octet >>= 1) { + if ((crc ^ current_octet) & 1) { + crc >>= 1; + crc ^= ethernet_polynomial_le; + } else + crc >>= 1; + } + } + return crc; +} + +static void +#ifdef NEW_MULTICAST +set_rx_mode_8002(struct device *dev) +#else +static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs); +#endif { struct net_local *lp = (struct net_local *)dev->priv; short ioaddr = dev->base_addr; - int num_addrs=dev->mc_list; - - if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC)) - num_addrs=1; - /* - * We must make the kernel realise we had to move - * into promisc mode or we start all out war on - * the cable. - AC - */ - if(num_addrs) - dev->flags|=IFF_PROMISC; - lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal; + + if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) { + /* We must make the kernel realise we had to move + * into promisc mode or we start all out war on + * the cable. - AC + */ + dev->flags|=IFF_PROMISC; + lp->addr_mode = CMR2h_PROMISC; + } else + lp->addr_mode = CMR2h_Normal; + write_reg_high(ioaddr, CMR2, lp->addr_mode); +} + +static void +#ifdef NEW_MULTICAST +set_rx_mode_8012(struct device *dev) +#else +static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs); +#endif +{ + struct net_local *lp = (struct net_local *)dev->priv; + short ioaddr = dev->base_addr; + unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */ + int i; + + if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ + new_mode = CMR2h_PROMISC; + } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) { + /* Too many to filter perfectly -- accept all multicasts. */ + memset(mc_filter, 0xff, sizeof(mc_filter)); + new_mode = CMR2h_Normal; + } else { + struct dev_mc_list *mclist; + + memset(mc_filter, 0, sizeof(mc_filter)); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) + set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f, + mc_filter); + new_mode = CMR2h_Normal; + } + lp->addr_mode = new_mode; + write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */ + for (i = 0; i < 8; i++) + write_reg_byte(ioaddr, i, mc_filter[i]); + if (net_debug > 2 || 1) { + lp->addr_mode = 1; + printk("%s: Mode %d, setting multicast filter to", + dev->name, lp->addr_mode); + for (i = 0; i < 8; i++) + printk(" %2.2x", mc_filter[i]); + printk(".\n"); + } + write_reg_high(ioaddr, CMR2, lp->addr_mode); + write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */ +} + +#ifdef MODULE +static int debug = 1; +int +init_module(void) +{ + net_debug = debug; + root_atp_dev = NULL; + atp_init(0); + return 0; } + +void +cleanup_module(void) +{ + struct device *next_dev; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + /* No need to release_region(), since we never snarf it. */ + while (root_atp_dev) { + next_dev = ((struct net_local *)root_atp_dev->priv)->next_module; + unregister_netdev(root_atp_dev); + kfree(root_atp_dev); + root_atp_dev = next_dev; + } +} +#endif /* MODULE */ + /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c" + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c atp.c" * version-control: t * kept-new-versions: 5 * tab-width: 4 diff --git a/drivers/net/atp.h b/drivers/net/atp.h index e58f8c100c2f..b4d19337383e 100644 --- a/drivers/net/atp.h +++ b/drivers/net/atp.h @@ -1,3 +1,6 @@ +/* Linux header file for the ATP pocket ethernet adapter. */ +/* v1.04 4/1/97 becker@cesdis.gsfc.nasa.gov. */ + #include #include #include @@ -6,16 +9,19 @@ struct net_local { #ifdef __KERNEL__ struct enet_statistics stats; #endif + struct device *next_module; + struct timer_list timer; /* Media selection timer. */ + long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */ ushort saved_tx_size; - unsigned char - re_tx, /* Number of packet retransmissions. */ - tx_unit_busy, - addr_mode, /* Current Rx filter e.g. promiscuous, etc. */ - pac_cnt_in_tx_buf; + unsigned tx_unit_busy:1; + unsigned char re_tx, /* Number of packet retransmissions. */ + addr_mode, /* Current Rx filter e.g. promiscuous, etc. */ + pac_cnt_in_tx_buf, + chip_type; }; struct rx_header { - ushort pad; /* The first read is always corrupted. */ + ushort pad; /* Pad. */ ushort rx_count; ushort rx_status; /* Unknown bit assignments :-<. */ ushort cur_addr; /* Apparently the current buffer address(?) */ @@ -25,6 +31,8 @@ struct rx_header { #define PAR_STATUS 1 #define PAR_CONTROL 2 +enum chip_type { RTL8002, RTL8012 }; + #define Ctrl_LNibRead 0x08 /* LP_PSELECP */ #define Ctrl_HNibRead 0 #define Ctrl_LNibWrite 0x08 /* LP_PSELECP */ @@ -47,7 +55,8 @@ enum page0_regs ISR = 10, IMR = 11, /* Interrupt status and mask. */ CMR1 = 12, /* Command register 1. */ CMR2 = 13, /* Command register 2. */ - MAR = 14, /* Memory address register. */ + MODSEL = 14, /* Mode select register. */ + MAR = 14, /* Memory address register (?). */ CMR2_h = 0x1d, }; enum eepage_regs @@ -59,6 +68,7 @@ enum eepage_regs #define ISR_TxErr 0x02 #define ISRh_RxErr 0x11 /* ISR, high nibble */ +#define CMR1h_MUX 0x08 /* Select printer multiplexor on 8012. */ #define CMR1h_RESET 0x04 /* Reset. */ #define CMR1h_RxENABLE 0x02 /* Rx unit enable. */ #define CMR1h_TxENABLE 0x01 /* Tx unit enable. */ diff --git a/drivers/net/de4x5.c b/drivers/net/de4x5.c index 2c56bf7213a2..4ead9d538b2d 100644 --- a/drivers/net/de4x5.c +++ b/drivers/net/de4x5.c @@ -1,12 +1,35 @@ -/* de4x5.c: A DIGITAL DE425/DE434/DE435/DE450/DE500 ethernet driver for Linux. +/* de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500 + ethernet driver for Linux. Copyright 1994, 1995 Digital Equipment Corporation. - This software may be used and distributed according to the terms of - the GNU Public License, incorporated herein by reference. + Testing resources for this driver have been made available + in part by NASA Ames Research Center (mjacob@nas.nasa.gov). - This driver is written for the Digital Equipment Corporation series - of EtherWORKS ethernet cards: + The author may be reached at davies@maniac.ultranet.com. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Originally, this driver was written for the Digital Equipment + Corporation series of EtherWORKS ethernet cards: DE425 TP/COAX EISA DE434 TP PCI @@ -14,6 +37,25 @@ DE450 TP/COAX/AUI PCI DE500 10/100 PCI Fasternet + but it will now attempt to support all cards which conform to the + Digital Semiconductor SROM Specification. The driver currently + recognises the following chips: + + DC21040 (no SROM) + DC21041[A] + DC21140[A] + + I plan to add DC2114[23] support ASAP, time permitting. So far the + driver is known to work with the following cards: + + KINGSTON + Linksys + ZNYX342 + SMC8432 + SMC9332 (w/new SROM) + ZNYX31[45] + ZNYX346 10/100 4 port (can act as a 10/100 bridge!) + The driver has been tested on a relatively busy network using the DE425, DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred 16M of data to a DECstation 5000/200 as follows: @@ -29,12 +71,12 @@ measurement. Their error is +/-20k on a quiet (private) network and also depend on what load the CPU has. - The author may be reached at davies@maniac.ultranet.com. - ========================================================================= - This driver has been written substantially from scratch, although its + This driver has been written substantially from scratch, although its inheritance of style and stack interface from 'ewrk3.c' and in turn from - Donald Becker's 'lance.c' should be obvious. + Donald Becker's 'lance.c' should be obvious. With the module autoload of + every usable DECchip board, I pinched Donald's 'next_module' field to + link my modules together. Upto 15 EISA cards can be supported under this driver, limited primarily by the available IRQ lines. I have checked different configurations of @@ -46,43 +88,37 @@ to the differences in the EISA and PCI CSR address offsets from the base address. - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long reboot sequences). Loadable module support under PCI and EISA has been achieved by letting the driver autoprobe as if it were compiled into the - kernel, except that there is no autoprobing of the IRQ lines. This is of - no great consequence except do make sure you're not sharing interrupts - with anything that cannot accommodate interrupt sharing! The existing - register_netdevice() code will only allow one device to be registered at - a time. - - ************************************************************************ - For now, please only use the 'io=??' assignment (see 2. below, ?? != 0) - when loading a module. - ************************************************************************ - - Essentially, the I/O address and IRQ information are ignored and filled - in later by the PCI BIOS during the PCI probe. Note that the board - should be in the system at boot time so that its I/O address and IRQ are - allocated by the PCI BIOS automatically. + kernel. Do make sure you're not sharing interrupts with anything that + cannot accommodate interrupt sharing! To utilise this ability, you have to do 8 things: 0) have a copy of the loadable modules code installed on your system. 1) copy de4x5.c from the /linux/drivers/net directory to your favourite temporary directory. - 2) edit the source code near line 3779 to reflect the I/O address you're - using (only if you want to manually load the module), or assign these - when loading by: + 2) for fixed autoprobes (not recommended), edit the source code near + line 5005 to reflect the I/O address you're using, or assign these when + loading by: - insmod de4x5.o io=0xghh where g = bus number + insmod de4x5 io=0xghh where g = bus number hh = device number + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5 + + to load all available boards. For a specific board, still use + the 'io=?' above. 3) compile de4x5.c, but include -DMODULE in the command line to ensure that the correct bits are compiled (see end of source code). 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a kernel with the de4x5 configuration turned off and reboot. - 5) insmod de4x5.o [io=0xghh] + 5) insmod de4x5 [io=0xghh] 6) run the net startup bits for your new eth?? interface(s) manually (usually /etc/rc.inet[12] at boot time). 7) enjoy! @@ -98,13 +134,7 @@ By default, the driver will now autodetect any DECchip based card. Should you have a need to restrict the driver to DIGITAL only cards, you can compile with a DEC_ONLY define, or if loading as a module, use the - 'dec_only=1' parameter. However, this "feature" is in no way supported - nor tested in this driver and the user may use it at his/her sole - discretion. I have had 2 conflicting reports that my driver will or - won't work with Znyx. Try Donald Becker's 'tulip.c' if this driver - doesn't work for you. I will not be supporting Znyx and SMC cards since - I have no information on them and can't test them in a system (this - applies most particularly to the DC21140 based cards). + 'dec_only=1' parameter. I've changed the timing routines to use the kernel timer and scheduling functions so that the hangs and other assorted problems that occurred @@ -125,6 +155,30 @@ aligned DMA transfers and the Alphas get alignment traps with non longword aligned data copies (which makes them really slow). No comment. + I have added SROM decoding routines to make this driver work with any + card that supports the Digital Semiconductor SROM spec. This will help + all cards running the dc2114x series chips in particular. Cards using + the dc2104x chips should run correctly with the basic driver. I'm in + debt to for the testing and feedback that helped get + this feature working. So far we have tested KINGSTON, SMC8432, SMC9332 + (with the latest SROM complying with the SROM spec V3: their first was + broken), ZNYX342 and LinkSys. ZYNX314 (dual 21041 MAC) and ZNYX 315 + (quad 21041 MAC) cards also appear to work despite their incorrectly + wired IRQs. + + I have added a temporary fix for interrupt problems when some SCSI cards + share the same interrupt as the DECchip based cards. The problem occurs + because the SCSI card wants to grab the interrupt as a fast interrupt + (runs the service routine with interrupts turned off) vs. this card + which really needs to run the service routine with interrupts turned on. + This driver will now add the interrupt service routine as a fast + interrupt if it is bounced from the slow interrupt. THIS IS NOT A + RECOMMENDED WAY TO RUN THE DRIVER and has been done for a limited time + until people sort out their compatibility issues and the kernel + interrupt service code is fixed. YOU SHOULD SEPARATE OUT THE FAST + INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not + run on the same interrupt. PCMCIA/CardBus is another can of worms... + TO DO: ------ @@ -208,16 +262,49 @@ with 0.44 13-Aug-96 Fix RX overflow bug in 2114[023] chips. Fix EISA probe bugs reported by - and + and . 0.441 9-Sep-96 Change dc21041_autoconf() to probe quiet BNC media with a loopback packet. 0.442 9-Sep-96 Include AUI in dc21041 media printout. Bug reported by + 0.45 8-Dec-96 Include endian functions for PPC use, from work + by and . + 0.451 28-Dec-96 Added fix to allow autoprobe for modules after + suggestion from . + 0.5 30-Jan-97 Added SROM decoding functions. + Updated debug flags. + Fix sleep/wakeup calls for PCI cards, bug reported + by . + Added multi-MAC, one SROM feature from discussion + with . + Added full module autoprobe capability. + Added attempt to use an SMC9332 with broken SROM. + Added fix for ZYNX multi-mac cards that didn't + get their IRQs wired correctly. + 0.51 13-Feb-97 Added endian fixes for the SROM accesses from + + Fix init_connection() to remove extra device reset. + Fix MAC/PHY reset ordering in dc21140m_autoconf(). + Fix initialisation problem with lp->timeout in + typeX_infoblock() from . + Fix MII PHY reset problem from work done by + . + 0.52 26-Apr-97 Some changes may not credit the right people - + a disk crash meant I lost some mail. + Change RX interrupt routine to drop rather than + defer packets to avoid hang reported by + . + Fix srom_exec() to return for COMPACT and type 1 + infoblocks. + Added DC21142 and DC21143 functions. + Added byte counters from + Added SA_INTERRUPT temporary fix from + . ========================================================================= */ -static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n"; #include @@ -235,7 +322,8 @@ static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\ #include #include #include -#include +#include +#include #include #include @@ -250,14 +338,37 @@ static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\ #define c_char const char +#include +#if LINUX_VERSION_CODE < ((2 << 16) | (1 << 8)) +#define net_device_stats enet_statistics +#define copy_to_user(a,b,c) memcpy_tofs(a,b,c) +#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c) +#define le16_to_cpu(a) cpu_to_le16(a) +#define le32_to_cpu(a) cpu_to_le32(a) +#ifdef __powerpc__ +#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8)) +#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\ + (((a) & 0x0000ff00U) << 8) |\ + (((a) & 0x00ff0000U) >> 8) |\ + (((a) & 0xff000000U) >> 24)) +#else +#define cpu_to_le16(a) (a) +#define cpu_to_le32(a) (a) +#endif /* __powerpc__ */ +#include +#else +#include +#endif /* LINUX_VERSION_CODE */ +#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a))) + /* ** MII Information */ struct phy_table { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ int ta; /* One cycle TA time - 802.3u is confusing here */ - struct { /* Non autonegotiation (parallel) speed det. */ + struct { /* Non autonegotiation (parallel) speed det. */ int reg; int mask; int value; @@ -265,19 +376,35 @@ struct phy_table { }; struct mii_phy { - int reset; /* Hard reset required? */ - int id; /* IEEE OUI */ - int ta; /* One cycle TA time */ + int reset; /* Hard reset required? */ + int id; /* IEEE OUI */ + int ta; /* One cycle TA time */ struct { /* Non autonegotiation (parallel) speed det. */ int reg; int mask; int value; } spd; - int addr; /* MII address for the PHY */ + int addr; /* MII address for the PHY */ + u_char *gep; /* Start of GEP sequence block in SROM */ + u_char *rst; /* Start of reset sequence in SROM */ + u_int mc; /* Media Capabilities */ + u_int ana; /* NWay Advertisement */ + u_int fdx; /* Full DupleX capabilites for each media */ + u_int ttm; /* Transmit Threshold Mode for each media */ }; #define DE4X5_MAX_PHY 8 /* Allow upto 8 attached PHY devices per board */ +struct sia_phy { + u_char mc; /* Media Code */ + u_char ext; /* csr13-15 valid when set */ + int csr13; /* SIA Connectivity Register */ + int csr14; /* SIA TX/RX Register */ + int csr15; /* SIA General Register */ + int gepc; /* SIA GEP Control Information */ + int gep; /* SIA GEP Data */ +}; + /* ** Define the know universe of PHY devices that can be ** recognised by this driver @@ -285,8 +412,8 @@ struct mii_phy { static struct phy_table phy_info[] = { {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}}, /* National TX */ {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}}, /* Broadcom T4 */ - {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ - {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */ + {0, SEEQ_T4 , 1, {0x12, 0x10, 0x10}}, /* SEEQ T4 */ + {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}} /* Cypress T4 */ }; /* @@ -300,11 +427,23 @@ static c_char enet_det[][ETH_ALEN] = { #define SMC 1 #define ACCTON 2 +/* +** SROM Repair definitions. If a broken SROM is detected a card may +** use this information to help figure out what to do. This is a +** "stab in the dark" and so far for SMC9332's only. +*/ +static c_char srom_repair_info[][100] = { + {0x00,0x1e,0x00,0x00,0x00,0x08, /* SMC9332 */ + 0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02, + 0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50, + 0x00,0x18,} +}; + #ifdef DE4X5_DEBUG static int de4x5_debug = DE4X5_DEBUG; #else -static int de4x5_debug = 1; +static int de4x5_debug = (0); #endif #ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */ @@ -449,7 +588,7 @@ struct de4x5_srom { char id_block_crc; char reserved2; char version; - char num_adapters; + char num_controllers; char ieee_addr[6]; char info[100]; short chksum; @@ -494,7 +633,7 @@ struct de4x5_private { int tx_new, tx_old; /* TX descriptor ring pointers */ char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */ char frame[64]; /* Min sized packet for loopback*/ - struct enet_statistics stats; /* Public stats */ + struct net_device_stats stats; /* Public stats */ struct { u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */ u_int unicast; @@ -512,19 +651,21 @@ struct de4x5_private { char txRingSize; int bus; /* EISA or PCI */ int bus_num; /* PCI Bus number */ + int device; /* Device number on PCI bus */ int state; /* Adapter OPENED or CLOSED */ int chipset; /* DC21040, DC21041 or DC21140 */ s32 irq_mask; /* Interrupt Mask (Enable) bits */ s32 irq_en; /* Summary interrupt bits */ int media; /* Media (eg TP), mode (eg 100B)*/ int c_media; /* Remember the last media conn */ + int fdx; /* media full duplex flag */ int linkOK; /* Link is OK */ int autosense; /* Allow/disallow autosensing */ int tx_enable; /* Enable descriptor polling */ - int lostMedia; /* Possibly lost media */ int setup_f; /* Setup frame filtering type */ int local_state; /* State within a 'media' state */ struct mii_phy phy[DE4X5_MAX_PHY]; /* List of attached PHY devices */ + struct sia_phy sia; /* SIA PHY Information */ int active; /* Index to active PHY device */ int mii_cnt; /* Number of attached PHY's */ int timeout; /* Scheduling counter */ @@ -537,13 +678,31 @@ struct de4x5_private { s32 csr0; /* Saved Bus Mode Register */ s32 csr6; /* Saved Operating Mode Reg. */ s32 csr7; /* Saved IRQ Mask Register */ + s32 gep; /* Saved General Purpose Reg. */ + s32 gepc; /* Control info for GEP */ s32 csr13; /* Saved SIA Connectivity Reg. */ s32 csr14; /* Saved SIA TX/RX Register */ s32 csr15; /* Saved SIA General Register */ int save_cnt; /* Flag if state already saved */ struct sk_buff *skb; /* Save the (re-ordered) skb's */ } cache; + struct de4x5_srom srom; /* A copy of the SROM */ + struct device *next_module; /* Link to the next module */ int rx_ovf; /* Check for 'RX overflow' tag */ + int useSROM; /* For non-DEC card use SROM */ + int useMII; /* Infoblock using the MII */ + int asBitValid; /* Autosense bits in GEP? */ + int asPolarity; /* 0 => asserted high */ + int asBit; /* Autosense bit number in GEP */ + int defMedium; /* SROM default medium */ + int tcount; /* Last infoblock number */ + int infoblock_init; /* Initialised this infoblock? */ + int infoleaf_offset; /* SROM infoleaf for controller */ + s32 infoblock_csr6; /* csr6 value in SROM infoblock */ + int infoblock_media; /* infoblock media */ + int (*infoleaf_fn)(struct device *); /* Pointer to infoleaf function */ + u_char *rst; /* Pointer to Type 5 reset info */ + u_char ibn; /* Infoblock number */ }; /* @@ -558,8 +717,28 @@ static struct bus_type { int chipset; struct de4x5_srom srom; int autosense; + int useSROM; } bus; +/* +** To get around certain poxy cards that don't provide an SROM +** for the second and more DECchip, I have to key off the first +** chip's address. I'll assume there's not a bad SROM iff: +** +** o the chipset is the same +** o the bus number is the same and > 0 +** o the sum of all the returned hw address bytes is 0 or 0x5fa +** +** Also have to save the irq for those cards whose hardware designers +** can't follow the PCI to PCI Bridge Architecture spec. +*/ +static struct { + int chipset; + int bus; + int irq; + u_char addr[ETH_ALEN]; +} last = {0,}; + /* ** The transmit ring full condition is described by the tx_old and tx_new ** pointers by: @@ -580,7 +759,7 @@ static int de4x5_open(struct device *dev); static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev); static void de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int de4x5_close(struct device *dev); -static struct enet_statistics *de4x5_get_stats(struct device *dev); +static struct net_device_stats *de4x5_get_stats(struct device *dev); static void de4x5_local_stats(struct device *dev, char *buf, int pkt_len); static void set_multicast_list(struct device *dev); static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd); @@ -605,6 +784,7 @@ static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_b static int dc21040_autoconf(struct device *dev); static int dc21041_autoconf(struct device *dev); static int dc21140m_autoconf(struct device *dev); +static int srom_autoconf(struct device *dev); static int de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *)); static int dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int)); static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec); @@ -642,6 +822,10 @@ static short srom_data(u_int command, u_long address); /*static void srom_busy(u_int command, u_long address);*/ static void sendto_srom(u_int command, u_long addr); static int getfrom_srom(u_long addr); +static void srom_map_media(struct device *dev); +static int srom_infoleaf_info(struct device *dev); +static void srom_init(struct device *dev); +static void srom_exec(struct device *dev, u_char *p); static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr); static void mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr); static int mii_rdata(u_long ioaddr); @@ -655,6 +839,8 @@ static int mii_get_oui(u_char phyaddr, u_long ioaddr); static int mii_get_phy(struct device *dev); static void SetMulticastFilter(struct device *dev); static int get_hw_addr(struct device *dev); +static void srom_repair(struct device *dev, int card); +static int test_bad_enet(struct device *dev, int status); static void eisa_probe(struct device *dev, u_long iobase); static void pci_probe(struct device *dev, u_long iobase); @@ -664,20 +850,33 @@ static struct device *insert_device(struct device *dev, u_long iobase, static char *build_setup_frame(struct device *dev, int mode); static void disable_ast(struct device *dev); static void enable_ast(struct device *dev, u32 time_out); -static long de4x5_switch_to_srl(struct device *dev); -static long de4x5_switch_to_mii(struct device *dev); +static long de4x5_switch_mac_port(struct device *dev); static void timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec); +static void yawn(struct device *dev, int state); static int de4x5_dev_index(char *s); +static void link_modules(struct device *dev, struct device *tmp); static void de4x5_dbg_open(struct device *dev); static void de4x5_dbg_mii(struct device *dev, int k); static void de4x5_dbg_media(struct device *dev); static void de4x5_dbg_srom(struct de4x5_srom *p); static void de4x5_dbg_rx(struct sk_buff *skb, int len); static int de4x5_strncmp(char *a, char *b, int n); +static int dc21041_infoleaf(struct device *dev); +static int dc21140_infoleaf(struct device *dev); +static int dc21142_infoleaf(struct device *dev); +static int dc21143_infoleaf(struct device *dev); +static int type0_infoblock(struct device *dev, u_char count, u_char *p); +static int type1_infoblock(struct device *dev, u_char count, u_char *p); +static int type2_infoblock(struct device *dev, u_char count, u_char *p); +static int type3_infoblock(struct device *dev, u_char count, u_char *p); +static int type4_infoblock(struct device *dev, u_char count, u_char *p); +static int type5_infoblock(struct device *dev, u_char count, u_char *p); +static int compact_infoblock(struct device *dev, u_char count, u_char *p); #ifdef MODULE int init_module(void); void cleanup_module(void); +static struct device *unlink_modules(struct device *p); static int autoprobed = 0, loading_module = 1; # else static int autoprobed = 0, loading_module = 0; @@ -686,7 +885,37 @@ static int autoprobed = 0, loading_module = 0; static char name[DE4X5_NAME_LENGTH + 1]; static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST; static int num_de4x5s = 0, num_eth = 0; -static int cfrv = 0; +static int cfrv = 0, useSROM = 0; + +/* +** List the SROM infoleaf functions and chipsets +*/ +struct InfoLeaf { + int chipset; + int (*fn)(struct device *); +}; +static struct InfoLeaf infoleaf_array[] = { + {DC21041, dc21041_infoleaf}, + {DC21140, dc21140_infoleaf}, + {DC21142, dc21142_infoleaf}, + {DC21143, dc21143_infoleaf} +}; +#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *))) + +/* +** List the SROM info block functions +*/ +static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = { + type0_infoblock, + type1_infoblock, + type2_infoblock, + type3_infoblock, + type4_infoblock, + type5_infoblock, + compact_infoblock +}; + +#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1) /* ** Miscellaneous defines... @@ -703,6 +932,13 @@ static int cfrv = 0; de4x5_ms_delay(1);\ } +#define PHY_HARD_RESET {\ + outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */\ + udelay(1000); /* Assert for 1ms */\ + outl(0x00, DE4X5_GEP);\ + udelay(2000); /* Wait for 2ms */\ +} + /* ** Autoprobing in modules is allowed here. See the top of the file for @@ -712,17 +948,12 @@ static int cfrv = 0; int de4x5_probe(struct device *dev) { - int tmp = num_de4x5s, status = -ENODEV; + int status = -ENODEV; u_long iobase = dev->base_addr; eisa_probe(dev, iobase); pci_probe(dev, iobase); - if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) { - printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name, - iobase); - } - /* ** Walk the device list to check that at least one device ** initialised OK @@ -731,7 +962,7 @@ de4x5_probe(struct device *dev) if (dev->priv) status = 0; if (iobase == 0) autoprobed = 1; - + return status; } @@ -739,16 +970,18 @@ static int de4x5_hw_init(struct device *dev, u_long iobase) { struct bus_type *lp = &bus; - int tmpbus, tmpchs, status=0; - int i, media = *((char *)&(lp->srom) + *((char *)&(lp->srom) + 19) * 3); + int i, status=0; char *tmp; /* Ensure we're not sleeping */ - if (lp->chipset == DC21041) { - outl(0, PCI_CFDA); - de4x5_ms_delay(10); + if (lp->bus == EISA) { + outb(WAKEUP, PCI_CFPM); + } else { + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); } - + de4x5_ms_delay(10); + RESET_DE4X5; if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) { @@ -758,6 +991,7 @@ de4x5_hw_init(struct device *dev, u_long iobase) /* ** Now find out what kind of DC21040/DC21041/DC21140 board we have. */ + useSROM = FALSE; if (lp->bus == PCI) { PCI_signature(name, lp); } else { @@ -784,9 +1018,6 @@ de4x5_hw_init(struct device *dev, u_long iobase) } printk("%2.2x,\n", dev->dev_addr[i]); - tmpbus = lp->bus; - tmpchs = lp->chipset; - if (status != 0) { printk(" which has an Ethernet PROM CRC error.\n"); return -ENXIO; @@ -810,26 +1041,23 @@ de4x5_hw_init(struct device *dev, u_long iobase) dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN); lp = (struct de4x5_private *)dev->priv; memset(dev->priv, 0, sizeof(struct de4x5_private)); - lp->bus = tmpbus; - lp->chipset = tmpchs; + lp->bus = bus.bus; + lp->bus_num = bus.bus_num; + lp->device = bus.device; + lp->chipset = bus.chipset; lp->cache.priv = tmp; + lp->cache.gepc = GEP_INIT; + lp->asBit = GEP_SLNK; + lp->asPolarity = GEP_SLNK; + lp->asBitValid = TRUE; + lp->timeout = -1; + lp->useSROM = useSROM; + memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom)); - /* - ** Check for an MII interface - */ - if (media & MEDIA_MII) { /* MII interface? */ - if (!mii_get_phy(dev)) { - printk("%s: MII search failed, no device found when one was expected\n", dev->name); - return -ENXIO; - } - } else { - mii_get_phy(dev); /* Search the MII anyway! */ - } - /* ** Choose correct autosensing in case someone messed up */ - if (de4x5_autosense & AUTO) { + if ((de4x5_autosense & AUTO) || lp->useSROM) { lp->autosense = AUTO; } else { if (lp->chipset != DC21140) { @@ -844,14 +1072,14 @@ de4x5_hw_init(struct device *dev, u_long iobase) lp->autosense = de4x5_autosense & 0x00c0; } } - + lp->fdx = de4x5_full_duplex; sprintf(lp->adapter_name,"%s (%s)", name, dev->name); /* ** Set up the RX descriptor ring (Intels) ** Allocate contiguous receive buffers, long word aligned (Alphas) */ -#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) for (i=0; irx_ring[i].status = 0; lp->rx_ring[i].des1 = RX_BUFF_SZ; @@ -871,8 +1099,8 @@ de4x5_hw_init(struct device *dev, u_long iobase) tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN); for (i=0; irx_ring[i].status = 0; - lp->rx_ring[i].des1 = RX_BUFF_SZ; - lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ); + lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); + lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ)); lp->rx_ring[i].next = 0; lp->rx_skb[i] = (struct sk_buff *) 1; /* Dummy entry */ } @@ -888,8 +1116,8 @@ de4x5_hw_init(struct device *dev, u_long iobase) lp->txRingSize = NUM_TX_DESC; /* Write the end of list marker to the descriptor lists */ - lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER; - lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER; + lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER); + lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER); /* Tell the adapter where the TX/RX rings are located. */ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA); @@ -903,20 +1131,35 @@ de4x5_hw_init(struct device *dev, u_long iobase) create_packet(dev, lp->frame, sizeof(lp->frame)); /* Check if the RX overflow bug needs testing for */ - tmpchs = cfrv & 0x000000fe; - if ((lp->chipset == DC21140) && (tmpchs == 0x20)) { + i = cfrv & 0x000000fe; + if ((lp->chipset == DC21140) && (i == 0x20)) { lp->rx_ovf = 1; } - /* Initialise the adapter state */ + /* Initialise the SROM pointers if possible */ + if (lp->useSROM) { + lp->state = INITIALISED; + de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); + if (srom_infoleaf_info(dev)) { + return -ENXIO; + } + srom_init(dev); + } + lp->state = CLOSED; + /* + ** Check for an MII interface + */ + if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) { + mii_get_phy(dev); + } + printk(" and requires IRQ%d (provided by %s).\n", dev->irq, ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); - } - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_VERSION) { printk(version); } @@ -930,14 +1173,11 @@ de4x5_hw_init(struct device *dev, u_long iobase) dev->mem_start = 0; - /* Fill in the generic field of the device structure. */ + /* Fill in the generic fields of the device structure. */ ether_setup(dev); /* Let the adapter sleep to save power */ - if (lp->chipset == DC21041) { - outl(0, DE4X5_SICR); - outl(CFDA_PSM, PCI_CFDA); - } + yawn(dev, SLEEP); return status; } @@ -962,10 +1202,7 @@ de4x5_open(struct device *dev) /* ** Wake up the adapter */ - if (lp->chipset == DC21041) { - outl(0, PCI_CFDA); - de4x5_ms_delay(10); - } + yawn(dev, WAKEUP); /* ** Re-initialize the DE4X5... @@ -977,20 +1214,31 @@ de4x5_open(struct device *dev) if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, lp->adapter_name, dev)) { - printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq); - status = -EAGAIN; - } else { - dev->tbusy = 0; - dev->start = 1; - dev->interrupt = UNMASK_INTERRUPTS; - dev->trans_start = jiffies; - - START_DE4X5; - - de4x5_setup_intr(dev); + printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq); + if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ, + lp->adapter_name, dev)) { + printk("\n Cannot get IRQ- reconfigure your hardware.\n"); + disable_ast(dev); + de4x5_free_rx_buffs(dev); + de4x5_free_tx_buffs(dev); + yawn(dev, SLEEP); + lp->state = CLOSED; + return -EAGAIN; + } else { + printk("\n Succeeded, but you should reconfigure your hardware to avoid this.\n"); + printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n"); + } } + dev->tbusy = 0; + dev->start = 1; + dev->interrupt = UNMASK_INTERRUPTS; + dev->trans_start = jiffies; + + START_DE4X5; + + de4x5_setup_intr(dev); - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_OPEN) { printk("\tsts: 0x%08x\n", inl(DE4X5_STS)); printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR)); printk("\timr: 0x%08x\n", inl(DE4X5_IMR)); @@ -1037,12 +1285,15 @@ de4x5_sw_reset(struct device *dev) s32 bmr, omr; /* Select the MII or SRL port now and RESET the MAC */ - if (lp->phy[lp->active].id == 0) { - de4x5_switch_to_srl(dev); - } else { - de4x5_switch_to_mii(dev); + if (!lp->useSROM) { + if (lp->phy[lp->active].id != 0) { + lp->infoblock_csr6 = OMR_PS | OMR_HBD; + } else { + lp->infoblock_csr6 = OMR_TTM; + } + de4x5_switch_mac_port(dev); } - + /* ** Set the programmable burst length to 8 longwords for all the DC21140 ** Fasternet chips and 4 longwords for all others: DMA errors result @@ -1063,11 +1314,11 @@ de4x5_sw_reset(struct device *dev) lp->tx_new = lp->tx_old = 0; for (i = 0; i < lp->rxRingSize; i++) { - lp->rx_ring[i].status = R_OWN; + lp->rx_ring[i].status = cpu_to_le32(R_OWN); } for (i = 0; i < lp->txRingSize; i++) { - lp->tx_ring[i].status = 0; + lp->tx_ring[i].status = cpu_to_le32(0); } barrier(); @@ -1082,7 +1333,7 @@ de4x5_sw_reset(struct device *dev) sti(); /* Ensure timer interrupts */ for (j=0, i=0;(i<500) && (j==0);i++) { /* Upto 500ms delay */ udelay(1000); - if (lp->tx_ring[lp->tx_new].status >= 0) j=1; + if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1; } outl(omr, DE4X5_OMR); /* Stop everything! */ @@ -1137,8 +1388,8 @@ de4x5_queue_pkt(struct sk_buff *skb, struct device *dev) } else { de4x5_put_cache(dev, skb); } - if (de4x5_debug > 1) { - printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n lostMedia:%d\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO")); + if (de4x5_debug & DEBUG_TX) { + printk("%s: transmit busy, lost media or stale skb found:\n STS:%08x\n tbusy:%ld\n IMR:%08x\n OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO")); } } else if (skb->len > 0) { /* If we already have stuff queued locally, use that first */ @@ -1151,6 +1402,9 @@ de4x5_queue_pkt(struct sk_buff *skb, struct device *dev) cli(); set_bit(0, (void*)&dev->tbusy); load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb); +#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + lp->stats.tx_bytes += skb->len; +#endif outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */ lp->tx_new = (++lp->tx_new) % lp->txRingSize; @@ -1200,6 +1454,9 @@ de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) printk("%s: Re-entering the interrupt handler.\n", dev->name); DISABLE_IRQs; /* Ensure non re-entrancy */ +#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + synchronize_irq(); +#endif dev->interrupt = MASK_INTERRUPTS; for (limit=0; limit<8; limit++) { @@ -1215,7 +1472,6 @@ de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs) de4x5_tx(dev); if (sts & STS_LNF) { /* TP Link has failed */ - lp->lostMedia = LOST_MEDIA_THRESHOLD + 1; lp->irq_mask &= ~IMR_LFM; } @@ -1253,8 +1509,9 @@ de4x5_rx(struct device *dev) int entry; s32 status; - for (entry=lp->rx_new; lp->rx_ring[entry].status>=0;entry=lp->rx_new) { - status = lp->rx_ring[entry].status; + for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0; + entry=lp->rx_new) { + status = (s32)le32_to_cpu(lp->rx_ring[entry].status); if (lp->rx_ovf) { if (inl(DE4X5_MFC) & MFC_FOCM) { @@ -1268,7 +1525,7 @@ de4x5_rx(struct device *dev) } if (status & RD_LS) { /* Valid frame status */ - lp->linkOK++; + if (lp->tx_enable) lp->linkOK++; if (status & RD_ES) { /* There was an error. */ lp->stats.rx_errors++; /* Update the error stats. */ if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++; @@ -1281,31 +1538,35 @@ de4x5_rx(struct device *dev) if (status & RD_OF) lp->pktStats.rx_overflow++; } else { /* A valid frame received */ struct sk_buff *skb; - short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4; + short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status) + >> 16) - 4; if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) { printk("%s: Insufficient memory; nuking packet.\n", dev->name); - lp->stats.rx_dropped++; /* Really, deferred. */ - break; - } - de4x5_dbg_rx(skb, pkt_len); + lp->stats.rx_dropped++; + } else { + de4x5_dbg_rx(skb, pkt_len); - /* Push up the protocol stack */ - skb->protocol=eth_type_trans(skb,dev); - netif_rx(skb); + /* Push up the protocol stack */ + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); - /* Update stats */ - lp->stats.rx_packets++; - de4x5_local_stats(dev, skb->data, pkt_len); + /* Update stats */ + lp->stats.rx_packets++; +#if LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8)) + lp->stats.rx_bytes += pkt_len; +#endif + de4x5_local_stats(dev, skb->data, pkt_len); + } } /* Change buffer ownership for this frame, back to the adapter */ for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) { - lp->rx_ring[lp->rx_old].status = R_OWN; + lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN); barrier(); } - lp->rx_ring[entry].status = R_OWN; + lp->rx_ring[entry].status = cpu_to_le32(R_OWN); barrier(); } @@ -1330,7 +1591,7 @@ de4x5_tx(struct device *dev) s32 status; for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) { - status = lp->tx_ring[entry].status; + status = (s32)le32_to_cpu(lp->tx_ring[entry].status); if (status < 0) { /* Buffer not sent yet */ break; } else if (status != 0x7fffffff) { /* Not setup frame */ @@ -1342,16 +1603,12 @@ de4x5_tx(struct device *dev) if (status & TD_EC) lp->pktStats.excessive_collisions++; if (status & TD_DE) lp->stats.tx_aborted_errors++; - if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) { - lp->lostMedia++; - } if (TX_PKT_PENDING) { outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */ } } else { /* Packet sent */ lp->stats.tx_packets++; - lp->lostMedia = 0; /* Remove transient problem */ - lp->linkOK++; + if (lp->tx_enable) lp->linkOK++; } /* Update the collision counter */ lp->stats.collisions += ((status & TD_EC) ? 16 : @@ -1384,7 +1641,9 @@ de4x5_ast(struct device *dev) disable_ast(dev); - if (lp->chipset == DC21140) { + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21140) { next_tick = dc21140m_autoconf(dev); } else if (lp->chipset == DC21041) { next_tick = dc21041_autoconf(dev); @@ -1431,8 +1690,8 @@ de4x5_rx_ovfc(struct device *dev) outl(omr & ~OMR_SR, DE4X5_OMR); while (inl(DE4X5_STS) & STS_RS); - for (; lp->rx_ring[lp->rx_new].status>=0;) { - lp->rx_ring[lp->rx_new].status = R_OWN; + for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) { + lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN); lp->rx_new = (++lp->rx_new % lp->rxRingSize); } @@ -1452,7 +1711,7 @@ de4x5_close(struct device *dev) dev->start = 0; dev->tbusy = 1; - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_CLOSE) { printk("%s: Shutting down ethercard, status was %8.8x.\n", dev->name, inl(DE4X5_STS)); } @@ -1474,15 +1733,12 @@ de4x5_close(struct device *dev) MOD_DEC_USE_COUNT; /* Put the adapter to sleep to save power */ - if (lp->chipset == DC21041) { - outl(0, DE4X5_SICR); - outl(CFDA_PSM, PCI_CFDA); - } + yawn(dev, SLEEP); return 0; } -static struct enet_statistics * +static struct net_device_stats * de4x5_get_stats(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; @@ -1529,12 +1785,12 @@ load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf); - lp->tx_ring[lp->tx_new].des1 &= TD_TER; - lp->tx_ring[lp->tx_new].des1 |= flags; + lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf)); + lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER); + lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags); lp->tx_skb[lp->tx_new] = skb; barrier(); - lp->tx_ring[lp->tx_new].status = T_OWN; + lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN); barrier(); return; @@ -1548,7 +1804,7 @@ set_multicast_list(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - + /* First, double check that the adapter is open */ if (lp->state == OPEN) { if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ @@ -1586,7 +1842,7 @@ SetMulticastFilter(struct device *dev) u32 omr, crc, poly = CRC_POLYNOMIAL_LE; char *pa; unsigned char *addrs; - + omr = inl(DE4X5_OMR); omr &= ~(OMR_PR | OMR_PM); pa = build_setup_frame(dev, ALL); /* Build the basic frame */ @@ -1639,15 +1895,15 @@ SetMulticastFilter(struct device *dev) static void eisa_probe(struct device *dev, u_long ioaddr) { - int i, maxSlots, status; - u_short vendor, device; - s32 cfid; + int i, maxSlots, status, device; + u_short vendor; + u32 cfid; u_long iobase; struct bus_type *lp = &bus; char name[DE4X5_STRLEN]; struct device *tmp; - if (!ioaddr && autoprobed) return; /* Been here before ! */ + if (autoprobed) return; /* Been here before ! */ lp->bus = EISA; @@ -1661,17 +1917,18 @@ eisa_probe(struct device *dev, u_long ioaddr) maxSlots = i + 1; } - for (status = -ENODEV; (i> 16); + cfid = (u32) inl(PCI_CFID); + cfrv = (u_short) inl(PCI_CFRV); + device = (cfid >> 8) & 0x00ffff00; vendor = (u_short) cfid; /* Read the EISA Configuration Registers */ dev->irq = inb(EISA_REG0); dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03]; + if (is_DC2114x) device |= (cfrv & 0x00f0); lp->chipset = device; DevicePresent(DE4X5_APROM); /* Write the PCI Configuration Registers */ @@ -1683,6 +1940,9 @@ eisa_probe(struct device *dev, u_long ioaddr) if ((tmp = alloc_device(dev, iobase)) != NULL) { if ((status = de4x5_hw_init(tmp, iobase)) == 0) { num_de4x5s++; + if (loading_module) link_modules(dev, tmp); + } else if (loading_module && (tmp != dev)) { + kfree(tmp); } } } else if (autoprobed) { @@ -1714,13 +1974,13 @@ pci_probe(struct device *dev, u_long ioaddr) { u_char irq; u_char pb, pbus, dev_num, dnum, dev_fn; - u_short vendor, device, index, status; + u_short dev_id, vendor, index, status; u_int class = DE4X5_CLASS_CODE; - u_int iobase; + u_int device, iobase; struct bus_type *lp = &bus; struct device *tmp; - if ((!ioaddr || !loading_module) && autoprobed) return; + if (autoprobed) return; if (!pcibios_present()) return; /* No PCI bus in this machine! */ @@ -1738,21 +1998,32 @@ pci_probe(struct device *dev, u_long ioaddr) (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND); index++) { dev_num = PCI_SLOT(dev_fn); - + if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) { + device = 0; pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device); - if (!(is_DC21040 || is_DC21041 || is_DC21140)) continue; + pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id); + device = dev_id; + device <<= 8; + if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) { + continue; + } + + /* Get the chip configuration revision register */ + pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); /* Set the device number information */ lp->device = dev_num; lp->bus_num = pb; /* Set the chipset information */ + if (is_DC2114x) device |= (cfrv & 0x00f0); lp->chipset = device; - - /* Get the chip configuration revision register */ - pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv); + + if (is_DC21142 || is_DC21143) { + printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143")); + continue; + } /* Get the board I/O address */ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase); @@ -1778,6 +2049,9 @@ pci_probe(struct device *dev, u_long ioaddr) tmp->irq = irq; if ((status = de4x5_hw_init(tmp, iobase)) == 0) { num_de4x5s++; + if (loading_module) link_modules(dev, tmp); + } else if (loading_module && (tmp != dev)) { + kfree(tmp); } } } else if (autoprobed) { @@ -1802,9 +2076,17 @@ alloc_device(struct device *dev, u_long iobase) struct device *adev = NULL; int fixed = 0, new_dev = 0; + if (!dev) return dev; num_eth = de4x5_dev_index(dev->name); - if (loading_module) return dev; - + + if (loading_module) { + if (dev->priv) { + dev = insert_device(dev, iobase, de4x5_probe); + } + num_eth++; + return dev; + } + while (1) { if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) { adev=dev; @@ -1851,20 +2133,22 @@ insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)) printk("eth%d: Device not initialised, insufficient memory\n",num_eth); return NULL; } else { - new->next = dev->next; - dev->next = new; - dev = dev->next; /* point to the new device */ - dev->name = (char *)(dev + 1); - if (num_eth > 9999) { - sprintf(dev->name,"eth????");/* New device name */ - } else { - sprintf(dev->name,"eth%d", num_eth);/* New device name */ + memset((char *)new, 0, sizeof(struct device)+8); + new->name = (char *)(new + 1); + new->base_addr = iobase; /* assign the io address */ + new->init = init; /* initialisation routine */ + if (!loading_module) { + new->next = dev->next; + dev->next = new; + if (num_eth > 9999) { + sprintf(new->name,"eth????");/* New device name */ + } else { + sprintf(new->name,"eth%d", num_eth);/* New device name */ + } } - dev->base_addr = iobase; /* assign the io address */ - dev->init = init; /* initialisation routine */ } - return dev; + return new; } static int @@ -1882,6 +2166,26 @@ de4x5_dev_index(char *s) return i; } +static void +link_modules(struct device *dev, struct device *tmp) +{ + struct device *p=dev; + + if (p) { + while (((struct de4x5_private *)(p->priv))->next_module) { + p = ((struct de4x5_private *)(p->priv))->next_module; + } + + if (dev != tmp) { + ((struct de4x5_private *)(p->priv))->next_module = tmp; + } else { + ((struct de4x5_private *)(p->priv))->next_module = NULL; + } + } + + return; +} + /* ** Auto configure the media here rather than setting the port at compile ** time. This routine is called by de4x5_init() and when a loss of media is @@ -1901,13 +2205,16 @@ autoconf_media(struct device *dev) disable_ast(dev); inl(DE4X5_MFC); /* Zero the lost frames counter */ lp->media = INIT; - if (lp->chipset == DC21040) { + if (lp->useSROM) { + next_tick = srom_autoconf(dev); + } else if (lp->chipset == DC21040) { next_tick = dc21040_autoconf(dev); } else if (lp->chipset == DC21041) { next_tick = dc21041_autoconf(dev); } else if (lp->chipset == DC21140) { next_tick = dc21140m_autoconf(dev); } + enable_ast(dev, next_tick); return (lp->media); @@ -2046,7 +2353,7 @@ de4x5_suspect_state(struct device *dev, int timeout, int prev_state, switch (lp->local_state) { case 1: - if (lp->linkOK && !LOST_MEDIA) { + if (lp->linkOK) { lp->media = prev_state; } else { lp->local_state++; @@ -2063,6 +2370,7 @@ de4x5_suspect_state(struct device *dev, int timeout, int prev_state, lp->media = prev_state; } else { lp->media = INIT; + lp->tcount++; } } @@ -2110,7 +2418,7 @@ dc21041_autoconf(struct device *dev) case TP_NW: if (lp->timeout < 0) { omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FD, DE4X5_OMR); + outl(omr | OMR_FDX, DE4X5_OMR); } irqs = STS_LNF | STS_LNP; irq_mask = IMR_LFM | IMR_LPM; @@ -2157,7 +2465,7 @@ dc21041_autoconf(struct device *dev) if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for TP */ - outl(omr & ~OMR_FD, DE4X5_OMR); + outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = STS_LNF | STS_LNP; irq_mask = IMR_LFM | IMR_LPM; @@ -2191,7 +2499,7 @@ dc21041_autoconf(struct device *dev) if (!lp->tx_enable) { if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */ - outl(omr & ~OMR_FD, DE4X5_OMR); + outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = 0; irq_mask = 0; @@ -2222,7 +2530,7 @@ dc21041_autoconf(struct device *dev) case 0: if (lp->timeout < 0) { omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */ - outl(omr & ~OMR_FD, DE4X5_OMR); + outl(omr & ~OMR_FDX, DE4X5_OMR); } irqs = 0; irq_mask = 0; @@ -2261,7 +2569,7 @@ dc21041_autoconf(struct device *dev) case NC: omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */ - outl(omr | OMR_FD, DE4X5_OMR); + outl(omr | OMR_FDX, DE4X5_OMR); reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */ if (lp->media != lp->c_media) { de4x5_dbg_media(dev); @@ -2287,34 +2595,45 @@ dc21140m_autoconf(struct device *dev) int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr; int next_tick = DE4X5_AUTOSENSE_MS; u_long imr, omr; - + switch(lp->media) { case INIT: - DISABLE_IRQs; - lp->tx_enable = FALSE; - lp->timeout = -1; + if (lp->timeout < 0) { + DISABLE_IRQs; + lp->tx_enable = FALSE; + lp->linkOK = 0; + de4x5_save_skbs(dev); /* Save non transmitted skb's */ + } if ((next_tick = de4x5_reset_phy(dev)) < 0) { next_tick &= ~TIMER_CB; } else { - de4x5_save_skbs(dev); /* Save non transmitted skb's */ - lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ - SET_10Mb; - if (lp->autosense == _100Mb) { - lp->media = _100Mb; - } else if (lp->autosense == _10Mb) { - lp->media = _10Mb; - } else if ((lp->autosense == AUTO) && - ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { - ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); - ana &= (de4x5_full_duplex ? ~0 : ~MII_ANA_FDAM); - mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); - lp->media = ANS; - } else if (lp->autosense == AUTO) { - lp->media = SPD_DET; - } else if (is_spd_100(dev) && is_100_up(dev)) { - lp->media = _100Mb; + if (lp->useSROM) { + srom_map_media(dev); + srom_exec(dev, lp->phy[lp->active].gep); + if (lp->infoblock_media == ANS) { + ana = lp->phy[lp->active].ana | MII_ANA_CSMA; + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + } } else { - lp->media = NC; + lp->tmp = MII_SR_ASSC; /* Fake out the MII speed set */ + SET_10Mb; + if (lp->autosense == _100Mb) { + lp->media = _100Mb; + } else if (lp->autosense == _10Mb) { + lp->media = _10Mb; + } else if ((lp->autosense == AUTO) && + ((sr=is_anc_capable(dev)) & MII_SR_ANC)) { + ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA); + ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM); + mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII); + lp->media = ANS; + } else if (lp->autosense == AUTO) { + lp->media = SPD_DET; + } else if (is_spd_100(dev) && is_100_up(dev)) { + lp->media = _100Mb; + } else { + lp->media = NC; + } } lp->local_state = 0; next_tick = dc21140m_autoconf(dev); @@ -2354,10 +2673,10 @@ dc21140m_autoconf(struct device *dev) if (!(anlpa & MII_ANLPA_RF) && (cap = anlpa & MII_ANLPA_TAF & ana)) { if (cap & MII_ANA_100M) { - de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE); lp->media = _100Mb; } else if (cap & MII_ANA_10M) { - de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); + lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE); lp->media = _10Mb; } @@ -2390,7 +2709,7 @@ dc21140m_autoconf(struct device *dev) break; case _100Mb: /* Set 100Mb/s */ - next_tick = 3000; + next_tick = 3000; if (!lp->tx_enable) { SET_100Mb; de4x5_init_connection(dev); @@ -2398,6 +2717,7 @@ dc21140m_autoconf(struct device *dev) if (!lp->linkOK && (lp->autosense == AUTO)) { if (!(is_spd_100(dev) && is_100_up(dev))) { lp->media = INIT; + lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; } } @@ -2405,7 +2725,7 @@ dc21140m_autoconf(struct device *dev) break; case _10Mb: /* Set 10Mb/s */ - next_tick = 3000; + next_tick = 3000; if (!lp->tx_enable) { SET_10Mb; de4x5_init_connection(dev); @@ -2413,6 +2733,7 @@ dc21140m_autoconf(struct device *dev) if (!lp->linkOK && (lp->autosense == AUTO)) { if (!(!is_spd_100(dev) && is_10_up(dev))) { lp->media = INIT; + lp->tcount++; next_tick = DE4X5_AUTOSENSE_MS; } } @@ -2432,6 +2753,72 @@ dc21140m_autoconf(struct device *dev) return next_tick; } +static int +srom_autoconf(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + return lp->infoleaf_fn(dev); +} + +/* +** This mapping keeps the original media codes and FDX flag unchanged. +** While it isn't strictly necessary, it helps me for the moment... +*/ +static void +srom_map_media(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + + lp->fdx = 0; + switch(lp->infoblock_media) { + case SROM_10BASETF: + lp->fdx = TRUE; + case SROM_10BASET: + if (lp->chipset == DC21140) { + lp->media = _10Mb; + } else { + lp->media = TP; + } + break; + + case SROM_10BASE2: + lp->media = BNC; + break; + + case SROM_10BASE5: + lp->media = AUI; + break; + + case SROM_100BASETF: + lp->fdx = TRUE; + case SROM_100BASET: + lp->media = _100Mb; + break; + + case SROM_100BASET4: + lp->media = _100Mb; + break; + + case SROM_100BASEFF: + lp->fdx = TRUE; + case SROM_100BASEF: + lp->media = _100Mb; + break; + + case ANS: + lp->media = ANS; + break; + + default: + printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, + lp->infoblock_media); + break; + } + + return; +} + static void de4x5_init_connection(struct device *dev) { @@ -2442,11 +2829,10 @@ de4x5_init_connection(struct device *dev) de4x5_dbg_media(dev); lp->c_media = lp->media; /* Stop scrolling media messages */ } - de4x5_restore_skbs(dev); + cli(); - de4x5_rx(dev); + de4x5_restore_skbs(dev); de4x5_setup_intr(dev); - lp->lostMedia = 0; lp->tx_enable = YES; dev->tbusy = 0; sti(); @@ -2456,6 +2842,11 @@ de4x5_init_connection(struct device *dev) return; } +/* +** General PHY reset function. Some MII devices don't reset correctly +** since their MII address pins can float at voltages that are dependent +** on the signal pin use. Do a double reset to ensure a reset. +*/ static int de4x5_reset_phy(struct device *dev) { @@ -2463,17 +2854,30 @@ de4x5_reset_phy(struct device *dev) u_long iobase = dev->base_addr; int next_tick = 0; - if (lp->phy[lp->active].id) { + if ((lp->useSROM) || (lp->phy[lp->active].id)) { if (lp->timeout < 0) { - outl(GEP_HRST, DE4X5_GEP); /* Hard RESET the PHY dev. */ - udelay(1000); /* Assert for 1ms */ - outl(0x00, DE4X5_GEP); - udelay(2000); /* Wait for 2ms */ - mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + if (lp->useSROM) { + if (lp->phy[lp->active].rst) { /* MII device specific reset */ + srom_exec(dev, lp->phy[lp->active].rst); + srom_exec(dev, lp->phy[lp->active].rst); + } else if (lp->rst) { /* Type 5 infoblock reset */ + srom_exec(dev, lp->rst); + srom_exec(dev, lp->rst); + } + } else { + PHY_HARD_RESET; + } + if (lp->useMII) { + mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII); + } + } + if (lp->useMII) { + next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); } - next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500); + } else if (lp->chipset == DC21140) { + PHY_HARD_RESET; } - + return next_tick; } @@ -2546,7 +2950,7 @@ test_sym_link(struct device *dev, int msec) lp->timeout = msec/100; } - if (lp->phy[lp->active].id) { + if (lp->phy[lp->active].id || lp->useSROM) { gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0); } else { gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP)); @@ -2594,7 +2998,11 @@ is_spd_100(struct device *dev) u_long iobase = dev->base_addr; int spd; - if (lp->phy[lp->active].id) { + if (lp->useSROM && !lp->useMII) { + spd = (lp->asBitValid & + (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid); + } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII); spd = ~(spd ^ lp->phy[lp->active].spd.value); spd &= lp->phy[lp->active].spd.mask; @@ -2611,7 +3019,11 @@ is_100_up(struct device *dev) struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->phy[lp->active].id) { + if (lp->useSROM && !lp->useMII) { + return ((lp->asBitValid & + (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); @@ -2626,7 +3038,11 @@ is_10_up(struct device *dev) struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->phy[lp->active].id) { + if (lp->useSROM && !lp->useMII) { + return ((lp->asBitValid & + (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) | + (lp->linkOK & ~lp->asBitValid)); + } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { /* Double read for sticky bits & temporary drops */ mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII); return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS); @@ -2641,7 +3057,7 @@ is_anc_capable(struct device *dev) struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - if (lp->phy[lp->active].id) { + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII)); } else { return 0; @@ -2670,11 +3086,14 @@ ping_media(struct device *dev, int msec) sisr = inl(DE4X5_SISR); - if ((!(sisr & SISR_NCR)) && (lp->tx_ring[lp->tmp].status < 0) && (--lp->timeout)) { + if ((!(sisr & SISR_NCR)) && + ((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && + (--lp->timeout)) { sisr = 100 | TIMER_CB; } else { if ((!(sisr & SISR_NCR)) && - !(lp->tx_ring[lp->tmp].status & (T_OWN | TD_ES)) && lp->timeout) { + !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) && + lp->timeout) { sisr = 0; } else { sisr = 1; @@ -2696,7 +3115,7 @@ de4x5_alloc_rx_buff(struct device *dev, int index, int len) struct de4x5_private *lp = (struct de4x5_private *)dev->priv; struct sk_buff *p; -#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY) struct sk_buff *ret; u_long i=0, tmp; @@ -2728,10 +3147,13 @@ de4x5_alloc_rx_buff(struct device *dev, int index, int len) skb_reserve(p, 2); /* Align */ if (index < lp->rx_old) { /* Wrapped buffer */ short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; - memcpy(skb_put(p,tlen), bus_to_virt(lp->rx_ring[lp->rx_old].buf),tlen); - memcpy(skb_put(p,len-tlen), bus_to_virt(lp->rx_ring[0].buf), len-tlen); + memcpy(skb_put(p,tlen), + bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen); + memcpy(skb_put(p,len-tlen), + bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen); } else { /* Linear buffer */ - memcpy(skb_put(p,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf),len); + memcpy(skb_put(p,len), + bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len); } return p; @@ -2810,13 +3232,26 @@ de4x5_restore_skbs(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; + int i; s32 omr; if (lp->cache.save_cnt) { STOP_DE4X5; - de4x5_cache_state(dev, DE4X5_SAVE_STATE); - de4x5_sw_reset(dev); - de4x5_cache_state(dev, DE4X5_RESTORE_STATE); + outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA); + outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA); + + lp->rx_new = lp->rx_old = 0; + lp->tx_new = lp->tx_old = 0; + + for (i = 0; i < lp->rxRingSize; i++) { + lp->rx_ring[i].status = cpu_to_le32(R_OWN); + } + + for (i = 0; i < lp->txRingSize; i++) { + lp->tx_ring[i].status = cpu_to_le32(0); + } + + barrier(); lp->cache.save_cnt--; START_DE4X5; } @@ -2829,7 +3264,6 @@ de4x5_cache_state(struct device *dev, int flag) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; u_long iobase = dev->base_addr; - s32 gep; switch(flag) { case DE4X5_SAVE_STATE: @@ -2848,12 +3282,8 @@ de4x5_cache_state(struct device *dev, int flag) outl(lp->cache.csr6, DE4X5_OMR); outl(lp->cache.csr7, DE4X5_IMR); if (lp->chipset == DC21140) { - outl(GEP_INIT, DE4X5_GEP); - gep = (lp->media == _100Mb ? GEP_MODE : 0); - if (!lp->phy[lp->active].id && !de4x5_full_duplex) { - gep |= GEP_FDXD; - } - outl(gep, DE4X5_GEP); + outl(lp->cache.gepc, DE4X5_GEP); + outl(lp->cache.gep, DE4X5_GEP); } else { reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15); @@ -2975,7 +3405,7 @@ reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr) } /* -** Create a loopback ethernet packet with an invalid CRC +** Create a loopback ethernet packet */ static void create_packet(struct device *dev, char *frame, int len) @@ -3069,13 +3499,10 @@ PCI_signature(char *name, struct bus_type *lp) if (lp->chipset == DC21040) { strcpy(name, "DE434/5"); - } else { + return status; + } else { /* Search for a DEC name in the SROM */ int i = *((char *)&lp->srom + 19) * 3; - if (lp->chipset == DC21041) { - strncpy(name, (char *)&lp->srom + 26 + i, 8); - } else if (lp->chipset == DC21140) { - strncpy(name, (char *)&lp->srom + 26 + i, 8); - } + strncpy(name, (char *)&lp->srom + 26 + i, 8); } name[8] = '\0'; for (i=0; ichipset == DC21040) ? "DC21040" : ((lp->chipset == DC21041) ? "DC21041" : - ((lp->chipset == DC21140) ? "DC21140" : "UNKNOWN" - ))))); + ((lp->chipset == DC21140) ? "DC21140" : + ((lp->chipset == DC21142) ? "DC21142" : + ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN" + ))))))); + } + if (lp->chipset != DC21041) { + useSROM = TRUE; /* card is not recognisably DEC */ } } @@ -3108,9 +3540,10 @@ DevicePresent(u_long aprom_addr) if (lp->chipset == DC21040) { outl(0, aprom_addr); /* Reset Ethernet Address ROM Pointer */ } else { /* Read new srom */ - short *p = (short *)&lp->srom; + u_short tmp, *p = (short *)&lp->srom; for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) { - *p++ = srom_rd(aprom_addr, i); + tmp = srom_rd(aprom_addr, i); + *p++ = le16_to_cpu(tmp); } de4x5_dbg_srom((struct de4x5_srom *)&lp->srom); } @@ -3118,6 +3551,12 @@ DevicePresent(u_long aprom_addr) return; } +/* +** For the bad status case and no SROM, then add one to the previous +** address. However, need to add one backwards in case we have 0xff +** as one or more of the bytes. Only the last 3 bytes should be checked +** as the first three are invariant - assigned to an organisation. +*/ static int get_hw_addr(struct device *dev) { @@ -3171,6 +3610,12 @@ get_hw_addr(struct device *dev) if ((k != chksum) && (dec_only)) status = -1; } + /* If possible, try to fix a broken card - SMC only so far */ + srom_repair(dev, broken); + + /* Test for a bad enet address */ + status = test_bad_enet(dev, status); + return status; } @@ -3210,6 +3655,55 @@ de4x5_strncmp(char *a, char *b, int n) return ret; } +static void +srom_repair(struct device *dev, int card) +{ + struct bus_type *lp = &bus; + + switch(card) { + case SMC: + memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom)); + memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN); + memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100); + useSROM = TRUE; + break; + } + + return; +} + +static int +test_bad_enet(struct device *dev, int status) +{ + struct bus_type *lp = &bus; + int i, tmp; + + for (tmp=0,i=0; idev_addr[i]; + if ((tmp == 0) || (tmp == 0x5fa)) { + if ((lp->chipset == last.chipset) && + (lp->bus_num == last.bus) && (lp->bus_num > 0)) { + for (i=0; idev_addr[i] = last.addr[i]; + for (i=ETH_ALEN-1; i>2; --i) { + dev->dev_addr[i] += 1; + if (dev->dev_addr[i] != 0) break; + } + for (i=0; idev_addr[i]; + if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) && + (lp->chipset == DC21040)) { + dev->irq = last.irq; + } + status = 0; + } + } else if (!status) { + last.chipset = lp->chipset; + last.bus = lp->bus_num; + last.irq = dev->irq; + for (i=0; idev_addr[i]; + } + + return status; +} + /* ** SROM Read */ @@ -3321,9 +3815,394 @@ getfrom_srom(u_long addr) return tmp; } -/* -** MII Read/Write -*/ +static int +srom_infoleaf_info(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int i, count; + u_char *p; + + /* Find the infoleaf decoder function that matches this chipset */ + for (i=0; ichipset == infoleaf_array[i].chipset) break; + } + if (i == INFOLEAF_SIZE) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct chipset for SROM decoding!\n", + dev->name); + return -ENXIO; + } + + lp->infoleaf_fn = infoleaf_array[i].fn; + + /* Find the information offset that this function should use */ + count = *((u_char *)&lp->srom + 19); + p = (u_char *)&lp->srom + 26; + + if (count > 1) { + for (i=count; i; --i, p+=3) { + if (lp->device == *p) break; + } + if (i == 0) { + lp->useSROM = FALSE; + printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", + dev->name, lp->device); + return -ENXIO; + } + } + + lp->infoleaf_offset = TWIDDLE(p+1); + + return 0; +} + +/* +** This routine loads any type 1 or 3 MII info into the mii device +** struct and executes any type 5 code to reset PHY devices for this +** controller. +** The info for the MII devices will be valid since the index used +** will follow the discovery process from MII address 1-31 then 0. +*/ +static void +srom_init(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + u_char count; + + p+=2; + if (lp->chipset == DC21140) { + lp->cache.gepc = (*p++ | GEP_CTRL); + outl(lp->cache.gepc, DE4X5_GEP); + } + + /* Block count */ + count = *p++; + + /* Jump the infoblocks to find types */ + for (;count; --count) { + if (*p < 128) { + p += COMPACT_LEN; + } else if (*(p+1) == 5) { + type5_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 3) { + type3_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else if (*(p+1) == 1) { + type1_infoblock(dev, 1, p); + p += ((*p & BLOCK_LEN) + 1); + } else { + p += ((*p & BLOCK_LEN) + 1); + } + } + + return; +} + +static void +srom_exec(struct device *dev, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char count = (p ? *p++ : 0); + + while (count--) { + if (lp->chipset == DC21140) { + outl(*p++, DE4X5_GEP); + } + udelay(2000); /* 2ms per action */ + } + + return; +} + +/* +** Basically this function is a NOP since it will never be called, +** unless I implement the DC21041 SROM functions. There's no need +** since the existing code will be satisfactory for all boards. +*/ +static int +dc21041_infoleaf(struct device *dev) +{ + return DE4X5_AUTOSENSE_MS; +} + +static int +dc21140_infoleaf(struct device *dev) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char count = 0; + u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset; + int next_tick = DE4X5_AUTOSENSE_MS; + + /* Read the connection type */ + p+=2; + + /* GEP control */ + lp->cache.gepc = (*p++ | GEP_CTRL); + + /* Block count */ + count = *p++; + + /* Recursively figure out the info blocks */ + if (*p < 128) { + next_tick = dc_infoblock[COMPACT](dev, count, p); + } else { + next_tick = dc_infoblock[*(p+1)](dev, count, p); + } + + if (lp->tcount == count) { + lp->media = NC; + if (lp->media != lp->c_media) { + de4x5_dbg_media(dev); + lp->c_media = lp->media; + } + lp->media = INIT; + lp->tcount = 0; + lp->tx_enable = FALSE; + } + + return next_tick & ~TIMER_CB; +} + +static int +dc21142_infoleaf(struct device *dev) +{ +printk("dc21142_infoleaf()\n"); + return DE4X5_AUTOSENSE_MS; +} + +static int +dc21143_infoleaf(struct device *dev) +{ +printk("dc21143_infoleaf()\n"); + return DE4X5_AUTOSENSE_MS; +} + +/* +** The compact infoblock is only designed for DC21140[A] chips, so +** we'll reuse the dc21140m_autoconf function. Non MII media only. +*/ +static int +compact_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char flags, csr6; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+COMPACT_LEN) < 128) { + return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN); + } else { + return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + outl(lp->cache.gepc, DE4X5_GEP); + lp->infoblock_media = (*p++) & COMPACT_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* +** This block describes non MII media for the DC21140[A] only. + */ +static int +type0_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if ((lp->media == INIT) && (lp->timeout < 0)) { + outl(lp->cache.gepc, DE4X5_GEP); + p+=2; + lp->infoblock_media = (*p++) & BLOCK0_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->useMII = FALSE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +/* These functions are under construction! */ + +static int +type1_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + p += 2; + if (lp->state == INITIALISED) { + lp->ibn = 1; + lp->active = *p++; + lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); + return 0; + } else if ((lp->media == INIT) && (lp->timeout < 0)) { + lp->ibn = 1; + lp->active = *p; + lp->infoblock_csr6 = OMR_PS | OMR_HBD; + lp->useMII = TRUE; + lp->infoblock_media = ANS; + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +static int +type2_infoblock(struct device *dev, u_char count, u_char *p) +{ + return DE4X5_AUTOSENSE_MS; +} + +static int +type3_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_char flags, csr6, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + if (lp->state == INITIALISED) { + lp->ibn = 3; p += 2; + lp->active = *p++; + lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1); + lp->phy[lp->active].mc = TWIDDLE(p); p += 2; + lp->phy[lp->active].ana = TWIDDLE(p); p += 2; + lp->phy[lp->active].fdx = TWIDDLE(p); p += 2; + lp->phy[lp->active].ttm = TWIDDLE(p); + return 0; + } else if (lp->media == INIT) { + p+=2; + lp->infoblock_media = (*p++) & BLOCK0_MC; + lp->cache.gep = *p++; + csr6 = *p++; + flags = *p++; + + lp->asBitValid = (flags & 0x80) ? 0 : -1; + lp->defMedium = (flags & 0x40) ? -1 : 0; + lp->asBit = 1 << ((csr6 >> 1) & 0x07); + lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit; + lp->infoblock_csr6 = (csr6 & 0x71) << 18; + lp->useMII = TRUE; + + de4x5_switch_mac_port(dev); + } + + return dc21140m_autoconf(dev); +} + +static int +type4_infoblock(struct device *dev, u_char count, u_char *p) +{ + return DE4X5_AUTOSENSE_MS; +} + +/* +** This block type provides information for resetting external devices +** (chips) through the General Purpose Register. +*/ +static int +type5_infoblock(struct device *dev, u_char count, u_char *p) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + u_long iobase = dev->base_addr; + u_char i, j, len = (*p & BLOCK_LEN)+1; + + /* Recursively figure out the info blocks */ + if (--count > lp->tcount) { + if (*(p+len) < 128) { + return dc_infoblock[COMPACT](dev, count, p+len); + } else { + return dc_infoblock[*(p+len+1)](dev, count, p+len); + } + } + + /* Must be initializing to run this code */ + if ((lp->state == INITIALISED) || (lp->media == INIT)) { + p+=2; + lp->rst = p; + i = *p++; + for (j=0;i;--i) { + if (lp->chipset == DC21140) { + if (!j) { + outl(*p++ | GEP_CTRL, DE4X5_GEP); + j++; + } + outl(*p++, DE4X5_GEP); + } else if (lp->chipset == DC21142) { + } else if (lp->chipset == DC21143) { + } + } + + } + + return DE4X5_AUTOSENSE_MS; +} + +/* +** MII Read/Write +*/ static int mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr) @@ -3494,23 +4373,25 @@ mii_get_oui(u_char phyaddr, u_long ioaddr) return r2; /* (I did it) My way */ } +/* +** The SROM spec forces us to search addresses [1-31 0]. Bummer. +*/ static int mii_get_phy(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int iobase = dev->base_addr; - int i, j, k, limit=sizeof(phy_info)/sizeof(struct phy_table); + int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table); int id; - /* Issue a hard PHY reset - Broadcom is screwed up otherwise */ - outl(GEP_HRST, DE4X5_GEP); - udelay(1000); /* Assert for 1ms */ - outl(0x00, DE4X5_GEP); - udelay(2000); /* Wait for 2ms */ - - /* Search the MII address space for possible PHY devices */ lp->active = 0; - for (lp->mii_cnt=0, i=1; iuseMII = TRUE; + + /* Search the MII address space for possible PHY devices */ + for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) { + lp->phy[lp->active].addr = i; + if (i==0) n++; /* Count cycles */ + while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */ id = mii_get_oui(i, DE4X5_MII); if ((id == 0) || (id == -1)) continue; /* Valid ID? */ for (j=0; jphy[k].addr = i; lp->mii_cnt++; + lp->active++; } else { i = DE4X5_MAX_MII; /* Stop the search */ j = limit; } } } - if (lp->phy[lp->active].id) { /* Reset the PHY devices */ + lp->active = 0; + if (lp->phy[0].id) { /* Reset the PHY devices */ for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/ mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII); while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST); @@ -3590,15 +4473,17 @@ disable_ast(struct device *dev) } static long -de4x5_switch_to_mii(struct device *dev) +de4x5_switch_mac_port(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int iobase = dev->base_addr; - long omr; - + s32 omr; + /* Assert the OMR_PS bit in CSR6 */ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR)); - omr |= (OMR_PS | OMR_HBD); + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | + OMR_FDX)); + omr |= lp->infoblock_csr6; + if (omr & OMR_PS) omr |= OMR_HBD; outl(omr, DE4X5_OMR); /* Soft Reset */ @@ -3606,42 +4491,13 @@ de4x5_switch_to_mii(struct device *dev) /* Restore the GEP */ if (lp->chipset == DC21140) { - outl(GEP_INIT, DE4X5_GEP); - outl(0, DE4X5_GEP); + outl(lp->cache.gepc, DE4X5_GEP); + outl(lp->cache.gep, DE4X5_GEP); } - - /* Restore CSR6 */ - outl(omr, DE4X5_OMR); - - /* Reset CSR8 */ - inl(DE4X5_MFC); - - return omr; -} -static long -de4x5_switch_to_srl(struct device *dev) -{ - struct de4x5_private *lp = (struct de4x5_private *)dev->priv; - int iobase = dev->base_addr; - long omr; - - /* Deassert the OMR_PS bit in CSR6 */ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR)); - outl(omr, DE4X5_OMR); - - /* Soft Reset */ - RESET_DE4X5; - - /* Restore the GEP */ - if (lp->chipset == DC21140) { - outl(GEP_INIT, DE4X5_GEP); - outl(0, DE4X5_GEP); - } - /* Restore CSR6 */ outl(omr, DE4X5_OMR); - + /* Reset CSR8 */ inl(DE4X5_MFC); @@ -3670,13 +4526,61 @@ timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec) return; } +static void +yawn(struct device *dev, int state) +{ + struct de4x5_private *lp = (struct de4x5_private *)dev->priv; + int iobase = dev->base_addr; + + if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return; + + if(lp->bus == EISA) { + switch(state) { + case WAKEUP: + outb(WAKEUP, PCI_CFPM); + de4x5_ms_delay(10); + break; + + case SNOOZE: + outb(SNOOZE, PCI_CFPM); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + outb(SLEEP, PCI_CFPM); + break; + } + } else { + switch(state) { + case WAKEUP: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, WAKEUP); + de4x5_ms_delay(10); + break; + + case SNOOZE: + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SNOOZE); + break; + + case SLEEP: + outl(0, DE4X5_SICR); + pcibios_write_config_byte(lp->bus_num, lp->device << 3, + PCI_CFDA_PSM, SLEEP); + break; + } + } + + return; +} + static void de4x5_dbg_open(struct device *dev) { struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int i; - if (de4x5_debug > 1) { + if (de4x5_debug & DEBUG_OPEN) { printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq); printk("\tphysical address: "); for (i=0;i<6;i++) { @@ -3702,17 +4606,17 @@ de4x5_dbg_open(struct device *dev) printk("Descriptor buffers:\nRX: "); for (i=0;irxRingSize-1;i++){ if (i < 3) { - printk("0x%8.8x ",lp->rx_ring[i].buf); + printk("0x%8.8x ",le32_to_cpu(lp->rx_ring[i].buf)); } } - printk("...0x%8.8x\n",lp->rx_ring[i].buf); + printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf)); printk("TX: "); for (i=0;itxRingSize-1;i++){ if (i < 3) { - printk("0x%8.8x ", lp->tx_ring[i].buf); + printk("0x%8.8x ", le32_to_cpu(lp->tx_ring[i].buf)); } } - printk("...0x%8.8x\n", lp->tx_ring[i].buf); + printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf)); printk("Ring size: \nRX: %d\nTX: %d\n", (short)lp->rxRingSize, (short)lp->txRingSize); @@ -3727,7 +4631,7 @@ de4x5_dbg_mii(struct device *dev, int k) struct de4x5_private *lp = (struct de4x5_private *)dev->priv; int iobase = dev->base_addr; - if (de4x5_debug > 2) { + if (de4x5_debug & DEBUG_MII) { printk("\nMII CR: %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII)); printk("MII SR: %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII)); printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII)); @@ -3754,7 +4658,7 @@ de4x5_dbg_media(struct device *dev) struct de4x5_private *lp = (struct de4x5_private *)dev->priv; if (lp->media != lp->c_media) { - if (de4x5_debug > 0) { + if (de4x5_debug & DEBUG_MEDIA) { if (lp->chipset != DC21140) { printk("%s: media is %s\n", dev->name, (lp->media == NC ? "unconnected!" : @@ -3786,10 +4690,12 @@ de4x5_dbg_srom(struct de4x5_srom *p) { int i; - if (de4x5_debug > 1) { - printk("Sub-system Vendor ID: %04x\n", (u_short)*(p->sub_vendor_id)); - printk("Sub-system ID: %04x\n", (u_short)*(p->sub_system_id)); + if (de4x5_debug & DEBUG_SROM) { + printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id)); + printk("Sub-system ID: %04x\n", *((u_short *)p->sub_system_id)); printk("ID Block CRC: %02x\n", (u_char)(p->id_block_crc)); + printk("SROM version: %02x\n", (u_char)(p->version)); + printk("# controllers: %02x\n", (u_char)(p->num_controllers)); printk("Hardware Address: "); for (i=0;i 2) { + if (de4x5_debug & DEBUG_RX) { printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n", (u_char)skb->data[0], (u_char)skb->data[1], @@ -3827,7 +4733,7 @@ de4x5_dbg_rx(struct sk_buff *skb, int len) (u_char)skb->data[12], (u_char)skb->data[13], len); - if (de4x5_debug > 3) { + if (de4x5_debug & DEBUG_RX) { for (j=0; len>0;j+=16, len-=16) { printk(" %03x: ",j); for (i=0; i<16 && i> 1]; - u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2]; + u8 addr[144]; + u16 sval[72]; + u32 lval[36]; } tmp; switch(ioc->cmd) { @@ -3868,7 +4774,7 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) for (i=0; idev_addr[i]; } - memcpy_tofs(ioc->data, tmp.addr, ioc->len); + copy_to_user(ioc->data, tmp.addr, ioc->len); break; case DE4X5_SET_HWADDR: /* Set the hardware address */ @@ -3879,7 +4785,7 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) if (!suser()) break; status = 0; - memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN); + copy_from_user(tmp.addr, ioc->data, ETH_ALEN); for (i=0; idev_addr[i] = tmp.addr[i]; } @@ -3918,39 +4824,12 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) break; case DE4X5_GET_MCA: /* Get the multicast address table */ - ioc->len = (HASH_TABLE_LEN >> 3); - status = verify_area(VERIFY_WRITE, ioc->data, ioc->len); - if (!status) { - memcpy_tofs(ioc->data, lp->setup_frame, ioc->len); - } - break; case DE4X5_SET_MCA: /* Set a multicast address */ - if (suser()) { - /******* FIX ME! ********/ - if (ioc->len != HASH_TABLE_LEN) { /* MCA changes */ - if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) { - memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len); - set_multicast_list(dev); - } - } else { - set_multicast_list(dev); - } - } else { - status = -EPERM; - } - break; case DE4X5_CLR_MCA: /* Clear all multicast addresses */ - if (suser()) { - /******* FIX ME! ********/ - set_multicast_list(dev); - } else { - status = -EPERM; - } - break; - case DE4X5_MCA_EN: /* Enable pass all multicast addressing */ + case DE4X5_MCA_EN: /* Enable pass all multicast addresses*/ if (suser()) { omr = inl(DE4X5_OMR); omr |= OMR_PM; @@ -3967,7 +4846,7 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) break; cli(); - memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); + copy_to_user(ioc->data, &lp->pktStats, ioc->len); sti(); break; @@ -3984,14 +4863,14 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) case DE4X5_GET_OMR: /* Get the OMR Register contents */ tmp.addr[0] = inl(DE4X5_OMR); if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) { - memcpy_tofs(ioc->data, tmp.addr, 1); + copy_to_user(ioc->data, tmp.addr, 1); } break; case DE4X5_SET_OMR: /* Set the OMR Register contents */ if (suser()) { if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) { - memcpy_fromfs(tmp.addr, ioc->data, 1); + copy_from_user(tmp.addr, ioc->data, 1); outl(tmp.addr[0], DE4X5_OMR); } } else { @@ -4011,11 +4890,12 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) tmp.lval[7] = inl(DE4X5_SIGR); j+=4; ioc->len = j; if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, tmp.addr, ioc->len); + copy_to_user(ioc->data, tmp.addr, ioc->len); } break; -#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */ +/* +#define DE4X5_DUMP 0x0f / * Dump the DE4X5 Status * / case DE4X5_DUMP: j = 0; @@ -4042,22 +4922,22 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) for (i=0;irxRingSize-1;i++){ if (i < 3) { - tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; } } - tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4; for (i=0;itxRingSize-1;i++){ if (i < 3) { - tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; } } - tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4; + tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4; for (i=0;irxRingSize;i++){ - tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4; + tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4; } for (i=0;itxRingSize;i++){ - tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4; + tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4; } tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4; @@ -4078,7 +4958,7 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; } tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; - if (lp->phy[lp->active].id) { + if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) { tmp.lval[j>>2] = lp->active; j+=4; tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4; @@ -4102,10 +4982,11 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) ioc->len = j; if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) { - memcpy_tofs(ioc->data, tmp.addr, ioc->len); + copy_to_user(ioc->data, tmp.addr, ioc->len); } break; +*/ default: status = -EOPNOTSUPP; } @@ -4118,85 +4999,60 @@ de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd) ** Note now that module autoprobing is allowed under EISA and PCI. The ** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes ** to "do the right thing". -** -** NB: Current register_netdevice() code does not permit assigning io=0 as -** this driver will autoprobe all instances of acceptable DECchips. The -** cleanup_module() code needs work still....(just to unload modules owned -** by this driver). */ -static char devicename[9] = { 0, }; -static struct device thisDE4X5 = { - devicename, /* device name inserted by /linux/drivers/net/net_init.c */ - 0, 0, 0, 0, - 0, 0, /* I/O address, IRQ */ - 0, 0, 0, NULL, de4x5_probe }; - -static int io=0x0b; /* EDIT THESE LINES FOR YOUR CONFIGURATION */ +#define LP(a) ((struct de4x5_private *)(a)) +static struct device *mdev = NULL; +static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED */ int init_module(void) { - struct device *p = (struct device *)&thisDE4X5; - - thisDE4X5.base_addr = io; /* Now autoprobe the module */ - thisDE4X5.irq = 0; - - for (; p!=NULL; p=p->next) { + struct device *p; + + if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) + return -ENOMEM; + + for (p = mdev; p != NULL; p = LP(p->priv)->next_module) { if (register_netdev(p) != 0) return -EIO; } - io=0; + return 0; } void cleanup_module(void) { - struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv; - struct device *p = (struct device *)&thisDE4X5; - int keep_loaded = 0; - - for (; p!=NULL; p=p->next) { - keep_loaded += (p->flags & IFF_UP); /* Is an interface up? */ - } - - if (keep_loaded) { - printk("de4x5: Cannot unload modules - %d interface%s%s still active.\n", - keep_loaded, (keep_loaded>1 ? "s ": " "), - (keep_loaded>1 ? "are": "is")); - return; - } - - for (p=thisDE4X5.next; p!=NULL; p=p->next) { - if (p->priv) { /* Private area allocated? */ - struct de4x5_private *lp = (struct de4x5_private *)p->priv; - if (lp->cache.buf) { /* MAC buffers allocated? */ - kfree(lp->cache.buf); /* Free the MAC buffers */ - } - release_region(p->base_addr, (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - kfree(lp->cache.priv); /* Free the private area */ - } - unregister_netdev(p); - kfree(p); /* Free the device structure */ + while (mdev != NULL) { + mdev = unlink_modules(mdev); } - if (thisDE4X5.priv) { - if (lp->cache.buf) { /* Are MAC buffers allocated */ - kfree(lp->cache.buf); + return; +} + +static struct device * +unlink_modules(struct device *p) +{ + struct device *next = NULL; + + if (p->priv) { /* Private areas allocated? */ + struct de4x5_private *lp = (struct de4x5_private *)p->priv; + + next = lp->next_module; + if (lp->cache.buf) { /* MAC buffers allocated? */ + kfree(lp->cache.buf); /* Free the MAC buffers */ } - release_region(thisDE4X5.base_addr, - (lp->bus == PCI ? - DE4X5_PCI_TOTAL_SIZE : - DE4X5_EISA_TOTAL_SIZE)); - kfree(lp->cache.priv); - thisDE4X5.priv = NULL; + kfree(lp->cache.priv); /* Free the private area */ + release_region(p->base_addr, (lp->bus == PCI ? + DE4X5_PCI_TOTAL_SIZE : + DE4X5_EISA_TOTAL_SIZE)); } - unregister_netdev(&thisDE4X5); - - return; + unregister_netdev(p); + kfree(p); /* Free the device structure */ + + return next; } + #endif /* MODULE */ diff --git a/drivers/net/de4x5.h b/drivers/net/de4x5.h index 3ad781c60141..525e0122d47b 100644 --- a/drivers/net/de4x5.h +++ b/drivers/net/de4x5.h @@ -62,6 +62,8 @@ #define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */ #define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */ #define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */ +#define PCI_CFDD iobase+0x0041 /* PCI Driver Dependent Area Register */ +#define PCI_CFPM iobase+0x0043 /* PCI Power Management Area Register */ /* ** EISA Configuration Register 0 bit definitions @@ -95,16 +97,20 @@ #define ER3_LSR 0x02 /* Local Software Reset */ /* -** PCI Configuration ID Register (PCI_CFID) +** PCI Configuration ID Register (PCI_CFID). The Device IDs are left +** shifted 8 bits to allow detection of DC21142 and DC21143 variants with +** the configuration revision register step number. */ #define CFID_DID 0xff00 /* Device ID */ #define CFID_VID 0x00ff /* Vendor ID */ -#define DC21040_DID 0x0002 /* Unique Device ID # */ +#define DC21040_DID 0x0200 /* Unique Device ID # */ #define DC21040_VID 0x1011 /* DC21040 Manufacturer */ -#define DC21041_DID 0x0014 /* Unique Device ID # */ +#define DC21041_DID 0x1400 /* Unique Device ID # */ #define DC21041_VID 0x1011 /* DC21041 Manufacturer */ -#define DC21140_DID 0x0009 /* Unique Device ID # */ +#define DC21140_DID 0x0900 /* Unique Device ID # */ #define DC21140_VID 0x1011 /* DC21140 Manufacturer */ +#define DC2114x_DID 0x1900 /* Unique Device ID # */ +#define DC2114x_VID 0x1011 /* DC2114[23] Manufacturer */ /* ** Chipset defines @@ -112,10 +118,16 @@ #define DC21040 DC21040_DID #define DC21041 DC21041_DID #define DC21140 DC21140_DID +#define DC2114x DC2114x_DID +#define DC21142 (DC2114x_DID | 0x0010) +#define DC21143 (DC2114x_DID | 0x0020) #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID)) #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID)) #define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID)) +#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID)) +#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142)) +#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143)) /* ** PCI Configuration Command/Status Register (PCI_CFCS) @@ -127,7 +139,7 @@ #define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */ #define CFCS_DPR 0x01000000 /* Data Parity Report (S) */ #define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */ -#define CFCS_SLE 0x00000100 /* System Error Enable (C) */ +#define CFCS_SEE 0x00000100 /* System Error Enable (C) */ #define CFCS_PER 0x00000040 /* Parity Error Response (C) */ #define CFCS_MO 0x00000004 /* Master Operation (C) */ #define CFCS_MSA 0x00000002 /* Memory Space Access (C) */ @@ -138,8 +150,8 @@ */ #define CFRV_BC 0xff000000 /* Base Class */ #define CFRV_SC 0x00ff0000 /* Subclass */ -#define CFRV_SN 0x000000f0 /* Step Number */ -#define CFRV_RN 0x0000000f /* Revision Number */ +#define CFRV_RN 0x000000f0 /* Revision Number */ +#define CFRV_SN 0x0000000f /* Step Number */ #define BASE_CLASS 0x02000000 /* Indicates Network Controller */ #define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */ #define STEP_NUMBER 0x00000020 /* Increments for future chips */ @@ -157,6 +169,19 @@ #define CBIO_MASK 0xffffff80 /* Base I/O Address Mask */ #define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */ +/* +** PCI Configuration Card Information Structure Register (PCI_CCIS) +*/ +#define CCIS_ROMI 0xf0000000 /* ROM Image */ +#define CCIS_ASO 0x0ffffff8 /* Address Space Offset */ +#define CCIS_ASI 0x00000007 /* Address Space Indicator */ + +/* +** PCI Configuration Subsystem ID Register (PCI_SSID) +*/ +#define SSID_SSID 0xffff0000 /* Subsystem ID */ +#define SSID_SVID 0x0000ffff /* Subsystem Vendor ID */ + /* ** PCI Configuration Expansion ROM Base Address Register (PCI_CBER) */ @@ -164,13 +189,27 @@ #define CBER_ROME 0x00000001 /* ROM Enable */ /* -** PCI Configuration Driver Area Register (PCI_CFDA) +** PCI Configuration Interrupt Register (PCI_CFIT) */ -#define CFDA_PSM 0x80000000 /* Power Saving Mode */ +#define CFIT_MXLT 0xff000000 /* MAX_LAT Value (0.25us periods) */ +#define CFIT_MNGT 0x00ff0000 /* MIN_GNT Value (0.25us periods) */ +#define CFIT_IRQP 0x0000ff00 /* Interrupt Pin */ +#define CFIT_IRQL 0x000000ff /* Interrupt Line */ + +/* +** PCI Configuration Power Management Area Register (PCI_CFPM) +*/ +#define SLEEP 0x80 /* Power Saving Sleep Mode */ +#define SNOOZE 0x40 /* Power Saving Snooze Mode */ +#define WAKEUP 0x00 /* Power Saving Wakeup */ + +#define PCI_CFDA_DSU 0x41 /* 8 bit Configuration Space Address */ +#define PCI_CFDA_PSM 0x43 /* 8 bit Configuration Space Address */ /* ** DC21040 Bus Mode Register (DE4X5_BMR) */ +#define BMR_RML 0x00200000 /* [Memory] Read Multiple */ #define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */ #define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */ #define BMR_DAS 0x00010000 /* Diagnostic Address Space */ @@ -181,6 +220,7 @@ #define BMR_BAR 0x00000002 /* Bus ARbitration */ #define BMR_SWR 0x00000001 /* Software Reset */ + /* Timings here are for 10BASE-T/AUI only*/ #define TAP_NOPOLL 0x00000000 /* No automatic polling */ #define TAP_200US 0x00020000 /* TX automatic polling every 200us */ #define TAP_800US 0x00040000 /* TX automatic polling every 800us */ @@ -232,18 +272,21 @@ #define TRBA 0xfffffffc /* TX Descriptor List Start Address */ /* -** DC21040 Status Register (DE4X5_STS) +** Status Register (DE4X5_STS) */ +#define STS_GPI 0x04000000 /* General Purpose Port Interrupt */ #define STS_BE 0x03800000 /* Bus Error Bits */ #define STS_TS 0x00700000 /* Transmit Process State */ #define STS_RS 0x000e0000 /* Receive Process State */ #define STS_NIS 0x00010000 /* Normal Interrupt Summary */ #define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */ #define STS_ER 0x00004000 /* Early Receive */ +#define STS_FBE 0x00002000 /* Fatal Bus Error */ #define STS_SE 0x00002000 /* System Error */ #define STS_LNF 0x00001000 /* Link Fail */ #define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */ #define STS_TM 0x00000800 /* Timer Expired (DC21041) */ +#define STS_ETI 0x00000400 /* Early Transmit Interupt */ #define STS_AT 0x00000400 /* AUI/TP Pin */ #define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */ #define STS_RPS 0x00000100 /* Receive Process Stopped */ @@ -251,6 +294,7 @@ #define STS_RI 0x00000040 /* Receive Interrupt */ #define STS_UNF 0x00000020 /* Transmit Underflow */ #define STS_LNP 0x00000010 /* Link Pass */ +#define STS_ANC 0x00000010 /* Autonegotiation Complete */ #define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */ #define STS_TU 0x00000004 /* Transmit Buffer Unavailable */ #define STS_TPS 0x00000002 /* Transmit Process Stopped */ @@ -283,8 +327,10 @@ #define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */ /* -** DC21040 Operation Mode Register (DE4X5_OMR) +** Operation Mode Register (DE4X5_OMR) */ +#define OMR_SC 0x80000000 /* Special Capture Effect Enable */ +#define OMR_RA 0x40000000 /* Receive All */ #define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ #define OMR_SCR 0x01000000 /* Scrambler Mode */ #define OMR_PCS 0x00800000 /* PCS Function */ @@ -298,7 +344,7 @@ #define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ #define OMR_FC 0x00001000 /* Force Collision Mode */ #define OMR_OM 0x00000c00 /* Operating Mode */ -#define OMR_FD 0x00000200 /* Full Duplex Mode */ +#define OMR_FDX 0x00000200 /* Full Duplex Mode */ #define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */ #define OMR_PM 0x00000080 /* Pass All Multicast */ #define OMR_PR 0x00000040 /* Promiscuous Mode */ @@ -309,27 +355,31 @@ #define OMR_SR 0x00000002 /* Start/Stop Receive */ #define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */ -#define TR_72 0x00000000 /* Threshold set to 72 bytes */ -#define TR_96 0x00004000 /* Threshold set to 96 bytes */ -#define TR_128 0x00008000 /* Threshold set to 128 bytes */ -#define TR_160 0x0000c000 /* Threshold set to 160 bytes */ +#define TR_72 0x00000000 /* Threshold set to 72 (128) bytes */ +#define TR_96 0x00004000 /* Threshold set to 96 (256) bytes */ +#define TR_128 0x00008000 /* Threshold set to 128 (512) bytes */ +#define TR_160 0x0000c000 /* Threshold set to 160 (1024) bytes */ /* ** DC21040 Interrupt Mask Register (DE4X5_IMR) */ +#define IMR_GPM 0x04000000 /* General Purpose Port Mask */ #define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */ #define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */ #define IMR_ERM 0x00004000 /* Early Receive Mask */ +#define IMR_FBM 0x00002000 /* Fatal Bus Error Mask */ #define IMR_SEM 0x00002000 /* System Error Mask */ #define IMR_LFM 0x00001000 /* Link Fail Mask */ #define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */ #define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */ +#define IMR_ETM 0x00000400 /* Early Transmit Interrupt Mask */ #define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */ #define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */ #define IMR_RSM 0x00000100 /* Receive Stopped Mask */ #define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */ #define IMR_RIM 0x00000040 /* Receive Interrupt Mask */ #define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */ +#define IMR_ANM 0x00000010 /* Autonegotiation Complete Mask */ #define IMR_LPM 0x00000010 /* Link Pass */ #define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */ #define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */ @@ -337,13 +387,7 @@ #define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */ /* -** DC21040 Missed Frames Counter (DE4X5_MFC) -*/ -#define MFC_OVFL 0x00010000 /* Missed Frames Counter Overflow Bit */ -#define MFC_CNTR 0x0000ffff /* Missed Frames Counter Bits */ - -/* -** DC21140 Missed Frames and FIFO Overflow Counters (DE4X5_MFC) +** Missed Frames and FIFO Overflow Counters (DE4X5_MFC) */ #define MFC_FOCO 0x10000000 /* FIFO Overflow Counter Overflow Bit */ #define MFC_FOC 0x0ffe0000 /* FIFO Overflow Counter Bits */ @@ -444,7 +488,7 @@ ** MII Management Auto Negotiation Advertisement Register */ #define MII_ANA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANA_T4AM 0x0400 /* T4 Technology Ability Mask */ +#define MII_ANA_T4AM 0x0200 /* T4 Technology Ability Mask */ #define MII_ANA_TXAM 0x0180 /* TX Technology Ability Mask */ #define MII_ANA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ #define MII_ANA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ @@ -459,7 +503,7 @@ #define MII_ANLPA_ACK 0x4000 /* Remote Acknowledge */ #define MII_ANLPA_RF 0x2000 /* Remote Fault */ #define MII_ANLPA_TAF 0x03e0 /* Technology Ability Field */ -#define MII_ANLPA_T4AM 0x0400 /* T4 Technology Ability Mask */ +#define MII_ANLPA_T4AM 0x0200 /* T4 Technology Ability Mask */ #define MII_ANLPA_TXAM 0x0180 /* TX Technology Ability Mask */ #define MII_ANLPA_FDAM 0x0140 /* Full Duplex Technology Ability Mask */ #define MII_ANLPA_HDAM 0x02a0 /* Half Duplex Technology Ability Mask */ @@ -477,6 +521,76 @@ #define MEDIA_TP 0x0002 /* TP Media present */ #define MEDIA_BNC 0x0001 /* BNC Media present */ +/* +** SROM Definitions (Digital Semiconductor Format) +*/ +#define SROM_SSVID 0x0000 /* Sub-system Vendor ID offset */ +#define SROM_SSID 0x0002 /* Sub-system ID offset */ +#define SROM_CISPL 0x0004 /* CardBus CIS Pointer low offset */ +#define SROM_CISPH 0x0006 /* CardBus CIS Pointer high offset */ +#define SROM_IDCRC 0x0010 /* ID Block CRC offset*/ +#define SROM_RSVD2 0x0011 /* ID Reserved 2 offset */ +#define SROM_SFV 0x0012 /* SROM Format Version offset */ +#define SROM_CCNT 0x0013 /* Controller Count offset */ +#define SROM_HWADD 0x0014 /* Hardware Address offset */ +#define SROM_MRSVD 0x007c /* Manufacturer Reserved offset*/ +#define SROM_CRC 0x007e /* SROM CRC offset */ + +/* +** SROM Media Connection Definitions +*/ +#define SROM_10BT 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BTN 0x0100 /* 10BASE-T with Nway */ +#define SROM_10BTF 0x0204 /* 10BASE-T full duplex */ +#define SROM_10BTNLP 0x0400 /* 10BASE-T without Link Pass test */ +#define SROM_10B2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10B5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BTH 0x0003 /* 100BASE-T half duplex */ +#define SROM_100BTF 0x0205 /* 100BASE-T full duplex */ +#define SROM_100BT4 0x0006 /* 100BASE-T4 */ +#define SROM_100BFX 0x0007 /* 100BASE-FX half duplex (Fiber) */ +#define SROM_M10BT 0x0009 /* MII 10BASE-T half duplex */ +#define SROM_M10BTF 0x020a /* MII 10BASE-T full duplex */ +#define SROM_M100BT 0x000d /* MII 100BASE-T half duplex */ +#define SROM_M100BTF 0x020e /* MII 100BASE-T full duplex */ +#define SROM_M100BT4 0x000f /* MII 100BASE-T4 */ +#define SROM_M100BF 0x0010 /* MII 100BASE-FX half duplex */ +#define SROM_M100BFF 0x0211 /* MII 100BASE-FX full duplex */ +#define SROM_PDA 0x0800 /* Powerup & Dynamic Autosense */ +#define SROM_PAO 0x8800 /* Powerup Autosense Only */ +#define SROM_NSMI 0xffff /* No Selected Media Information */ + +/* +** SROM Media Definitions +*/ +#define SROM_10BASET 0x0000 /* 10BASE-T half duplex */ +#define SROM_10BASE2 0x0001 /* 10BASE-2 (BNC) */ +#define SROM_10BASE5 0x0002 /* 10BASE-5 (AUI) */ +#define SROM_100BASET 0x0003 /* 100BASE-T half duplex */ +#define SROM_10BASETF 0x0004 /* 10BASE-T full duplex */ +#define SROM_100BASETF 0x0005 /* 100BASE-T full duplex */ +#define SROM_100BASET4 0x0006 /* 100BASE-T4 */ +#define SROM_100BASEF 0x0007 /* 100BASE-FX half duplex */ +#define SROM_100BASEFF 0x0008 /* 100BASE-FX full duplex */ + +#define BLOCK_LEN 0x7f /* Extended blocks length mask */ +#define EXT_FIELD 0x40 /* Extended blocks extension field bit */ +#define MEDIA_CODE 0x3f /* Extended blocks media code mask */ + +/* +** SROM Compact Format Block Masks +*/ +#define COMPACT_FI 0x80 /* Format Indicator */ +#define COMPACT_LEN 0x04 /* Length */ +#define COMPACT_MC 0x3f /* Media Code */ + +/* +** SROM Extended Format Block Type 0 Masks +*/ +#define BLOCK0_FI 0x80 /* Format Indicator */ +#define BLOCK0_MCS 0x80 /* Media Code byte Sign */ +#define BLOCK0_MC 0x3f /* Media Code */ + /* ** DC21040 Full Duplex Register (DE4X5_FDR) */ @@ -501,17 +615,21 @@ #define GEP_FLED 0x00000002 /* Force Activity LED on (output) */ #define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */ #define GEP_INIT 0x0000011f /* Setup inputs (0) and outputs (1) */ - +#define GEP_CTRL 0x00000100 /* GEP control bit */ /* -** DC21040 SIA Status Register (DE4X5_SISR) +** SIA Status Register (DE4X5_SISR) */ #define SISR_LPC 0xffff0000 /* Link Partner's Code Word */ #define SISR_LPN 0x00008000 /* Link Partner Negotiable */ #define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */ -#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected */ +#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected (DC21041) */ +#define SISR_TRF 0x00000800 /* Transmit Remote Fault */ +#define SISR_NSND 0x00000400 /* Non Stable NLPs Detected (DC21142) */ #define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/ +#define SISR_TRA 0x00000200 /* 10BASE-T Receive Port Activity */ #define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */ +#define SISR_ARA 0x00000100 /* AUI Receive Port Activity */ #define SISR_SRA 0x00000100 /* Selected Port Receive Activity */ #define SISR_DAO 0x00000080 /* PLL All One */ #define SISR_DAZ 0x00000040 /* PLL All Zero */ @@ -521,7 +639,7 @@ #define SISR_LKF 0x00000004 /* Link Fail Status */ #define SISR_NCR 0x00000002 /* Network Connection Error */ #define SISR_PAUI 0x00000001 /* AUI_TP Indication */ -#define SIA_RESET 0x00000000 /* SIA Reset */ +#define SISR_MRA 0x00000001 /* MII Receive Port Activity */ #define ANS_NDIS 0x00000000 /* Nway disable */ #define ANS_TDIS 0x00001000 /* Transmit Disable */ @@ -532,7 +650,7 @@ #define ANS_LCHK 0x00006000 /* Link Check */ /* -** DC21040 SIA Connectivity Register (DE4X5_SICR) +** SIA Connectivity Register (DE4X5_SICR) */ #define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */ #define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */ @@ -551,14 +669,14 @@ #define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */ #define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */ #define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */ -#define SICR_AUI 0x00000008 /* 10Base-T or AUI */ +#define SICR_AUI 0x00000008 /* 10Base-T (0) or AUI (1) */ #define SICR_CAC 0x00000004 /* CSR Auto Configuration */ #define SICR_PS 0x00000002 /* Pin AUI/TP Selection */ #define SICR_SRL 0x00000001 /* SIA Reset */ -#define SICR_RESET 0xffff0000 /* Reset value for SICR */ +#define SIA_RESET 0x00000000 /* SIA Reset Value */ /* -** DC21040 SIA Transmit and Receive Register (DE4X5_STRR) +** SIA Transmit and Receive Register (DE4X5_STRR) */ #define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */ #define STRR_SPP 0x00004000 /* Set Polarity Plus */ @@ -578,8 +696,20 @@ #define STRR_RESET 0xffffffff /* Reset value for STRR */ /* -** DC21040 SIA General Register (DE4X5_SIGR) -*/ +** SIA General Register (DE4X5_SIGR) +*/ +#define SIGR_RMI 0x40000000 /* Receive Match Interrupt */ +#define SIGR_GI1 0x20000000 /* General Port Interrupt 1 */ +#define SIGR_GI0 0x10000000 /* General Port Interrupt 0 */ +#define SIGR_CWE 0x08000000 /* Control Write Enable */ +#define SIGR_RME 0x04000000 /* Receive Match Enable */ +#define SIGR_GEI1 0x02000000 /* GEP Interrupt Enable on Port 1 */ +#define SIGR_GEI0 0x01000000 /* GEP Interrupt Enable on Port 0 */ +#define SIGR_LGS3 0x00800000 /* LED/GEP3 Select */ +#define SIGR_LGS2 0x00400000 /* LED/GEP2 Select */ +#define SIGR_LGS1 0x00200000 /* LED/GEP1 Select */ +#define SIGR_LGS0 0x00100000 /* LED/GEP0 Select */ +#define SIGR_MD 0x000f0000 /* General Purpose Mode and Data */ #define SIGR_LV2 0x00008000 /* General Purpose LED2 value */ #define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */ #define SIGR_FRL 0x00002000 /* Force Receiver Low */ @@ -602,7 +732,8 @@ ** Receive Descriptor Bit Summary */ #define R_OWN 0x80000000 /* Own Bit */ -#define RD_FL 0x7fff0000 /* Frame Length */ +#define RD_FF 0x40000000 /* Filtering Fail */ +#define RD_FL 0x3fff0000 /* Frame Length */ #define RD_ES 0x00008000 /* Error Summary */ #define RD_LE 0x00004000 /* Length Error */ #define RD_DT 0x00003000 /* Data Type */ @@ -614,6 +745,7 @@ #define RD_CS 0x00000040 /* Collision Seen */ #define RD_FT 0x00000020 /* Frame Type */ #define RD_RJ 0x00000010 /* Receive Watchdog */ +#define RD_RE 0x00000008 /* Report on MII Error */ #define RD_DB 0x00000004 /* Dribbling Bit */ #define RD_CE 0x00000002 /* CRC Error */ #define RD_OF 0x00000001 /* Overflow */ @@ -649,40 +781,53 @@ #define TD_TCH 0x01000000 /* Second Address Chained */ #define TD_DPD 0x00800000 /* Disabled Padding */ #define TD_FT0 0x00400000 /* Filtering Type */ -#define TD_RBS2 0x003ff800 /* Buffer 2 Size */ -#define TD_RBS1 0x000007ff /* Buffer 1 Size */ +#define TD_TBS2 0x003ff800 /* Buffer 2 Size */ +#define TD_TBS1 0x000007ff /* Buffer 1 Size */ #define PERFECT_F 0x00000000 #define HASH_F TD_FT0 #define INVERSE_F TD_FT1 -#define HASH_O_F TD_FT1| TD_F0 +#define HASH_O_F (TD_FT1 | TD_F0) /* ** Media / mode state machine definitions */ -#define NC 0x0000 /* No Connection */ -#define TP 0x0001 /* 10Base-T */ -#define TP_NW 0x0002 /* 10Base-T with Nway */ -#define BNC 0x0004 /* Thinwire */ -#define AUI 0x0008 /* Thickwire */ +#define NC 0x0000 /* No Connection */ +#define TP 0x0001 /* 10Base-T */ +#define TP_NW 0x0002 /* 10Base-T with Nway */ +#define BNC 0x0004 /* Thinwire */ +#define AUI 0x0008 /* Thickwire */ #define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */ -#define ANS 0x0020 /* Intermediate AutoNegotiation State */ -#define ANS_1 0x0021 /* Intermediate AutoNegotiation State */ - -#define _10Mb 0x0040 /* 10Mb/s Ethernet */ -#define _100Mb 0x0080 /* 100Mb/s Ethernet */ -#define SPD_DET 0x0100 /* Parallel speed detection */ -#define INIT 0x0200 /* Initial state */ -#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ -#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ -#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ -#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ -#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ -#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ -#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ - -#define AUTO 0x4000 /* Auto sense the media or speed */ -#define TIMER_CB 0x80000000 /* Timer callback detection */ +#define ANS 0x0020 /* Intermediate AutoNegotiation State */ +#define _10Mb 0x0040 /* 10Mb/s Ethernet */ +#define _100Mb 0x0080 /* 100Mb/s Ethernet */ +#define SPD_DET 0x0100 /* Parallel speed detection */ +#define INIT 0x0200 /* Initial state */ +#define EXT_SIA 0x0400 /* External SIA for motherboard chip */ +#define ANS_SUSPECT 0x0802 /* Suspect the ANS (TP) port is down */ +#define TP_SUSPECT 0x0803 /* Suspect the TP port is down */ +#define BNC_AUI_SUSPECT 0x0804 /* Suspect the BNC or AUI port is down */ +#define EXT_SIA_SUSPECT 0x0805 /* Suspect the EXT SIA port is down */ +#define BNC_SUSPECT 0x0806 /* Suspect the BNC port is down */ +#define AUI_SUSPECT 0x0807 /* Suspect the AUI port is down */ +#define MII 0x1000 /* MII on the 21143 */ + +#define AUTO 0x4000 /* Auto sense the media or speed */ +#define TIMER_CB 0x80000000 /* Timer callback detection */ + +/* +** DE4X5 DEBUG Options +*/ +#define DEBUG_NONE 0x0000 /* No DEBUG messages */ +#define DEBUG_VERSION 0x0001 /* Print version message */ +#define DEBUG_MEDIA 0x0002 /* Print media messages */ +#define DEBUG_TX 0x0004 /* Print TX (queue_pkt) messages */ +#define DEBUG_RX 0x0008 /* Print RX (de4x5_rx) messages */ +#define DEBUG_SROM 0x0010 /* Print SROM messages */ +#define DEBUG_MII 0x0020 /* Print MII messages */ +#define DEBUG_OPEN 0x0040 /* Print de4x5_open() messages */ +#define DEBUG_CLOSE 0x0080 /* Print de4x5_close() messages */ +#define DEBUG_PCICFG 0x0100 /* ** Miscellaneous @@ -699,7 +844,6 @@ #define POLL_DEMAND 1 #define LOST_MEDIA_THRESHOLD 3 -#define LOST_MEDIA (lp->lostMedia > LOST_MEDIA_THRESHOLD) #define MASK_INTERRUPTS 1 #define UNMASK_INTERRUPTS 0 @@ -728,11 +872,16 @@ */ #define NO 0 #define FALSE 0 -#define CLOSED 0 #define YES ~0 #define TRUE ~0 -#define OPEN ~0 + +/* +** Adapter state +*/ +#define INITIALISED 0 /* After h/w initialised and mem alloc'd */ +#define CLOSED 1 /* Ready for opening */ +#define OPEN 2 /* Running */ /* ** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since @@ -748,55 +897,67 @@ ** Speed Selection stuff */ #define SET_10Mb {\ - if (lp->phy[lp->active].id) {\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ - mii_wr(MII_CR_10|(de4x5_full_duplex?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ + mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ }\ - omr |= ((de4x5_full_duplex ? OMR_FD : 0) | OMR_TTM);\ + omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\ outl(omr, DE4X5_OMR);\ - outl(0, DE4X5_GEP);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | lp->infoblock_csr6, DE4X5_OMR);\ } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ - omr |= (de4x5_full_duplex ? OMR_FD : 0);\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ outl(omr | OMR_TTM, DE4X5_OMR);\ - outl((de4x5_full_duplex ? 0 : GEP_FDXD), DE4X5_GEP);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\ }\ } #define SET_100Mb {\ - if (lp->phy[lp->active].id) {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ int fdx=0;\ if (lp->phy[lp->active].id == NATIONAL_TX) {\ mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\ 0x18, lp->phy[lp->active].addr, DE4X5_MII);\ }\ - omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\ + omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\ sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\ - if (!(sr & MII_ANA_T4AM) && de4x5_full_duplex) fdx=1;\ + if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\ if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\ mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ }\ - if (fdx) omr |= OMR_FD;\ + if (fdx) omr |= OMR_FDX;\ outl(omr, DE4X5_OMR);\ + if (!lp->useSROM) lp->cache.gep = 0;\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ + outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\ } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ - omr |= (de4x5_full_duplex ? OMR_FD : 0);\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + omr |= (lp->fdx ? OMR_FDX : 0);\ outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ - outl((de4x5_full_duplex ? 0 : GEP_FDXD) | GEP_MODE, DE4X5_GEP);\ + lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\ }\ } /* FIX ME so I don't jam 10Mb networks */ #define SET_100Mb_PDET {\ - if (lp->phy[lp->active].id) {\ + if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\ mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\ - omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ + outl(omr, DE4X5_OMR);\ + } else if (lp->useSROM && !lp->useMII) {\ + omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ outl(omr, DE4X5_OMR);\ } else {\ - omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\ + omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\ outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\ - outl(GEP_FDXD | GEP_MODE, DE4X5_GEP);\ + lp->cache.gep = (GEP_FDXD | GEP_MODE);\ }\ } @@ -817,7 +978,7 @@ struct de4x5_ioctl { ** Recognised commands for the driver */ #define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */ -#define DE4X5_SET_HWADDR 0x02 /* Get the hardware address */ +#define DE4X5_SET_HWADDR 0x02 /* Set the hardware address */ #define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */ #define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */ #define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */ diff --git a/drivers/net/eql.c b/drivers/net/eql.c index 5caef729b12d..1f36de5b4acc 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -17,7 +17,7 @@ */ static const char *version = - "Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n"; + "Equalizer1996: $Revision: 1.9 $ $Date: 1996/10/12 11:14:37 $ Simon Janes (simon@ncm.com)\n"; /* * Sources: @@ -31,6 +31,9 @@ static const char *version = /* * $Log: eql.c,v $ + * Revision 1.9 1996/10/12 11:14:37 davem + * Quick merge to 2.0.20 + * * Revision 1.2 1996/04/11 17:51:52 guru * Added one-line eql_remove_slave patch. * @@ -262,6 +265,7 @@ int eql_init(struct device *dev) dev->hard_header = eql_header; dev->rebuild_header = eql_rebuild_header; + dev->hard_header_len = MAX_HEADER; /* enough space for any slave */ /* * Now we undo some of the things that eth_setup does @@ -371,10 +375,19 @@ static int eql_slave_xmit(struct sk_buff *skb, struct device *dev) equalizer_t *eql = (equalizer_t *) dev->priv; struct device *slave_dev = 0; slave_t *slave; + struct sk_buff *skb2; if (skb == NULL) return 0; +#if 0 + /* Make a copy we can free so we don't mess up the skb->dev pointer */ + skb2 = skb_clone(skb, GFP_ATOMIC); + + if (skb2 == NULL) + return 1; +#endif + eql_schedule_slaves (eql->queue); slave_dev = eql_best_slave_dev (eql->queue); @@ -388,20 +401,48 @@ static int eql_slave_xmit(struct sk_buff *skb, struct device *dev) dev->name, eql_number_slaves (eql->queue), skb->len, slave_dev->name); #endif - dev_queue_xmit (skb, slave_dev, 1); - eql->stats->tx_packets++; - slave->bytes_queued += skb->len; - } - else - { - /* - * The alternative for this is the return 1 and have - * dev_queue_xmit just queue it up on the eql's queue. + + /* Rip off the fake header */ + skb_pull(skb,MAX_HEADER); + + /* The original code had no hard header constructed. + * If a frame is fragmented on EQL and then passed to PPP, + * or ISDN, the result will be a panic. + * The solution is to call the hard_header constructor + * for the device we point to just before we send the packet. + * If this fails we drop the packet. + * We don't know any special parameters for the hard_header + * constructor at this point, so we pass in made up values + * that will cause the constructor to fail on every device + * except those that we are allowed to use EQL on: + * PPP, SLIP and ISDN (in some cases!). + * The worst thing that happens is if some fool + * configures EQL to enslave something that needs + * these parameters it throws out packets. + * This is not an issue for the things EQL is intended for + * anyway, and probably would have crashed the kernel + * or sent garbage down the wire previously. */ - eql->stats->tx_dropped++; - dev_kfree_skb(skb, FREE_WRITE); - } + if (slave_dev->hard_header == NULL + || slave_dev->hard_header(skb,slave_dev, + ETH_P_IP,NULL,NULL,skb->len) >= 0) { + dev_queue_xmit (skb, slave_dev, 1); + eql->stats->tx_packets++; + slave->bytes_queued += skb->len; + /* dev_kfree_skb(skb, FREE_WRITE); */ + return 0; + } + } + + /* + * The alternative for this is the return 1 and have + * dev_queue_xmit just queue it up on the eql's queue. + */ + + eql->stats->tx_dropped++; + /* dev_kfree_skb(skb2, FREE_WRITE); */ + dev_kfree_skb(skb, FREE_WRITE); return 0; } @@ -417,7 +458,9 @@ static int eql_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { - return 0; + /* Fake header to keep space during buggy IP fragmentation. */ + skb_push(skb,MAX_HEADER); + return MAX_HEADER; } diff --git a/drivers/net/ibmtr.c b/drivers/net/ibmtr.c index 10902b96b170..5fb27845d658 100644 --- a/drivers/net/ibmtr.c +++ b/drivers/net/ibmtr.c @@ -971,14 +971,15 @@ void tok_interrupt (int irq, void *dev_id, struct pt_regs *regs) } /* ARB response */ if (status & SSB_RESP_INT) { /* SSB response */ - + unsigned char retcode; switch (readb(ti->ssb)) { /* SSB command check */ case XMIT_DIR_FRAME: case XMIT_UI_FRAME: - if (readb(ti->ssb+2)) /* checks ret_code */ + retcode = readb(ti->ssb+2); + if (retcode && (retcode != 0x22)) /* checks ret_code */ DPRINTK("xmit ret_code: %02X xmit error code: %02X\n", - (int)readb(ti->ssb+2), (int)readb(ti->ssb+6)); + (int)retcode, (int)readb(ti->ssb+6)); else ti->tr_stats.tx_packets++; break; diff --git a/drivers/net/new_tunnel.c b/drivers/net/new_tunnel.c index 002ef3925e45..cd42358f1d7e 100644 --- a/drivers/net/new_tunnel.c +++ b/drivers/net/new_tunnel.c @@ -161,7 +161,7 @@ static int tunnel_xmit(struct sk_buff *skb, struct device *dev) * routing tables */ iph = (struct iphdr *) skb->data; - if ((rt = ip_rt_route(iph->daddr, 0)) == NULL) + if ((rt = ip_rt_route(iph->daddr, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL) { /* No route to host */ /* Where did the packet come from? */ @@ -194,7 +194,7 @@ static int tunnel_xmit(struct sk_buff *skb, struct device *dev) } ip_rt_put(rt); - if ((rt = ip_rt_route(target, 0)) == NULL) + if ((rt = ip_rt_route(target, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL) { /* No route to host */ /* Where did the packet come from? */ diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c index 7f2b1583eaa5..632afa094ca8 100644 --- a/drivers/net/ppp.c +++ b/drivers/net/ppp.c @@ -6,7 +6,7 @@ * Dynamic PPP devices by Jim Freeman . * ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid * - * ==FILEVERSION 960528== + * ==FILEVERSION 970703== * * NOTE TO MAINTAINERS: * If you modify this file at all, please set the number above to the @@ -406,7 +406,14 @@ ppp_init_dev (struct device *dev) skb_queue_head_init (&dev->buffs[indx]); /* New-style flags */ +#ifdef IFF_SOFTHEADERS + /* Needed to make SOCK_PACKET work correctly in + * memory fussy kernels. + */ + dev->flags = IFF_POINTOPOINT|IFF_SOFTHEADERS; +#else dev->flags = IFF_POINTOPOINT; +#endif dev->family = AF_INET; dev->pa_addr = 0; dev->pa_brdaddr = 0; @@ -697,7 +704,7 @@ ppp_release (struct ppp *ppp) if (dev && dev->flags & IFF_UP) { dev_close (dev); /* close the device properly */ - dev->flags = 0; /* prevent recursion */ + dev->flags &= ~IFF_UP; /* prevent recursion */ } ppp_free_buf (ppp->rbuf); @@ -3079,7 +3086,7 @@ ppp_dev_xmit (sk_buff *skb, struct device *dev) * Fetch the pointer to the data */ len = skb->len; - data = skb_data(skb); + data = skb_data(skb) + PPP_HARD_HDR_LEN; /* * Bug trap for null data. Release the skb and bail out. */ @@ -3152,7 +3159,12 @@ static int ppp_dev_header (sk_buff *skb, struct device *dev, __u16 type, void *daddr, void *saddr, unsigned int len) { - return (0); + /* On the PPP device the hard header must be ignored + * by the SOCK_PACKET layer. (Backward compatability). + */ + skb->mac.raw = skb->data; + skb_push(skb,PPP_HARD_HDR_LEN); + return PPP_HARD_HDR_LEN; } static int diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index ace29d3109d4..32325377aa2f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -47,9 +47,15 @@ struct pci_dev_info dev_info[] = { DEVICE( NCR, NCR_53C820, "53c820"), DEVICE( NCR, NCR_53C825, "53c825"), DEVICE( NCR, NCR_53C815, "53c815"), + DEVICE( NCR, NCR_53C860, "53c860"), + DEVICE( NCR, NCR_53C896, "53c896"), + DEVICE( NCR, NCR_53C895, "53c895"), + DEVICE( NCR, NCR_53C885, "53c885"), + DEVICE( NCR, NCR_53C875, "53c875"), DEVICE( ATI, ATI_68800, "68800AX"), DEVICE( ATI, ATI_215CT222, "215CT222"), DEVICE( ATI, ATI_210888CX, "210888CX"), + DEVICE( ATI, ATI_215GT, "Mach64 GT (Rage II)"), DEVICE( ATI, ATI_210888GX, "210888GX"), DEVICE( VLSI, VLSI_82C592, "82C592-FC1"), DEVICE( VLSI, VLSI_82C593, "82C593-FC1"), @@ -61,6 +67,7 @@ struct pci_dev_info dev_info[] = { DEVICE( TSENG, TSENG_W32P_b, "ET4000W32P rev B"), DEVICE( TSENG, TSENG_W32P_c, "ET4000W32P rev C"), DEVICE( TSENG, TSENG_W32P_d, "ET4000W32P rev D"), + DEVICE( TSENG, TSENG_ET6000, "ET6000"), DEVICE( WEITEK, WEITEK_P9000, "P9000"), DEVICE( WEITEK, WEITEK_P9100, "P9100"), BRIDGE( DEC, DEC_BRD, "DC21050", 0x00), @@ -69,16 +76,21 @@ struct pci_dev_info dev_info[] = { DEVICE( DEC, DEC_TULIP_FAST, "DC21140"), DEVICE( DEC, DEC_FDDI, "DEFPA"), DEVICE( DEC, DEC_TULIP_PLUS, "DC21041"), + DEVICE( DEC, DEC_21142, "DC21142"), DEVICE( DEC, DEC_21052, "DC21052"), DEVICE( DEC, DEC_21152, "DC21152"), + DEVICE( CIRRUS, CIRRUS_7548, "GD 7548"), DEVICE( CIRRUS, CIRRUS_5430, "GD 5430"), DEVICE( CIRRUS, CIRRUS_5434_4, "GD 5434"), DEVICE( CIRRUS, CIRRUS_5434_8, "GD 5434"), DEVICE( CIRRUS, CIRRUS_5436, "GD 5436"), + DEVICE( CIRRUS, CIRRUS_5446, "GD 5446"), DEVICE( CIRRUS, CIRRUS_6729, "CL 6729"), DEVICE( CIRRUS, CIRRUS_7542, "CL 7542"), DEVICE( CIRRUS, CIRRUS_7543, "CL 7543"), + DEVICE( CIRRUS, CIRRUS_7541, "CL 7541"), DEVICE( IBM, IBM_82G2675, "82G2675"), + DEVICE( IBM, IBM_82351, "82351"), DEVICE( WD, WD_7197, "WD 7197"), DEVICE( AMD, AMD_LANCE, "79C970"), DEVICE( AMD, AMD_SCSI, "53C974"), @@ -93,6 +105,7 @@ struct pci_dev_info dev_info[] = { DEVICE( CT, CT_65545, "65545"), DEVICE( CT, CT_65548, "65548"), DEVICE( CT, CT_65550, "65550"), + DEVICE( CT, CT_65554, "65554"), DEVICE( MIRO, MIRO_36050, "ZR36050"), DEVICE( FD, FD_36C70, "TMC-18C30"), DEVICE( SI, SI_6201, "6201"), @@ -104,6 +117,8 @@ struct pci_dev_info dev_info[] = { DEVICE( SI, SI_601, "85C601"), DEVICE( SI, SI_5511, "85C5511"), DEVICE( SI, SI_5513, "85C5513"), + DEVICE( SI, SI_5571, "5571"), + DEVICE( SI, SI_7001, "7001"), DEVICE( HP, HP_J2585A, "J2585A"), DEVICE( HP, HP_J2585B, "J2585B (Lassen)"), DEVICE( PCTECH, PCTECH_RZ1000, "RZ1000 (buggy)"), @@ -121,6 +136,9 @@ struct pci_dev_info dev_info[] = { DEVICE( BUSLOGIC, BUSLOGIC_FLASHPOINT, "FlashPoint"), DEVICE( OAK, OAK_OTI107, "OTI107"), DEVICE( WINBOND2, WINBOND2_89C940,"NE2000-PCI"), + DEVICE( MOTOROLA, MOTOROLA_MPC105,"MPC105 Eagle"), + DEVICE( MOTOROLA, MOTOROLA_MPC106,"MPC106 Grackle"), + DEVICE( MOTOROLA, MOTOROLA_RAVEN, "Raven"), DEVICE( PROMISE, PROMISE_5300, "DC5030"), DEVICE( N9, N9_I128, "Imagine 128"), DEVICE( N9, N9_I128_2, "Imagine 128v2"), @@ -143,14 +161,19 @@ struct pci_dev_info dev_info[] = { DEVICE( CMD, CMD_646, "646"), DEVICE( VISION, VISION_QD8500, "QD-8500"), DEVICE( VISION, VISION_QD8580, "QD-8580"), + DEVICE( BROOKTREE, BROOKTREE_848, "Bt848"), DEVICE( SIERRA, SIERRA_STB, "STB Horizon 64"), DEVICE( ACC, ACC_2056, "2056"), DEVICE( WINBOND, WINBOND_83769, "W83769F"), DEVICE( WINBOND, WINBOND_82C105, "SL82C105"), + DEVICE( WINBOND, WINBOND_83C553, "W83C553"), DEVICE( 3COM, 3COM_3C590, "3C590 10bT"), DEVICE( 3COM, 3COM_3C595TX, "3C595 100bTX"), DEVICE( 3COM, 3COM_3C595T4, "3C595 100bT4"), DEVICE( 3COM, 3COM_3C595MII, "3C595 100b-MII"), + DEVICE( 3COM, 3COM_3C900TPO, "3C900 10bTPO"), + DEVICE( 3COM, 3COM_3C900COMBO,"3C900 10b Combo"), + DEVICE( 3COM, 3COM_3C905TX, "3C905 100bTX"), DEVICE( AL, AL_M1445, "M1445"), DEVICE( AL, AL_M1449, "M1449"), DEVICE( AL, AL_M1451, "M1451"), @@ -159,18 +182,26 @@ struct pci_dev_info dev_info[] = { DEVICE( AL, AL_M1511, "M1511"), DEVICE( AL, AL_M1513, "M1513"), DEVICE( AL, AL_M4803, "M4803"), + DEVICE( NEOMAGIC, NEOMAGIC_MAGICGRAPH_NM2070, "Magicgraph NM2070"), DEVICE( ASP, ASP_ABP940, "ABP940"), + DEVICE( ASP, ASP_ABP940U, "ABP940U"), DEVICE( CERN, CERN_SPSB_PMC, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( CERN, CERN_SPSB_PCI, "STAR/RD24 SCI-PCI (PMC)"), DEVICE( IMS, IMS_8849, "8849"), DEVICE( TEKRAM2, TEKRAM2_690c, "DC690c"), + DEVICE( TUNDRA, TUNDRA_CA91C042,"CA91C042 Universe"), DEVICE( AMCC, AMCC_MYRINET, "Myrinet PCI (M2-PCI-32)"), + DEVICE( AMCC, AMCC_S5933, "S5933"), DEVICE( INTERG, INTERG_1680, "IGA-1680"), + DEVICE( INTERG, INTERG_1682, "IGA-1682"), DEVICE( REALTEK, REALTEK_8029, "8029"), DEVICE( INIT, INIT_320P, "320 P"), DEVICE( VIA, VIA_82C505, "VT 82C505"), DEVICE( VIA, VIA_82C561, "VT 82C561"), + DEVICE( VIA, VIA_82C586_1, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C576, "VT 82C576 3V"), + DEVICE( VIA, VIA_82C585, "VT 82C585VP Apollo VP-1"), + DEVICE( VIA, VIA_82C586_0, "VT 82C586 Apollo VP-1"), DEVICE( VIA, VIA_82C416, "VT 82C416MV"), DEVICE( VORTEX, VORTEX_GDT60x0, "GDT 60x0"), DEVICE( VORTEX, VORTEX_GDT6000B,"GDT 6000b"), @@ -191,16 +222,20 @@ struct pci_dev_info dev_info[] = { DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"), DEVICE( FORE, FORE_PCA200PC, "PCA-200PC"), DEVICE( FORE, FORE_PCA200E, "PCA-200E"), + DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"), DEVICE( PLX, PLX_9060, "PCI9060 i960 bridge"), DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"), DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"), DEVICE( VMIC, VMIC_VME, "VMIVME-7587"), DEVICE( DIGI, DIGI_RIGHTSWITCH, "RightSwitch SE-6"), DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"), + DEVICE( TOSHIBA, TOSHIBA_601, "Laptop"), DEVICE( ZEITNET, ZEITNET_1221, "1221"), DEVICE( ZEITNET, ZEITNET_1225, "1225"), + DEVICE( OMEGA, OMEGA_PCMCIA, "PCMCIA"), DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"), DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"), + DEVICE( ZORAN, ZORAN_36120, "ZR36120"), DEVICE( COMPEX, COMPEX_ENET100VG4, "Readylink ENET100-VG4"), DEVICE( COMPEX, COMPEX_RL2000, "ReadyLink 2000"), DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"), @@ -211,10 +246,20 @@ struct pci_dev_info dev_info[] = { DEVICE( CYCLADES, CYCLOM_Y_Hi, "Cyclom-Y above 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Lo, "Cyclom-Z below 1Mbyte"), DEVICE( CYCLADES, CYCLOM_Z_Hi, "Cyclom-Z above 1Mbyte"), + DEVICE( 3DFX, 3DFX_VOODOO, "Voodoo"), + DEVICE( SIGMA_DESIGNS, SD_REALMAGIC64GX, "REALmagic64/GX (SD 6425)"), + DEVICE( OPTIBASE, OPTIBASE_FORGE, "MPEG Forge"), + DEVICE( OPTIBASE, OPTIBASE_FUSION,"MPEG Fusion"), + DEVICE( OPTIBASE, OPTIBASE_VPLEX, "VideoPlex"), + DEVICE( OPTIBASE, OPTIBASE_VPLEXCC,"VideoPlex CC"), + DEVICE( OPTIBASE, OPTIBASE_VQUEST,"VideoQuest"), DEVICE( SYMPHONY, SYMPHONY_101, "82C101"), DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"), DEVICE( 3DLABS, 3DLABS_300SX, "GLINT 300SX"), + DEVICE( 3DLABS, 3DLABS_DELTA, "GLINT Delta"), + DEVICE( 3DLABS, 3DLABS_PERMEDIA,"PERMEDIA"), DEVICE( AVANCE, AVANCE_2302, "ALG-2302"), + DEVICE( S3, S3_PLATO_PXS, "PLATO/PX (system)"), DEVICE( S3, S3_ViRGE, "ViRGE"), DEVICE( S3, S3_TRIO, "Trio32/Trio64"), DEVICE( S3, S3_AURORA64VP, "Aurora64V+"), @@ -227,11 +272,16 @@ struct pci_dev_info dev_info[] = { DEVICE( S3, S3_964_1, "Vision 964-P"), DEVICE( S3, S3_964_2, "Vision 964-P"), DEVICE( S3, S3_968, "Vision 968"), + DEVICE( S3, S3_TRIO64V2, "Trio64V2/DX or /GX"), + DEVICE( S3, S3_PLATO_PXG, "PLATO/PX (graphics)"), + DEVICE( S3, S3_ViRGE_DXGX, "ViRGE/DX or /GX"), DEVICE( INTEL, INTEL_82375, "82375EB"), BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00), DEVICE( INTEL, INTEL_82378, "82378IB"), DEVICE( INTEL, INTEL_82430, "82430ZX Aries"), BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00), + DEVICE( INTEL, INTEL_82092AA_0,"82092AA PCMCIA bridge"), + DEVICE( INTEL, INTEL_82092AA_1,"82092AA EIDE"), DEVICE( INTEL, INTEL_7116, "SAA7116"), DEVICE( INTEL, INTEL_82596, "82596"), DEVICE( INTEL, INTEL_82865, "82865"), @@ -239,14 +289,22 @@ struct pci_dev_info dev_info[] = { DEVICE( INTEL, INTEL_82437, "82437"), DEVICE( INTEL, INTEL_82371_0, "82371 Triton PIIX"), DEVICE( INTEL, INTEL_82371_1, "82371 Triton PIIX"), + DEVICE( INTEL, INTEL_82371MX, "82371MX Mobile PCI I/O IDE Xcelerator (MPIIX)"), + DEVICE( INTEL, INTEL_82430MX, "82430MX Mobile PCIset"), DEVICE( INTEL, INTEL_82441, "82441FX Natoma"), DEVICE( INTEL, INTEL_82439, "82439HX Triton II"), DEVICE( INTEL, INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"), DEVICE( INTEL, INTEL_82437VX, "82437VX Triton II"), - DEVICE( INTEL, INTEL_82371AB, "82371AB 430TX PIIX4"), + DEVICE( INTEL, INTEL_82439TX, "82439TX"), + DEVICE( INTEL, INTEL_82371AB_0,"82371AB PIIX4"), + DEVICE( INTEL, INTEL_82371AB, "82371AB PIIX4"), + DEVICE( INTEL, INTEL_82371AB_2,"82371AB PIIX4"), + DEVICE( INTEL, INTEL_82371AB_3,"82371AB PIIX4 Power Management"), DEVICE( INTEL, INTEL_P6, "Orion P6"), + DEVICE( INTEL, INTEL_82450GX, "82450GX Orion"), + DEVICE( KTI, KTI_ET32P2, "ET32P2"), DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"), DEVICE( ADAPTEC, ADAPTEC_7855, "AIC-7855"), DEVICE( ADAPTEC, ADAPTEC_7860, "AIC-7860"), @@ -262,8 +320,9 @@ struct pci_dev_info dev_info[] = { DEVICE( ADAPTEC, ADAPTEC_7883, "AIC-7883U"), DEVICE( ADAPTEC, ADAPTEC_7884, "AIC-7884U"), DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"), - DEVICE( HER, HER_STING, "Stingray"), - DEVICE( HER, HER_STINGARK, "Stingray ARK 2000PV") + DEVICE( ARK, ARK_STING, "Stingray"), + DEVICE( ARK, ARK_STINGARK, "Stingray ARK 2000PV"), + DEVICE( ARK, ARK_2000MT, "2000MT") }; @@ -487,6 +546,7 @@ const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_BUSLOGIC: return "BusLogic"; case PCI_VENDOR_ID_OAK: return "OAK"; case PCI_VENDOR_ID_WINBOND2: return "Winbond"; + case PCI_VENDOR_ID_MOTOROLA: return "Motorola"; case PCI_VENDOR_ID_PROMISE: return "Promise Technology"; case PCI_VENDOR_ID_N9: return "Number Nine"; case PCI_VENDOR_ID_UMC: return "UMC"; @@ -499,15 +559,18 @@ const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_OLICOM: return "Olicom"; case PCI_VENDOR_ID_CMD: return "CMD"; case PCI_VENDOR_ID_VISION: return "Vision"; + case PCI_VENDOR_ID_BROOKTREE: return "Brooktree"; case PCI_VENDOR_ID_SIERRA: return "Sierra"; case PCI_VENDOR_ID_ACC: return "ACC MICROELECTRONICS"; case PCI_VENDOR_ID_WINBOND: return "Winbond"; case PCI_VENDOR_ID_3COM: return "3Com"; case PCI_VENDOR_ID_AL: return "Acer Labs"; + case PCI_VENDOR_ID_NEOMAGIC: return "Neomagic"; case PCI_VENDOR_ID_ASP: return "Advanced System Products"; case PCI_VENDOR_ID_CERN: return "CERN"; case PCI_VENDOR_ID_IMS: return "IMS"; case PCI_VENDOR_ID_TEKRAM2: return "Tekram"; + case PCI_VENDOR_ID_TUNDRA: return "Tundra"; case PCI_VENDOR_ID_AMCC: return "AMCC"; case PCI_VENDOR_ID_INTERG: return "Intergraphics"; case PCI_VENDOR_ID_REALTEK: return "Realtek"; @@ -528,6 +591,7 @@ const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_COMPEX: return "Compex"; case PCI_VENDOR_ID_RP: return "Comtrol"; case PCI_VENDOR_ID_CYCLADES: return "Cyclades"; + case PCI_VENDOR_ID_SIGMA_DESIGNS: return "Sigma Designs"; case PCI_VENDOR_ID_SYMPHONY: return "Symphony"; case PCI_VENDOR_ID_TEKRAM: return "Tekram"; case PCI_VENDOR_ID_3DLABS: return "3Dlabs"; @@ -536,7 +600,9 @@ const char *pci_strvendor(unsigned int vendor) case PCI_VENDOR_ID_INTEL: return "Intel"; case PCI_VENDOR_ID_ADAPTEC: return "Adaptec"; case PCI_VENDOR_ID_ATRONICS: return "Atronics"; +#if 0 case PCI_VENDOR_ID_HER: return "Hercules"; +#endif default: return "Unknown vendor"; } } diff --git a/drivers/scsi/in2000.readme b/drivers/scsi/in2000.readme new file mode 100644 index 000000000000..1aefc0a6b6e6 --- /dev/null +++ b/drivers/scsi/in2000.readme @@ -0,0 +1,155 @@ + +UPDATE NEWS: version 1.28 - 07 May 96 + + Tightened up the "interrupts enabled/disabled" discipline + in 'in2000_queuecommand()' and maybe 1 or 2 other places. + I _think_ it may have been a little too lax, causing an + occasional crash during full moon. A fully functional + /proc interface is now in place - if you want to play + with it, start by doing 'cat /proc/scsi/in2000/0'. You + can also use it to change a few run-time parameters on + the fly, but it's mostly for debugging. The curious + should take a good look at 'in2000_proc_info()' in the + in2000.c file to get an understanding of what it's all + about; I figure that people who are really into it will + want to add features suited to their own needs... + Also, sync is now DISABLED by default. + +UPDATE NEWS: version 1.27 - 10 Apr 96 + + Fixed a well-hidden bug in the adaptive-disconnect code + that would show up every now and then during extreme + heavy loads involving 2 or more simultaneously active + devices. Thanks to Joe Mack for keeping my nose to the + grindstone on this one. + +UPDATE NEWS: version 1.26 - 07 Mar 96 + + 1.25 had a nasty bug that bit people with swap partitions + and tape drives. Also, in my attempt to guess my way + through Intel assembly language, I made an error in the + inline code for IO writes. Made a few other changes and + repairs - this version (fingers crossed) should work well. + +UPDATE NEWS: version 1.25 - 05 Mar 96 + + Kernel 1.3.70 interrupt mods added; old kernels still OK. + Big help from Bill Earnest and David Willmore on speed + testing and optimizing: I think there's a real improvement + in this area. + New! User-friendly command-line interface for LILO and + module loading - the old method is gone, so you'll need + to read the comments for 'setup_strings' near the top + of in2000.c. For people with CDROM's or other devices + that have a tough time with sync negotiation, you can + now selectively disable sync on individual devices - + search for the 'nosync' keyword in the command-line + comments. Some of you disable the BIOS on the card, which + caused the auto-detect function to fail; there is now a + command-line option to force detection of a ROM-less card. + +UPDATE NEWS: version 1.24a - 24 Feb 96 + + There was a bug in the synchronous transfer code. Only + a few people downloaded before I caught it - could have + been worse. + +UPDATE NEWS: version 1.24 - 23 Feb 96 + + Lots of good changes. Advice from Bill Earnest resulted + in much better detection of cards, more efficient usage + of the fifo, and (hopefully) faster data transfers. The + jury is still out on speed - I hope it's improved some. + One nifty new feature is a cool way of doing disconnect/ + reselect. The driver defaults to what I'm calling + 'adaptive disconnect' - meaning that each command is + evaluated individually as to whether or not it should be + run with the option to disconnect/reselect (if the device + chooses), or as a "SCSI-bus-hog". When several devices + are operating simultaneously, disconnects are usually an + advantage. In a single device system, or if only 1 device + is being accessed, transfers usually go faster if disconnects + are not allowed. + + + +The default arguments (you get these when you don't give an 'in2000' +command-line argument, or you give a blank argument) will cause +the driver to do adaptive disconnect, synchronous transfers, and a +minimum of debug messages. If you want to fool with the options, +search for 'setup_strings' near the top of the in2000.c file and +check the 'hostdata->args' section in in2000.h - but be warned! Not +everything is working yet (some things will never work, probably). +I believe that disabling disconnects (DIS_NEVER) will allow you +to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't +spent a lot of time testing this. You might try 'ENABLE_CLUSTERING' +to see what happens: my tests showed little difference either way. +There's also a define called 'DEFAULT_SX_PER'; this sets the data +transfer speed for the asynchronous mode. I've put it at 500 ns +despite the fact that the card could handle settings of 376 or +252, because I'm not really sure if certain devices or maybe bad +cables might have trouble at higher speeds. I couldn't find any +info in my various SCSI references that talk about this in language +I could understand, so decided to compromise with 500. This is still +faster than the old driver was set at (I think). Can someone explain +the significance of the bus transfer speed setting? Do devices on +the bus ever care what it is? Is cable quality a factor here? +Regardless, you can choose your own default through the command- +line with the 'period' keyword. + + +------------------------------------------------ +*********** DIP switch settings ************** +------------------------------------------------ + + sw1-1 sw1-2 BIOS address (hex) + ----------------------------------------- + off off C8000 - CBFF0 + on off D8000 - DBFF0 + off on D0000 - D3FF0 + on on BIOS disabled + + sw1-3 sw1-4 IO port address (hex) + ------------------------------------ + off off 220 - 22F + on off 200 - 20F + off on 110 - 11F + on on 100 - 10F + + sw1-5 sw1-6 sw1-7 Interrupt + ------------------------------ + off off off 15 + off on off 14 + off off on 11 + off on on 10 + on - - disabled + + sw1-8 function depends on BIOS version. In earlier versions this + controlled synchronous data transfer support for MSDOS: + off = disabled + on = enabled + In later ROMs (starting with 01.3 in April 1994) sw1-8 controls + the "greater than 2 disk drive" feature that first appeared in + MSDOS 5.0 (ignored by linux): + off = 2 drives maximum + on = 7 drives maximum + + sw1-9 Floppy controller + -------------------------- + off disabled + on enabled + +------------------------------------------------ + + I should mention that Drew Eckhardt's 'Generic NCR5380' sources + were my main inspiration, with lots of reference to the IN2000 + driver currently distributed in the kernel source. I also owe + much to a driver written by Hamish Macdonald for Linux-m68k(!). + And to Eric Wright for being an ALPHA guinea pig. And to Bill + Earnest for 2 tons of great input and information. And to David + Willmore for extensive 'bonnie' testing. And to Joe Mack for + continual testing and feedback. + + + John Shifflett jshiffle@netcom.com + diff --git a/drivers/sound/sequencer.c b/drivers/sound/sequencer.c index f1d3acbb65c0..546d81f8a86d 100644 --- a/drivers/sound/sequencer.c +++ b/drivers/sound/sequencer.c @@ -2038,7 +2038,7 @@ sequencer_init (void) iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * IEV_SZ)); if (sound_nblocks < 1024) sound_nblocks++;; - if (queue == NULL) + if (iqueue == NULL) { printk ("Sound: Can't allocate memory for sequencer input queue\n"); return; diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 4a15ff68f37e..45fbe67c1f9a 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -880,7 +880,7 @@ static int dump_seek(struct file *file, off_t off) */ static inline int maydump(struct vm_area_struct *vma) { - if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC))) + if (!(vma->vm_flags & (VM_READ|VM_EXEC))) return 0; #if 1 if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN)) @@ -1140,7 +1140,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs) set_fs(fs); len = current->mm->arg_end - current->mm->arg_start; - len = len >= ELF_PRARGSZ ? ELF_PRARGSZ : len; + len = (len >= ELF_PRARGSZ-1) ? ELF_PRARGSZ-1 : len; memcpy_fromfs(&psinfo.pr_psargs, (const char *)current->mm->arg_start, len); for(i = 0; i < len; i++) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 6b1ec6de435d..8e3b0c73fa10 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -476,6 +476,34 @@ int isofs_bmap(struct inode * inode,int block) printk("_isofs_bmap: block<0"); return 0; } + + /* + * If we are beyond the end of this file, don't give out any + * blocks. + */ + if( (block << ISOFS_BUFFER_BITS(inode)) > inode->i_size ) + { + off_t max_legal_read_offset; + + /* + * If we are *way* beyond the end of the file, print a message. + * Access beyond the end of the file up to the next page boundary + * is normal because of the way the page cache works. + * In this case, we just return 0 so that we can properly fill + * the page with useless information without generating any + * I/O errors. + */ + max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1); + if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset ) + { + + printk("_isofs_bmap: block>= EOF(%d, %d)", block, + inode->i_size); + } + return 0; + } + return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block; } diff --git a/fs/isofs/namei.c b/fs/isofs/namei.c index 34e32270d3c0..606e05c1f08c 100644 --- a/fs/isofs/namei.c +++ b/fs/isofs/namei.c @@ -242,9 +242,12 @@ int isofs_lookup(struct inode * dir,const char * name, int len, if (!ino) { char *lcname; - /* If mounted with check=relaxed (and most likely norock), - then first convert this name to lower case. */ - if (dir->i_sb->u.isofs_sb.s_name_check == 'r' + /* First try the original name. If that doesn't work and the fs + * was mounted with check=relaxed, convert the name to lower + * case and try again. + */ + if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back)) + && dir->i_sb->u.isofs_sb.s_name_check == 'r' && (lcname = kmalloc(len, GFP_KERNEL)) != NULL) { int i; char c; @@ -256,8 +259,7 @@ int isofs_lookup(struct inode * dir,const char * name, int len, } bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back); kfree(lcname); - } else - bh = isofs_find_entry(dir,name,len, &ino, &ino_back); + } if (!bh) { iput(dir); diff --git a/include/asm-alpha/socket.h b/include/asm-alpha/socket.h index 47bdbf7ce818..05d68a007823 100644 --- a/include/asm-alpha/socket.h +++ b/include/asm-alpha/socket.h @@ -33,4 +33,6 @@ #define SO_PRIORITY 12 #define SO_BSDCOMPAT 14 +#define SO_BINDTODEVICE 25 + #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index f7cbe44d6985..f48954ca8259 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -375,6 +375,8 @@ extern inline void pte_free_kernel(pte_t * pte) free_page((unsigned long) pte); } +extern const char bad_pmd_string[]; + extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address) { address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1); @@ -391,7 +393,7 @@ extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address) free_page((unsigned long) page); } if (pmd_bad(*pmd)) { - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + printk(bad_pmd_string, pmd_val(*pmd)); pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE; return NULL; } @@ -444,7 +446,7 @@ freenew: } fix: - printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd)); + printk(bad_pmd_string, pmd_val(*pmd)); oom: pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE; return NULL; diff --git a/include/asm-i386/socket.h b/include/asm-i386/socket.h index 529a3ffefa6f..73015115afa4 100644 --- a/include/asm-i386/socket.h +++ b/include/asm-i386/socket.h @@ -22,4 +22,6 @@ #define SO_BSDCOMPAT 14 /* To add :#define SO_REUSEPORT 15 */ +#define SO_BINDTODEVICE 25 + #endif /* _ASM_SOCKET_H */ diff --git a/include/linux/if.h b/include/linux/if.h index 10799419b0af..7dee13a0a612 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -39,6 +39,12 @@ #define IFF_SLAVE 0x800 /* slave of a load balancer */ #define IFF_MULTICAST 0x1000 /* Supports multicast */ +#define IFF_SOFTHEADERS 0x2000 /* Device cannot construct headers + * until broadcast time. Therefore + * SOCK_PACKET must call header + * construction. Private flag. + * Never visible outside of kernel. + */ /* * The ifaddr structure contains information about one address diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index d9b59b045b61..ebcfa55dad30 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -33,6 +33,7 @@ enum { DIGI_BH, SERIAL_BH, RISCOM8_BH, + SPECIALIX_BH, BAYCOM_BH, NET_BH, IMMEDIATE_BH, diff --git a/include/linux/major.h b/include/linux/major.h index c7f6382d621e..c50c92ac5d7a 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -69,6 +69,9 @@ #define APBLOCK_MAJOR 60 /* AP1000 Block device */ #define DDV_MAJOR 61 /* AP1000 DDV block device */ +#define SPECIALIX_NORMAL_MAJOR 75 +#define SPECIALIX_CALLOUT_MAJOR 76 + /* * Tests for SCSI devices. */ diff --git a/include/linux/net_alias.h b/include/linux/net_alias.h index 67a9f9beaa2c..5d1bb49e214f 100644 --- a/include/linux/net_alias.h +++ b/include/linux/net_alias.h @@ -2,7 +2,7 @@ * NET_ALIAS network device aliasing definitions. * * - * Version: @(#)net_alias.h 0.43 12/20/95 + * Version: @(#)net_alias.h 0.50 4/20/97 * * Author: Juan Jose Ciarlante, * @@ -12,6 +12,10 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * + * Fixes: + * Juan Jose Ciarlante : Added tx/rx stats for aliases. + * Juan Jose Ciarlante : hash_tab size now in net_alias.c + * Juan Jose Ciarlante : added sysctl interface */ #ifndef _NET_ALIAS_H @@ -21,12 +25,6 @@ #include #include -/* - * max. alias slot number allowed - */ - -#define NET_ALIAS_MAX_SLOT 256 - struct net_alias; struct net_alias_info; struct net_alias_type; @@ -47,6 +45,8 @@ struct net_alias struct device *main_dev; /* pointer to main device */ struct net_alias_type *nat; /* alias type object bound */ struct net_alias *next; /* next alias (hashed linked list) */ + unsigned long rx_lookups; /* 'fake' rx pkts */ + unsigned long tx_lookups; /* 'fake' tx pkts */ }; @@ -58,8 +58,11 @@ struct net_alias struct net_alias_info { int n_aliases; /* num aliases */ + int truesize; /* actual malloc size for struct + hashtab */ struct device *taildev; /* my last (alias) device */ - struct net_alias *hash_tab[16]; /* hashed alias table */ + int max_aliases; /* max aliases allowed for main device */ + unsigned hash_tab_size; /* hash_tab size in elements */ + struct net_alias *hash_tab[0]; /* hashed alias table */ }; /* @@ -110,23 +113,42 @@ net_alias_has(struct device *dev) return (dev->alias_info != NULL); } - +/* + * Initialise net_alias module + */ extern void net_alias_init(void); +/* + * dev_get() with added aliasing magic + */ extern struct device * net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, struct sockaddr *sa, void *data); extern int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa); +/* + * PROC_FS entries + */ extern int net_alias_getinfo(char *buf, char **, off_t , int , int ); extern int net_alias_types_getinfo(char *buf, char **, off_t , int , int ); +/* + * net_alias_type (address family) registration + */ extern int register_net_alias_type(struct net_alias_type *nat, int type); extern int unregister_net_alias_type(struct net_alias_type *nat); +/* + * get alias device _with_ specified address + */ extern struct device * net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa, int flags_on, int flags_off); extern struct device * net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32, int flags_on, int flags_off); -extern struct device * net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst); -extern struct device * net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst); +/* + * get 'closest' device to specified address (returns main_dev if + * nothing better) + * if succesfull, also increment rx stats. + */ +extern struct device * net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst); +extern struct device * net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst); /* @@ -169,4 +191,30 @@ net_alias_nextdev_set(struct device *dev, struct device *nextdev) return nextdev; } +/* + * lookup counters (used for alias devices stats) + */ +static __inline__ void net_alias_inc_rx(struct net_alias *alias) +{ + if (alias != NULL) alias->rx_lookups++; +} +static __inline__ void net_alias_inc_tx(struct net_alias *alias) +{ + if (alias != NULL) alias->tx_lookups++; +} + +/* + * To be called when passing down a pkt, to _switch_ from alias device + * to actual device, also incr. alias tx counter. + */ +static __inline__ struct device *net_alias_dev_tx(struct device *dev) +{ + struct net_alias *alias = dev->my_alias; + if (alias) { + net_alias_inc_tx(alias); + return alias->main_dev; + } + return dev; +} + #endif /* _NET_ALIAS_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 8045b2efffa1..4295a33482d9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -31,14 +31,14 @@ * PCI-CPU bridge or PCI-ISA bridge. * - If you can't find the actual information in your hardware * booklet, try to read the references of the chip on the board. - * - Send all that to linux-pcisupport@cao-vlsi.ibp.fr, + * - Send all that to linux-pcisupport@cck.uni-kl.de * and I'll add your device to the list as soon as possible * * BEFORE you send a mail, please check the latest linux releases * to be sure it has not been recently added. * * Thanks - * Frederic Potter. + * Jens Maurer */ @@ -228,11 +228,17 @@ #define PCI_DEVICE_ID_NCR_53C820 0x0002 #define PCI_DEVICE_ID_NCR_53C825 0x0003 #define PCI_DEVICE_ID_NCR_53C815 0x0004 +#define PCI_DEVICE_ID_NCR_53C860 0x0006 +#define PCI_DEVICE_ID_NCR_53C896 0x000b +#define PCI_DEVICE_ID_NCR_53C895 0x000c +#define PCI_DEVICE_ID_NCR_53C885 0x000d +#define PCI_DEVICE_ID_NCR_53C875 0x000f #define PCI_VENDOR_ID_ATI 0x1002 #define PCI_DEVICE_ID_ATI_68800 0x4158 #define PCI_DEVICE_ID_ATI_215CT222 0x4354 #define PCI_DEVICE_ID_ATI_210888CX 0x4358 +#define PCI_DEVICE_ID_ATI_215GT 0x4754 #define PCI_DEVICE_ID_ATI_210888GX 0x4758 #define PCI_VENDOR_ID_VLSI 0x1004 @@ -252,6 +258,7 @@ #define PCI_DEVICE_ID_TSENG_W32P_b 0x3205 #define PCI_DEVICE_ID_TSENG_W32P_c 0x3206 #define PCI_DEVICE_ID_TSENG_W32P_d 0x3207 +#define PCI_DEVICE_ID_TSENG_ET6000 0x3208 #define PCI_VENDOR_ID_WEITEK 0x100e #define PCI_DEVICE_ID_WEITEK_P9000 0x9001 @@ -264,20 +271,25 @@ #define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009 #define PCI_DEVICE_ID_DEC_FDDI 0x000F #define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 +#define PCI_DEVICE_ID_DEC_21142 0x0019 #define PCI_DEVICE_ID_DEC_21052 0x0021 #define PCI_DEVICE_ID_DEC_21152 0x0024 #define PCI_VENDOR_ID_CIRRUS 0x1013 +#define PCI_DEVICE_ID_CIRRUS_7548 0x0038 #define PCI_DEVICE_ID_CIRRUS_5430 0x00a0 #define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4 #define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8 #define PCI_DEVICE_ID_CIRRUS_5436 0x00ac +#define PCI_DEVICE_ID_CIRRUS_5446 0x00b8 #define PCI_DEVICE_ID_CIRRUS_6729 0x1100 #define PCI_DEVICE_ID_CIRRUS_7542 0x1200 #define PCI_DEVICE_ID_CIRRUS_7543 0x1202 +#define PCI_DEVICE_ID_CIRRUS_7541 0x1204 #define PCI_VENDOR_ID_IBM 0x1014 #define PCI_DEVICE_ID_IBM_82G2675 0x001d +#define PCI_DEVICE_ID_IBM_82351 0x0022 #define PCI_VENDOR_ID_WD 0x101c #define PCI_DEVICE_ID_WD_7197 0x3296 @@ -304,6 +316,7 @@ #define PCI_DEVICE_ID_CT_65545 0x00d8 #define PCI_DEVICE_ID_CT_65548 0x00dc #define PCI_DEVICE_ID_CT_65550 0x00e0 +#define PCI_DEVICE_ID_CT_65554 0x00e4 #define PCI_VENDOR_ID_MIRO 0x1031 #define PCI_DEVICE_ID_MIRO_36050 0x5601 @@ -321,6 +334,8 @@ #define PCI_DEVICE_ID_SI_601 0x0601 #define PCI_DEVICE_ID_SI_5511 0x5511 #define PCI_DEVICE_ID_SI_5513 0x5513 +#define PCI_DEVICE_ID_SI_5571 0x5571 +#define PCI_DEVICE_ID_SI_7001 0x7001 #define PCI_VENDOR_ID_HP 0x103c #define PCI_DEVICE_ID_HP_J2585A 0x1030 @@ -352,10 +367,15 @@ #define PCI_VENDOR_ID_OAK 0x104e #define PCI_DEVICE_ID_OAK_OTI107 0x0107 -/* Winbond have two vendor ID! See 0x10ad as well */ +/* Winbond have two vendor IDs! See 0x10ad as well */ #define PCI_VENDOR_ID_WINBOND2 0x1050 #define PCI_DEVICE_ID_WINBOND2_89C940 0x0940 +#define PCI_VENDOR_ID_MOTOROLA 0x1057 +#define PCI_DEVICE_ID_MOTOROLA_MPC105 0x0001 +#define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 +#define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 + #define PCI_VENDOR_ID_PROMISE 0x105a #define PCI_DEVICE_ID_PROMISE_5300 0x5300 @@ -402,6 +422,9 @@ #define PCI_DEVICE_ID_VISION_QD8500 0x0001 #define PCI_DEVICE_ID_VISION_QD8580 0x0002 +#define PCI_VENDOR_ID_BROOKTREE 0x109e +#define PCI_DEVICE_ID_BROOKTREE_848 0x0350 + #define PCI_VENDOR_ID_SIERRA 0x10a8 #define PCI_DEVICE_ID_SIERRA_STB 0x0000 @@ -411,12 +434,16 @@ #define PCI_VENDOR_ID_WINBOND 0x10ad #define PCI_DEVICE_ID_WINBOND_83769 0x0001 #define PCI_DEVICE_ID_WINBOND_82C105 0x0105 +#define PCI_DEVICE_ID_WINBOND_83C553 0x0565 #define PCI_VENDOR_ID_3COM 0x10b7 #define PCI_DEVICE_ID_3COM_3C590 0x5900 #define PCI_DEVICE_ID_3COM_3C595TX 0x5950 #define PCI_DEVICE_ID_3COM_3C595T4 0x5951 #define PCI_DEVICE_ID_3COM_3C595MII 0x5952 +#define PCI_DEVICE_ID_3COM_3C900TPO 0x9000 +#define PCI_DEVICE_ID_3COM_3C900COMBO 0x9001 +#define PCI_DEVICE_ID_3COM_3C905TX 0x9050 #define PCI_VENDOR_ID_AL 0x10b9 #define PCI_DEVICE_ID_AL_M1445 0x1445 @@ -428,8 +455,12 @@ #define PCI_DEVICE_ID_AL_M1513 0x1513 #define PCI_DEVICE_ID_AL_M4803 0x5215 +#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 +#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_NM2070 0x0001 + #define PCI_VENDOR_ID_ASP 0x10cd #define PCI_DEVICE_ID_ASP_ABP940 0x1200 +#define PCI_DEVICE_ID_ASP_ABP940U 0x1300 #define PCI_VENDOR_ID_CERN 0x10dc #define PCI_DEVICE_ID_CERN_SPSB_PMC 0x0001 @@ -441,11 +472,16 @@ #define PCI_VENDOR_ID_TEKRAM2 0x10e1 #define PCI_DEVICE_ID_TEKRAM2_690c 0x690c +#define PCI_VENDOR_ID_TUNDRA 0x10e3 +#define PCI_DEVICE_ID_TUNDRA_CA91C042 0x0000 + #define PCI_VENDOR_ID_AMCC 0x10e8 #define PCI_DEVICE_ID_AMCC_MYRINET 0x8043 +#define PCI_DEVICE_ID_AMCC_S5933 0x807d #define PCI_VENDOR_ID_INTERG 0x10ea #define PCI_DEVICE_ID_INTERG_1680 0x1680 +#define PCI_DEVICE_ID_INTERG_1682 0x1682 #define PCI_VENDOR_ID_REALTEK 0x10ec #define PCI_DEVICE_ID_REALTEK_8029 0x8029 @@ -456,7 +492,10 @@ #define PCI_VENDOR_ID_VIA 0x1106 #define PCI_DEVICE_ID_VIA_82C505 0x0505 #define PCI_DEVICE_ID_VIA_82C561 0x0561 +#define PCI_DEVICE_ID_VIA_82C586_1 0x0571 #define PCI_DEVICE_ID_VIA_82C576 0x0576 +#define PCI_DEVICE_ID_VIA_82C585 0x0585 +#define PCI_DEVICE_ID_VIA_82C586_0 0x0586 #define PCI_DEVICE_ID_VIA_82C416 0x1571 #define PCI_VENDOR_ID_VORTEX 0x1119 @@ -503,15 +542,22 @@ #define PCI_DEVICE_ID_MUTECH_MV1000 0x0001 #define PCI_VENDOR_ID_TOSHIBA 0x1179 +#define PCI_DEVICE_ID_TOSHIBA_601 0x0601 #define PCI_VENDOR_ID_ZEITNET 0x1193 #define PCI_DEVICE_ID_ZEITNET_1221 0x0001 #define PCI_DEVICE_ID_ZEITNET_1225 0x0002 +#define PCI_VENDOR_ID_OMEGA 0x119b +#define PCI_DEVICE_ID_OMEGA_PCMCIA 0x1221 + #define PCI_VENDOR_ID_SPECIALIX 0x11cb #define PCI_DEVICE_ID_SPECIALIX_XIO 0x4000 #define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000 +#define PCI_VENDOR_ID_ZORAN 0x11de +#define PCI_DEVICE_ID_ZORAN_36120 0x6120 + #define PCI_VENDOR_ID_COMPEX 0x11f6 #define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112 #define PCI_DEVICE_ID_COMPEX_RL2000 0x1401 @@ -528,19 +574,38 @@ #define PCI_DEVICE_ID_CYCLOM_Z_Lo 0x0200 #define PCI_DEVICE_ID_CYCLOM_Z_Hi 0x0201 +#define PCI_VENDOR_ID_SIGMA_DESIGNS 0x1236 +#define PCI_DEVICE_ID_SD_REALMAGIC64GX 0x6401 + +#define PCI_VENDOR_ID_3DFX 0x121a +#define PCI_DEVICE_ID_3DFX_VOODOO 0x0001 + +#define PCI_VENDOR_ID_SIGMADES 0x1236 +#define PCI_DEVICE_ID_SIGMADES_6425 0x6401 + +#define PCI_VENDOR_ID_OPTIBASE 0x1255 +#define PCI_DEVICE_ID_OPTIBASE_FORGE 0x1110 +#define PCI_DEVICE_ID_OPTIBASE_FUSION 0x1210 +#define PCI_DEVICE_ID_OPTIBASE_VPLEX 0x2120 +#define PCI_DEVICE_ID_OPTIBASE_VPLEXCC 0x2120 +#define PCI_DEVICE_ID_OPTIBASE_VQUEST 0x2130 + #define PCI_VENDOR_ID_SYMPHONY 0x1c1c #define PCI_DEVICE_ID_SYMPHONY_101 0x0001 #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 -#define PCI_VENDOR_ID_3DLABS 0x3D3D +#define PCI_VENDOR_ID_3DLABS 0x3d3d #define PCI_DEVICE_ID_3DLABS_300SX 0x0001 +#define PCI_DEVICE_ID_3DLABS_DELTA 0x0003 +#define PCI_DEVICE_ID_3DLABS_PERMEDIA 0x0004 #define PCI_VENDOR_ID_AVANCE 0x4005 #define PCI_DEVICE_ID_AVANCE_2302 0x2302 #define PCI_VENDOR_ID_S3 0x5333 +#define PCI_DEVICE_ID_S3_PLATO_PXS 0x0551 #define PCI_DEVICE_ID_S3_ViRGE 0x5631 #define PCI_DEVICE_ID_S3_TRIO 0x8811 #define PCI_DEVICE_ID_S3_AURORA64VP 0x8812 @@ -553,6 +618,9 @@ #define PCI_DEVICE_ID_S3_964_1 0x88d0 #define PCI_DEVICE_ID_S3_964_2 0x88d1 #define PCI_DEVICE_ID_S3_968 0x88f0 +#define PCI_DEVICE_ID_S3_TRIO64V2 0x8901 +#define PCI_DEVICE_ID_S3_PLATO_PXG 0x8902 +#define PCI_DEVICE_ID_S3_ViRGE_DXGX 0x8a01 #define PCI_VENDOR_ID_INTEL 0x8086 #define PCI_DEVICE_ID_INTEL_82375 0x0482 @@ -560,6 +628,8 @@ #define PCI_DEVICE_ID_INTEL_82378 0x0484 #define PCI_DEVICE_ID_INTEL_82430 0x0486 #define PCI_DEVICE_ID_INTEL_82434 0x04a3 +#define PCI_DEVICE_ID_INTEL_82092AA_0 0x1221 +#define PCI_DEVICE_ID_INTEL_82092AA_1 0x1222 #define PCI_DEVICE_ID_INTEL_7116 0x1223 #define PCI_DEVICE_ID_INTEL_82596 0x1226 #define PCI_DEVICE_ID_INTEL_82865 0x1227 @@ -567,14 +637,24 @@ #define PCI_DEVICE_ID_INTEL_82437 0x122d #define PCI_DEVICE_ID_INTEL_82371_0 0x122e #define PCI_DEVICE_ID_INTEL_82371_1 0x1230 +#define PCI_DEVICE_ID_INTEL_82371MX 0x1234 +#define PCI_DEVICE_ID_INTEL_82430MX 0x1235 #define PCI_DEVICE_ID_INTEL_82441 0x1237 #define PCI_DEVICE_ID_INTEL_82439 0x1250 #define PCI_DEVICE_ID_INTEL_82371SB_0 0x7000 #define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 #define PCI_DEVICE_ID_INTEL_82371SB_2 0x7020 #define PCI_DEVICE_ID_INTEL_82437VX 0x7030 +#define PCI_DEVICE_ID_INTEL_82439TX 0x7100 +#define PCI_DEVICE_ID_INTEL_82371AB_0 0x7110 #define PCI_DEVICE_ID_INTEL_82371AB 0x7111 +#define PCI_DEVICE_ID_INTEL_82371AB_2 0x7112 +#define PCI_DEVICE_ID_INTEL_82371AB_3 0x7113 #define PCI_DEVICE_ID_INTEL_P6 0x84c4 +#define PCI_DEVICE_ID_INTEL_82450GX 0x84c5 + +#define PCI_VENDOR_ID_KTI 0x8e2e +#define PCI_DEVICE_ID_KTI_ET32P2 0x3000 #define PCI_VENDOR_ID_ADAPTEC 0x9004 #define PCI_DEVICE_ID_ADAPTEC_7850 0x5078 @@ -595,9 +675,10 @@ #define PCI_VENDOR_ID_ATRONICS 0x907f #define PCI_DEVICE_ID_ATRONICS_2015 0x2015 -#define PCI_VENDOR_ID_HER 0xedd8 -#define PCI_DEVICE_ID_HER_STING 0xa091 -#define PCI_DEVICE_ID_HER_STINGARK 0xa099 +#define PCI_VENDOR_ID_ARK 0xedd8 +#define PCI_DEVICE_ID_ARK_STING 0xa091 +#define PCI_DEVICE_ID_ARK_STINGARK 0xa099 +#define PCI_DEVICE_ID_ARK_2000MT 0xa0a1 /* * The PCI interface treats multi-function devices as independent diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 2498662facdc..bb56a64f799f 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -105,6 +105,7 @@ enum net_directory_inos { PROC_NET_STRIP_STATUS, PROC_NET_STRIP_TRACE, PROC_NET_IPAUTOFW, + PROC_NET_Z8530, PROC_NET_LAST }; diff --git a/include/linux/route.h b/include/linux/route.h index 5be4853e5a76..8f210b61c188 100644 --- a/include/linux/route.h +++ b/include/linux/route.h @@ -52,6 +52,7 @@ struct rtentry #define RTF_WINDOW 0x0080 /* per route window clamping */ #define RTF_IRTT 0x0100 /* Initial round trip time */ #define RTF_REJECT 0x0200 /* Reject route */ +#define RTF_NOTCACHED 0x0400 /* this route isn't cached */ /* * This structure is passed from the kernel to user space by netlink diff --git a/include/linux/scc.h b/include/linux/scc.h index 0ad094599d44..851ed072c475 100644 --- a/include/linux/scc.h +++ b/include/linux/scc.h @@ -1,8 +1,10 @@ -/* $Id: scc.h,v 1.15 1995/11/16 20:19:26 jreuter Exp jreuter $ */ +/* $Id: scc.h,v 1.29 1997/04/10 15:27:21 jreuter Exp jreuter $ */ #ifndef _SCC_H #define _SCC_H +#include + /* selection of hardware types */ #define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */ @@ -16,19 +18,38 @@ #define SCC_PARANOIA_CHECK /* tell the user if something is going wrong */ -/* ioctl() commands */ - -#define TIOCSCCCFG 0x2200 /* set hardware parameters */ -#define TIOCSCCINI 0x2201 /* init driver */ -#define TIOCCHANINI 0x2202 /* init channel */ - -#define TIOCCHANMEM 0x2210 /* adjust buffer pools */ - -#define TIOCGKISS 0x2282 /* get kiss parameter */ -#define TIOCSKISS 0x2283 /* set kiss parameter */ - -#define TIOCSCCSTAT 0x2284 /* get scc status */ - +/* TTY ioctl() commands */ + +#define TIOCSCCCFG _IOW('Z', 0, sizeof(struct scc_hw_config)) /* set hardware parameters */ +#define TIOCSCCINI _IO('Z', 1) /* init driver */ +#define TIOCSCCCHANINI _IOW('Z', 2, sizeof(struct scc_modem)) /* init channel */ +#define TIOCSCCSMEM _IOW('Z', 3, sizeof(struct scc_mem_config)) /* adjust buffer pools */ +#define TIOCSCCGKISS _IOWR('Z', 4, sizeof(struct scc_kiss_cmd)) /* get kiss parameter */ +#define TIOCSCCSKISS _IOW('Z', 5, sizeof(struct scc_kiss_cmd)) /* set kiss parameter */ +#define TIOCSCCGSTAT _IOR('Z', 6, sizeof(struct scc_stat)) /* get scc status */ + +/* old TTY ioctl() commands */ + +#define TIOCSCCCFG_OLD 0x2200 +#define TIOCSCCINI_OLD 0x2201 +#define TIOCCHANINI_OLD 0x2202 +#define TIOCCHANMEM_OLD 0x2210 +#define TIOCSKISS_OLD 0x2282 +#define TIOCGKISS_OLD 0x2283 +#define TIOCSCCSTAT_OLD 0x2284 + +/* DEV ioctl() commands */ + +enum SCC_IOCTL_CMD { + SIOCSCCRESERVED=SIOCDEVPRIVATE, + SIOCSCCCFG, + SIOCSCCINI, + SIOCSCCCHANINI, + SIOCSCCSMEM, + SIOCSCCGKISS, + SIOCSCCSKISS, + SIOCSCCGSTAT +}; /* magic number */ @@ -42,37 +63,60 @@ /* KISS state machine */ -#define KISS_IDLE 0 -#define KISS_DATA 1 -#define KISS_ESCAPE 2 -#define KISS_RXFRAME 3 +enum SCC_KISS_STATES { + KISS_IDLE, + KISS_DATA, + KISS_ESCAPE, + KISS_RXFRAME +}; /* Device parameter control (from WAMPES) */ -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_TXTAIL 4 -#define PARAM_FULLDUP 5 -#define PARAM_SOFTDCD 6 /* was: PARAM_HW */ -#define PARAM_MUTE 7 /* ??? */ -#define PARAM_DTR 8 -#define PARAM_RTS 9 -#define PARAM_SPEED 10 -#define PARAM_ENDDELAY 11 /* ??? */ -#define PARAM_GROUP 12 -#define PARAM_IDLE 13 -#define PARAM_MIN 14 -#define PARAM_MAXKEY 15 -#define PARAM_WAIT 16 -#define PARAM_MAXDEFER 17 -#define PARAM_TX 18 -#define PARAM_SLIP 19 -#define PARAM_RETURN 255 /* reset kiss mode */ +enum SCC_KISS_PARAMS { + PARAM_TXDELAY=1, + PARAM_PERSIST, + PARAM_SLOTTIME, + PARAM_TXTAIL, + PARAM_FULLDUP, + PARAM_SOFTDCD, /* was: PARAM_HW */ + PARAM_MUTE, /* ??? */ + PARAM_DTR, + PARAM_RTS, + PARAM_SPEED, + PARAM_ENDDELAY, /* ??? */ + PARAM_GROUP, + PARAM_IDLE, + PARAM_MIN, + PARAM_MAXKEY, + PARAM_WAIT, + PARAM_MAXDEFER, + PARAM_TX, + PARAM_HWEVENT=31, + PARAM_RETURN=255 /* reset kiss mode */ +}; + +/* fulldup parameter */ + +enum SCC_KISS_DUPLEX_MODES { + KISS_DUPLEX_HALF, /* normal CSMA operation */ + KISS_DUPLEX_FULL, /* fullduplex, key down trx after transmission */ + KISS_DUPLEX_LINK, /* fullduplex, key down trx after 'idletime' sec */ + KISS_DUPLEX_OPTIMA, /* fullduplex, let the protocol layer control the hw */ +}; + +/* misc. parameters */ #define TIMER_OFF 65535U /* to switch off timers */ #define NO_SUCH_PARAM 65534U /* param not implemented */ +/* HWEVENT parameter */ + +enum SCC_HWEV_PARAMETERS { + HWEV_DCD_ON, + HWEV_DCD_OFF, + HWEV_ALL_SENT +}; + /* channel grouping */ #define RXGROUP 0x100 /* if set, only tx when all channels clear */ @@ -80,20 +124,26 @@ /* Tx/Rx clock sources */ -#define CLK_DPLL 0 /* normal halfduplex operation */ -#define CLK_EXTERNAL 1 /* external clocking (G3RUH/DF9IC modems) */ -#define CLK_DIVIDER 2 /* Rx = DPLL, Tx = divider (fullduplex with */ - /* modems without clock regeneration */ +enum SCC_CLK_SOURCES { + CLK_DPLL, /* normal halfduplex operation */ + CLK_EXTERNAL, /* external clocking (G3RUH/DF9IC modems) */ + CLK_DIVIDER /* Rx = DPLL, Tx = divider (fullduplex with */ + /* modems without clock regeneration */ +}; /* Tx state */ -#define TXS_IDLE 0 /* Transmitter off, no data pending */ -#define TXS_BUSY 1 /* waiting for permission to send / tailtime */ -#define TXS_ACTIVE 2 /* Transmitter on, sending data */ -#define TXS_NEWFRAME 3 /* reset CRC and send (next) frame */ +enum SCC_TX_STATES { + TXS_IDLE, /* Transmitter off, no data pending */ + TXS_BUSY, /* waiting for permission to send / tailtime */ + TXS_ACTIVE, /* Transmitter on, sending data */ + TXS_NEWFRAME, /* reset CRC and send (next) frame */ + TXS_IDLE2, /* Transmitter on, no data pending */ + TXS_WAIT, /* Waiting for Mintime to expire */ + TXS_TIMEOUT /* We had a transmission timeout */ +}; -#define TX_ON 1 /* command for scc_key_trx() */ -#define TX_OFF 0 /* dto */ +enum SCC_TX_KEY {TX_OFF, TX_ON}; /* command for scc_key_trx() */ /* Buffer management */ @@ -115,13 +165,7 @@ typedef unsigned short ioaddr; /* old def */ #define Outb(port, val) outb(val, port) #endif -/* some nasty macros (esp. Expired) */ - -#define TIMER_STOPPED 65535U -#define Running(k) (scc->k != TIMER_STOPPED) -#define Expired(k) (scc->k != TIMER_STOPPED) && (!(scc->k) || (--(scc->k) == 0)) -#define Stop_Timer(k) scc->k = TIMER_STOPPED - +#define TIMER_OFF 65535U /* Basic message buffer structure */ @@ -140,17 +184,18 @@ struct scc_kiss { unsigned char txdelay; /* Transmit Delay 10 ms/cnt */ unsigned char persist; /* Persistence (0-255) as a % */ unsigned char slottime; /* Delay to wait on persistence hit */ - unsigned char tailtime; /* Delay after XMTR OFF */ + unsigned char tailtime; /* Delay after last byte written */ unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */ unsigned char waittime; /* Waittime before any transmit attempt */ unsigned int maxkeyup; /* Maximum time to transmit (seconds) */ - unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout */ + unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */ unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */ unsigned int maxdefer; /* Timer for CSMA channel busy limit */ unsigned char tx_inhibit; /* Transmit is not allowed when set */ - unsigned char group; /* group ID for AX.25 TX interlocking */ - unsigned char not_slip; /* set to zero: use SLIP instead of KISS */ - unsigned char softdcd; /* use DPLL instead of DCD pin for carrier detect */ + unsigned char group; /* Group ID for AX.25 TX interlocking */ + unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */ + + unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */ }; @@ -182,6 +227,8 @@ struct scc_stat { unsigned int rxbuffers; /* allocated rx_buffers */ unsigned int txbuffers; /* allocated tx_buffers */ unsigned int bufsize; /* used buffersize */ + + unsigned char is_netdev;/* If set: act as network instead of character device */ }; @@ -191,13 +238,11 @@ struct scc_modem { char nrz; /* NRZ instead of NRZI */ }; -struct ioctl_command { +struct scc_kiss_cmd { int command; /* one of the KISS-Commands defined above */ unsigned param; /* KISS-Param */ }; -/* currently unused */ - struct scc_hw_config { io_port data_a; /* data port channel A */ io_port ctrl_a; /* control port channel A */ @@ -214,14 +259,15 @@ struct scc_hw_config { char escc; /* use ext. features of a 8580/85180/85280 */ }; +/* (#) only one INTACK latch allowed. */ + + struct scc_mem_config { unsigned int rxbuffers; unsigned int txbuffers; unsigned int bufsize; }; -/* (#) only one INTACK latch allowed. */ - /* SCC channel structure */ @@ -232,6 +278,10 @@ struct scc_channel { struct tty_struct *tty; /* link to tty control structure */ char tty_opened; /* No. of open() calls... */ char throttled; /* driver is throttled */ + + struct device *dev; /* link to device control structure */ + struct enet_statistics dev_stat; + /* device statistics */ char brand; /* manufacturer of the board */ long clock; /* used clock */ @@ -239,6 +289,7 @@ struct scc_channel { io_port ctrl; /* I/O address of CONTROL register */ io_port data; /* I/O address of DATA register */ io_port special; /* I/O address of special function port */ + int irq; /* Number of Interrupt */ char option; char enhanced; /* Enhanced SCC support */ @@ -250,6 +301,7 @@ struct scc_channel { struct scc_stat stat; /* statistical information */ struct scc_modem modem; /* modem information */ + char mempool; /* pool empty or allocated? */ struct mbuf *rx_buffer_pool; /* free buffers for rx/tx frames are */ struct mbuf *tx_buffer_pool; /* linked in these ring chains */ @@ -264,18 +316,9 @@ struct scc_channel { /* Timer */ struct timer_list tx_t; /* tx timer for this channel */ + struct timer_list tx_wdog; + /* tx watchdogs */ struct timer_list rx_t; /* rx timer */ - - /* rx timer counters */ - - unsigned int t_dwait; /* wait time (DWAIT) */ - unsigned int t_slot; /* channel sample frequency */ - unsigned int t_txdel; /* TX delay */ - unsigned int t_tail; /* tail time */ - unsigned int t_maxk; /* max. key up */ - unsigned int t_min; /* minimal key up */ - unsigned int t_idle; /* */ - unsigned int t_mbusy; /* time until defer if channel busy */ }; @@ -564,8 +607,4 @@ struct scc_channel { /* global functions */ -#ifdef PREV_LINUX_1_3_33 -extern long scc_init(long kmem_start); -#else extern int scc_init(void); -#endif diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 6629027a6ad5..0689e4fc32db 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -82,6 +82,7 @@ struct __sysctl_args { #define NET_BRIDGE 10 /* /proc/sys/net/core */ +#define NET_CORE_NET_ALIAS_MAX 1 /* /proc/sys/net/ethernet */ @@ -98,6 +99,7 @@ struct __sysctl_args { #define NET_IPV4_ARP_CONFIRM_INTERVAL 6 #define NET_IPV4_ARP_CONFIRM_TIMEOUT 7 #define NET_IPV4_FORWARD 8 +#define NET_IPV4_DYNADDR 9 /* /proc/sys/net/ipx */ diff --git a/include/linux/tty.h b/include/linux/tty.h index 6c9fd350f5b7..6fe00af54806 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -292,6 +292,7 @@ extern int cy_init(void); extern int stl_init(void); extern int stli_init(void); extern int riscom8_init(void); +extern int specialix_init(void); extern int baycom_init(void); extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device, diff --git a/include/net/ip.h b/include/net/ip.h index c1a88c7cd4f4..5437b3dd11dc 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -122,6 +122,9 @@ extern int ip_build_xmit(struct sock *sk, extern struct ip_mib ip_statistics; +extern int sysctl_ip_dynaddr; +int ip_rewrite_addrs(struct sock *sk, struct sk_buff *skb, struct device *dev); + /* * Functions provided by ip_fragment.o */ diff --git a/include/net/ipx.h b/include/net/ipx.h index 9b8ba7a0b2c0..13d3dbbee00a 100644 --- a/include/net/ipx.h +++ b/include/net/ipx.h @@ -16,6 +16,8 @@ #include #include +/* #define CONFIG_IPX_INTERN 1 */ + typedef struct { unsigned long net; diff --git a/include/net/route.h b/include/net/route.h index de59bda48cb4..fa072b7a45a4 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -13,6 +13,7 @@ * Alan Cox : Reformatted. Added ip_rt_local() * Alan Cox : Support for TCP parameters. * Alexey Kuznetsov: Major changes for new routing code. + * Elliot Poger : Added support for SO_BINDTODEVICE. * * FIXME: * Make atomic ops more generic and hide them in asm/... @@ -83,7 +84,7 @@ struct rtable extern void ip_rt_flush(struct device *dev); extern void ip_rt_update(int event, struct device *dev); extern void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev); -extern struct rtable *ip_rt_slow_route(__u32 daddr, int local); +extern struct rtable *ip_rt_slow_route(__u32 daddr, int local, struct device *dev); extern int rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy); extern int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy); extern int ip_rt_ioctl(unsigned int cmd, void *arg); @@ -131,9 +132,9 @@ extern __inline__ void ip_rt_put(struct rtable * rt) #endif #ifdef CONFIG_KERNELD -extern struct rtable * ip_rt_route(__u32 daddr, int local); +extern struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev); #else -extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local) +extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev) #ifndef MODULE { struct rtable * rth; @@ -142,7 +143,8 @@ extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local) for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next) { - if (rth->rt_dst == daddr) + /* If an interface is specified, make sure this route points to it. */ + if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) ) { rth->rt_lastuse = jiffies; atomic_inc(&rth->rt_use); @@ -151,23 +153,23 @@ extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local) return rth; } } - return ip_rt_slow_route (daddr, local); + return ip_rt_slow_route (daddr, local, dev); } #else ; #endif #endif -extern __inline__ struct rtable * ip_check_route(struct rtable ** rp, - __u32 daddr, int local) +extern __inline__ struct rtable * ip_check_route(struct rtable ** rp, __u32 daddr, + int local, struct device *dev) { struct rtable * rt = *rp; - if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP) + if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP) || (dev!=NULL) || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0))) { ip_rt_put(rt); - rt = ip_rt_route(daddr, local); + rt = ip_rt_route(daddr, local, dev); *rp = rt; } return rt; diff --git a/include/net/sock.h b/include/net/sock.h index 4d0f5ea5044d..1063cbddee97 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -23,6 +23,7 @@ * Pauline Middelink : identd support * Alan Cox : Eliminate low level recv/recvfrom * David S. Miller : New socket lookup architecture for ISS. + * Elliot Poger : New field for SO_BINDTODEVICE option. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -196,6 +197,7 @@ struct sock broadcast, nonagle, bsdism; + struct device * bound_device; unsigned long lingertime; int proc; diff --git a/include/net/tcp.h b/include/net/tcp.h index 0bcbc997a8a4..2fa5a9700480 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -41,7 +41,23 @@ extern struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE]; /* These are AF independant. */ static __inline__ int tcp_bhashfn(__u16 lport) { - return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1); + return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE-1); +} + +/* Find the next port that hashes h that is larger than lport. + * If you change the hash, change this function to match, or you will + * break TCP port selection. This function must also NOT wrap around + * when the next number exceeds the largest possible port (2^16-1). + */ +static __inline__ int tcp_bhashnext(__u16 short lport, __u16 h) +{ + __u32 s; /* don't change this to a smaller type! */ + + s = (lport ^ (h ^ tcp_bhashfn(lport))); + if (s > lport) + return s; + s = lport + TCP_BHTABLE_SIZE; + return (s ^ (h ^ tcp_bhashfn(s))); } static __inline__ int tcp_sk_bhashfn(struct sock *sk) @@ -116,7 +132,7 @@ static __inline__ void tcp_sk_unbindify(struct sock *sk) stacks do signed 16bit maths! */ #define MIN_WINDOW 2048 #define MAX_ACK_BACKLOG 2 -#define MAX_DUP_ACKS 2 +#define MAX_DUP_ACKS 3 #define MIN_WRITE_SPACE 2048 #define TCP_WINDOW_DIFF 2048 diff --git a/init/main.c b/init/main.c index ce00f04b5d7d..b0d63dbb4292 100644 --- a/init/main.c +++ b/init/main.c @@ -173,6 +173,9 @@ extern void pcxx_setup(char *str, int *ints); #ifdef CONFIG_RISCOM8 extern void riscom8_setup(char *str, int *ints); #endif +#ifdef CONFIG_SPECIALIX +extern void specialix_setup(char *str, int *ints); +#endif #ifdef CONFIG_BAYCOM extern void baycom_setup(char *str, int *ints); #endif @@ -426,6 +429,9 @@ struct { #ifdef CONFIG_RISCOM8 { "riscom8=", riscom8_setup }, #endif +#ifdef CONFIG_SPECIALIX + { "specialix=", specialix_setup }, +#endif #ifdef CONFIG_BAYCOM { "baycom=", baycom_setup }, #endif diff --git a/kernel/fork.c b/kernel/fork.c index 5c1becd9aec4..791d5ea5a076 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -122,6 +122,7 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk) *mm = *current->mm; mm->count = 1; mm->def_flags = 0; + mm->mmap_sem = MUTEX; tsk->mm = mm; tsk->min_flt = tsk->maj_flt = 0; tsk->cmin_flt = tsk->cmaj_flt = 0; diff --git a/net/Config.in b/net/Config.in index bc6e936e2010..7e2fb6b64a39 100644 --- a/net/Config.in +++ b/net/Config.in @@ -11,9 +11,6 @@ if [ "$CONFIG_INET" = "y" ]; then fi comment ' ' tristate 'The IPX protocol' CONFIG_IPX -if [ "$CONFIG_IPX" != "n" ]; then - bool 'Full internal IPX network' CONFIG_IPX_INTERN -fi tristate 'Appletalk DDP' CONFIG_ATALK bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 if [ "$CONFIG_AX25" = "y" ]; then diff --git a/net/core/dev.c b/net/core/dev.c index ac3fbe6098ce..e50c2ad88643 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -392,7 +392,7 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) #ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) - skb->dev = dev = net_alias_main_dev(dev); + skb->dev = dev = net_alias_dev_tx(dev); #endif /* @@ -436,7 +436,18 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) struct sk_buff *skb2; if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) break; + /* FIXME?: Wrong when the hard_header_len + * is an upper bound. Is this even + * used anywhere? + */ skb2->h.raw = skb2->data + dev->hard_header_len; + /* On soft header devices we + * yank the header before mac.raw + * back off. This is set by + * dev->hard_header(). + */ + if (dev->flags&IFF_SOFTHEADERS) + skb_pull(skb2,skb2->mac.raw-skb2->data); skb2->mac.raw = skb2->data; ptype->func(skb2, skb->dev, ptype); } @@ -1095,7 +1106,7 @@ static int dev_ifsioc(void *arg, unsigned int getset) switch(getset) { case SIOCGIFFLAGS: /* Get interface flags */ - ifr.ifr_flags = dev->flags; + ifr.ifr_flags = (dev->flags & ~IFF_SOFTHEADERS); goto rarok; case SIOCSIFFLAGS: /* Set interface flags */ @@ -1117,7 +1128,7 @@ static int dev_ifsioc(void *arg, unsigned int getset) IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER - | IFF_MULTICAST)) | (dev->flags & IFF_UP); + | IFF_MULTICAST)) | (dev->flags & (IFF_SOFTHEADERS|IFF_UP)); /* * Load in the correct multicast list now the flags have changed. */ diff --git a/net/core/net_alias.c b/net/core/net_alias.c index 03095567f73a..242dff325f06 100644 --- a/net/core/net_alias.c +++ b/net/core/net_alias.c @@ -2,7 +2,8 @@ * NET_ALIAS network device aliasing module. * * - * Version: @(#)net_alias.c 0.43 12/20/95 + * Version: @(#)net_alias.c 0.50 4/20/97 + * * * Authors: Juan Jose Ciarlante, * Marcelo Fabian Roccasalva, @@ -14,13 +15,16 @@ * - fast hashed alias address lookup * - net_alias_type objs registration/unreg., module-ables. * - /proc/net/aliases & /proc/net/alias_types entries + * - tx and rx stats + * - /proc/sys/net/core/net_alias_max entry * Fixes: - * JJC : several net_alias_type func. renamed. - * JJC : net_alias_type object methods now pass + * Juan Jose Ciarlante : several net_alias_type func. renamed. + * Juan Jose Ciarlante : net_alias_type object methods now pass * *this. - * JJC : xxx_rcv device selection based on - * addrs + * Juan Jose Ciarlante : xxx_rcv device selection based on addrs * Andreas Schultz : Kerneld support. + * Juan Jose Ciarlante : Added tx/rx stats for aliases. + * Juan Jose Ciarlante : Added sysctl interface for max aliases per device * * FIXME: * - User calls sleep/wake_up locking. @@ -39,10 +43,12 @@ #include #include #include +#include #include #include #include #include +#include #ifdef ALIAS_USER_LAND_DEBUG #include "net_alias.h" @@ -55,11 +61,35 @@ #include #endif + /* - * Only allow the following flags to pass from main device to aliases + * NET_ALIAS_MAX_DEFAULT: max. alias slot number allowed by default, + * can be changed by sysctl + * NET_ALIAS_HASH_TAB_SIZE: hash table size (addr lookup), 1 per aliased + * device, due to hash optimizations, MUST be 16 or 256 only. */ -#define NET_ALIAS_IFF_MASK (IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT) +#define NET_ALIAS_MAX_DEFAULT 256 + +/* DO NOT CHANGE the line below ! */ +#define NET_ALIAS_HASH_TAB_SIZE(n) ( ((n)>=NET_ALIAS_MAX_DEFAULT) ? 256 : 16 ) + +/* + * set default max_aliases per device + */ +int sysctl_net_alias_max = NET_ALIAS_MAX_DEFAULT; + +/* + * Only allow the following flags to pass from main device to aliases + * Note that IFF_BROADCAST is not passed by default, this make sense + * because: + * a) if same-net alias: broadcasts are already handled by main device + * b) if diff-net alias: bcasts will be set by 'broadcast' ifconfig opt. + * I prefer this approach instead of setting '-broadcast' for each + * same-net alias device --JJC. + */ + +#define NET_ALIAS_IFF_MASK (IFF_SOFTHEADERS|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT) static struct net_alias_type * nat_getbytype(int type); static int nat_attach_chg(struct net_alias_type *nat, int delta); @@ -68,6 +98,7 @@ static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias); static int net_alias_devinit(struct device *dev); +static struct enet_statistics *net_alias_dev_stats(struct device *dev); static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev); static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa); static struct net_alias **net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias); @@ -79,7 +110,22 @@ static void net_alias_free(struct device *dev); * net_alias_type base array, will hold net_alias_type obj hashed list heads. */ -struct net_alias_type *nat_base[16]; +struct net_alias_type *net_alias_type_base[16]; + + +/* + * get_stats function: return rx/tx pkts based on lookups + */ +static struct enet_statistics *net_alias_dev_stats(struct device *dev) +{ + static struct enet_statistics alias_stats; + struct net_alias *alias = dev->my_alias; + + memset (&alias_stats, 0, sizeof (alias_stats)); + alias_stats.rx_packets = alias->rx_lookups; + alias_stats.tx_packets = alias->tx_lookups; + return &alias_stats; +} /* @@ -90,7 +136,7 @@ static __inline__ struct net_alias_type * nat_getbytype(int type) { struct net_alias_type *nat; - for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next) + for(nat = net_alias_type_base[type & 0x0f]; nat ; nat = nat->next) { if (nat->type == type) return nat; } @@ -119,11 +165,14 @@ nat_addr32(struct net_alias_type *nat, struct sockaddr *sa) */ static __inline__ unsigned -HASH(__u32 addr, int af) +hash_key(unsigned hsize, __u32 addr) { unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */ tmp ^= (tmp>>8); /* 2 -> 1 */ - return (tmp^(tmp>>4)^af) & 0x0f; /* 1 -> 1/2 */ + if (hsize == 256) + return (tmp & 0xff); + else + return (tmp^(tmp>>4)) & 0x0f; /* 1 -> 1/2 */ } @@ -135,9 +184,9 @@ HASH(__u32 addr, int af) */ static __inline__ int -nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa) +nat_hash_key(struct net_alias_type *nat, unsigned hsize, struct sockaddr *sa) { - return HASH(nat_addr32(nat,sa), sa->sa_family); + return hash_key(hsize, nat_addr32(nat,sa)); } @@ -236,13 +285,13 @@ net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev) static int -net_alias_open(struct device * dev) +net_alias_dev_open(struct device * dev) { return 0; } static int -net_alias_close(struct device * dev) +net_alias_dev_close(struct device * dev) { return 0; } @@ -271,13 +320,16 @@ net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, dev = &alias->dev; memset(dev, '\0', sizeof(struct device)); family = (sa)? sa->sa_family : main_dev->family; - + alias->rx_lookups = 0; + alias->tx_lookups = 0; dev->alias_info = NULL; /* no aliasing recursion */ dev->my_alias = alias; /* point to alias */ dev->name = alias->name; dev->type = main_dev->type; - dev->open = net_alias_open; - dev->stop = net_alias_close; + dev->open = net_alias_dev_open; + dev->stop = net_alias_dev_close; + dev->get_stats = net_alias_dev_stats; + dev->hard_header_len = main_dev->hard_header_len; memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN); memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN); @@ -328,7 +380,7 @@ net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias) */ n_aliases = alias_info->n_aliases; - for (idx=0; idx < 16 ; idx++) + for (idx=0; idx < alias_info->hash_tab_size ; idx++) for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next) if (*aliasp == alias) return aliasp; @@ -353,6 +405,7 @@ net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockadd unsigned long flags; int family; __u32 addr32; + int max_aliases; /* FIXME: lock */ alias_info = main_dev->alias_info; @@ -387,6 +440,19 @@ net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockadd #endif } + *err = -EINVAL; + + if (!alias_info) + /* + * At this point we take the sysctl value(s) + */ + max_aliases = sysctl_net_alias_max; + else + max_aliases = alias_info->max_aliases; + + if (slot >= max_aliases ) /* 0-based (eth0:0 _IS_ valid) */ + return NULL; + /* * do not allow creation over downed devices */ @@ -403,10 +469,25 @@ net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockadd *err = -ENOMEM; if (!alias_info) - { - alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL); - if (!alias_info) return NULL; /* ENOMEM */ - memset(alias_info, 0, sizeof(struct net_alias_info)); + { + /* + * Allocate space for struct net_alias_info plus hash table + */ + int truesize; + + /* net_alias_info size */ + truesize = sizeof(struct net_alias_info); + /* add hash_tab size * sizeof(elem) */ + truesize += NET_ALIAS_HASH_TAB_SIZE(max_aliases) * sizeof (struct net_alias *); + + alias_info = kmalloc( truesize , GFP_KERNEL); + + if (!alias_info) return NULL; /* ENOMEM */ + + memset(alias_info, 0, truesize); + alias_info->truesize = truesize; + alias_info->max_aliases = max_aliases; + alias_info->hash_tab_size = NET_ALIAS_HASH_TAB_SIZE(max_aliases); } if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL))) @@ -453,7 +534,7 @@ net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockadd * store hash key in alias: will speed-up rehashing and deletion */ - alias->hash = HASH(addr32, family); + alias->hash = hash_key(alias_info->hash_tab_size, addr32); /* * insert alias in hashed linked list @@ -595,7 +676,7 @@ net_alias_dev_delete(struct device *main_dev, int slot, int *err) kfree_s(alias, sizeof(struct net_alias)); if (main_dev->alias_info == NULL) - kfree_s(alias_info, sizeof(struct net_alias_info)); + kfree_s(alias_info, alias_info->truesize); /* * deletion ok (*err=0), NULL device returned. @@ -723,14 +804,12 @@ net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, if (!(dev=dev_get(dev_name))) return NULL; *sptr++=':'; - + /* * fetch slot number */ slot = simple_strtoul(sptr,&eptr,10); - if (slot >= NET_ALIAS_MAX_SLOT) - return NULL; /* * if last char is '-', it is a deletion request @@ -829,7 +908,7 @@ net_alias_dev_rehash(struct device *dev, struct sockaddr *sa) * new hash key. if same as old AND same type (family) return; */ - n_hash = nat_hash_key(n_nat, sa); + n_hash = nat_hash_key(n_nat, alias_info->hash_tab_size, sa); if (n_hash == alias->hash && o_nat == n_nat ) return 0; @@ -905,7 +984,7 @@ int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length unsigned idx; len=sprintf(buffer,"type name n_attach\n"); for (idx=0 ; idx < 16 ; idx++) - for (nat = nat_base[idx]; nat ; nat = nat->next) + for (nat = net_alias_type_base[idx]; nat ; nat = nat->next) { len += sprintf(buffer+len, "%-7d %-15s %-7d\n", nat->type, nat->name,nat->n_attach); @@ -1027,7 +1106,9 @@ static __inline__ struct device * nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off) { struct net_alias *alias; - for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)]; + unsigned hsize = alias_info->hash_tab_size; + + for(alias = alias_info->hash_tab[nat_hash_key(nat,hsize,sa)]; alias; alias = alias->next) { if (alias->dev.family != sa->sa_family) continue; @@ -1052,7 +1133,9 @@ static __inline__ struct device * nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off) { struct net_alias *alias; - for (alias=alias_info->hash_tab[HASH(addr32,family)]; + unsigned hsize = alias_info->hash_tab_size; + + for (alias=alias_info->hash_tab[hash_key(hsize, addr32)]; alias; alias=alias->next) { if (alias->dev.family != family) continue; @@ -1126,7 +1209,7 @@ net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32, */ struct device * -net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst) +net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst) { int family; struct net_alias_type *nat; @@ -1166,7 +1249,10 @@ net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct s dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0); - if (dev != NULL) return dev; + if (dev != NULL) { + net_alias_inc_rx(dev->my_alias); + return dev; + } } /* @@ -1181,24 +1267,29 @@ net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct s /* * dev ok only if it is alias of main_dev */ - - dev = net_alias_is(dev)? - ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; + if (net_alias_is(dev)) { + struct net_alias *alias=dev->my_alias; + if (alias->main_dev == main_dev) { + net_alias_inc_rx(alias); + return dev; + } + } + /* * do not return NULL. */ - return (dev)? dev : main_dev; + return main_dev; } /* - * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols. + * dev_rx32: dev_rx selection for 'pa_addr' protocols. */ struct device * -net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst) +net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst) { struct net_alias_type *nat; struct net_alias_info *alias_info; @@ -1236,7 +1327,10 @@ net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 ds if (dst) { dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0); - if (dev) return dev; + if (dev) { + net_alias_inc_rx(dev->my_alias); + return dev; + } } /* @@ -1255,19 +1349,23 @@ net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 ds /* * dev ok only if it is alias of main_dev */ - - dev = net_alias_is(dev)? - ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; + + if (net_alias_is(dev)) { + struct net_alias *alias=dev->my_alias; + if (alias->main_dev == main_dev) { + net_alias_inc_rx(alias); + return dev; + } + } /* * do not return NULL. */ - return (dev)? dev : main_dev; + return main_dev; } - /* * device event hook */ @@ -1333,8 +1431,8 @@ int register_net_alias_type(struct net_alias_type *nat, int type) hash = nat->type & 0x0f; save_flags(flags); cli(); - nat->next = nat_base[hash]; - nat_base[hash] = nat; + nat->next = net_alias_type_base[hash]; + net_alias_type_base[hash] = nat; restore_flags(flags); return 0; } @@ -1366,7 +1464,7 @@ int unregister_net_alias_type(struct net_alias_type *nat) hash = nat->type & 0x0f; save_flags(flags); cli(); - for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next) + for (natp = &net_alias_type_base[hash]; *natp ; natp = &(*natp)->next) { if (nat==(*natp)) { @@ -1380,3 +1478,26 @@ int unregister_net_alias_type(struct net_alias_type *nat) return -EINVAL; } +/* + * Log sysctl's net_alias_max changes. + */ +int proc_do_net_alias_max(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int old = sysctl_net_alias_max; + int ret; + + ret = proc_dointvec(ctl, write, filp, buffer, lenp); + if (write) { + if (sysctl_net_alias_max != old) { + printk(KERN_INFO "sysctl: net_alias_max changed (max.aliases=%d, hashsize=%d).\n", + sysctl_net_alias_max, + NET_ALIAS_HASH_TAB_SIZE(sysctl_net_alias_max)); + if (!sysctl_net_alias_max) + printk(KERN_INFO "sysctl: net_alias creation disabled.\n"); + if (!old) + printk(KERN_INFO "sysctl: net_alias creation enabled.\n"); + } + } + return ret; +} diff --git a/net/core/sock.c b/net/core/sock.c index 61c31a551f28..398315fc3079 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -71,6 +71,7 @@ * Alan Cox : Generic socket allocation to make hooks * easier (suggested by Craig Metz). * Michael Pall : SO_ERROR returns positive errno again + * Elliot Poger : Added support for SO_BINDTODEVICE. * * To Fix: * @@ -128,6 +129,7 @@ int sock_setsockopt(struct sock *sk, int level, int optname, int valbool; int err; struct linger ling; + struct ifreq req; /* * Options without arguments @@ -232,7 +234,37 @@ int sock_setsockopt(struct sock *sk, int level, int optname, case SO_BSDCOMPAT: sk->bsdism = valbool; return 0; - + +#ifdef CONFIG_NET + case SO_BINDTODEVICE: + /* Bind this socket to a particular device like "eth0", + * as specified in an ifreq structure. If the device + * is "", socket is NOT bound to a device. */ + if (!valbool) { + sk->bound_device = NULL; + } else { + err=verify_area(VERIFY_READ,optval,sizeof(req)); + if(err) + return err; + memcpy_fromfs(&req,optval,sizeof(req)); + + /* Remove any cached route for this socket. */ + if (sk->ip_route_cache) { + ip_rt_put(sk->ip_route_cache); + sk->ip_route_cache=NULL; + } + + if (*(req.ifr_name) == '\0') { + sk->bound_device = NULL; + } else { + sk->bound_device = dev_get(req.ifr_name); + if (sk->bound_device == NULL) + return -EINVAL; + } + } + return 0; +#endif + default: return(-ENOPROTOOPT); } @@ -462,9 +494,9 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne return NULL; } - + mem=sk->wmem_alloc; - + if(!fallback) skb = sock_wmalloc(sk, size, 0, sk->allocation); else diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 8b5848e6b1cf..4930f1569e00 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -7,7 +7,17 @@ #include #include +#include + +#ifdef CONFIG_NET_ALIAS +extern int sysctl_net_alias_max; +extern int proc_do_net_alias_max(ctl_table *, int, struct file *, void *, size_t *); +#endif ctl_table core_table[] = { +#ifdef CONFIG_NET_ALIAS + {NET_CORE_NET_ALIAS_MAX, "net_alias_max", &sysctl_net_alias_max, sizeof(int), + 0644, NULL, &proc_do_net_alias_max }, +#endif {0} }; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 938fe744341e..43e0a15ebeb4 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -1973,7 +1973,7 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) memcpy(ha, proxy_entry->ha, dev->addr_len); arp_unlock(); - rt = ip_rt_route(tip, 0); + rt = ip_rt_route(tip, 0, NULL); if (rt && rt->rt_dev != dev) arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha); ip_rt_put(rt); @@ -2046,7 +2046,7 @@ static int arp_req_set(struct arpreq *r, struct device * dev) if (!dev) { struct rtable * rt; - rt = ip_rt_route(ip, 0); + rt = ip_rt_route(ip, 0, NULL); if (!rt) return -ENETUNREACH; dev = rt->rt_dev; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index e5bf8890f190..1ad978f1286d 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -39,6 +39,7 @@ * Thomas Quinot : ICMP Dest Unreach codes up to 15 are * valid (RFC 1812). * Alan Cox : Spoofing and junk icmp protections. + * Elliot Poger : Added support for SO_BINDTODEVICE. * * * RFC1122 (Host Requirements -- Comm. Layer) Status: @@ -993,8 +994,14 @@ static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, struct devi */ /* This should work with the new hashes now. -DaveM */ -extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport); -extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport); +extern struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, + unsigned short rnum, unsigned long laddr, + unsigned long paddr, unsigned short pnum, + struct device *dev); +extern struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr, + unsigned short rnum, unsigned long laddr, + unsigned long paddr, unsigned short pnum, + struct device *dev); int icmp_chkaddr(struct sk_buff *skb) { @@ -1010,7 +1017,8 @@ int icmp_chkaddr(struct sk_buff *skb) { struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); - sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest); + sk = tcp_v4_proxy_lookup(th->source, iph->daddr, th->dest, + iph->saddr, 0, 0, skb->dev); if (!sk) return 0; if (sk->saddr != iph->saddr) return 0; if (sk->daddr != iph->daddr) return 0; @@ -1024,7 +1032,8 @@ int icmp_chkaddr(struct sk_buff *skb) { struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2)); - sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest); + sk = udp_v4_proxy_lookup(uh->source, iph->daddr, uh->dest, + iph->saddr, 0, 0, skb->dev); if (!sk) return 0; if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR) return 0; diff --git a/net/ipv4/ip_alias.c b/net/ipv4/ip_alias.c index 488de23d4242..9c76eab0aa3d 100644 --- a/net/ipv4/ip_alias.c +++ b/net/ipv4/ip_alias.c @@ -102,7 +102,7 @@ struct device *ip_alias_dev_select(struct net_alias_type *this, struct device *m * net_alias module will check if returned device is main_dev's alias */ - rt = ip_rt_route(addr, 0); + rt = ip_rt_route(addr, 0, NULL); if(rt) { dev=rt->rt_dev; diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index da1d427def61..d9e44c9023f5 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -177,7 +177,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, * and give it to the IP sender for further processing. */ - rt = ip_rt_route(target_addr, 0); + rt = ip_rt_route(target_addr, 0, NULL); if (rt == NULL) { @@ -419,7 +419,7 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, skb2->dev=dev2; #ifdef CONFIG_IP_MROUTE if(is_frag&IPFWD_MULTITUNNEL) - ip_encap(skb,skb->len, dev2, raddr); + ip_encap(skb, 0, dev2, target_addr); else { #endif diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 5edd3a6a2353..ffb5e2e13b43 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -523,7 +523,7 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device prev = NULL; for(next = qp->fragments; next != NULL; next = next->next) { - if (next->offset > offset) + if (next->offset >= offset) break; /* bingo! */ prev = next; } diff --git a/net/ipv4/ip_fw.c b/net/ipv4/ip_fw.c index b07accd0ff31..63f6ebd19486 100644 --- a/net/ipv4/ip_fw.c +++ b/net/ipv4/ip_fw.c @@ -206,9 +206,9 @@ extern inline int port_match(unsigned short *portptr,int nports,unsigned short p int ip_fw_chk(struct iphdr *ip, struct device *rif, __u16 *redirport, struct ip_fw *chain, int policy, int mode) { struct ip_fw *f; - struct tcphdr *tcp=(struct tcphdr *)((unsigned long *)ip+ip->ihl); - struct udphdr *udp=(struct udphdr *)((unsigned long *)ip+ip->ihl); - struct icmphdr *icmp=(struct icmphdr *)((unsigned long *)ip+ip->ihl); + struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl); + struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl); + struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl); __u32 src, dst; __u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF; unsigned short f_prt=0, prt; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ade3e8a0ff64..53bbacdcf5e2 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -26,6 +26,8 @@ * Alexander Demenshin: Missing sk/skb free in ip_queue_xmit * (in case if packet not accepted by * output firewall rules) + * Elliot Poger : Added support for SO_BINDTODEVICE. + * Juan Jose Ciarlante: sk/skb source address rewriting */ #include @@ -65,6 +67,78 @@ #include #include +/* + * Allows dynamic re-writing of packet's addresses. + * if value > 1, be verbose about addr rewriting. + * Currently implemented: + * tcp_output.c if sk->state!=TCP_SYN_SENT + * ip_masq.c if no packet has been received by tunnel + */ +int sysctl_ip_dynaddr = 0; + +/* + * Very Promisc source address re-assignation. + * ONLY acceptable if socket is NOT connected yet. + * Caller already checked sysctl_ip_dynaddr != 0 and consistent sk->state + * (TCP_SYN_SENT for tcp, udp-connect sockets are set TCP_ESTABLISHED) + */ + +int ip_rewrite_addrs (struct sock *sk, struct sk_buff *skb, struct device *dev) +{ + u32 new_saddr = dev->pa_addr; + struct iphdr *iph; + + /* + * Be carefull: new_saddr must be !0 + */ + if (!new_saddr) { + printk(KERN_WARNING "ip_rewrite_addrs(): NULL device \"%s\" addr\n", + dev->name); + return 0; + } + + /* + * Ouch!, this should not happen. + */ + if (!sk->saddr || !sk->rcv_saddr) { + printk(KERN_WARNING "ip_rewrite_addrs(): not valid sock addrs: saddr=%08lX rcv_saddr=%08lX", + ntohl(sk->saddr), ntohl(sk->rcv_saddr)); + return 0; + } + + /* + * Be verbose if sysctl value > 1 + */ + if (sysctl_ip_dynaddr > 1) { + printk(KERN_INFO "ip_rewrite_addrs(): shifting saddr from %s", + in_ntoa(skb->saddr)); + printk(" to %s\n", in_ntoa(new_saddr)); + } + + iph = skb->ip_hdr; + + if (new_saddr != iph->saddr) { + iph->saddr = new_saddr; + skb->saddr = new_saddr; + ip_send_check(iph); + } else if (sysctl_ip_dynaddr > 1) { + printk(KERN_WARNING "ip_rewrite_addrs(): skb already changed (???).\n"); + return 0; + } + + /* + * Maybe whe are in a skb chain loop and socket address has + * yet been 'damaged'. + */ + if (new_saddr != sk->saddr) { + sk->saddr = new_saddr; + sk->rcv_saddr = new_saddr; + sk->prot->rehash(sk); + } else if (sysctl_ip_dynaddr > 1) + printk(KERN_NOTICE "ip_rewrite_addrs(): no change needed for sock\n"); + return 1; +} + /* * Loop a packet back to the sender. */ @@ -218,7 +292,7 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, #endif if (rp) { - rt = ip_check_route(rp, daddr, skb->localroute); + rt = ip_check_route(rp, daddr, skb->localroute, *dev); /* * If rp != NULL rt_put following below should not * release route, so that... @@ -227,7 +301,7 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr, atomic_inc(&rt->rt_refcnt); } else - rt = ip_rt_route(daddr, skb->localroute); + rt = ip_rt_route(daddr, skb->localroute, *dev); if (*dev == NULL) @@ -590,7 +664,7 @@ int ip_build_xmit(struct sock *sk, #endif rt = ip_check_route(&sk->ip_route_cache, daddr, sk->localroute || (flags&MSG_DONTROUTE) || - (opt && opt->is_strictroute)); + (opt && opt->is_strictroute), sk->bound_device); if (rt == NULL) { ip_statistics.IpOutNoRoutes++; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 7e90749aa6b9..6784ca56b963 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -12,6 +12,7 @@ * Martin Mares : TOS setting fixed. * Alan Cox : Fixed a couple of oopses in Martin's * TOS tweaks. + * Elliot Poger : Added support for SO_BINDTODEVICE. */ #include @@ -293,7 +294,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt /* * Not set so scan. */ - if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) + if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL) { dev=rt->rt_dev; atomic_dec(&rt->rt_use); @@ -345,7 +346,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt if(mreq.imr_interface.s_addr==INADDR_ANY) { - if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL) + if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL) { dev=rt->rt_dev; atomic_dec(&rt->rt_use); diff --git a/net/ipv4/packet.c b/net/ipv4/packet.c index 1c8635696c13..3d658a043e76 100644 --- a/net/ipv4/packet.c +++ b/net/ipv4/packet.c @@ -169,10 +169,11 @@ static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len, * raw protocol and you must do your own fragmentation at this level. */ - if(len>dev->mtu+dev->hard_header_len) + if ((dev->flags & IFF_SOFTHEADERS && len>dev->mtu) + || len>dev->mtu+dev->hard_header_len) return -EMSGSIZE; - skb = sock_wmalloc(sk, len, 0, GFP_KERNEL); + skb = sock_wmalloc(sk, len+dev->hard_header_len, 0, GFP_KERNEL); /* * If the write buffer is full, then tough. At this level the user gets to @@ -191,6 +192,16 @@ static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len, skb->sk = sk; skb->free = 1; + if (dev->flags & IFF_SOFTHEADERS) { + /* Do the hard header calls for drivers that must set the + * headers at transmission time by themselves. PPP and ISDN + * are the notable offenders here. A proper fix requires some + * changes at the device driver level, but that is a 2.1 issue. + */ + skb_reserve(skb,dev->hard_header_len); + if (dev->hard_header) + dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,len); + } memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); skb->arp = 1; /* No ARP needs doing on this (complete) frame */ skb->protocol = proto; diff --git a/net/ipv4/rarp.c b/net/ipv4/rarp.c index 772b3d0fe296..f39c7bd5e586 100644 --- a/net/ipv4/rarp.c +++ b/net/ipv4/rarp.c @@ -354,7 +354,7 @@ static int rarp_req_set(struct arpreq *req) * Is it reachable directly ? */ - rt = ip_rt_route(ip, 0); + rt = ip_rt_route(ip, 0, NULL); if (rt == NULL) return -ENETUNREACH; dev = rt->rt_dev; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 875d81c19867..bea126823b35 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -42,6 +42,9 @@ * Bjorn Ekwall : Kerneld route support. * Alan Cox : Multicast fixed (I hope) * Pavel Krauz : Limited broadcast fixed + * Elliot Poger : Added support for SO_BINDTODEVICE. + * Andi Kleen : Don't send multicast addresses to + * kerneld. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -226,10 +229,9 @@ static struct fib_node * fib_lookup_gateway(__u32 dst) for ( ; f; f = f->fib_next) { - if ((dst ^ f->fib_dst) & fz->fz_mask) + if (((dst ^ f->fib_dst) & fz->fz_mask) || + (f->fib_info->fib_flags & RTF_GATEWAY)) continue; - if (f->fib_info->fib_flags & RTF_GATEWAY) - return NULL; return f; } } @@ -248,9 +250,11 @@ static struct fib_node * fib_lookup_gateway(__u32 dst) * Host 193.233.7.129 is locally unreachable, * but old (<=1.3.37) code will send packets destined for it to eth1. * + * Calling routine can specify a particular interface by setting dev. If dev==NULL, + * any interface will do. */ -static struct fib_node * fib_lookup_local(__u32 dst) +static struct fib_node * fib_lookup_local(__u32 dst, struct device *dev) { struct fib_zone * fz; struct fib_node * f; @@ -268,6 +272,8 @@ static struct fib_node * fib_lookup_local(__u32 dst) { if ((dst ^ f->fib_dst) & fz->fz_mask) continue; + if ( (dev != NULL) && (dev != f->fib_info->fib_dev) ) + continue; if (!(f->fib_info->fib_flags & RTF_GATEWAY)) return f; longest_match_found = 1; @@ -290,7 +296,7 @@ static struct fib_node * fib_lookup_local(__u32 dst) * route add -host 193.233.7.255 eth0 */ -static struct fib_node * fib_lookup(__u32 dst) +static struct fib_node * fib_lookup(__u32 dst, struct device *dev) { struct fib_zone * fz; struct fib_node * f; @@ -306,6 +312,8 @@ static struct fib_node * fib_lookup(__u32 dst) { if ((dst ^ f->fib_dst) & fz->fz_mask) continue; + if ( (dev != NULL) && (dev != f->fib_info->fib_dev) ) + continue; return f; } } @@ -1255,7 +1263,7 @@ void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev) struct rt_req * rtr; struct rtable * rt; - rt = ip_rt_route(dst, 0); + rt = ip_rt_route(dst, 0, NULL); if (!rt) return; @@ -1324,7 +1332,7 @@ static void rt_cache_add(unsigned hash, struct rtable * rth) if (rth->rt_gateway != daddr) { ip_rt_fast_unlock(); - rtg = ip_rt_route(rth->rt_gateway, 0); + rtg = ip_rt_route(rth->rt_gateway, 0, NULL); ip_rt_fast_lock(); } @@ -1396,7 +1404,7 @@ static void rt_cache_add(unsigned hash, struct rtable * rth) */ -struct rtable * ip_rt_slow_route (__u32 daddr, int local) +struct rtable * ip_rt_slow_route (__u32 daddr, int local, struct device *dev) { unsigned hash = ip_rt_hash_code(daddr)^local; struct rtable * rth; @@ -1416,9 +1424,9 @@ struct rtable * ip_rt_slow_route (__u32 daddr, int local) } if (local) - f = fib_lookup_local(daddr); + f = fib_lookup_local(daddr, dev); else - f = fib_lookup (daddr); + f = fib_lookup (daddr, dev); if (f) { @@ -1493,7 +1501,15 @@ struct rtable * ip_rt_slow_route (__u32 daddr, int local) rth->rt_gateway = rth->rt_dst; if (ip_rt_lock == 1) - rt_cache_add(hash, rth); + { + /* Don't add this to the rt_cache if a device was specified, + * because we might have skipped better routes which didn't + * point at the right device. */ + if (dev != NULL) + rth->rt_flags |= RTF_NOTCACHED; + else + rt_cache_add(hash, rth); + } else { rt_free(rth); @@ -1510,9 +1526,14 @@ void ip_rt_put(struct rtable * rt) { if (rt) atomic_dec(&rt->rt_refcnt); + + /* If this rtable entry is not in the cache, we'd better free it once the + * refcnt goes to zero, because nobody else will... */ + if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) ) + rt_free(rt); } -struct rtable * ip_rt_route(__u32 daddr, int local) +struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev) { struct rtable * rth; @@ -1520,7 +1541,8 @@ struct rtable * ip_rt_route(__u32 daddr, int local) for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next) { - if (rth->rt_dst == daddr) + /* If a network device is specified, make sure this route points to it. */ + if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) ) { rth->rt_lastuse = jiffies; atomic_inc(&rth->rt_use); @@ -1529,7 +1551,7 @@ struct rtable * ip_rt_route(__u32 daddr, int local) return rth; } } - return ip_rt_slow_route (daddr, local); + return ip_rt_slow_route (daddr, local, dev); } /* diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 83129afaa820..27169448b1a4 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -18,7 +18,8 @@ extern int sysctl_arp_check_interval; extern int sysctl_arp_confirm_interval; extern int sysctl_arp_confirm_timeout; -extern int sysctl_ip_forward; +extern int sysctl_ip_forward; +extern int sysctl_ip_dynaddr; static int proc_doipforward(ctl_table *ctl, int write, struct file *filp, void *buffer, size_t *lenp) { @@ -58,5 +59,7 @@ ctl_table ipv4_table[] = { &proc_dointvec}, {NET_IPV4_FORWARD, "ip_forward", &sysctl_ip_forward, sizeof(int), 0644, NULL, &proc_doipforward }, + {NET_IPV4_DYNADDR, "ip_dynaddr", + &sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec}, {0} }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 311cbd470ea7..f1ffe8833593 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -205,6 +205,7 @@ * Theodore Ts'o : Do secure TCP sequence numbers. * David S. Miller : New socket lookup architecture for ISS. * This code is dedicated to John Dyson. + * Elliot Poger : Added support for SO_BINDTODEVICE. * * To Fix: * Fast path the code. Two things here - fix the window calculation @@ -471,6 +472,11 @@ static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum) unsigned char state = sk2->state; int sk2_reuse = sk2->reuse; + /* Two sockets can be bound to the same port if they're + * bound to different interfaces... */ + if (sk->bound_device != sk2->bound_device) + continue; + if(!sk2->rcv_saddr || !sk->rcv_saddr) { if((!sk2_reuse) || (!sk_reuse) || @@ -523,49 +529,58 @@ unsigned short tcp_good_socknum(void) int retval = 0, i, end, bc; SOCKHASH_LOCK(); - i = tcp_bhashfn(start); - end = i + TCP_BHTABLE_SIZE; - bc = binding_contour; - do { - struct sock *sk = tcp_bound_hash[tcp_bhashfn(i)]; - if(!sk) { - retval = (start + i); - start = (retval + 1); - - /* Check for decreasing load. */ - if(bc != 0) - binding_contour = 0; - goto done; - } else { - int j = 0; - do { sk = sk->bind_next; } while(++j < size && sk); - if(j < size) { - best = (start + i); - size = j; - if(bc && size <= bc) { - start = best + 1; - goto verify; - } - } - } - } while(++i != end); - - /* Socket load is increasing, adjust our load average. */ - binding_contour = size; + i = tcp_bhashfn(start); + end = i + TCP_BHTABLE_SIZE; + bc = binding_contour; + do { + struct sock *sk = tcp_bound_hash[i&(TCP_BHTABLE_SIZE-1)]; + if(!sk) { + /* find the smallest value no smaller than start + * that has this hash value. + */ + retval = tcp_bhashnext(start-1,i&(TCP_BHTABLE_SIZE-1)); + + /* Check for decreasing load. */ + if (bc != 0) + binding_contour = 0; + goto done; + } else { + int j = 0; + do { sk = sk->bind_next; } while (++j < size && sk); + if (j < size) { + best = i&(TCP_BHTABLE_SIZE-1); + size = j; + if (bc && size <= bc) + goto verify; + } + } + } while(++i != end); + i = best; + + /* Socket load is increasing, adjust our load average. */ + binding_contour = size; verify: - if(size < binding_contour) - binding_contour = size; - - if(best > 32767) - best -= (32768 - PROT_SOCK); + if (size < binding_contour) + binding_contour = size; + + retval = tcp_bhashnext(start-1,i); + + best = retval; /* mark the starting point to avoid infinite loops */ + while(tcp_lport_inuse(retval)) { + retval = tcp_bhashnext(retval,i); + if (retval > 32767) /* Upper bound */ + retval = tcp_bhashnext(PROT_SOCK,i); + if (retval == best) { + /* This hash chain is full. No answer. */ + retval = 0; + break; + } + } - while(tcp_lport_inuse(best)) - best += TCP_BHTABLE_SIZE; - retval = best; done: - if(start > 32767) - start -= (32768 - PROT_SOCK); - + start = (retval + 1); + if (start > 32767 || start < PROT_SOCK) + start = PROT_SOCK; SOCKHASH_UNLOCK(); return retval; @@ -731,6 +746,7 @@ void tcp_err(int type, int code, unsigned char *header, __u32 daddr, * here as well. */ sk->cong_window = 1; + sk->cong_count = 0; sk->high_seq = sk->sent_seq; return; } @@ -1254,7 +1270,10 @@ static int do_tcp_sendmsg(struct sock *sk, sk->write_seq += copy; seglen -= copy; } - if (tcp_size >= sk->mss || (flags & MSG_OOB) || !sk->packets_out) + /* If we have a full packet or a new OOB + * message, we have to force this packet out. + */ + if (tcp_size >= sk->mss || (flags & MSG_OOB)) tcp_send_skb(sk, skb); else tcp_enqueue_partial(skb, sk); @@ -1290,8 +1309,14 @@ static int do_tcp_sendmsg(struct sock *sk, delay = 0; tmp = copy + sk->prot->max_header + 15; - if (copy < sk->mss && !(flags & MSG_OOB) && sk->packets_out) - { + /* If won't fill the current packet, and it's not an OOB message, + * then we might want to delay to allow data in the later parts + * of iov to fill this packet out. Note that if we aren't + * Nagling or there are no packets currently out then the top + * level code in tcp_sendmsg() will force any partial packets out + * after we finish building the largest packets this write allows. + */ + if (copy < sk->mss && !(flags & MSG_OOB)) { tmp = tmp - copy + sk->mtu + 128; delay = 1; } @@ -2027,8 +2052,8 @@ static void tcp_close(struct sock *sk, unsigned long timeout) tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT); } - release_sock(sk); sk->dead = 1; + release_sock(sk); if(sk->state == TCP_CLOSE) tcp_v4_unhash(sk); @@ -2201,6 +2226,9 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) buff->free = 0; buff->localroute = sk->localroute; + /* If this socket is bound to a particular device, make sure we use it. */ + dev = sk->bound_device; + /* * Put in the IP header and routing stuff. */ @@ -2255,11 +2283,15 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) * but not bigger than device MTU */ - if(sk->mtu <32) - sk->mtu = 32; /* Sanity limit */ - sk->mtu = min(sk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); + /* Must check it here, just to be absolutely safe. If we end up + * with an sk->mtu of zero, we can thus end up with an sk->mss + * of zero, which causes us to bomb out in tcp_do_sendmsg. -DaveM + */ + if(sk->mtu < 32) + sk->mtu = 32; /* Sanity limit */ + /* * Put in the TCP options to say MTU. */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 777179a8e5a8..aca9aa5200e4 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -33,6 +33,7 @@ * : with SYN flooding attacks. * David S. Miller : New socket lookup architecture for ISS. * This code is dedicated to John Dyson. + * Elliot Poger : Added support for SO_BINDTODEVICE. */ #include @@ -154,13 +155,13 @@ extern __inline__ void tcp_rtt_estimator(struct sock *sk, struct sk_buff *oskb) /* * This code needs to be a bit more clever. - * Does 2 minute timeouts now. Still just a circular buffer. + * Does 300 second timeouts now. Still just a circular buffer. * At most 32 validations stored. New validations are ignored * if all 32 validations are currently valid. To do otherwise * allows a situation in which clearances are forgotten before * they can be used (provided valid traffic is coming fast enough). * The buffer should really be as long as the number of valid - * connections we want to accept in an 2 minute period. + * connections we want to accept in an 300 second period. * 32 is maybe to small. On the other hand, the validation check * algorithm has to walk the whole table, which is also stupid. * It would be better to have a combined hash/circular buffer. @@ -185,7 +186,7 @@ int tcp_clearance(__u32 saddr) int i; for (i = 0; i < 32; i++) if (clearances[i].saddr == saddr - && clearances[i].tstamp > jiffies-HZ*120) + && clearances[i].tstamp > jiffies-HZ*300) return 1; return 0; } @@ -207,10 +208,12 @@ void add_clearance(__u32 saddr) #ifdef CONFIG_SYN_COOKIES /* * MTU values we can represent in fall back mode. - * FIXME. I sort of picked these out of a hat. I should - * probably look around for docs on what common values are. + * These values are partially borrowed from Jeff Weisberg's SunOS + * implementation of SYNCOOKIES. I have added an extra limiting + * value of 64 to deal with the case of very small MTU values. + * (e.g. long delay packet radio links, 1200 baud modems.) */ -static __u32 cookie_mtu[8] = { 64, 128, 256, 296, 512, 576, 1024, 1500 }; +static __u32 cookie_mtu[8] = { 64, 256, 512, 536, 1024, 1440, 1460, 4312 }; #endif extern void tcp_v4_hash(struct sock *sk); @@ -222,27 +225,43 @@ extern void tcp_v4_rehash(struct sock *sk); * to specify the remote port nor the remote address for the * connection. So always assume those are both wildcarded * during the search since they can never be otherwise. - * - * XXX Later on, hash on both local port _and_ local address, - * XXX to handle a huge IP alias'd box. Keep in mind that - * XXX such a scheme will require us to run through the listener - * XXX hash twice, once for local addresses bound, and once for - * XXX the local address wildcarded (because the hash is different). */ -static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum) +static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum, + struct device *dev) { struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)]; struct sock *result = NULL; + int score, hiscore = 0; for(; sk; sk = sk->next) { if(sk->num == hnum) { __u32 rcv_saddr = sk->rcv_saddr; + score = 1; + /* If this socket is bound to a particular IP address, + * does the dest IPaddr of the packet match it? + */ if(rcv_saddr) { - if(rcv_saddr == daddr) - return sk; /* Best possible match. */ - } else if(!result) + if(rcv_saddr != daddr) + continue; + score++; + } + + /* If this socket is bound to a particular interface, + * did the packet come in on it? */ + if (sk->bound_device) { + if (dev != sk->bound_device) + continue; + score++; + } + + /* Check the score--max is 3. */ + if (score == 3) + return sk; /* Best possible match. */ + if (score > hiscore) { + hiscore = score; result = sk; + } } } return result; @@ -252,7 +271,8 @@ static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum) * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM */ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, - u32 saddr, u16 sport, u32 daddr, u16 dport) + u32 saddr, u16 sport, u32 daddr, + u16 dport, struct device *dev) { unsigned short hnum = ntohs(dport); struct sock *sk; @@ -266,21 +286,23 @@ static inline struct sock *__tcp_v4_lookup(struct tcphdr *th, if(sk->daddr == saddr && /* remote address */ sk->dummy_th.dest == sport && /* remote port */ sk->num == hnum && /* local port */ - sk->rcv_saddr == daddr) /* local address */ + sk->rcv_saddr == daddr && /* local address */ + ((sk->bound_device==NULL) || (sk->bound_device==dev)) ) goto hit; /* You sunk my battleship! */ - sk = tcp_v4_lookup_longway(daddr, hnum); + sk = tcp_v4_lookup_longway(daddr, hnum, dev); hit: return sk; } -__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport) +__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, + struct device *dev) { - return __tcp_v4_lookup(0, saddr, sport, daddr, dport); + return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dev); } #ifdef CONFIG_IP_TRANSPARENT_PROXY #define secondlist(hpnum, sk, fpass) \ -({ struct sock *s1; if(!(sk) && (fpass)--) \ +({ struct sock *s1; if((hpnum) && !(sk) && (fpass)--) \ s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \ else \ s1 = (sk); \ @@ -295,7 +317,8 @@ __inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, unsigned short rnum, unsigned long laddr, - unsigned long paddr, unsigned short pnum) + unsigned long paddr, unsigned short pnum, + struct device *dev) { struct sock *s, *result = NULL; int badness = -1; @@ -327,7 +350,12 @@ struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr, continue; score++; } - if(score == 3 && s->num == hnum) { + if(s->bound_device) { + if (s->bound_device != dev) + continue; + score++; + } + if(score == 4 && s->num == hnum) { result = s; break; } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) { @@ -473,7 +501,7 @@ static void tcp_options(struct sock *sk, struct tcphdr *th) switch(opcode) { case TCPOPT_EOL: - return; + goto ende; case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */ length--; ptr--; /* the opsize=*ptr++ above was a mistake */ @@ -481,7 +509,7 @@ static void tcp_options(struct sock *sk, struct tcphdr *th) default: if(opsize<=2) /* Avoid silly options looping forever */ - return; + goto ende; switch(opcode) { case TCPOPT_MSS: @@ -497,7 +525,7 @@ static void tcp_options(struct sock *sk, struct tcphdr *th) length-=opsize; } } - if (th->syn) +ende: if (th->syn) { if (! mss_seen) sk->mtu=min(sk->mtu, 536); /* default MSS if none sent */ @@ -590,7 +618,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, /* Only let this warning get printed once a minute. */ if (jiffies - warning_time > HZ*60) { warning_time = jiffies; - printk(KERN_INFO "Warning: possible SYN flooding. Sending cookies.\n"); + printk(KERN_INFO "Warning: possible SYN flooding on port %d. Sending cookies.\n", ntohs(th->dest)); } #ifdef CONFIG_RST_COOKIES tcp_send_synack_probe(daddr, saddr, th, &tcp_prot, @@ -598,6 +626,12 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, #endif #ifdef CONFIG_SYN_COOKIES send_cookie = 1; +#else + /* If we only have RST cookies we should + * not drop through to the rest of the response code. + */ + kfree_skb(skb, FREE_READ); + return; #endif #ifdef CONFIG_RST_COOKIES } else if (sk->ack_backlog >= 2*sk->max_ack_backlog) { @@ -634,6 +668,8 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, /* Or else we die! -DaveM */ newsk->sklist_next = NULL; + /* and die again -- erics */ + newsk->pprev = NULL; newsk->opt = NULL; newsk->ip_route_cache = NULL; @@ -667,7 +703,7 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, newsk->rtt = 0; newsk->rto = TCP_TIMEOUT_INIT; newsk->mdev = TCP_TIMEOUT_INIT; - newsk->max_window = 0; + newsk->max_window = 32; /* It cannot be left at zero. -DaveM */ /* * See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. @@ -736,8 +772,18 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, newsk->daddr = saddr; newsk->saddr = daddr; newsk->rcv_saddr = daddr; - tcp_v4_hash(newsk); - add_to_prot_sklist(newsk); +#ifdef CONFIG_SYN_COOKIES + /* Don't actually stuff the socket into the protocol lists + * if we are going to just destroy it anyway. We don't want any + * funnies happening if the next packet arrives before we get + * a chance to clean this one up. + */ + if (!send_cookie) +#endif + { + tcp_v4_hash(newsk); + add_to_prot_sklist(newsk); + } newsk->acked_seq = skb->seq + 1; newsk->copied_seq = skb->seq + 1; @@ -758,7 +804,8 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, * Note use of sk->user_mss, since user has no direct access to newsk */ - rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0); + rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0, + sk->bound_device); newsk->ip_route_cache = rt; if(rt!=NULL && (rt->rt_flags&RTF_WINDOW)) @@ -779,6 +826,14 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb, newsk->mtu = min(newsk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr)); + /* Must check it here, just to be absolutely safe. If we end up + * with a newsk->{max_window,mtu} of zero, we can thus end up with + * a newsk->mss of zero, which causes us to bomb out in + * tcp_do_sendmsg. -DaveM + */ + if(newsk->mtu < 32) + newsk->mtu = 32; + #ifdef CONFIG_SKIP /* @@ -918,7 +973,7 @@ static int tcp_conn_request_fake(struct sock *sk, struct sk_buff *skb, newsk->rtt = 0; newsk->rto = TCP_TIMEOUT_INIT; newsk->mdev = TCP_TIMEOUT_INIT; - newsk->max_window = 0; + newsk->max_window = 32; /* It cannot be left at zero. -DaveM */ /* * See draft-stevens-tcpca-spec-01 for discussion of the * initialization of these values. @@ -1001,7 +1056,8 @@ static int tcp_conn_request_fake(struct sock *sk, struct sk_buff *skb, newsk->ip_ttl=sk->ip_ttl; newsk->ip_tos=skb->ip_hdr->tos; - rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0); + rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0, + sk->bound_device); newsk->ip_route_cache = rt; if (rt!=NULL && (rt->rt_flags&RTF_WINDOW)) @@ -1263,8 +1319,12 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len) /* We need to be a bit careful to preserve the * count of packets that are out in the system here. */ - sk->ssthresh = max(sk->cong_window >> 1, 2); + sk->ssthresh = max( + min(sk->cong_window, + (sk->window_seq-sk->rcv_ack_seq)/max(sk->mss,1)) + >> 1, 2); sk->cong_window = sk->ssthresh+MAX_DUP_ACKS+1; + sk->cong_count = 0; tmp = sk->packets_out; tcp_do_retransmit(sk,0); sk->packets_out = tmp; @@ -1284,6 +1344,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len) if (sk->rcv_ack_cnt > MAX_DUP_ACKS) { /* Don't allow congestion window to drop to zero. */ sk->cong_window = max(sk->ssthresh, 1); + sk->cong_count = 0; } sk->window_seq = window_seq; sk->rcv_ack_seq = ack; @@ -1533,19 +1594,6 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len) break; } - /* - * We have nothing queued but space to send. Send any partial - * packets immediately (end of Nagle rule application). - */ - - if (sk->packets_out == 0 - && sk->partial != NULL - && skb_queue_empty(&sk->write_queue) - && sk->send_head == NULL) - { - tcp_send_partial(sk); - } - /* * In the LAST_ACK case, the other end FIN'd us. We then FIN'd them, and * we are now waiting for an acknowledge to our FIN. The other end is @@ -1618,6 +1666,14 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len) if (sk->state==TCP_SYN_RECV) { tcp_set_state(sk, TCP_ESTABLISHED); + + /* Must check for peer advertising zero sized window + * or else we get a sk->{mtu,mss} of zero and thus bomb out + * in tcp_do_sendmsg. -DaveM + */ + if(sk->max_window == 0) + sk->max_window = 32; + tcp_options(sk,th); #if 0 @@ -1628,11 +1684,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len) sk->copied_seq = sk->acked_seq; if(!sk->dead) sk->state_change(sk); - if(sk->max_window==0) - { - sk->max_window=32; /* Sanity check */ - sk->mss=min(sk->max_window,sk->mtu); - } + /* Reset the RTT estimator to the initial * state rather than testing to avoid * updating it on the ACK to the SYN packet. @@ -1650,11 +1702,20 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len) * If we are retransmitting, and we acked a packet on the retransmit * queue, and there is still something in the retransmit queue, * then we can output some retransmission packets. + * + * Note that we need to be a bit careful here about getting the + * correct TIME_WRITE timer set. If we just got an ack of a + * packet we where retransmitting, we will retransmit the next + * packet in the retransmit queue below, and the timeout + * should now start from the time we retransmitted that packet. + * The resetting of the TIME_WRITE timer above will have set it + * relative to the prior transmission time, which would be wrong. */ if (sk->send_head != NULL && (flag&2) && sk->retransmits) { tcp_do_retransmit(sk, 1); + tcp_reset_xmit_timer(sk, TIME_WRITE, sk->rto); } return 1; @@ -2196,7 +2257,8 @@ int tcp_chkaddr(struct sk_buff *skb) struct tcphdr *th = (struct tcphdr *)(skb->h.raw + iph->ihl*4); struct sock *sk; - sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest); + sk = tcp_v4_proxy_lookup(th->dest, iph->saddr, th->source, iph->daddr, + 0, 0, skb->dev); if (!sk) return 0; /* 0 means accept all LOCAL addresses here, not all the world... */ @@ -2265,10 +2327,10 @@ retry_search: #ifdef CONFIG_IP_TRANSPARENT_PROXY if (skb->redirport) sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr, - dev->pa_addr, skb->redirport); + dev->pa_addr, skb->redirport, dev); else #endif - sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest); + sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest, dev); if (!sk) goto no_tcp_socket; skb->sk = sk; @@ -2484,6 +2546,13 @@ retry_search: */ tcp_ack(sk,th,skb->ack_seq,len); + /* We must check here (before tcp_options) whether + * peer advertised a zero sized window on us, else + * we end up with a zero sk->{mtu,mss} and thus bomb + * out in tcp_do_sendmsg. -DaveM + */ + if(sk->max_window == 0) + sk->max_window = 32; /* * Ok.. it's good. Set up sequence numbers and @@ -2507,11 +2576,7 @@ retry_search: sk->state_change(sk); sock_wake_async(sk->socket, 0); } - if(sk->max_window==0) - { - sk->max_window = 32; - sk->mss = min(sk->max_window, sk->mtu); - } + /* Reset the RTT estimator to the initial * state rather than testing to avoid * updating it on the ACK to the SYN packet. @@ -2578,7 +2643,7 @@ retry_search: sk->shutdown = SHUTDOWN_MASK; #ifdef CONFIG_IP_TRANSPARENT_PROXY sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr, - dev->pa_addr, skb->redirport); + dev->pa_addr, skb->redirport, dev); #else sk = NULL; #endif @@ -2678,6 +2743,19 @@ rfc_step6: /* I'll clean this up later */ if(tcp_data(skb,sk, saddr, len)) kfree_skb(skb, FREE_READ); + /* + * If we had a partial packet being help up due to + * application of Nagle's rule we are now free to send it. + */ + if (th->ack + && sk->packets_out == 0 + && sk->partial != NULL + && skb_queue_empty(&sk->write_queue) + && sk->send_head == NULL) + { + tcp_send_partial(sk); + } + /* * If our receive queue has grown past its limits, * try to prune away duplicates etc.. diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f278494a9549..92df9b6c3f8f 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -32,6 +32,8 @@ * thus the outgoing device does as well, when * skb's are on the retransmit queue which still * refer to the old obsolete destination. + * Elliot Poger : Added support for SO_BINDTODEVICE. + * Juan Jose Ciarlante : Added sock dynamic source address rewriting */ #include @@ -50,22 +52,10 @@ * RECV.NEXT + RCV.WIN fixed until: * RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)" * - * Experiments against BSD and Solaris machines show that following - * these rules results in the BSD and Solaris machines making very - * bad guesses about how much data they can have in flight. - * - * Instead we follow the BSD lead and offer a window that gives - * the size of the current free space, truncated to a multiple - * of 1024 bytes. If the window is smaller than - * min(sk->mss, MAX_WINDOW/2) - * then we advertise the window as having size 0, unless this - * would shrink the window we offered last time. - * This results in as much as double the throughput as the original - * implementation. - * * We do BSD style SWS avoidance -- note that RFC1122 only says we * must do silly window avoidance, it does not require that we use - * the suggested algorithm. + * the suggested algorithm. Following BSD avoids breaking header + * prediction. * * The "rcvbuf" and "rmem_alloc" values are shifted by 1, because * they also contain buffer handling overhead etc, so the window @@ -73,33 +63,41 @@ */ int tcp_new_window(struct sock * sk) { - unsigned long window; + unsigned long window = sk->window; unsigned long minwin, maxwin; + unsigned long free_space; /* Get minimum and maximum window values.. */ minwin = sk->mss; if (!minwin) minwin = sk->mtu; + if (!minwin) { + printk(KERN_DEBUG "tcp_new_window: mss fell to 0.\n"); + minwin = 1; + } maxwin = sk->window_clamp; if (!maxwin) maxwin = MAX_WINDOW; + if (minwin > maxwin/2) minwin = maxwin/2; /* Get current rcvbuf size.. */ - window = sk->rcvbuf/2; - if (window < minwin) { + free_space = sk->rcvbuf/2; + if (free_space < minwin) { sk->rcvbuf = minwin*2; - window = minwin; + free_space = minwin; } /* Check rcvbuf against used and minimum window */ - window -= sk->rmem_alloc/2; - if ((long)(window - minwin) < 0) /* SWS avoidance */ - window = 0; + free_space -= sk->rmem_alloc/2; + if ((long)(free_space - minwin) < 0) /* SWS avoidance */ + return 0; + + /* Try to avoid the divide and multiply if we can */ + if (window <= free_space - minwin || window > free_space) + window = (free_space/minwin)*minwin; - if (window > 1023) - window &= ~1023; if (window > maxwin) window = maxwin; return window; @@ -166,8 +164,10 @@ void tcp_send_skb(struct sock *sk, struct sk_buff *skb) * anything for a long time, in which case we have no reason to * believe that our congestion window is still correct. */ - if (sk->send_head == 0 && (jiffies - sk->idletime) > sk->rto) + if (sk->send_head == 0 && (jiffies - sk->idletime) > sk->rto) { sk->cong_window = 1; + sk->cong_count = 0; + } /* * Actual processing. @@ -491,7 +491,8 @@ void tcp_do_retransmit(struct sock *sk, int all) /* ANK: UGLY, but the bug, that was here, should be fixed. */ struct options * opt = (struct options*)skb->proto_priv; - rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, skb->localroute); + rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, + skb->localroute, sk->bound_device); } iph->id = htons(ip_id_count++); @@ -534,6 +535,8 @@ void tcp_do_retransmit(struct sock *sk, int all) struct sk_buff *skb2 = sk->write_queue.next; while (skb2 && skb2->dev == skb->dev) { skb2->raddr=rt->rt_gateway; + if (sk->state == TCP_SYN_SENT && sysctl_ip_dynaddr) + ip_rewrite_addrs (sk, skb2, dev); skb2->dev = dev; skb2->arp=1; if (rt->rt_hh) @@ -557,6 +560,8 @@ void tcp_do_retransmit(struct sock *sk, int all) } } skb->raddr=rt->rt_gateway; + if (skb->dev !=dev && sk->state == TCP_SYN_SENT && sysctl_ip_dynaddr) + ip_rewrite_addrs(sk, skb, dev); skb->dev=dev; skb->arp=1; #ifdef CONFIG_FIREWALL @@ -987,8 +992,25 @@ void tcp_send_synack(struct sock * newsk, struct sock * sk, struct sk_buff * skb ptr[2] = ((newsk->mtu) >> 8) & 0xff; ptr[3] =(newsk->mtu) & 0xff; buff->csum = csum_partial(ptr, 4, 0); +#ifdef CONFIG_SYN_COOKIES + /* Don't save buff on the newsk chain if we are going to destroy + * newsk anyway in a second, it just delays getting rid of newsk. + */ + if (destroy) { + /* BUFF was charged to NEWSK, _this_ is what we want + * to undo so the SYN cookie can be killed now. SKB + * is charged to SK, below we will undo that when + * we kfree SKB. + */ + buff->sk = NULL; + atomic_sub(buff->truesize, &newsk->wmem_alloc); + } +#endif tcp_send_check(t1, newsk->saddr, newsk->daddr, sizeof(*t1)+4, buff); - newsk->prot->queue_xmit(newsk, ndev, buff, destroy); + if (destroy) + newsk->prot->queue_xmit(NULL, ndev, buff, 1); + else + newsk->prot->queue_xmit(newsk, ndev, buff, 0); #ifdef CONFIG_SYN_COOKIES @@ -1389,7 +1411,9 @@ void tcp_shrink_skb(struct sock *sk, struct sk_buff *skb, u32 ack) /* Update our starting seq number */ skb->seq = ack; th->seq = htonl(ack); + iph->tot_len = htons(ntohs(iph->tot_len)-diff); + ip_send_check(iph); /* Get the partial checksum for the IP options */ if (th->doff*4 - sizeof(*th) > 0) diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 4e9c6d2b5baf..72a03fa98ebc 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -138,7 +138,9 @@ void tcp_retransmit(struct sock *sk, int all) return; } - sk->ssthresh = sk->cong_window >> 1; /* remember window where we lost */ + /* remember window where we lost */ + sk->ssthresh = min(sk->cong_window, + (sk->window_seq-sk->rcv_ack_seq)/max(sk->mss,1)) >> 1; /* sk->ssthresh in theory can be zero. I guess that's OK */ sk->cong_count = 0; sk->cong_window = 1; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index e58d9342f0d8..dc20471d8705 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -52,6 +52,7 @@ * David S. Miller : New socket lookup architecture for ISS. * Last socket cache retained as it * does have a high hit rate. + * Elliot Poger : Added support for SO_BINDTODEVICE. * * * This program is free software; you can redistribute it and/or @@ -130,6 +131,11 @@ static int udp_v4_verify_bind(struct sock *sk, unsigned short snum) if((sk2->num == snum) && (sk2 != sk)) { int sk2_reuse = sk2->reuse; + /* Two sockets can be bound to the same port if they're + * bound to different interfaces... */ + if (sk->bound_device != sk2->bound_device) + continue; + if(!sk2->rcv_saddr || !sk->rcv_saddr) { if((!sk2_reuse) || (!sk_reuse)) { retval = 1; @@ -147,7 +153,7 @@ static int udp_v4_verify_bind(struct sock *sk, unsigned short snum) return retval; } -static inline int udp_lport_inuse(int num) +static inline int udp_lport_inuse(u16 num) { struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)]; @@ -161,36 +167,42 @@ static inline int udp_lport_inuse(int num) /* Shared by v4/v6 tcp. */ unsigned short udp_good_socknum(void) { - static int start = 0; - unsigned short base; - int i, best = 0, size = 32767; /* a big num. */ int result; - - base = PROT_SOCK + (start & 1023) + 1; + static int start = 0; + int i, best, best_size_so_far; SOCKHASH_LOCK(); - for(i = 0; i < UDP_HTABLE_SIZE; i++) { - struct sock *sk = udp_hash[i]; - if(!sk) { - start = (i + 1 + start) & 1023; - result = i + base + 1; + + /* Select initial not-so-random "best" */ + best = PROT_SOCK + 1 + (start & 1023); + best_size_so_far = 32767; /* "big" num */ + result = best; + for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { + struct sock *sk; + int size; + + sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)]; + + /* No clashes - take it */ + if (!sk) goto out; - } else { - int j = 0; - do { - if(++j >= size) - goto next; - } while((sk = sk->next)); - best = i; - size = j; - } - next: + + /* Is this one better than our best so far? */ + size = 0; + do { + if(++size >= best_size_so_far) + goto next; + } while((sk = sk->next) != NULL); + best_size_so_far = size; + best = result; +next: } - while(udp_lport_inuse(base + best + 1)) + while (udp_lport_inuse(best)) best += UDP_HTABLE_SIZE; - result = (best + base + 1); + result = best; out: + start = result; SOCKHASH_UNLOCK(); return result; } @@ -255,7 +267,8 @@ static void udp_v4_rehash(struct sock *sk) /* UDP is nearly always wildcards out the wazoo, it makes no sense to try * harder than this. -DaveM */ -__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport) +__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, + struct device *dev) { struct sock *sk, *result = NULL; unsigned short hnum = ntohs(dport); @@ -279,7 +292,15 @@ __inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport continue; score++; } - if(score == 3) { + /* If this socket is bound to a particular interface, + * did the packet come in on it? */ + if (sk->bound_device) { + if (dev == sk->bound_device) + score++; + else + continue; /* mismatch--not this sock */ + } + if(score == 4) { result = sk; break; } else if(score > badness) { @@ -308,7 +329,8 @@ __inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr, unsigned short rnum, unsigned long laddr, - unsigned long paddr, unsigned short pnum) + unsigned long paddr, unsigned short pnum, + struct device *dev) { struct sock *s, *result = NULL; int badness = -1; @@ -340,7 +362,14 @@ struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr, continue; score++; } - if(score == 3 && s->num == hnum) { + /* If this socket is bound to a particular interface, + * did the packet come in on it? */ + if(s->bound_device) { + if (s->bound_device != dev) + continue; + score++; + } + if(score == 4 && s->num == hnum) { result = s; break; } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) { @@ -363,7 +392,8 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk, unsigned short num, unsigned long raddr, unsigned short rnum, - unsigned long laddr) + unsigned long laddr, + struct device *dev) { struct sock *s = sk; unsigned short hnum = ntohs(num); @@ -372,6 +402,7 @@ static inline struct sock *udp_v4_mcast_next(struct sock *sk, (s->dead && (s->state == TCP_CLOSE)) || (s->daddr && s->daddr!=raddr) || (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) || + ((s->bound_device) && (s->bound_device!=dev)) || (s->rcv_saddr && s->rcv_saddr != laddr)) continue; break; @@ -408,7 +439,7 @@ void udp_err(int type, int code, unsigned char *header, __u32 daddr, uh = (struct udphdr *)header; - sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source); + sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source, NULL); if (sk == NULL) return; /* No socket for error */ @@ -850,7 +881,7 @@ int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len) if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST) return -EACCES; /* Must turn broadcast on first */ - rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute); + rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute, sk->bound_device); if (rt==NULL) return -ENETUNREACH; if(!sk->saddr) @@ -869,8 +900,9 @@ static void udp_close(struct sock *sk, unsigned long timeout) { lock_sock(sk); sk->state = TCP_CLOSE; - release_sock(sk); sk->dead = 1; + release_sock(sk); + udp_v4_unhash(sk); destroy_sock(sk); } @@ -918,7 +950,8 @@ int udp_chkaddr(struct sk_buff *skb) struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4); struct sock *sk; - sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest); + sk = udp_v4_proxy_lookup(uh->dest, iph->saddr, uh->source, iph->daddr, + 0, 0, skb->dev); if (!sk) return 0; /* 0 means accept all LOCAL addresses here, not all the world... */ @@ -940,7 +973,7 @@ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh, SOCKHASH_LOCK(); sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)]; - sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr); + sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, skb->dev); if(sk) { struct sock *sknext = NULL; @@ -948,7 +981,7 @@ static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh, struct sk_buff *skb1 = skb; sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr, - uh->source, daddr); + uh->source, daddr, skb->dev); if(sknext) skb1 = skb_clone(skb, GFP_ATOMIC); @@ -1066,10 +1099,10 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, #ifdef CONFIG_IP_TRANSPARENT_PROXY if(skb->redirport) sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source, - daddr, dev->pa_addr, skb->redirport); + daddr, dev->pa_addr, skb->redirport, dev); else #endif - sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest); + sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, dev); if (sk == NULL) { diff --git a/net/netsyms.c b/net/netsyms.c index ea42146d7487..e79546f968ca 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -104,6 +104,7 @@ static struct symbol_table net_syms = { X(ip_options_compile), X(ip_rt_put), X(arp_send), + X(arp_bind_cache), X(ip_id_count), X(ip_send_check), X(ip_forward), -- 2.39.5