]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.2.14pre4 2.2.14pre4
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:20:12 +0000 (15:20 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:20:12 +0000 (15:20 -0500)
o Signal race fix (Dave Miller)
o NFS root fix (Peter Braam)
o NFS root fix fix (Trond)
o COMX drivers (Gergely Madarasz)
o Wireless updates (Jean Tourrilhes)
o Netdev list has moved (Ralf Baechle)
o SMP and other fixes for scsi generic (Douglas Gilbert)
o Fix IP masquerade (Joseph Gooch)
o Bridge printk levels (Stuart Lynne)
o IPFW fix rule number logging, improve locking (Rusty)
o Fix bug in fd array expansion (Savochkin Andrey Vladimirovich)
o Make FPU emulator safe with newer compilers (Bill Metzenthen)
o SiS900 driver update (Ollie Lho)
o Documentation fix (Michael Babcock)
o EATA scsi update (Dario Ballabio)

57 files changed:
Documentation/Configure.help
Documentation/networking/comx.txt [new file with mode: 0644]
Documentation/sound/Introduction
Documentation/sound/PSS [new file with mode: 0644]
MAINTAINERS
Makefile
arch/i386/math-emu/README
arch/i386/math-emu/fpu_trig.c
arch/i386/math-emu/poly.h
arch/i386/math-emu/poly_sin.c
arch/i386/math-emu/poly_tan.c
arch/i386/math-emu/reg_round.S
drivers/char/Config.in
drivers/char/Makefile
drivers/char/mixcomwd.c [new file with mode: 0644]
drivers/net/Config.in
drivers/net/Makefile
drivers/net/comx-hw-comx.c [new file with mode: 0644]
drivers/net/comx-hw-locomx.c [new file with mode: 0644]
drivers/net/comx-hw-mixcom.c [new file with mode: 0644]
drivers/net/comx-proto-fr.c [new file with mode: 0644]
drivers/net/comx-proto-lapb.c [new file with mode: 0644]
drivers/net/comx-proto-ppp.c [new file with mode: 0644]
drivers/net/comx.c [new file with mode: 0644]
drivers/net/comx.h [new file with mode: 0644]
drivers/net/comxhw.h [new file with mode: 0644]
drivers/net/hscx.h [new file with mode: 0644]
drivers/net/mixcom.h [new file with mode: 0644]
drivers/net/sis900.c
drivers/net/sis900.h
drivers/net/syncppp.c
drivers/net/syncppp.h
drivers/net/wavelan.c
drivers/net/wavelan.p.h
drivers/net/z85230.c
drivers/net/z85230.h
drivers/scsi/eata.c
drivers/scsi/eata.h
drivers/scsi/sg.c
drivers/scsi/u14-34f.c
drivers/scsi/u14-34f.h
drivers/sound/maestro.c
fs/nfs/mount_clnt.c
fs/open.c
include/linux/i2c.h
include/linux/sched.h
include/linux/wireless.h
include/scsi/sg.h
kernel/exit.c
kernel/signal.c
net/bridge/br.c
net/core/dev.c
net/ipv4/ip_forward.c
net/ipv4/ip_fw.c
net/ipv4/ip_masq.c
net/ipv6/README
net/irda/irda_device.c

index 89d23240a240f9f556d5136f5e20917abd03d6e5..aca71364b07a3d120e710169cddc36050f65ad72 100644 (file)
@@ -3498,8 +3498,9 @@ CONFIG_PACKET
   to work, choose Y. This driver is also available as a module called
   af_packet.o ( = code which can be inserted in and removed from the
   running kernel whenever you want). If you want to compile it as a
-  module, say M here and read Documentation/modules.txt. If unsure,
-  say Y.
+  module, say M here and read Documentation/modules.txt.  You will
+  need to add 'alias net-pf-17 af_packet' to your /etc/conf.modules
+  file for the module version to function automatically.  If unsure, say Y.
 
 Kernel/User network link driver
 CONFIG_NETLINK
@@ -5574,6 +5575,77 @@ CONFIG_WANPIPE_PPP
   you say N, the PPP support will not be included in the driver (saves
   about 16 KB of kernel memory).
 
+MultiGate/COMX support
+CONFIG_COMX
+  Say Y if you want to use any board from the MultiGate (COMX) family. 
+  These boards are synchronous serial adapters for the PC, manufactured 
+  by ITConsult-Pro Co, Hungary. 
+
+  Read linux/Documentation/networking/comx.txt  for help on configuring 
+  and using COMX interfaces. Further info on these cards can be found at 
+  http://www.itc.hu or <info@itc.hu>.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx.o.
+
+COMX/CMX/HiCOMX board support
+CONFIG_COMX_HW_COMX
+  Hardware driver for the 'CMX', 'COMX' and 'HiCOMX' boards from the
+  MultiGate family. Say Y if you have one of these. 
+
+  You will need additional firmware to use these cards, which are
+  downloadable from ftp://ftp.itc.hu/.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-comx.o.
+
+LoCOMX board support
+CONFIG_COMX_HW_LOCOMX
+  Hardware driver for the 'LoCOMX' board from the MultiGate family. Say Y
+  if you have a board like this. 
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-locomx.o.
+
+MixCOM board support
+CONFIG_COMX_HW_MIXCOM
+  Hardware driver for the 'MixCOM' board from the MultiGate family. Say Y
+  if you have a board like this.
+
+  If you want to use the watchdog device on this card, you should
+  select it in the Watchdog Cards section of the Character Devices
+  configuration. The ISDN interface of this card is Teles 16.3 compatible,
+  you should enable it in the ISDN configuration menu. The driver for the 
+  flash ROM of this card is available separately on ftp://ftp.itc.hu/.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-hw-mixcom.o.
+
+MultiGate Cisco-HDLC and synchronous PPP protocol support
+CONFIG_COMX_PROTO_PPP
+  Cisco-HDLC and synchronous PPP protocol driver for all MultiGate boards. 
+  Say Y if you want to use either protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called 
+  comx-proto-ppp.o.
+
+MultiGate LAPB protocol support
+CONFIG_COMX_PROTO_LAPB
+  LAPB protocol driver for all MultiGate boards. Say Y if you 
+  want to use this protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-proto-lapb.o.
+
+MultiGate Frame Relay protocol support
+CONFIG_COMX_PROTO_FR
+  Frame Relay protocol driver for all MultiGate boards. Say Y if you 
+  want to use this protocol on your MultiGate boards.
+
+  If you want to compile this as a module, say M and read
+  Documentation/modules.txt. The module will be called comx-proto-fr.o.
+
 Ethernet (10 or 100Mbit)
 CONFIG_NET_ETHERNET
   Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common
diff --git a/Documentation/networking/comx.txt b/Documentation/networking/comx.txt
new file mode 100644 (file)
index 0000000..a58e78d
--- /dev/null
@@ -0,0 +1,248 @@
+
+               COMX drivers for the 2.2 kernel
+
+Originally written by: Tivadar Szemethy, <tiv@itc.hu>
+Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+
+Last change: 21/06/1999.
+
+INTRODUCTION
+
+This document describes the software drivers and their use for the 
+COMX line of synchronous serial adapters for Linux version 2.2.0 and
+above.
+The cards are produced and sold by ITC-Pro Ltd. Budapest, Hungary
+For further info contact <info@itc.hu> 
+or http://www.itc.hu (mostly in Hungarian).
+The firmware files and software are available from ftp://ftp.itc.hu
+
+Currently, the drivers support the following cards and protocols:
+
+COMX (2x64 kbps intelligent board)
+CMX (1x256 + 1x128 kbps intelligent board)
+HiCOMX (2x2Mbps intelligent board)
+LoCOMX (1x512 kbps passive board)
+MixCOM (1x512 or 2x512kbps passive board with a hardware watchdog an
+       optional BRI interface and optional flashROM (1-32M))
+
+At the moment of writing this document, the (Cisco)-HDLC, LAPB, SyncPPP and
+Frame Relay (DTE, rfc1294 IP encapsulation with partially implemented Q933a 
+LMI) protocols are available as link-level protocol. 
+X.25 support is being worked on.
+
+USAGE
+
+Load the comx.o module and the hardware-specific and protocol-specific 
+modules you'll need into the running kernel using the insmod utility.
+This creates the /proc/comx directory.
+See the example scripts in the 'etc' directory.
+
+/proc INTERFACE INTRO
+
+The COMX driver set has a new type of user interface based on the /proc 
+filesystem which eliminates the need for external user-land software doing 
+IOCTL calls. 
+Each network interface or device (i.e. those ones you configure with 'ifconfig'
+and 'route' etc.) has a corresponding directory under /proc/comx. You can
+dynamically create a new interface by saying 'mkdir /proc/comx/comx0' (or you
+can name it whatever you want up to 8 characters long, comx[n] is just a 
+convention).
+Generally the files contained in these directories are text files, which can
+be viewed by 'cat filename' and you can write a string to such a file by
+saying 'echo _string_ >filename'. This is very similar to the sysctl interface.
+Don't use a text editor to edit these files, always use 'echo' (or 'cat'
+where appropriate).
+When you've created the comx[n] directory, two files are created automagically
+in it: 'boardtype' and 'protocol'. You have to fill in these files correctly
+for your board and protocol you intend to use (see the board and protocol 
+descriptions in this file below or the example scripts in the 'etc' directory).
+After filling in these files, other files will appear in the directory for 
+setting the various hardware- and protocol-related informations (for example
+irq and io addresses, keepalive values etc.) These files are set to default 
+values upon creation, so you don't necessarily have to change all of them.
+
+When you're ready with filling in the files in the comx[n] directory, you can
+configure the corresponding network interface with the standard network 
+configuration utilites. If you're unble to bring the interfaces up, look up
+the various kernel log files on your system, and consult the messages for
+a probable reason.
+
+EXAMPLE
+
+To create the interface 'comx0' which is the first channel of a COMX card:
+
+insmod comx 
+# insmod comx-hw-comx ; insmod comx-proto-hdlc  (these are usually
+autoloaded if you use the kernel module loader)
+
+mkdir /proc/comx/comx0
+echo comx >/proc/comx/comx0/boardtype
+echo 0x360 >/proc/comx/comx0/io                <- jumper-selectable I/O port 
+echo 0x0a >/proc/comx/comx0/irq                <- jumper-selectable IRQ line
+echo 0xd000 >/proc/comx/comx0/memaddr  <- software-configurable memory
+                                          address. COMX uses 64 KB, and this
+                                          can be: 0xa000, 0xb000, 0xc000, 
+                                          0xd000, 0xe000. Avoid conflicts
+                                          with other hardware.
+cat </etc/siol1.rom >/proc/comx/comx0/firmware <- the firmware for the card
+echo HDLC >/proc/comx/comx0/protocol   <- the data-link protocol
+echo 10 >/proc/comx/comx0/keepalive    <- the keepalive for the protocol
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255 <-
+                                          finally configure it with ifconfig
+Check its status:
+cat /proc/comx/comx0/status
+
+If you want to use the second channel of this board:
+
+mkdir /proc/comx/comx1
+echo comx >/proc/comx/comx1/boardtype
+echo 0x360 >/proc/comx/comx1/io        
+echo 10 >/proc/comx/comx1/irq          
+echo 0xd000 >/proc/comx/comx1/memaddr
+echo 1 >/proc/comx/comx1/channel       <- channels are numbered 
+                                          as 0 (default) and 1
+
+Now, check if the driver recognized that you're going to use the other
+channel of the same adapter:
+
+cat /proc/comx/comx0/twin
+comx1
+cat /proc/comx/comx1/twin
+comx0
+
+You don't have to load the firmware twice, if you use both channels of
+an adapter, just write it into the channel 0's /proc firmware file.
+
+Default values: io 0x360 for COMX, 0x320 (HICOMX), irq 10, memaddr 0xd0000
+
+THE LOCOMX HARDWARE DRIVER
+
+The LoCOMX driver doesn't require firmware, and it doesn't use memory either,
+but it uses DMA channels 1 and 3. You can set the clock rate (if enabled by
+jumpers on the board) by writing the kbps value into the file named 'clock'.
+Set it to 'external' (it is the default) if you have external clock source.
+
+(Note: currently the LoCOMX driver does not support the internal clock)
+
+THE COMX, CMX AND HICOMX DRIVERS
+
+On the HICOMX, COMX and CMX, you have to load the firmware (it is different for
+the three cards!). All these adapters can share the same memory
+address (we usually use 0xd0000). On the CMX you can set the internal
+clock rate (if enabled by jumpers on the small adapter boards) by writing
+the kbps value into the 'clock' file. You have to do this before initializing
+the card. If you use both HICOMX and CMX/COMX cards, initialize the HICOMX
+first. The I/O address of the HICOMX board is not configurable by any
+method available to the user: it is hardwired to 0x320, and if you have to 
+change it, consult ITC-Pro Ltd.
+
+THE MIXCOM DRIVER
+
+The MixCOM board doesn't require firmware, the driver communicates with
+it through I/O ports. You can have three of these cards in one machine.
+
+THE HDLC LINE PROTOCOL DRIVER
+
+There's only one configurable parameter with this protocol: the 'keepalive'
+value. You can set this in seconds or set to 'off'. Agree with the administrator
+of your peer router on this setting. The default is 10 (seconds).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo hdlc >/proc/comx/comx0/protocol
+echo 10 >/proc/comx/comx0/keepalive    <- not necessary, 10 is the default
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE PPP LINE PROTOCOL DRIVER
+
+To use this driver, you have to have ppp-2.3.4, and have a modified version of
+pppd (this pppd will work as async pppd to, the modifiactions merely relax 
+some restricions in order to be able to use non-async lines too.
+If configured, this driver can use Van Jacobson TCP header compression (you'll
+need the slhc.o module for this).
+Additionaly to use this protocol, enable async ppp in your kernel config, and
+create the comx device special files in /dev. They're character special files
+with major 88, and their names must be the same as their network interface
+counterparts (i.e /dev/comx0 with minor 0 corresponds interface comx0 and so
+on).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo ppp >/proc/comx/comx0/protocol
+ifconfig comx0 up
+pppd comx0 1.2.3.4:5.6.7.8 persist     <- with this option pppd won't exit
+                                          when the line goes down
+
+THE LAPB LINE PROTOCOL DRIVER
+
+For this, you'll need to configure LAPB support (See 'LAPB Data Link Driver' in
+'Network options' section) into your kernel (thanks to Jonathan Naylor for his 
+excellent implementation). 
+comxlapb.o provides the following files in the appropriate directory
+(the default values in parens): t1 (5), t2 (1), n2 (20), mode (DTE, STD) and
+window (7). Agree with the administrator of your peer router on these
+settings (most people use defaults, but you have to know if you are DTE or
+DCE).
+
+EXAMPLE
+
+(setting up hw parameters, see above)
+echo lapb >/proc/comx/comx0/protocol
+echo dce >/proc/comx/comx0/mode                <- DCE interface in this example
+ifconfig comx0 1.2.3.4 pointopoint 5.6.7.8 netmask 255.255.255.255
+
+
+THE FRAME RELAY PROTOCOL DRIVER
+
+You DON'T need any other frame relay related modules from the kernel to use
+COMX-Frame Relay. This protocol is a bit more complicated than the others, 
+because it allows to use 'subinterfaces' or DLCIs within one physical device.
+First you have to create the 'master' device (the actual physical interface)
+as you would do for other protocols. Specify 'frad' as protocol type.
+Now you can bring this interface up by saying 'ifconfig comx0 up' (or whatever
+you've named the interface). Do not assign any IP address to this interface
+and do not set any routes through it.
+Then, set up your DLCIs the following way: create a comx interface for each
+DLCI you intend to use (with mkdir), and write 'dlci' to the 'boardtype' file, 
+and 'ietf-ip' to the 'protocol' file. Currently, the only supported 
+encapsulation type is this (also called as RFC1294/1490 IP encapsulation).
+Write the DLCI number to the 'dlci' file, and write the name of the physical
+COMX device to the file called 'master'. 
+Now you can assign an IP address to this interface and set routes using it.
+See the example file for further info and example config script.
+Notes: this driver implements a DTE interface with partially implemented
+Q933a LMI.
+You can find an extensively commented example in the 'etc' directory.
+
+FURTHER /proc FILES
+
+boardtype:
+Type of the hardware. Valid values are:
+ 'comx', 'hicomx', 'locomx', 'cmx'.
+
+protocol:
+Data-link protocol on this channel. Can be: HDLC, LAPB, PPP, FRAD
+
+status:
+You can read the channel's actual status from the 'status' file, for example
+'cat /proc/comx/comx3/status'.
+
+lineup_delay:
+Interpreted in seconds (default is 1). Used to avoid line jitter: the system
+will consider the line status 'UP' only if it is up for at least this number
+of seconds.
+
+debug: 
+You can set various debug options through this file. Valid options are:
+'comx_events', 'comx_tx', 'comx_rx', 'hw_events', 'hw_tx', 'hw_rx'.
+You can enable a debug options by writing its name prepended by a '+' into
+the debug file, for example 'echo +comx_rx >comx0/debug'. 
+Disabling an option happens similarly, use the '-' prefix 
+(e.g. 'echo -hw_rx >debug').
+Debug results can be read from the debug file, for example: 
+tail -f /proc/comx/comx2/debug
+
+
index e0ad92630166b6f035a8be9b11c0c96fbf540e0e..9924c309499c88ddf8dbb7c9b7e50da29fdb6d45 100644 (file)
@@ -25,7 +25,8 @@ History:
                    added info on multiple sound cards of similar types,]
                    added more diagnostics info, added info about esd.
                    added info on OSS and ALSA.
-
+1.1.1  19991031           Added notes on sound-slot- and sound-service.
+                       (Alan Cox)
 
 Modular Sound Drivers:
 ======================
@@ -321,6 +322,12 @@ in the Sound-HOWTO).
 
 7)  Turn on debug in drivers/sound/sound_config.h (DEB, DDB, MDB).
 
+8)  If the system reports insuffcient DMA memory then you may want to
+    load sound with the "dmabufs=1" option. Or in /etc/conf.modules add
+       
+       preinstall sound dmabufs=1
+
+    This makes the sound system allocate its buffers and hang onto them.
 
 Configuring Sound:
 ==================
@@ -335,7 +342,7 @@ There are several ways of configuring your sound:
 
 3)  In /etc/conf.modules when using modprobe.
 
-4)  Via Red Hat's /usr/sbin/sndconfig program (text based).
+4)  Via Red Hat's GPL'd /usr/sbin/sndconfig program (text based).
 
 5)  Via the OSS soundconf program (with the commercial version
     of the OSS driver.
@@ -344,6 +351,28 @@ And I am sure, several other ways.
 
 Anyone want to write a linuxconf module for configuring sound?
 
+Module Loading:
+===============
+
+When a sound card is first referenced and sound is modular the sound system
+will ask for the sound devices to be loaded. Initially it requests that
+the driver for the sound system is loaded. It then wwill ask for 
+sound-slot-0, where 0 is the first sound card. (sound-slot-1 the second and
+so on). Thus you can do
+
+alias sound-slot-0 sb
+
+To load a soundblaster at this point. If the slot loading does not provide
+the desired device - for example a soundblaster does not directly provide
+a midi synth in all cases then it will request "sound-service-0-n" where n
+is
+
+0      Mixer
+
+2      MIDI
+
+3, 4   DSP audio
+
 
 For More Information (RTFM):
 ============================
@@ -373,6 +402,3 @@ For More Information (RTFM):
 Contact Information:
 ====================
 Wade Hampton:  (whampton@staffnet.com)
-
-
-
diff --git a/Documentation/sound/PSS b/Documentation/sound/PSS
new file mode 100644 (file)
index 0000000..ee81f73
--- /dev/null
@@ -0,0 +1,41 @@
+The PSS cards and other ECHO based cards provide an onboard DSP with 
+downloadable programs and also has an AD1848 "Microsoft Sound System" 
+device. The PSS driver enables MSS and MPU401 modes of the card. SB 
+is not enabled since it doesn't work concurrently with MSS. 
+
+If you build this driver as a module then the driver takes the folowing
+parameters
+
+pss_io.        The I/O base the PSS card is configured at (normally 0x220
+               or 0x240)
+
+mss_io         The base address of the Microsoft Sound System interface.
+               This is normally 0x530, but may be 0x604 or other addresses.
+
+mss_irq                The interrupt assigned to the Microsoft Sound System
+               emulation. IRQ's 3,5,7,9,10,11 and 12 are available. If you
+               get IRQ errors be sure to check the interrupt is set to 
+               "ISA/Legacy" in the BIOS on modern machines.
+
+mss_dma                The DMA channel used by the Microsoft Sound System.
+               This can be 0, 1, or 3. DMA 0 is not available on older 
+               machines and will cause a crash on them.
+
+mpu_io         The MPU emulation base address. This sets the base of the
+               synthesizer. It is typically 0x330 but can be altered.
+
+mpu_irq                The interrupt to use for the synthesizer. It must differ
+               from the IRQ used by the Microsoft Sound System port.
+
+
+The mpu_io/mpu_irq fields are optional. If they are not specified the 
+synthesizer parts are not configured.
+
+When the module is loaded it looks for a file called 
+/etc/sound/pss_synth. This is the firmware file from the DOS install disks.
+This fil holds a general MIDI emulation. The file expected is called
+genmidi.ld on newer DOS driver install disks and synth.ld on older ones.
+
+You can also load alternative DSP algorithms into the card if you wish. One
+alternative driver can be found at http://www.mpg123.de/
+
index e712ce505a25ec1e26ad7453519ad1143da16d49..73040974fbfab1450ce433b383be2621e056a5e1 100644 (file)
@@ -204,6 +204,11 @@ M: kas@fi.muni.cz
 W:     http://www.fi.muni.cz/~kas/cosa/
 S:     Maintained
 
+COMX/MULTIGATE SYNC SERIAL DRIVERS
+P:     Gergely Madarasz
+M:     Gergely Madarasz <gorgo@itc.hu>
+S:     Supported
+
 CREDITS FILE
 P:     John A. Martin
 M:     jam@acm.org
@@ -234,7 +239,7 @@ P:  Steven Whitehouse
 M:     SteveW@ACM.org
 W:     http://www.sucs.swan.ac.uk/~rohan/
 W:     http://www-sigproc.eng.cam.ac.uk/~sjw44/
-L:     netdev@roxanne.nuclecu.unam.mx
+L:     netdev@oss.sgi.com
 S:     Maintained
 
 DEVICE NUMBER REGISTRY
@@ -574,7 +579,7 @@ S:  Maintained
 
 NETWORKING [GENERAL]
 P:     Networking Teak
-M:     netdev@nuclecu.unam.mx
+M:     netdev@oss.sgi.com
 L:     linux-net@vger.rutgers.edu
 W:     http://www.uk.linux.org/NetNews.html (2.0 only)
 S:     Maintained
@@ -586,7 +591,7 @@ P:  Andi Kleen
 M:     ak@muc.de
 P:     Alexey Kuznetsov
 M:     kuznet@ms2.inr.ac.ru
-L:     netdev@roxanne.nuclecu.unam.mx
+L:     netdev@oss.sgi.com
 S:     Maintained
 
 NFS CLIENT
index 9a8ade403dbc4db98b8614968fbac1a360065f93..bb3a1f244e868e1176311a486d81bf8a0f6a96ad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 2
 SUBLEVEL = 14
-EXTRAVERSION = pre3
+EXTRAVERSION = pre4
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
index 04434b618dad2694fa89d5ea12f67b71cd2e7e48..e6235491d6eb0b42b731ae2701868ae13532fc63 100644 (file)
@@ -1,9 +1,9 @@
  +---------------------------------------------------------------------------+
  |  wm-FPU-emu   an FPU emulator for 80386 and 80486SX microprocessors.      |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1995,1996,1997                               |
+ | Copyright (C) 1992,1993,1994,1995,1996,1997,1999                          |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail billm@suburbia.net               |
+ |                       Australia.  E-mail billm@melbpc.org.au              |
  |                                                                           |
  |    This program is free software; you can redistribute it and/or modify   |
  |    it under the terms of the GNU General Public License version 2 as      |
@@ -42,14 +42,15 @@ but is very close.  See "Limitations" later in this file for a list of
 some differences.
 
 Please report bugs, etc to me at:
-       billm@suburbia.net
+       billm@melbpc.org.au
+or     b.metzenthen@medoto.unimelb.edu.au
 
 For more information on the emulator and on floating point topics, see
 my web pages, currently at  http://www.suburbia.net/~billm/
 
 
 --Bill Metzenthen
-  December 1997
+  December 1999
 
 
 ----------------------- Internals of wm-FPU-emu -----------------------
@@ -98,7 +99,7 @@ form of re-entrancy which is required by the Linux kernel.
 ----------------------- Limitations of wm-FPU-emu -----------------------
 
 There are a number of differences between the current wm-FPU-emu
-(version 2.00) and the 80486 FPU (apart from bugs).  The differences
+(version 2.01) and the 80486 FPU (apart from bugs).  The differences
 are fewer than those which applied to the 1.xx series of the emulator.
 Some of the more important differences are listed below:
 
index 57a902162b2dd66bd4005731242337fe31785933..30b72910ed8c64cfb0667bba6a1c2671a39ab8f6 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Implementation of the FPU "transcendental" functions.                     |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1997                                         |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@suburbia.net             |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -85,7 +85,8 @@ static int trig_arg(FPU_REG *st0_ptr, int even)
          FPU_normalize(&tmp);
          tmptag =
            FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS,
-                     exponent16(&CONST_PI2extra) + exponent16(&tmp));
+                     exponent(&CONST_PI2extra) + exponent(&tmp));
+         setsign(&tmp, getsign(&CONST_PI2extra));
          st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION);
          if ( signnegative(st0_ptr) )
            {
@@ -117,7 +118,8 @@ static int trig_arg(FPU_REG *st0_ptr, int even)
          FPU_normalize(&tmp);         /* This must return TAG_Valid */
          tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION,
                             SIGN_POS,
-                            exponent16(&CONST_PI2extra) + exponent16(&tmp));
+                            exponent(&CONST_PI2extra) + exponent(&tmp));
+         setsign(&tmp, getsign(&CONST_PI2extra));
          st0_tag = FPU_sub(LOADED|(tmptag & 0x0f), (int)&tmp,
                            FULL_PRECISION);
          if ( (exponent(st0_ptr) == exponent(&CONST_PI2)) &&
@@ -827,18 +829,29 @@ static void rem_kernel(unsigned long long st0, unsigned long long *y,
                       unsigned long long st1,
                       unsigned long long q, int n)
 {
+  int dummy;
   unsigned long long x;
 
   x = st0 << n;
 
   /* Do the required multiplication and subtraction in the one operation */
-  asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
-                 movl %3,%%eax; mull %4; subl %%eax,%1;
-                 movl %2,%%eax; mull %5; subl %%eax,%1;"
-               :"=m" (x), "=m" (((unsigned *)&x)[1])
-               :"m" (st1),"m" (((unsigned *)&st1)[1]),
-                "m" (q),"m" (((unsigned *)&q)[1])
-               :"%ax","%dx");
+
+  /* lsw x -= lsw st1 * lsw q */
+  asm volatile ("mull %4; subl %%eax,%0; sbbl %%edx,%1"
+               :"=m" (((unsigned *)&x)[0]), "=m" (((unsigned *)&x)[1]),
+               "=a" (dummy)
+               :"2" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[0])
+               :"%dx");
+  /* msw x -= msw st1 * lsw q */
+  asm volatile ("mull %3; subl %%eax,%0"
+               :"=m" (((unsigned *)&x)[1]), "=a" (dummy)
+               :"1" (((unsigned *)&st1)[1]), "m" (((unsigned *)&q)[0])
+               :"%dx");
+  /* msw x -= lsw st1 * msw q */
+  asm volatile ("mull %3; subl %%eax,%0"
+               :"=m" (((unsigned *)&x)[1]), "=a" (dummy)
+               :"1" (((unsigned *)&st1)[0]), "m" (((unsigned *)&q)[1])
+               :"%dx");
 
   *y = x;
 }
index 37ddfa0ef2842497054c8611b505e8055c79b950..5cfce7bafc9bc04e254afa359c1bfdca7241f454 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  |  Header file for the FPU-emu poly*.c source files.                        |
  |                                                                           |
- | Copyright (C) 1994                                                        |
+ | Copyright (C) 1994,1999                                                   |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  | Declarations and definitions for functions operating on Xsig (12-byte     |
  | extended-significand) quantities.                                         |
@@ -55,15 +55,20 @@ asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest);
    actually be in-line.
    */
 
-/* Multiply two fixed-point 32 bit numbers. */
-extern inline void mul_32_32(const unsigned long arg1,
-                            const unsigned long arg2,
-                            unsigned long *out)
+/* Multiply two fixed-point 32 bit numbers, producing a 32 bit result.
+   The answer is the ms word of the product. */
+/* Some versions of gcc make it difficult to stop eax from being clobbered.
+   Merely specifying that it is used doesn't work...
+ */
+extern inline unsigned long mul_32_32(const unsigned long arg1,
+                                     const unsigned long arg2)
 {
-  asm volatile ("movl %1,%%eax; mull %2; movl %%edx,%0" \
-               :"=g" (*out) \
-               :"g" (arg1), "g" (arg2) \
-               :"ax","dx");
+  int retval;
+  asm volatile ("mull %2; movl %%edx,%%eax" \
+               :"=a" (retval) \
+               :"0" (arg1), "g" (arg2) \
+               :"dx");
+  return retval;
 }
 
 
index f03df4c00addcae05d5c13f4e75c1c9e19418e0c..a83b512062016231fd8151cd150aca43117d0680 100644 (file)
@@ -4,9 +4,9 @@
  |  Computation of an approximation of the sin function and the cosine       |
  |  function by a polynomial.                                                |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1997                                         |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
- |                  E-mail   billm@suburbia.net                              |
+ |                  E-mail   billm@melbpc.org.au                             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -132,6 +132,9 @@ void        poly_sine(FPU_REG *st0_ptr)
        }
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+      /* There is a special case which arises due to rounding, to fix here. */
+      if ( fixed_arg == 0xffffffffffffffffLL )
+       fixed_arg = 0;
 
       XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0;
       mul64_Xsig(&argSqrd, &fixed_arg);
@@ -172,10 +175,9 @@ void       poly_sine(FPU_REG *st0_ptr)
       if ( argSqrd.msw & 0xffc00000 )
        {
          /* Get about 32 bit precision in these: */
-         mul_32_32(0x898cc517, argSqrd.msw, &adj);
-         fix_up -= adj/6;
+         fix_up -= mul_32_32(0x898cc517, argSqrd.msw) / 6;
        }
-      mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up);
+      fix_up = mul_32_32(fix_up, LL_MSW(fixed_arg));
 
       adj = accumulator.lsw;    /* temp save */
       accumulator.lsw -= fix_up;
@@ -211,7 +213,6 @@ void        poly_cos(FPU_REG *st0_ptr)
   FPU_REG            result;
   long int            exponent, exp2, echange;
   Xsig                accumulator, argSqrd, fix_up, argTo4;
-  unsigned long       adj;
   unsigned long long  fixed_arg;
 
 #ifdef PARANOID
@@ -300,6 +301,9 @@ void        poly_cos(FPU_REG *st0_ptr)
        }
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       fixed_arg = 0x921fb54442d18469LL - fixed_arg;
+      /* There is a special case which arises due to rounding, to fix here. */
+      if ( fixed_arg == 0xffffffffffffffffLL )
+       fixed_arg = 0;
 
       exponent = -1;
       exp2 = -1;
@@ -363,10 +367,8 @@ void       poly_cos(FPU_REG *st0_ptr)
       if ( argSqrd.msw & 0xffc00000 )
        {
          /* Get about 32 bit precision in these: */
-         mul_32_32(0x898cc517, argSqrd.msw, &adj);
-         fix_up.msw -= adj/2;
-         mul_32_32(0x898cc517, argTo4.msw, &adj);
-         fix_up.msw += adj/24;
+         fix_up.msw -= mul_32_32(0x898cc517, argSqrd.msw) / 2;
+         fix_up.msw += mul_32_32(0x898cc517, argTo4.msw) / 24;
        }
 
       exp2 += norm_Xsig(&accumulator);
index 1743d6f0f423154a3f7a92896a442d0b64b94ff0..13a32b9fcfbb575dc2491f42338779ac257a9d53 100644 (file)
@@ -3,9 +3,9 @@
  |                                                                           |
  | Compute the tan of a FPU_REG, using a polynomial approximation.           |
  |                                                                           |
- | Copyright (C) 1992,1993,1994,1997                                         |
+ | Copyright (C) 1992,1993,1994,1997,1999                                    |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@suburbia.net             |
+ |                       Australia.  E-mail   billm@melbpc.org.au            |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -84,6 +84,14 @@ void poly_tan(FPU_REG *st0_ptr)
        }
       /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */
       XSIG_LL(accum) = 0x921fb54442d18469LL - XSIG_LL(accum);
+      /* This is a special case which arises due to rounding. */
+      if ( XSIG_LL(accum) == 0xffffffffffffffffLL )
+       {
+         FPU_settag0(TAG_Valid);
+         significand(st0_ptr) = 0x8a51e04daabda360LL;
+         setexponent16(st0_ptr, 0x41 + EXTENDED_Ebias | SIGN_Negative);
+         return;
+       }
 
       argSignif.lsw = accum.lsw;
       XSIG_LL(argSignif) = XSIG_LL(accum);
@@ -177,11 +185,11 @@ void      poly_tan(FPU_REG *st0_ptr)
       else if ( exponent > -30 )
        {
          adj = accum.msw >> -(exponent+1);      /* tan */
-         mul_32_32(adj, adj, &adj);           /* tan^2 */
+         adj = mul_32_32(adj, adj);             /* tan^2 */
        }
       else
        adj = 0;
-      mul_32_32(0x898cc517, adj, &adj);        /* delta * tan^2 */
+      adj = mul_32_32(0x898cc517, adj);          /* delta * tan^2 */
 
       fix_up.msw += adj;
       if ( !(fix_up.msw & 0x80000000) )   /* did fix_up overflow ? */
index 9ab32e99909aa217d2fa3ceb8ff44a9bfb9f3aa5..abfb8c4824e3032c017e779afc636f468b2330a9 100644 (file)
@@ -365,7 +365,7 @@ LRound_nearest_64:
        jne     LDo_64_round_up
 
        /* Now test for round-to-even */
-       testb   $1,%ebx
+       testb   $1,%bl
        jz      LCheck_truncate_64
 
 LDo_64_round_up:
index 3e56ff81ac49783ae566a40fa58507f4a8289676..084b170d732daf65ae11730b1055a4e2219a3a9e 100644 (file)
@@ -101,6 +101,7 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
   tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
   tristate '   Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
   tristate '   Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
+  tristate '   Mixcom Watchdog' CONFIG_MIXCOMWD 
   endmenu
 fi
 
index 3313a98e5812f1d813d3bcbaec324ec1955f5b20..8c706644326fcfeb227308497026ce1ac412f46e 100644 (file)
@@ -270,6 +270,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_MIXCOMWD),y)
+L_OBJS += mixcomwd.o
+else
+  ifeq ($(CONFIG_MIXCOMWD),m)
+  M_OBJS += mixcomwd.o
+  endif
+endif
+
 ifeq ($(CONFIG_AMIGAMOUSE),y)
 L_OBJS += amigamouse.o
 else
diff --git a/drivers/char/mixcomwd.c b/drivers/char/mixcomwd.c
new file mode 100644 (file)
index 0000000..e4bc1dc
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * MixCom Watchdog: A Simple Hardware Watchdog Device
+ * Based on Softdog driver by Alan Cox and PC Watchdog driver by Ken Hollis
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (c) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.1 (99/04/15):
+ *             - first version
+ *
+ * Version 0.2 (99/06/16):
+ *             - added kernel timer watchdog ping after close
+ *               since the hardware does not support watchdog shutdown
+ *
+ * Version 0.3 (99/06/21):
+ *             - added WDIOC_GETSTATUS and WDIOC_GETSUPPORT ioctl calls
+ *
+ * Version 0.3.1 (99/06/22):
+ *             - allow module removal while internal timer is active,
+ *               print warning about probable reset
+ *     
+ */
+
+#define VERSION "0.3.1" 
+  
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/ioport.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 };
+
+#define MIXCOM_WATCHDOG_OFFSET 0xc10
+#define MIXCOM_ID1 0x11
+#define MIXCOM_ID2 0x13
+
+static int mixcomwd_opened;
+static int mixcomwd_port;
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+static int mixcomwd_timer_alive;
+static struct timer_list mixcomwd_timer;
+#endif
+
+static void mixcomwd_ping(void)
+{
+       outb_p(55,mixcomwd_port+MIXCOM_WATCHDOG_OFFSET);
+       return;
+}
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+static void mixcomwd_timerfun(unsigned long d)
+{
+       mixcomwd_ping();
+       
+       mod_timer(&mixcomwd_timer,jiffies+ 5*HZ);
+}
+#endif
+
+/*
+ *     Allow only one person to hold it open
+ */
+static int mixcomwd_open(struct inode *inode, struct file *file)
+{
+       if(test_and_set_bit(0,&mixcomwd_opened)) {
+               return -EBUSY;
+       }
+       mixcomwd_ping();
+       
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+       if(mixcomwd_timer_alive) {
+               del_timer(&mixcomwd_timer);
+               mixcomwd_timer_alive=0;
+       } 
+#endif
+       MOD_INC_USE_COUNT;
+
+       return 0;
+}
+
+static int mixcomwd_release(struct inode *inode, struct file *file)
+{
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+       if(mixcomwd_timer_alive) {
+               printk(KERN_ERR "mixcomwd: release called while internal timer alive");
+               return -EBUSY;
+       }
+       init_timer(&mixcomwd_timer);
+       mixcomwd_timer.expires=jiffies + 5 * HZ;
+       mixcomwd_timer.function=mixcomwd_timerfun;
+       mixcomwd_timer.data=0;
+       mixcomwd_timer_alive=1;
+       add_timer(&mixcomwd_timer);
+#endif
+       MOD_DEC_USE_COUNT;
+
+       clear_bit(0,&mixcomwd_opened);
+       return 0;
+}
+
+
+static ssize_t mixcomwd_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+{
+       if (ppos != &file->f_pos) {
+               return -ESPIPE;
+       }
+
+       if(len)
+       {
+               mixcomwd_ping();
+               return 1;
+       }
+       return 0;
+}
+
+static int mixcomwd_ioctl(struct inode *inode, struct file *file,
+       unsigned int cmd, unsigned long arg)
+{
+       int status;
+        static struct watchdog_info ident = {
+               WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog"
+       };
+                                        
+       switch(cmd)
+       {
+               case WDIOC_GETSTATUS:
+                       status=mixcomwd_opened;
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+                       status|=mixcomwd_timer_alive;
+#endif
+                       if (copy_to_user((int *)arg, &status, sizeof(int))) {
+                               return -EFAULT;
+                       }
+                       break;
+               case WDIOC_GETSUPPORT:
+                       if (copy_to_user((struct watchdog_info *)arg, &ident, 
+                           sizeof(ident))) {
+                               return -EFAULT;
+                       }
+                       break;
+               case WDIOC_KEEPALIVE:
+                       mixcomwd_ping();
+                       break;
+               default:
+                       return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+static struct file_operations mixcomwd_fops=
+{
+       NULL,           /* Seek */
+       NULL,           /* Read */
+       mixcomwd_write, /* Write */
+       NULL,           /* Readdir */
+       NULL,           /* Select */
+       mixcomwd_ioctl, /* Ioctl */
+       NULL,           /* MMap */
+       mixcomwd_open,
+       NULL,           /* flush */
+       mixcomwd_release,
+       NULL,           
+       NULL            /* Fasync */
+};
+
+static struct miscdevice mixcomwd_miscdev=
+{
+       WATCHDOG_MINOR,
+       "watchdog",
+       &mixcomwd_fops
+};
+
+__initfunc(static int mixcomwd_checkcard(int port))
+{
+       int id;
+
+       if(check_region(port,1)) {
+               return 0;
+       }
+       
+       id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f;
+       if(id!=MIXCOM_ID1 && id!=MIXCOM_ID2) {
+               return 0;
+       }
+       return 1;
+}
+
+
+__initfunc(void mixcomwd_init(void))
+{
+       int i;
+       int found=0;
+
+       for (i = 0; mixcomwd_ioports[i] != 0; i++) {
+               if (mixcomwd_checkcard(mixcomwd_ioports[i])) {
+                       found = 1;
+                       mixcomwd_port = mixcomwd_ioports[i];
+                       break;
+               }
+       }
+
+       if (!found) {
+               printk("mixcomwd: No card detected, or port not available.\n");
+               return;
+       }
+
+       request_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1,"MixCOM watchdog");
+       
+       misc_register(&mixcomwd_miscdev);
+       printk("MixCOM watchdog driver v%s, MixCOM card at 0x%3x\n",VERSION,mixcomwd_port);
+}      
+
+#ifdef MODULE
+int init_module(void)
+{
+       mixcomwd_init();
+       return 0;
+}
+
+void cleanup_module(void)
+{
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+       if(mixcomwd_timer_alive) {
+               printk(KERN_WARNING "mixcomwd: I quit now, hardware will"
+                       " probably reboot!\n");
+               del_timer(&mixcomwd_timer);
+               mixcomwd_timer_alive=0;
+       }
+#endif
+       release_region(mixcomwd_port+MIXCOM_WATCHDOG_OFFSET,1);
+       misc_deregister(&mixcomwd_miscdev);
+}
+#endif
index 205bd758acb56bfa749b19db55835eaf3095129f..1196dc820a9286c9953133f2ce3bfc569108b2b7 100644 (file)
@@ -264,6 +264,24 @@ dep_tristate 'COSA/SRP sync serial boards support' CONFIG_COSA m
 #
 dep_tristate 'Sealevel Systems 4021 support' CONFIG_SEALEVEL_4021 m
 
+#
+# COMX drivers
+#
+tristate 'MultiGate (COMX) synchronous serial boards support' CONFIG_COMX
+if [ "$CONFIG_COMX" != "n" ]; then
+    dep_tristate '  Support for COMX/CMX/HiCOMX boards' CONFIG_COMX_HW_COMX $CONFIG_COMX
+    dep_tristate '  Support for LoCOMX board' CONFIG_COMX_HW_LOCOMX $CONFIG_COMX
+    dep_tristate '  Support for MixCOM board' CONFIG_COMX_HW_MIXCOM $CONFIG_COMX
+    dep_tristate '  Support for HDLC and syncPPP protocols on MultiGate boards' CONFIG_COMX_PROTO_PPP $CONFIG_COMX
+    if [ "$CONFIG_LAPB" = "y" ]; then
+        dep_tristate '  Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_COMX
+    fi
+    if [ "$CONFIG_LAPB" = "m" ]; then
+        dep_tristate '  Support for LAPB protocol on MultiGate boards' CONFIG_COMX_PROTO_LAPB $CONFIG_LAPB
+    fi
+    dep_tristate '  Support for Frame Relay on MultiGate boards' CONFIG_COMX_PROTO_FR $CONFIG_COMX
+fi
+
 tristate 'Frame relay DLCI support' CONFIG_DLCI
 if [ "$CONFIG_DLCI" != "n" ]; then
   int '  Max open DLCI' CONFIG_DLCI_COUNT 24
@@ -291,7 +309,6 @@ fi
 
 endmenu
 
-
 #
 # X.25 network drivers
 #
index c5853d2fce9973a548f0430e23a4b1ae618625a9..8d99b081bafc324ca86a4cf30854faaf35f12595 100644 (file)
@@ -836,14 +836,66 @@ else
   endif
 endif
 
-# If anything built-in uses syncppp, then build it into the kernel also.
-# If not, but a module uses it, build as a module.
+#
+# COMX drivers
+#
+ifeq ($(CONFIG_COMX),y)
+LX_OBJS += comx.o
+else 
+  ifeq ($(CONFIG_COMX),m)
+  MX_OBJS += comx.o
+  endif
+endif
 
-ifdef CONFIG_SYNCPPP_BUILTIN
-LX_OBJS += syncppp.o
-else
-  ifdef CONFIG_SYNCPPP_MODULE
-  MX_OBJS += syncppp.o
+ifeq ($(CONFIG_COMX_HW_COMX),y)
+L_OBJS += comx-hw-comx.o
+else 
+  ifeq ($(CONFIG_COMX_HW_COMX),m)
+  M_OBJS += comx-hw-comx.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_LOCOMX),y)
+L_OBJS += comx-hw-locomx.o
+CONFIG_85230_BUILTIN=y
+else 
+  ifeq ($(CONFIG_COMX_HW_LOCOMX),m)
+  M_OBJS += comx-hw-locomx.o
+  CONFIG_85230_MODULE=y
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_HW_MIXCOM),y)
+L_OBJS += comx-hw-mixcom.o
+else 
+  ifeq ($(CONFIG_COMX_HW_MIXCOM),m)
+  M_OBJS += comx-hw-mixcom.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_PPP),y)
+L_OBJS += comx-proto-ppp.o
+CONFIG_SYNCPPP_BUILTIN = y
+else 
+  ifeq ($(CONFIG_COMX_PROTO_PPP),m)
+  M_OBJS += comx-proto-ppp.o
+  CONFIG_SYNCPPP_MODULE = y
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_LAPB),y)
+L_OBJS += comx-proto-lapb.o
+else 
+  ifeq ($(CONFIG_COMX_PROTO_LAPB),m)
+  M_OBJS += comx-proto-lapb.o
+  endif
+endif
+
+ifeq ($(CONFIG_COMX_PROTO_FR),y)
+L_OBJS += comx-proto-fr.o
+else 
+  ifeq ($(CONFIG_COMX_PROTO_FR),m)
+  M_OBJS += comx-proto-fr.o
   endif
 endif
 
@@ -852,9 +904,22 @@ endif
 
 ifdef CONFIG_85230_BUILTIN
 LX_OBJS += z85230.o
+CONFIG_SYNCPPP_BUILTIN=y
 else
   ifdef CONFIG_85230_MODULE
   MX_OBJS += z85230.o
+  CONFIG_SYNCPPP_MODULE=y
+  endif
+endif
+
+# If anything built-in uses syncppp, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+
+ifdef CONFIG_SYNCPPP_BUILTIN
+LX_OBJS += syncppp.o
+else
+  ifdef CONFIG_SYNCPPP_MODULE
+  MX_OBJS += syncppp.o
   endif
 endif
 
diff --git a/drivers/net/comx-hw-comx.c b/drivers/net/comx-hw-comx.c
new file mode 100644 (file)
index 0000000..1e1bd6d
--- /dev/null
@@ -0,0 +1,1298 @@
+/*
+ * Hardware-level driver for the COMX and HICOMX cards
+ * for Linux kernel 2.2.X
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Rewritten by: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.80 (99/06/11):
+ *             - port back to kernel, add support builtin driver 
+ *             - cleaned up the source code a bit
+ *
+ * Version 0.81 (99/06/22):
+ *             - cleaned up the board load functions, no more long reset
+ *               timeouts
+ *             - lower modem lines on close
+ *             - some interrupt handling fixes
+ *
+ * Version 0.82 (99/08/24):
+ *             - fix multiple board support
+ * 
+ */
+
+#define VERSION "0.82"
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>, Tivadar Szemethy <tiv@itc.hu>, Arpad Bakay");
+MODULE_DESCRIPTION("Hardware-level driver for the COMX and HICOMX adapters\n");
+
+#define        COMX_readw(dev, offset) (readw(dev->mem_start + offset + \
+       (unsigned int)(((struct comx_privdata *)\
+       ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+       * COMX_CHANNEL_OFFSET))
+
+#define COMX_WRITE(dev, offset, value) (writew(value, dev->mem_start + offset \
+       + (unsigned int)(((struct comx_privdata *) \
+       ((struct comx_channel *)dev->priv)->HW_privdata)->channel) \
+       * COMX_CHANNEL_OFFSET))
+
+#define COMX_CMD(dev, cmd)     (COMX_WRITE(dev, OFF_A_L2_CMD, cmd))
+
+struct comx_firmware {
+       int     len;
+       unsigned char *data;
+};
+
+struct comx_privdata {
+       struct comx_firmware *firmware;
+       u16     clock;
+       char    channel;                // channel no.
+       int     memory_size;
+       short   io_extent;
+       u_long  histogram[5];
+};
+
+struct device *memory_used[(COMX_MEM_MAX - COMX_MEM_MIN) / 0x10000];
+extern struct comx_hardware hicomx_hw;
+extern struct comx_hardware comx_hw;
+extern struct comx_hardware cmx_hw;
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+static void COMX_board_on(struct device *dev)
+{
+       outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
+           COMX_ENABLE_BOARD_IT | COMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_board_off(struct device *dev)
+{
+       outb_p( (byte) (((dev->mem_start & 0xf0000) >> 16) | 
+          COMX_ENABLE_BOARD_IT), dev->base_addr);
+}
+
+static void HICOMX_board_on(struct device *dev)
+{
+       outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
+          HICOMX_ENABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void HICOMX_board_off(struct device *dev)
+{
+       outb_p( (byte) (((dev->mem_start & 0xf0000) >> 12) | 
+          HICOMX_DISABLE_BOARD_MEM), dev->base_addr);
+}
+
+static void COMX_set_clock(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+
+       COMX_WRITE(dev, OFF_A_L1_CLKINI, hw->clock);
+}
+
+static struct device *COMX_access_board(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct device *ret;
+       int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+       unsigned long flags;
+
+       save_flags(flags); cli();
+       if (memory_used[mempos] == dev) {
+               restore_flags(flags);
+               return dev;
+       }
+
+       if (ch->twin && memory_used[mempos] == ch->twin) {
+               memory_used[mempos] = dev;
+               ret = ch->twin;
+       } else {        
+               ret = memory_used[mempos];
+               if (ret) ch->HW_board_off(ret);
+               memory_used[mempos] = dev;
+               ch->HW_board_on(dev);
+       }
+       restore_flags(flags);
+       return ret;
+}
+
+static void COMX_release_board(struct device *dev, struct device *savep)
+{
+       unsigned long flags;
+       int mempos = (dev->mem_start - COMX_MEM_MIN) >> 16;
+       struct comx_channel *ch = dev->priv;
+
+       save_flags(flags); cli();
+       if (memory_used[mempos] == savep) {
+               restore_flags(flags);
+               return;
+       }
+
+       memory_used[mempos] = savep;
+       if (!ch->twin || ch->twin != savep) {
+               ch->HW_board_off(dev);
+               if (savep) ch->HW_board_on(savep);
+       }
+       restore_flags(flags);
+}
+
+static int COMX_txe(struct device *dev) 
+{
+       struct device *savep;
+       struct comx_channel *ch = dev->priv;
+       int rc = 0;
+
+       savep = ch->HW_access_board(dev);
+       if (COMX_readw(dev,OFF_A_L2_LINKUP) == LINKUP_READY) {
+               rc = COMX_readw(dev,OFF_A_L2_TxEMPTY);
+       }
+       ch->HW_release_board(dev,savep);
+       return rc;
+}
+
+static int COMX_send_packet(struct device *dev, struct sk_buff *skb)
+{
+       struct device *savep;
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       int ret = FRAME_DROPPED;
+
+       savep = ch->HW_access_board(dev);       
+
+       if (ch->debug_flags & DEBUG_HW_TX) {
+               comx_debug_bytes(dev, skb->data, skb->len,"COMX_send packet");
+       }
+
+       if (skb->len > COMX_MAX_TX_SIZE) {
+               dev_kfree_skb(skb);
+               return FRAME_DROPPED;
+       }
+
+       if ((ch->line_status & LINE_UP) && COMX_readw(dev, OFF_A_L2_TxEMPTY)) {
+               int lensave = skb->len;
+               int dest = COMX_readw(dev, OFF_A_L2_TxBUFP);
+               word *data = (word *)skb->data;
+
+               writew((unsigned short)skb->len, dev->mem_start + dest);
+               dest += 2;
+               while (skb->len > 1) {
+                       writew(*data++, dev->mem_start + dest);
+                       dest += 2; skb->len -= 2;
+               }
+               if (skb->len == 1) {
+                       writew(*((byte *)data), dev->mem_start + dest);
+               }
+               writew(0, dev->mem_start + (int)hw->channel * 
+                  COMX_CHANNEL_OFFSET + OFF_A_L2_TxEMPTY);
+               ch->stats.tx_packets++; 
+               ch->stats.tx_bytes += lensave; 
+               ret = FRAME_ACCEPTED;
+       } else {
+               ch->stats.tx_dropped++;
+               printk(KERN_INFO "%s: frame dropped\n",dev->name);
+       }
+       
+       ch->HW_release_board(dev, savep);
+       dev_kfree_skb(skb);
+       return ret;
+}
+
+static inline int comx_read_buffer(struct device *dev) 
+{
+       struct comx_channel *ch = dev->priv;
+       word rbuf_offs;
+       struct sk_buff *skb;
+       word len;
+       int i=0;
+       word *writeptr;
+
+       i = 0;
+       rbuf_offs = COMX_readw(dev, OFF_A_L2_RxBUFP);
+       len = readw(dev->mem_start + rbuf_offs);
+       if ((skb = dev_alloc_skb(len + 16)) == NULL) {
+               ch->stats.rx_dropped++;
+               COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+               return 0;
+       }
+       rbuf_offs += 2;
+       skb_reserve(skb, 16);
+       skb_put(skb, len);
+       skb->dev = dev;
+       writeptr = (word *)skb->data;
+       while (i < len) {
+               *writeptr++ = readw(dev->mem_start + rbuf_offs);
+               rbuf_offs += 2; 
+               i += 2;
+       }
+       COMX_WRITE(dev, OFF_A_L2_DAV, 0);
+       ch->stats.rx_packets++;
+       ch->stats.rx_bytes += len;
+       if (ch->debug_flags & DEBUG_HW_RX) {
+               comx_debug_skb(dev, skb, "COMX_interrupt receiving");
+       }
+       ch->LINE_rx(dev, skb);
+       return 1;
+}
+
+static inline char comx_line_change(struct device *dev, char linestat)
+{
+       struct comx_channel *ch=dev->priv;
+       char idle=1;
+       
+       
+       if (linestat & LINE_UP) { /* Vonal fol */
+               if (ch->lineup_delay) {
+                       if (!test_and_set_bit(0, &ch->lineup_pending)) {
+                               ch->lineup_timer.function = comx_lineup_func;
+                               ch->lineup_timer.data = (unsigned long)dev;
+                               ch->lineup_timer.expires = jiffies +
+                                       HZ*ch->lineup_delay;
+                               add_timer(&ch->lineup_timer);
+                               idle=0;
+                       }
+               } else {
+                       idle=0;
+                       ch->LINE_status(dev, ch->line_status |= LINE_UP);
+               }
+       } else { /* Vonal le */
+               idle=0;
+               if (test_and_clear_bit(0, &ch->lineup_pending)) {
+                       del_timer(&ch->lineup_timer);
+               } else {
+                       ch->line_status &= ~LINE_UP;
+                       if (ch->LINE_status) {
+                               ch->LINE_status(dev, ch->line_status);
+                       }
+               }
+       }
+       return idle;
+}
+
+
+
+static void COMX_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct device *dev = dev_id;
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       struct device *interrupted;
+       unsigned long jiffs;
+       char idle = 0;
+       int count = 0;
+       word tmp;
+
+       if (dev == NULL) {
+               printk(KERN_ERR "COMX_interrupt: irq %d for unknown device\n", irq);
+               return;
+       }
+
+       if (dev->interrupt) {
+               printk(KERN_ERR "%s: re-entering interrupt handler!\n", dev->name);
+               return;
+       }
+
+       printk("%s: entering interrupt handler\n", dev->name);
+
+       jiffs = jiffies;
+
+       dev->interrupt = 1;
+       interrupted = ch->HW_access_board(dev);
+
+       while (!idle && count < 5000) {
+               char channel = 0;
+               idle = 1;
+
+               while (channel < 2) {
+                       char linestat = 0;
+                       char buffers_emptied = 0;
+
+                       if (channel == 1) {
+                               if (ch->twin) {
+                                       dev->interrupt = 0;
+                                       dev = ch->twin;
+                                       dev->interrupt = 1;
+                                       ch = dev->priv;
+                                       hw = ch->HW_privdata;
+                               } else {
+                                       break;
+                               }
+                       } else {
+                               COMX_WRITE(dev, OFF_A_L1_REPENA, 
+                                   COMX_readw(dev, OFF_A_L1_REPENA) & 0xFF00);
+                       }
+                       channel++;
+
+                       if ((ch->init_status & (HW_OPEN | LINE_OPEN)) != 
+                          (HW_OPEN | LINE_OPEN)) {
+                               continue;
+                       }
+       
+                       /* Collect stats */
+                       tmp = COMX_readw(dev, OFF_A_L1_ABOREC);
+                       COMX_WRITE(dev, OFF_A_L1_ABOREC, 0);
+                       ch->stats.rx_missed_errors += (tmp >> 8) & 0xff;
+                       ch->stats.rx_over_errors += tmp & 0xff;
+                       tmp = COMX_readw(dev, OFF_A_L1_CRCREC);
+                       COMX_WRITE(dev, OFF_A_L1_CRCREC, 0);
+                       ch->stats.rx_crc_errors += (tmp >> 8) & 0xff;
+                       ch->stats.rx_missed_errors += tmp & 0xff;
+
+                       if ((ch->line_status & LINE_UP) && ch->LINE_rx) {
+                               while (COMX_readw(dev, OFF_A_L2_DAV)) {
+                                       idle=0;
+                                       buffers_emptied+=comx_read_buffer(dev);
+                               }
+                       }
+
+                       if (COMX_readw(dev, OFF_A_L2_TxEMPTY) && ch->LINE_tx) {
+                               ch->LINE_tx(dev);
+                       }
+
+                       if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+                               linestat &= ~LINE_UP;
+                       } else {
+                               linestat |= LINE_UP;
+                       }
+
+                       if ((linestat & LINE_UP) != (ch->line_status & LINE_UP)) {
+                               ch->stats.tx_carrier_errors++;
+                               idle &= comx_line_change(dev,linestat);
+                       }
+                               
+                       hw->histogram[(int)buffers_emptied]++;
+               }
+               count++;
+       }
+
+       if(count==5000) {
+               printk(KERN_WARNING "%s: interrupt stuck\n",dev->name);
+       }
+
+       ch->HW_release_board(dev, interrupted);
+       printk("%s: leaving interrupt handler\n", dev->name);
+       dev->interrupt = 0;
+}
+
+static int COMX_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       struct proc_dir_entry *procfile = ch->procdir->subdir;
+       unsigned long jiffs;
+       struct device *savep;
+
+       if (!dev->base_addr || !dev->irq || !dev->mem_start) {
+               return -ENODEV;
+       }
+
+       if (!ch->twin || 
+          (!(((struct comx_channel *)(ch->twin->priv))->init_status & HW_OPEN))) {
+               if (check_region(dev->base_addr, hw->io_extent)) {
+                       return -EAGAIN;
+               }
+               if (request_irq(dev->irq, COMX_interrupt, SA_INTERRUPT, dev->name, 
+                  (void *)dev)) {
+                       printk(KERN_ERR "comx-hw-comx: unable to obtain irq %d\n", dev->irq);
+                       return -EAGAIN;
+               }
+               ch->init_status |= IRQ_ALLOCATED;
+               request_region(dev->base_addr, hw->io_extent, dev->name);
+               if (!ch->HW_load_board || ch->HW_load_board(dev)) {
+                       release_region(dev->base_addr, hw->io_extent);
+                       free_irq(dev->irq, (void *)dev);
+                       ch->init_status &= ~IRQ_ALLOCATED;
+                       return -ENODEV;
+               }
+       }
+
+       savep = ch->HW_access_board(dev);
+       COMX_WRITE(dev, OFF_A_L2_LINKUP, 0);
+
+       if (ch->HW_set_clock) {
+               ch->HW_set_clock(dev);
+       }
+
+       COMX_CMD(dev, COMX_CMD_INIT); 
+       jiffs = jiffies;
+       while (COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiffs + HZ) {
+               schedule_timeout(1);
+       }
+       
+       ch->HW_release_board(dev, savep);
+       if (jiffies > jiffs + HZ) {
+               printk(KERN_ERR "%s: board timeout on INIT command\n", dev->name);
+               release_region(dev->base_addr, hw->io_extent);
+               free_irq(dev->irq, (void *)dev);
+               return -EIO;
+       }
+
+       savep = ch->HW_access_board(dev);
+
+       /* Ide kellene irni, hogy DTE vagy DCE ? */
+       COMX_CMD(dev, COMX_CMD_OPEN);
+
+       ch->init_status |= HW_OPEN;
+       
+       /* Ez eleg ciki, de ilyen a rendszer */
+       if (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) {
+               ch->line_status &= ~LINE_UP;
+       } else {
+               ch->line_status |= LINE_UP;
+       }
+       
+       if (ch->LINE_status) {
+               ch->LINE_status(dev, ch->line_status);
+       }
+
+       ch->HW_release_board(dev, savep);
+
+       for ( ; procfile ; procfile = procfile->next) {
+               if (strcmp(procfile->name, FILENAME_IRQ) == 0 
+                   || strcmp(procfile->name, FILENAME_IO) == 0
+                   || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+                   || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+                   || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+                   || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+                       procfile->mode = S_IFREG | 0444;
+               
+               }
+       }       
+       
+       return 0;       
+}
+
+static int COMX_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct proc_dir_entry *procfile = ch->procdir->subdir;
+       struct comx_privdata *hw = ch->HW_privdata;
+       struct comx_channel *twin_ch;
+       struct device *savep;
+
+       savep = ch->HW_access_board(dev);
+
+       COMX_CMD(dev, COMX_CMD_CLOSE);
+       udelay(1000);
+       COMX_CMD(dev, COMX_CMD_EXIT);
+
+       ch->HW_release_board(dev, savep);
+
+       if (ch->init_status & IRQ_ALLOCATED) {
+               free_irq(dev->irq, (void *)dev);
+               ch->init_status &= ~IRQ_ALLOCATED;
+       }
+       release_region(dev->base_addr, hw->io_extent);
+
+       if (ch->twin && (twin_ch = ch->twin->priv) && 
+           (twin_ch->init_status & HW_OPEN)) {
+               /* Pass the irq to the twin */
+               if (request_irq(dev->irq, COMX_interrupt, 0, ch->twin->name, 
+                  (void *)ch->twin) == 0) {
+                       twin_ch->init_status |= IRQ_ALLOCATED;
+               }
+       }
+
+       for ( ; procfile ; procfile = procfile->next) {
+               if (strcmp(procfile->name, FILENAME_IRQ) == 0 
+                   || strcmp(procfile->name, FILENAME_IO) == 0
+                   || strcmp(procfile->name, FILENAME_MEMADDR) == 0
+                   || strcmp(procfile->name, FILENAME_CHANNEL) == 0
+                   || strcmp(procfile->name, FILENAME_FIRMWARE) == 0
+                   || strcmp(procfile->name, FILENAME_CLOCK) == 0) {
+                       procfile->mode = S_IFREG | 0644;
+               }
+       }
+       
+       ch->init_status &= ~HW_OPEN;
+       return 0;
+}
+
+static int COMX_statistics(struct device *dev, char *page)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       struct device *savep;
+       int len = 0;
+
+       savep = ch->HW_access_board(dev);
+
+       len += sprintf(page + len, "Board data: %s %s %s %s\nPBUFOVR: %02x, "
+               "MODSTAT: %02x, LINKUP: %02x, DAV: %02x\nRxBUFP: %02x, "
+               "TxEMPTY: %02x, TxBUFP: %02x\n",
+               (ch->init_status & HW_OPEN) ? "HW_OPEN" : "",
+               (ch->init_status & LINE_OPEN) ? "LINE_OPEN" : "",
+               (ch->init_status & FW_LOADED) ? "FW_LOADED" : "",
+               (ch->init_status & IRQ_ALLOCATED) ? "IRQ_ALLOCATED" : "",
+               COMX_readw(dev, OFF_A_L1_PBUFOVR) & 0xff,
+               (COMX_readw(dev, OFF_A_L1_PBUFOVR) >> 8) & 0xff,
+               COMX_readw(dev, OFF_A_L2_LINKUP) & 0xff,
+               COMX_readw(dev, OFF_A_L2_DAV) & 0xff,
+               COMX_readw(dev, OFF_A_L2_RxBUFP) & 0xff,
+               COMX_readw(dev, OFF_A_L2_TxEMPTY) & 0xff,
+               COMX_readw(dev, OFF_A_L2_TxBUFP) & 0xff);
+
+       len += sprintf(page + len, "hist[0]: %8lu hist[1]: %8lu hist[2]: %8lu\n"
+               "hist[3]: %8lu hist[4]: %8lu\n",hw->histogram[0],hw->histogram[1],
+               hw->histogram[2],hw->histogram[3],hw->histogram[4]);
+
+       ch->HW_release_board(dev, savep);
+
+       return len;
+}
+
+static int COMX_load_board(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       struct comx_firmware *fw = hw->firmware;
+       word board_segment = dev->mem_start >> 16;
+       unsigned long jiff;
+       unsigned char id1, id2;
+       int len;
+       byte *COMX_address;
+
+       if (!fw || !fw->len) {
+               struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+               struct comx_privdata *twin_hw;
+
+               if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+                       return -EAGAIN;
+               }
+
+               if (!(fw = twin_hw->firmware) || !fw->len) {
+                       return -EAGAIN;
+               }
+       }
+
+       id1 = fw->data[OFF_FW_L1_ID]; 
+       id2 = fw->data[OFF_FW_L1_ID + 1];
+
+       if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_COMX) {
+               printk(KERN_ERR "%s: incorrect firmware, load aborted\n", 
+                       dev->name);
+               return -EAGAIN;
+       }
+
+       printk(KERN_INFO "%s: Loading COMX Layer 1 firmware %s\n", dev->name, 
+               (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+       id1 = fw->data[OFF_FW_L2_ID]; 
+       id2 = fw->data[OFF_FW_L2_ID + 1];
+       if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+               printk(KERN_INFO "with Layer 2 code %s\n", 
+                       (char *)(fw->data + OFF_FW_L2_ID + 2));
+       }
+
+       outb_p(board_segment | COMX_BOARD_RESET, dev->base_addr);
+       /* 10 usec should be enough here */
+       udelay(100);
+       outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+       writeb(0, dev->mem_start + COMX_JAIL_OFFSET);   
+       jiff=jiffies;
+       while(jiffies < jiff + HZ && readb(dev->mem_start + COMX_JAIL_OFFSET) 
+           != COMX_JAIL_VALUE) {
+               schedule_timeout(1);
+       }       
+       
+       if (readb(dev->mem_start + COMX_JAIL_OFFSET) != COMX_JAIL_VALUE) {
+               printk(KERN_ERR "%s: Can't reset board, JAIL value is %02x\n",
+                       dev->name, readb(dev->mem_start + COMX_JAIL_OFFSET));
+               outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+               return -ENODEV;
+       }
+
+       writeb(0x55, dev->mem_start + 0x18ff);
+       jiff=jiffies;
+       while(jiffies < jiff + 3*HZ && readb(dev->mem_start + 0x18ff) != 0) {
+               schedule_timeout(1);
+       }
+
+       if(readb(dev->mem_start + 0x18ff) != 0) {
+               printk(KERN_ERR "%s: Can't reset board, reset timeout\n",
+                       dev->name);
+               outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+               return -ENODEV;
+       }               
+
+       len = 0;
+       COMX_address = (byte *)dev->mem_start;
+       while (fw->len > len) {
+               writeb(fw->data[len++], COMX_address++);
+       }
+
+       len = 0;
+       COMX_address = (byte *)dev->mem_start;
+       while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+               len++;
+       }
+
+       if (len != fw->len) {
+               printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+                       "instead of 0x%02x\n", dev->name, len, 
+                       readb(COMX_address - 1), fw->data[len]);
+               outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+               return -EAGAIN;
+       }
+
+       writeb(0, dev->mem_start + COMX_JAIL_OFFSET);
+
+       jiff = jiffies;
+       while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) {
+               schedule_timeout(1);
+       }
+
+       if (jiffies > jiff + 3*HZ) {
+               printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+                       dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+               outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+               return -EAGAIN;
+       }
+
+       outb_p(board_segment | COMX_DISABLE_BOARD_MEM, dev->base_addr);
+       ch->init_status |= FW_LOADED;
+       return 0;
+}
+
+static int CMX_load_board(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       struct comx_firmware *fw = hw->firmware;
+       word board_segment = dev->mem_start >> 16;
+       unsigned long jiff;
+       #if 0
+       unsigned char id1, id2;
+       #endif
+       int len;
+       byte *COMX_address;
+
+       if (!fw || !fw->len) {
+               struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+               struct comx_privdata *twin_hw;
+
+               if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+                       return -EAGAIN;
+               }
+
+               if (!(fw = twin_hw->firmware) || !fw->len) {
+                       return -EAGAIN;
+               }
+       }
+
+       /* Ide kell olyat tenni, hogy ellenorizze az ID-t */
+
+       if (inb_p(dev->base_addr) != CMX_ID_BYTE) {
+               printk(KERN_ERR "%s: CMX id byte is invalid(%02x)\n", dev->name,
+                       inb_p(dev->base_addr));
+               return -ENODEV;
+       }
+
+       printk(KERN_INFO "%s: Loading CMX Layer 1 firmware %s\n", dev->name, 
+               (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+       outb_p(board_segment | COMX_ENABLE_BOARD_MEM | COMX_BOARD_RESET, 
+               dev->base_addr);
+
+       len = 0;
+       COMX_address = (byte *)dev->mem_start;
+       while (fw->len > len) {
+               writeb(fw->data[len++], COMX_address++);
+       }
+
+       len = 0;
+       COMX_address = (byte *)dev->mem_start;
+       while (len != fw->len && readb(COMX_address++) == fw->data[len]) {
+               len++;
+       }
+
+       outb_p(board_segment | COMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+       if (len != fw->len) {
+               printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+                       "instead of 0x%02x\n", dev->name, len, 
+                       readb(COMX_address - 1), fw->data[len]);
+               outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+               return -EAGAIN;
+       }
+
+       jiff = jiffies;
+       while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) {
+               schedule_timeout(1);
+       }
+
+       if (jiffies > jiff + 3*HZ) {
+               printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+                       dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+               outb_p(board_segment | COMX_DISABLE_ALL, dev->base_addr);
+               return -EAGAIN;
+       }
+
+       outb_p(board_segment | COMX_DISABLE_BOARD_MEM, dev->base_addr);
+       ch->init_status |= FW_LOADED;
+       return 0;
+}
+
+static int HICOMX_load_board(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       struct comx_firmware *fw = hw->firmware;
+       word board_segment = dev->mem_start >> 12;
+       unsigned long jiff;
+       unsigned char id1, id2;
+       int len;
+       word *HICOMX_address;
+       char id = 1;
+
+       if (!fw || !fw->len) {
+               struct comx_channel *twin_ch = ch->twin ? ch->twin->priv : NULL;
+               struct comx_privdata *twin_hw;
+
+               if (!twin_ch || !(twin_hw = twin_ch->HW_privdata)) {
+                       return -EAGAIN;
+               }
+
+               if (!(fw = twin_hw->firmware) || !fw->len) {
+                       return -EAGAIN;
+               }
+       }
+
+       while (id != 4) {
+               if (inb_p(dev->base_addr + id++) != HICOMX_ID_BYTE) {
+                       break;
+               }
+       }
+
+       if (id != 4) {
+               printk(KERN_ERR "%s: can't find HICOMX at 0x%04x, id[%d] = %02x\n",
+                       dev->name, (unsigned int)dev->base_addr, id - 1,
+                       inb_p(dev->base_addr + id - 1));
+               return -1;      
+       }
+
+       id1 = fw->data[OFF_FW_L1_ID]; 
+       id2 = fw->data[OFF_FW_L1_ID + 1];
+       if (id1 != FW_L1_ID_1 || id2 != FW_L1_ID_2_HICOMX) {
+               printk(KERN_ERR "%s: incorrect firmware, load aborted\n", dev->name);
+               return -EAGAIN;
+       }
+
+       printk(KERN_INFO "%s: Loading HICOMX Layer 1 firmware %s\n", dev->name, 
+               (char *)(fw->data + OFF_FW_L1_ID + 2));
+
+       id1 = fw->data[OFF_FW_L2_ID]; 
+       id2 = fw->data[OFF_FW_L2_ID + 1];
+       if (id1 == FW_L2_ID_1 && (id2 == 0xc0 || id2 == 0xc1 || id2 == 0xc2)) {
+               printk(KERN_INFO "with Layer 2 code %s\n", 
+                       (char *)(fw->data + OFF_FW_L2_ID + 2));
+       }
+
+       outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+       udelay(10);     
+       outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+       outb_p(HICOMX_PRG_MEM, dev->base_addr + 1);
+
+       len = 0;
+       HICOMX_address = (word *)dev->mem_start;
+       while (fw->len > len) {
+               writeb(fw->data[len++], HICOMX_address++);
+       }
+
+       len = 0;
+       HICOMX_address = (word *)dev->mem_start;
+       while (len != fw->len && (readw(HICOMX_address++) & 0xff) == fw->data[len]) {
+               len++;
+       }
+
+       if (len != fw->len) {
+               printk(KERN_ERR "%s: error loading firmware: [%d] is 0x%02x "
+                       "instead of 0x%02x\n", dev->name, len, 
+                       readw(HICOMX_address - 1) & 0xff, fw->data[len]);
+               outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+               outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+               return -EAGAIN;
+       }
+
+       outb_p(board_segment | HICOMX_BOARD_RESET, dev->base_addr);
+       outb_p(HICOMX_DATA_MEM, dev->base_addr + 1);
+       outb_p(board_segment | HICOMX_ENABLE_BOARD_MEM, dev->base_addr);
+
+       jiff = jiffies;
+       while ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 && jiffies < jiff + 3*HZ ) {
+               schedule_timeout(1);
+       }
+
+       if ( COMX_readw(dev, OFF_A_L2_LINKUP) != 1 ) {
+               printk(KERN_ERR "%s: error starting firmware, linkup word is %04x\n",
+                       dev->name, COMX_readw(dev, OFF_A_L2_LINKUP));
+               outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+               return -EAGAIN;
+       }
+
+       outb_p(board_segment | HICOMX_DISABLE_ALL, dev->base_addr);
+       ch->init_status |= FW_LOADED;
+       return 0;
+}
+
+static struct device *comx_twin_check(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+       struct comx_privdata *hw = ch->HW_privdata;
+
+       struct device *twin;
+       struct comx_channel *ch_twin;
+       struct comx_privdata *hw_twin;
+
+
+       for ( ; procfile ; procfile = procfile->next) {
+       
+               if(!S_ISDIR(procfile->mode)) {
+                       continue;
+               }
+       
+               twin=procfile->data;
+               ch_twin=twin->priv;
+               hw_twin=ch_twin->HW_privdata;
+
+
+               if (twin != dev && dev->irq && dev->base_addr && dev->mem_start &&
+                  dev->irq == twin->irq && dev->base_addr == twin->base_addr &&
+                  dev->mem_start == twin->mem_start &&
+                  hw->channel == (1 - hw_twin->channel) &&
+                  ch->hardware == ch_twin->hardware) {
+                       return twin;
+               }
+       }
+       return NULL;
+}
+
+static int comxhw_write_proc(struct file *file, const char *buffer, 
+       u_long count, void *data)
+{
+       struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+       struct device *dev = entry->parent->data;
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       char *page;
+
+
+       if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+               printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+               return -EIO;
+       }
+
+       if(ch->init_status & HW_OPEN) {
+               return -EAGAIN; 
+       }
+       
+       if (strcmp(FILENAME_FIRMWARE, entry->name) != 0) {
+               if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+                       return -ENOMEM;
+               }
+               copy_from_user(page, buffer, count = (min(count, PAGE_SIZE)));
+               if (*(page + count - 1) == '\n') {
+                       *(page + count - 1) = 0;
+               }
+       } else {
+               byte *tmp;
+
+               if (!hw->firmware) {
+                       if ((hw->firmware = kmalloc(sizeof(struct comx_firmware), 
+                           GFP_KERNEL)) == NULL) {
+                               return -ENOMEM;
+                       }
+                       hw->firmware->len = 0;
+                       hw->firmware->data = NULL;
+               }
+               
+               if ((tmp = kmalloc(count + file->f_pos, GFP_KERNEL)) == NULL) {
+                       return -ENOMEM;
+               }
+               
+               /* Ha nem 0 a fpos, akkor meglevo file-t irunk. Gyenge trukk. */
+               if (hw->firmware && hw->firmware->len && file->f_pos 
+                   && hw->firmware->len < count + file->f_pos) {
+                       memcpy(tmp, hw->firmware->data, hw->firmware->len);
+               }
+               if (hw->firmware->data) {
+                       kfree(hw->firmware->data);
+               }
+               copy_from_user(tmp + file->f_pos, buffer, count);
+               hw->firmware->len = entry->size = file->f_pos + count;
+               hw->firmware->data = tmp;
+               file->f_pos += count;
+               return count;
+       }
+
+       if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+               hw->channel = simple_strtoul(page, NULL, 0);
+               if (hw->channel >= MAX_CHANNELNO) {
+                       printk(KERN_ERR "Invalid channel number\n");
+                       hw->channel = 0;
+               }
+               if ((ch->twin = comx_twin_check(dev)) != NULL) {
+                       struct comx_channel *twin_ch = ch->twin->priv;
+                       twin_ch->twin = dev;
+               }
+       } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+               dev->irq = simple_strtoul(page, NULL, 0);
+               if (dev->irq == 2) {
+                       dev->irq = 9;
+               }
+               if (dev->irq < 3 || dev->irq > 15) {
+                       printk(KERN_ERR "comxhw: Invalid irq number\n");
+                       dev->irq = 0;
+               }
+               if ((ch->twin = comx_twin_check(dev)) != NULL) {
+                       struct comx_channel *twin_ch = ch->twin->priv;
+                       twin_ch->twin = dev;
+               }
+       } else if (strcmp(entry->name, FILENAME_IO) == 0) {
+               dev->base_addr = simple_strtoul(page, NULL, 0);
+               if ((dev->base_addr & 3) != 0 || dev->base_addr < 0x300 
+                  || dev->base_addr > 0x3fc) {
+                       printk(KERN_ERR "Invalid io value\n");
+                       dev->base_addr = 0;
+               }
+               if ((ch->twin = comx_twin_check(dev)) != NULL) {
+                       struct comx_channel *twin_ch = ch->twin->priv;
+
+                       twin_ch->twin = dev;
+               }
+       } else if (strcmp(entry->name, FILENAME_MEMADDR) == 0) {
+               dev->mem_start = simple_strtoul(page, NULL, 0);
+               if (dev->mem_start <= 0xf000 && dev->mem_start >= 0xa000) {
+                       dev->mem_start *= 16;
+               }
+               if ((dev->mem_start & 0xfff) != 0 || dev->mem_start < COMX_MEM_MIN
+                   || dev->mem_start + hw->memory_size > COMX_MEM_MAX) {
+                       printk(KERN_ERR "Invalid memory page\n");
+                       dev->mem_start = 0;
+               }
+               dev->mem_end = dev->mem_start + hw->memory_size;
+               if ((ch->twin = comx_twin_check(dev)) != NULL) {
+                       struct comx_channel *twin_ch = ch->twin->priv;
+
+                       twin_ch->twin = dev;
+               }
+       } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+               if (strncmp("ext", page, 3) == 0) {
+                       hw->clock = 0;
+               } else {
+                       int kbps;
+
+                       kbps = simple_strtoul(page, NULL, 0);
+                       hw->clock = kbps ? COMX_CLOCK_CONST/kbps : 0;
+               }
+       }
+
+       free_page((unsigned long)page);
+       return count;
+}
+
+static int comxhw_read_proc(char *page, char **start, off_t off, int count,
+       int *eof, void *data)
+{
+       struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+       struct device *dev = file->parent->data;
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+       int len = 0;
+
+
+       if (strcmp(file->name, FILENAME_IO) == 0) {
+               len = sprintf(page, "0x%03x\n", (unsigned int)dev->base_addr);
+       } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+               len = sprintf(page, "0x%02x\n", dev->irq == 9 ? 2 : dev->irq);
+       } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+               len = sprintf(page, "%01d\n", hw->channel);
+       } else if (strcmp(file->name, FILENAME_MEMADDR) == 0) {
+               len = sprintf(page, "0x%05x\n", (unsigned int)dev->mem_start);
+       } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+               len = sprintf(page, "%s\n", ch->twin ? ch->twin->name : "none");
+       } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+               if (hw->clock) {
+                       len = sprintf(page, "%-8d\n", COMX_CLOCK_CONST/hw->clock);
+               } else {
+                       len = sprintf(page, "external\n");
+               }
+       } else if (strcmp(file->name, FILENAME_FIRMWARE) == 0) {
+               len = min(FILE_PAGESIZE, min(count, 
+                       hw->firmware ? (hw->firmware->len - off) :  0));
+               if (len < 0) {
+                       len = 0;
+               }
+               *start = hw->firmware ? (hw->firmware->data + off) : NULL;
+               if (off + len >= (hw->firmware ? hw->firmware->len : 0) || len == 0) {
+                       *eof = 1;
+               }
+               return len;
+       }       
+
+       if (off >= len) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+       if (count >= len - off) {
+               *eof = 1;
+       }
+       return(min(count, len - off));
+}
+
+/* Called on echo comx >boardtype */
+static int COMX_init(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw;
+       struct proc_dir_entry *new_file;
+
+       if ((ch->HW_privdata = kmalloc(sizeof(struct comx_privdata), 
+           GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+       memset(hw = ch->HW_privdata, 0, sizeof(struct comx_privdata));
+
+       if (ch->hardware == &comx_hw || ch->hardware == &cmx_hw) {
+               hw->memory_size = COMX_MEMORY_SIZE;
+               hw->io_extent = COMX_IO_EXTENT;
+               dev->base_addr = COMX_DEFAULT_IO;
+               dev->irq = COMX_DEFAULT_IRQ;
+               dev->mem_start = COMX_DEFAULT_MEMADDR;
+               dev->mem_end = COMX_DEFAULT_MEMADDR + COMX_MEMORY_SIZE;
+       } else if (ch->hardware == &hicomx_hw) {
+               hw->memory_size = HICOMX_MEMORY_SIZE;
+               hw->io_extent = HICOMX_IO_EXTENT;
+               dev->base_addr = HICOMX_DEFAULT_IO;
+               dev->irq = HICOMX_DEFAULT_IRQ;
+               dev->mem_start = HICOMX_DEFAULT_MEMADDR;
+               dev->mem_end = HICOMX_DEFAULT_MEMADDR + HICOMX_MEMORY_SIZE;
+       } else {
+               printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+       }
+
+       if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, ch->procdir))
+           == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &comxhw_read_proc;
+       new_file->write_proc = &comxhw_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 6;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, ch->procdir))
+           == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &comxhw_read_proc;
+       new_file->write_proc = &comxhw_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 5;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &comxhw_read_proc;
+       new_file->write_proc = &comxhw_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 2;             // Ezt tudjuk
+       new_file->nlink = 1;
+
+       if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+               if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+                  ch->procdir)) == NULL) {
+                       return -EIO;
+               }
+               new_file->data = (void *)new_file;
+               new_file->read_proc = &comxhw_read_proc;
+               new_file->write_proc = &comxhw_write_proc;
+               new_file->ops = &comx_normal_inode_ops;
+               new_file->size = 9;
+               new_file->nlink = 1;
+       }
+
+       if ((new_file = create_proc_entry(FILENAME_MEMADDR, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &comxhw_read_proc;
+       new_file->write_proc = &comxhw_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 8;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &comxhw_read_proc;
+       new_file->write_proc = NULL;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_FIRMWARE, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &comxhw_read_proc;
+       new_file->write_proc = &comxhw_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+       if (ch->hardware == &comx_hw) {
+               ch->HW_board_on = COMX_board_on;
+               ch->HW_board_off = COMX_board_off;
+               ch->HW_load_board = COMX_load_board;
+       } else if (ch->hardware == &cmx_hw) {
+               ch->HW_board_on = COMX_board_on;
+               ch->HW_board_off = COMX_board_off;
+               ch->HW_load_board = CMX_load_board;
+               ch->HW_set_clock = COMX_set_clock;
+       } else if (ch->hardware == &hicomx_hw) {
+               ch->HW_board_on = HICOMX_board_on;
+               ch->HW_board_off = HICOMX_board_off;
+               ch->HW_load_board = HICOMX_load_board;
+               ch->HW_set_clock = COMX_set_clock;
+       } else {
+               printk(KERN_ERR "SERIOUS INTERNAL ERROR in %s, line %d\n", __FILE__, __LINE__);
+       }
+
+       ch->HW_access_board = COMX_access_board;
+       ch->HW_release_board = COMX_release_board;
+       ch->HW_txe = COMX_txe;
+       ch->HW_open = COMX_open;
+       ch->HW_close = COMX_close;
+       ch->HW_send_packet = COMX_send_packet;
+       ch->HW_statistics = COMX_statistics;
+
+       if ((ch->twin = comx_twin_check(dev)) != NULL) {
+               struct comx_channel *twin_ch = ch->twin->priv;
+
+               twin_ch->twin = dev;
+       }
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+/* Called on echo valami >boardtype */
+static int COMX_exit(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_privdata *hw = ch->HW_privdata;
+
+       if (hw->firmware) {
+               if (hw->firmware->data) kfree(hw->firmware->data);
+               kfree(hw->firmware);
+       } if (ch->twin) {
+               struct comx_channel *twin_ch = ch->twin->priv;
+
+               twin_ch->twin = NULL;
+       }
+       
+       kfree(ch->HW_privdata);
+       remove_proc_entry(FILENAME_IO, ch->procdir);
+       remove_proc_entry(FILENAME_IRQ, ch->procdir);
+       remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+       remove_proc_entry(FILENAME_MEMADDR, ch->procdir);
+       remove_proc_entry(FILENAME_FIRMWARE, ch->procdir);
+       remove_proc_entry(FILENAME_TWIN, ch->procdir);
+       if (ch->hardware == &hicomx_hw || ch->hardware == &cmx_hw) {
+               remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+       }
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int COMX_dump(struct device *dev)
+{
+       printk(KERN_INFO "%s: COMX_dump called, why ?\n", dev->name);
+       return 0;
+}
+
+static struct comx_hardware comx_hw = {
+       "comx",
+       VERSION,
+       COMX_init,
+       COMX_exit,
+       COMX_dump,
+       NULL
+};
+
+static struct comx_hardware cmx_hw = {
+       "cmx",
+       VERSION,
+       COMX_init,
+       COMX_exit,
+       COMX_dump,
+       NULL
+};
+
+static struct comx_hardware hicomx_hw = {
+       "hicomx",
+       VERSION,
+       COMX_init,
+       COMX_exit,
+       COMX_dump,
+       NULL
+};
+
+#ifdef MODULE
+#define comx_hw_comx_init init_module
+#endif
+
+__initfunc(int comx_hw_comx_init(void))
+{
+       comx_register_hardware(&comx_hw);
+       comx_register_hardware(&cmx_hw);
+       comx_register_hardware(&hicomx_hw);
+       memset(memory_used, 0, sizeof(memory_used));
+       return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       comx_unregister_hardware("comx");
+       comx_unregister_hardware("cmx");
+       comx_unregister_hardware("hicomx");
+}
+#endif
diff --git a/drivers/net/comx-hw-locomx.c b/drivers/net/comx-hw-locomx.c
new file mode 100644 (file)
index 0000000..1fcc3bf
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * Hardware driver for the LoCOMX card, using the generic z85230
+ * functions
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Based on skeleton code and old LoCOMX driver by Tivadar Szemethy <tiv@itc.hu> 
+ * and the hostess_sv11 driver
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.10 (99/06/17):
+ *             - rewritten for the z85230 layer
+ *
+ * Version 0.11 (99/06/21):
+ *             - some printk's fixed
+ *             - get rid of a memory leak (it was impossible though :))
+ * 
+ * Version 0.12 (99/07/07):
+ *             - check CTS for modem lines, not DCD (which is always high
+ *               in case of this board)
+ * Version 0.13 (99/07/08):
+ *             - Fix the transmitter status check
+ *             - Handle the net device statistics better
+ */
+
+#define VERSION "0.13"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+
+#include "comx.h"
+#include "z85230.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware driver for the LoCOMX board");
+
+#define RX_DMA 3
+#define TX_DMA 1
+#define LOCOMX_ID 0x33
+#define LOCOMX_IO_EXTENT 8
+#define LOCOMX_DEFAULT_IO 0x368
+#define LOCOMX_DEFAULT_IRQ 7
+
+u8 z8530_locomx[] = {
+       11,     TCRTxCP,
+       14,     DTRREQ,
+       255
+};
+
+struct locomx_data {
+       int     io_extent;
+       struct  z8530_dev board;
+       struct timer_list status_timer;
+};
+
+static int LOCOMX_txe(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct locomx_data *hw = ch->HW_privdata;
+
+       return (!hw->board.chanA.tx_next_skb);
+}
+
+
+static void locomx_rx(struct z8530_channel *c, struct sk_buff *skb)
+{
+       struct device *dev=c->netdevice;
+       struct comx_channel *ch=dev->priv;
+       
+       if (ch->debug_flags & DEBUG_HW_RX) {
+               comx_debug_skb(dev, skb, "locomx_rx receiving");
+       }
+       ch->LINE_rx(dev,skb);
+}
+
+static int LOCOMX_send_packet(struct device *dev, struct sk_buff *skb) 
+{
+       struct comx_channel *ch = (struct comx_channel *)dev->priv;
+       struct locomx_data *hw = ch->HW_privdata;
+
+       if (ch->debug_flags & DEBUG_HW_TX) {
+               comx_debug_bytes(dev, skb->data, skb->len, "LOCOMX_send_packet");
+       }
+
+       if (!(ch->line_status & LINE_UP)) {
+               return FRAME_DROPPED;
+       }
+
+       if(z8530_queue_xmit(&hw->board.chanA,skb)) {
+               printk(KERN_WARNING "%s: FRAME_DROPPED\n",dev->name);
+               return FRAME_DROPPED;
+       }
+
+       if (ch->debug_flags & DEBUG_HW_TX) {
+               comx_debug(dev, "%s: LOCOMX_send_packet was successful\n\n", dev->name);
+       }
+
+       if(!hw->board.chanA.tx_next_skb) {
+               return FRAME_QUEUED;
+       } else {
+               return FRAME_ACCEPTED;
+       }
+}
+
+static void locomx_status_timerfun(unsigned long d)
+{
+       struct device *dev=(struct device *)d;
+       struct comx_channel *ch=dev->priv;
+       struct locomx_data *hw=ch->HW_privdata;
+
+       if(!(ch->line_status & LINE_UP) &&
+           (hw->board.chanA.status & CTS)) {
+               ch->LINE_status(dev, ch->line_status | LINE_UP);
+       }
+       if((ch->line_status & LINE_UP) &&
+           !(hw->board.chanA.status & CTS)) {
+               ch->LINE_status(dev, ch->line_status & ~LINE_UP);
+       }
+       mod_timer(&hw->status_timer,jiffies + ch->lineup_delay * HZ);
+}
+
+
+static int LOCOMX_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct locomx_data *hw = ch->HW_privdata;
+       struct proc_dir_entry *procfile = ch->procdir->subdir;
+       unsigned long flags;
+       int ret;
+
+       if (!dev->base_addr || !dev->irq) {
+               return -ENODEV;
+       }
+
+       if (check_region(dev->base_addr, hw->io_extent)) {
+               return -EAGAIN;
+       }
+
+       request_region(dev->base_addr, hw->io_extent, dev->name);
+
+       hw->board.chanA.ctrlio=dev->base_addr + 5;
+       hw->board.chanA.dataio=dev->base_addr + 7;
+       
+       hw->board.irq=dev->irq;
+       hw->board.chanA.netdevice=dev;
+       hw->board.chanA.dev=&hw->board;
+       hw->board.name=dev->name;
+       hw->board.chanA.txdma=TX_DMA;
+       hw->board.chanA.rxdma=RX_DMA;
+       hw->board.chanA.irqs=&z8530_nop;
+       hw->board.chanB.irqs=&z8530_nop;
+
+       if(request_irq(dev->irq, z8530_interrupt, SA_INTERRUPT, 
+           dev->name, &hw->board)) {
+               printk(KERN_ERR "%s: unable to obtain irq %d\n", dev->name, 
+                       dev->irq);
+               ret=-EAGAIN;
+               goto irq_fail;
+       }
+       if(request_dma(TX_DMA,"LoCOMX (TX)")) {
+               printk(KERN_ERR "%s: unable to obtain TX DMA (DMA channel %d)\n", 
+                       dev->name, TX_DMA);
+               ret=-EAGAIN;
+               goto dma1_fail;
+       }
+
+       if(request_dma(RX_DMA,"LoCOMX (RX)")) {
+               printk(KERN_ERR "%s: unable to obtain RX DMA (DMA channel %d)\n", 
+                       dev->name, RX_DMA);
+               ret=-EAGAIN;
+               goto dma2_fail;
+       }
+       
+       save_flags(flags); 
+       cli();
+
+       if(z8530_init(&hw->board)!=0)
+       {
+               printk(KERN_ERR "%s: Z8530 device not found.\n",dev->name);
+               ret=-ENODEV;
+               goto z8530_fail;
+       }
+
+       hw->board.chanA.dcdcheck=CTS;
+
+       z8530_channel_load(&hw->board.chanA, z8530_hdlc_kilostream_85230);
+       z8530_channel_load(&hw->board.chanA, z8530_locomx);
+       z8530_channel_load(&hw->board.chanB, z8530_dead_port);
+
+       z8530_describe(&hw->board, "I/O", dev->base_addr);
+
+       if((ret=z8530_sync_dma_open(dev, &hw->board.chanA))!=0) {
+               goto z8530_fail;
+       }
+
+       restore_flags(flags);
+
+
+       hw->board.active=1;
+       hw->board.chanA.rx_function=locomx_rx;
+
+       ch->init_status |= HW_OPEN;
+       if (hw->board.chanA.status & DCD) {
+               ch->line_status |= LINE_UP;
+       } else {
+               ch->line_status &= ~LINE_UP;
+       }
+
+       comx_status(dev, ch->line_status);
+
+       init_timer(&hw->status_timer);
+       hw->status_timer.function=locomx_status_timerfun;
+       hw->status_timer.data=(unsigned long)dev;
+       hw->status_timer.expires=jiffies + ch->lineup_delay * HZ;
+       add_timer(&hw->status_timer);
+
+       for (; procfile ; procfile = procfile->next) {
+               if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+                    strcmp(procfile->name, FILENAME_IRQ) == 0) {
+                       procfile->mode = S_IFREG |  0444;
+               }
+       }
+       return 0;
+
+z8530_fail:
+       restore_flags(flags);
+       free_dma(RX_DMA);
+dma2_fail:
+       free_dma(TX_DMA);
+dma1_fail:
+       free_irq(dev->irq, &hw->board);
+irq_fail:
+       release_region(dev->base_addr, hw->io_extent);
+       return ret;
+}
+
+static int LOCOMX_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct locomx_data *hw = ch->HW_privdata;
+       struct proc_dir_entry *procfile = ch->procdir->subdir;
+
+       hw->board.chanA.rx_function=z8530_null_rx;
+       dev->tbusy=1;
+       z8530_sync_dma_close(dev, &hw->board.chanA);
+
+       z8530_shutdown(&hw->board);
+
+       del_timer(&hw->status_timer);
+       free_dma(RX_DMA);
+       free_dma(TX_DMA);
+       free_irq(dev->irq,&hw->board);
+       release_region(dev->base_addr,8);
+
+       for (; procfile ; procfile = procfile->next) {
+               if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+                   strcmp(procfile->name, FILENAME_IRQ) == 0) {
+                       procfile->mode = S_IFREG |  0644;
+               }
+       }
+
+       ch->init_status &= ~HW_OPEN;
+       return 0;
+}
+
+static int LOCOMX_statistics(struct device *dev,char *page)
+{
+       int len = 0;
+
+       len += sprintf(page + len, "Hello\n");
+
+       return len;
+}
+
+static int LOCOMX_dump(struct device *dev) {
+       printk(KERN_INFO "LOCOMX_dump called\n");
+       return(-1);
+}
+
+static int locomx_read_proc(char *page, char **start, off_t off, int count,
+       int *eof, void *data)
+{
+       struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+       struct device *dev = file->parent->data;
+       int len = 0;
+
+       if (strcmp(file->name, FILENAME_IO) == 0) {
+               len = sprintf(page, "0x%x\n", (unsigned int)dev->base_addr);
+       } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+               len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+       } else {
+               printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
+                       file->name);
+               return -EBADF;
+       }
+
+       if (off >= len) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+       if (count >= len - off) {
+               *eof = 1;
+       }
+       return ( min(count, len - off) );
+}
+
+static int locomx_write_proc(struct file *file, const char *buffer,
+       u_long count, void *data)
+{
+       struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+       struct device *dev = (struct device *)entry->parent->data;
+       int val;
+       char *page;
+
+       if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+               printk(KERN_ERR "hw_write_proc: file <-> data internal error\n");
+               return -EIO;
+       }
+
+       if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+               return -ENOMEM;
+       }
+
+       copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+       if (*(page + count - 1) == '\n') {
+               *(page + count - 1) = 0;
+       }
+
+       if (strcmp(entry->name, FILENAME_IO) == 0) {
+               val = simple_strtoul(page, NULL, 0);
+               if (val != 0x360 && val != 0x368 && val != 0x370 && 
+                  val != 0x378) {
+                       printk(KERN_ERR "LoCOMX: incorrect io address!\n");     
+               } else {
+                       dev->base_addr = val;
+               }
+       } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+               val = simple_strtoul(page, NULL, 0);
+               if (val != 3 && val != 4 && val != 5 && val != 6 && val != 7) {
+                       printk(KERN_ERR "LoCOMX: incorrect irq value!\n");
+               } else {
+                       dev->irq = val;
+               }       
+       } else {
+               printk(KERN_ERR "locomx_write_proc: internal error, filename %s\n", 
+                       entry->name);
+               free_page((unsigned long)page);
+               return -EBADF;
+       }
+
+       free_page((unsigned long)page);
+       return count;
+}
+
+
+
+static int LOCOMX_init(struct device *dev) 
+{
+       struct comx_channel *ch = (struct comx_channel *)dev->priv;
+       struct locomx_data *hw;
+       struct proc_dir_entry *new_file;
+
+       /* Alloc data for private structure */
+       if ((ch->HW_privdata = kmalloc(sizeof(struct locomx_data), 
+          GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+
+       memset(hw = ch->HW_privdata, 0, sizeof(struct locomx_data));
+       hw->io_extent = LOCOMX_IO_EXTENT;
+
+       /* Register /proc files */
+       if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &locomx_read_proc;
+       new_file->write_proc = &locomx_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
+           ch->procdir)) == NULL)  {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &locomx_read_proc;
+       new_file->write_proc = &locomx_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+/*     No clock yet */
+/*
+       if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &locomx_read_proc;
+       new_file->write_proc = &locomx_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+*/
+
+       ch->HW_access_board = NULL;
+       ch->HW_release_board = NULL;
+       ch->HW_txe = LOCOMX_txe;
+       ch->HW_open = LOCOMX_open;
+       ch->HW_close = LOCOMX_close;
+       ch->HW_send_packet = LOCOMX_send_packet;
+       ch->HW_statistics = LOCOMX_statistics;
+       ch->HW_set_clock = NULL;
+
+       ch->current_stats = &hw->board.chanA.stats;
+       memcpy(ch->current_stats, &ch->stats, sizeof(struct net_device_stats));
+
+       dev->base_addr = LOCOMX_DEFAULT_IO;
+       dev->irq = LOCOMX_DEFAULT_IRQ;
+       
+       
+       /* O.K. Count one more user on this module */
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+
+static int LOCOMX_exit(struct device *dev)
+{
+       struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+       ch->HW_access_board = NULL;
+       ch->HW_release_board = NULL;
+       ch->HW_txe = NULL;
+       ch->HW_open = NULL;
+       ch->HW_close = NULL;
+       ch->HW_send_packet = NULL;
+       ch->HW_statistics = NULL;
+       ch->HW_set_clock = NULL;
+       memcpy(&ch->stats, ch->current_stats, sizeof(struct net_device_stats));
+       ch->current_stats = &ch->stats;
+
+       kfree(ch->HW_privdata);
+
+       remove_proc_entry(FILENAME_IO, ch->procdir);
+       remove_proc_entry(FILENAME_IRQ, ch->procdir);
+//     remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static struct comx_hardware locomx_hw = {
+       "locomx",
+       VERSION,
+       LOCOMX_init, 
+       LOCOMX_exit,
+       LOCOMX_dump,
+       NULL
+};
+       
+#ifdef MODULE
+#define comx_hw_locomx_init init_module
+#endif
+
+__initfunc(int comx_hw_locomx_init(void))
+{
+       comx_register_hardware(&locomx_hw);
+       return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       comx_unregister_hardware("locomx");
+       return;
+}
+#endif
diff --git a/drivers/net/comx-hw-mixcom.c b/drivers/net/comx-hw-mixcom.c
new file mode 100644 (file)
index 0000000..67237df
--- /dev/null
@@ -0,0 +1,945 @@
+/* 
+ * Hardware driver for the MixCom synchronous serial board 
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton driver code and a preliminary hscx driver by 
+ * Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.60 (99/06/11):
+ *             - ported to the kernel, now works as builtin code
+ *
+ * Version 0.61 (99/06/11):
+ *             - recognize the one-channel MixCOM card (id byte = 0x13)
+ *             - printk fixes
+ * 
+ * Version 0.62 (99/07/15):
+ *             - fixes according to the new hw docs 
+ *             - report line status when open
+ *
+ * Version 0.63 (99/09/21):
+ *             - line status report fixes
+ */
+
+#define VERSION "0.63"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include "comx.h"
+#include "mixcom.h"
+#include "hscx.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Hardware-level driver for the serial port of the MixCom board");
+
+#define MIXCOM_DATA(d) ((struct mixcom_privdata *)(COMX_CHANNEL(d)-> \
+       HW_privdata))
+
+#define MIXCOM_BOARD_BASE(d) (d->base_addr - MIXCOM_SERIAL_OFFSET - \
+       (1 - MIXCOM_DATA(d)->channel) * MIXCOM_CHANNEL_OFFSET)
+
+#define MIXCOM_DEV_BASE(port,channel) (port + MIXCOM_SERIAL_OFFSET + \
+       (1 - channel) * MIXCOM_CHANNEL_OFFSET)
+
+/* Values used to set the IRQ line */
+static unsigned char mixcom_set_irq[]={0xFF, 0xFF, 0xFF, 0x0, 0xFF, 0x2, 0x4, 0x6, 0xFF, 0xFF, 0x8, 0xA, 0xC, 0xFF, 0xE, 0xFF};
+
+static unsigned char* hscx_versions[]={"A1", NULL, "A2", NULL, "A3", "2.1"};
+
+struct mixcom_privdata {
+       u16     clock;
+       char    channel;
+       char    txbusy;
+       struct sk_buff *sending;
+       unsigned tx_ptr;
+       struct sk_buff *recving;
+       unsigned rx_ptr;
+       unsigned char status;
+       char    card_has_status;
+};
+
+static inline void wr_hscx(struct device *dev, int reg, unsigned char val) 
+{
+       outb(val, dev->base_addr + reg);
+}
+
+static inline unsigned char rd_hscx(struct device *dev, int reg)
+{
+       return inb(dev->base_addr + reg);
+}
+
+static inline void hscx_cmd(struct device *dev, int cmd)
+{
+       unsigned long jiffs = jiffies;
+       unsigned char cec;
+       unsigned delay = 0;
+
+       while ((cec = (rd_hscx(dev, HSCX_STAR) & HSCX_CEC) != 0) && 
+           (jiffs + HZ > jiffies)) {
+               udelay(1);
+               if (++delay > (100000 / HZ)) break;
+       }
+       if (cec) {
+               printk(KERN_WARNING "%s: CEC stuck, probably no clock!\n",dev->name);
+       } else {
+               wr_hscx(dev, HSCX_CMDR, cmd);
+       }
+}
+
+static inline void hscx_fill_fifo(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+       register word to_send = hw->sending->len - hw->tx_ptr;
+
+
+       outsb(dev->base_addr + HSCX_FIFO,
+               &(hw->sending->data[hw->tx_ptr]), min(to_send, 32));
+       if (to_send <= 32) {
+               hscx_cmd(dev, HSCX_XTF | HSCX_XME);
+               kfree_skb(hw->sending);
+               hw->sending = NULL; 
+               hw->tx_ptr = 0;
+        } else {
+               hscx_cmd(dev, HSCX_XTF);
+               hw->tx_ptr += 32;
+        }
+}
+
+static inline void hscx_empty_fifo(struct device *dev, int cnt)
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+
+       if (hw->recving == NULL) {
+               if (!(hw->recving = dev_alloc_skb(HSCX_MTU + 16))) {
+                       ch->stats.rx_dropped++;
+                       hscx_cmd(dev, HSCX_RHR);
+                } else {
+                       skb_reserve(hw->recving, 16);
+                       skb_put(hw->recving, HSCX_MTU);
+                }
+               hw->rx_ptr = 0;
+        }
+       if (cnt > 32 || !cnt || hw->recving == NULL) {
+               printk(KERN_ERR "hscx_empty_fifo: cnt is %d, hw->recving %p\n",
+                       cnt, (void *)hw->recving);
+               return;
+        }
+        
+       insb(dev->base_addr + HSCX_FIFO, &(hw->recving->data[hw->rx_ptr]),cnt);
+       hw->rx_ptr += cnt;
+       hscx_cmd(dev, HSCX_RMC);
+}
+
+
+static int MIXCOM_txe(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+
+       return !test_bit(0, &hw->txbusy);
+}
+
+static int mixcom_probe(struct device *dev)
+{
+       unsigned long flags;
+       int id, vstr, ret=0;
+
+       save_flags(flags); cli();
+
+       id=inb_p(MIXCOM_BOARD_BASE(dev) + MIXCOM_ID_OFFSET);
+
+       if (id != MIXCOM_ID ) {
+               ret=-ENODEV;
+               printk(KERN_WARNING "%s: no MixCOM board found at 0x%04lx\n",dev->name, dev->base_addr);
+               goto out;
+       }
+
+       vstr=inb_p(dev->base_addr + HSCX_VSTR) & 0x0f;
+       if(vstr>=sizeof(hscx_versions)/sizeof(char*) || 
+           hscx_versions[vstr]==NULL) {
+               printk(KERN_WARNING "%s: board found but no HSCX chip detected at 0x%4lx (vstr = 0x%1x)\n",dev->name,dev->base_addr,vstr);
+               ret = -ENODEV;
+       } else {
+               printk(KERN_INFO "%s: HSCX chip version %s\n",dev->name,hscx_versions[vstr]);
+               ret = 0;
+       }
+
+out:
+
+       restore_flags(flags);
+       return ret;
+}
+
+#if 0
+static void MIXCOM_set_clock(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+
+       if (hw->clock) {
+               ;
+       } else {
+               ;
+       }
+}
+#endif
+
+static void mixcom_board_on(struct device *dev)
+{
+       outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+       udelay(1000);
+       outb_p(mixcom_set_irq[dev->irq] | MIXCOM_ON, 
+               MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+       udelay(1000);
+}
+
+static void mixcom_board_off(struct device *dev)
+{
+       outb_p(MIXCOM_OFF , MIXCOM_BOARD_BASE(dev) + MIXCOM_IT_OFFSET);
+       udelay(1000);
+}
+
+static void mixcom_off(struct device *dev)
+{
+       wr_hscx(dev, HSCX_CCR1, 0x0);
+}
+
+static void mixcom_on(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+
+       wr_hscx(dev, HSCX_CCR1, HSCX_PU | HSCX_ODS | HSCX_ITF); // power up, push-pull
+       wr_hscx(dev, HSCX_CCR2, HSCX_CIE /* | HSCX_RIE */ );
+       wr_hscx(dev, HSCX_MODE, HSCX_TRANS | HSCX_ADM8 | HSCX_RAC | HSCX_RTS );
+       wr_hscx(dev, HSCX_RLCR, HSCX_RC | 47); // 1504 bytes
+       wr_hscx(dev, HSCX_MASK, HSCX_RSC | HSCX_TIN );
+       hscx_cmd(dev, HSCX_XRES | HSCX_RHR);
+
+       if (ch->HW_set_clock) ch->HW_set_clock(dev);
+
+}
+
+static int MIXCOM_send_packet(struct device *dev, struct sk_buff *skb) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+       unsigned long flags;
+
+       if (ch->debug_flags & DEBUG_HW_TX) {
+               comx_debug_bytes(dev, skb->data, skb->len, "MIXCOM_send_packet");
+       }
+
+       if (!(ch->line_status & LINE_UP)) {
+               return FRAME_DROPPED;
+       }
+
+       if (skb->len > HSCX_MTU) {
+               ch->stats.tx_errors++;  
+               return FRAME_ERROR;
+       }
+
+       save_flags(flags); cli();
+
+       if (test_and_set_bit(0, &hw->txbusy)) {
+               printk(KERN_ERR "%s: transmitter called while busy... dropping frame (length %d)\n", dev->name, skb->len);
+               restore_flags(flags);
+               return FRAME_DROPPED;
+       }
+
+
+       hw->sending = skb;
+       hw->tx_ptr = 0;
+       hw->txbusy = 1;
+//     atomic_inc(&skb->users);        // save it
+       hscx_fill_fifo(dev);
+       restore_flags(flags);
+
+       ch->stats.tx_packets++;
+       ch->stats.tx_bytes += skb->len; 
+
+       if (ch->debug_flags & DEBUG_HW_TX) {
+               comx_debug(dev, "MIXCOM_send_packet was successful\n\n");
+       }
+
+       return FRAME_ACCEPTED;
+}
+
+static inline void mixcom_receive_frame(struct device *dev) 
+{
+       struct comx_channel *ch=dev->priv;
+       struct mixcom_privdata *hw=ch->HW_privdata;
+       register byte rsta;
+       register word length;
+
+       rsta = rd_hscx(dev, HSCX_RSTA) & (HSCX_VFR | HSCX_RDO | 
+               HSCX_CRC | HSCX_RAB);
+       length = ((rd_hscx(dev, HSCX_RBCH) & 0x0f) << 8) | 
+               rd_hscx(dev, HSCX_RBCL);
+
+       if ( length > hw->rx_ptr ) {
+               hscx_empty_fifo(dev, length - hw->rx_ptr);
+       }
+       
+       if (!(rsta & HSCX_VFR)) {
+               ch->stats.rx_length_errors++;
+       }
+       if (rsta & HSCX_RDO) {
+               ch->stats.rx_over_errors++;
+       }
+       if (!(rsta & HSCX_CRC)) {
+               ch->stats.rx_crc_errors++;
+       }
+       if (rsta & HSCX_RAB) {
+               ch->stats.rx_frame_errors++;
+       }
+       ch->stats.rx_packets++; 
+       ch->stats.rx_bytes += length;
+
+       if (rsta == (HSCX_VFR | HSCX_CRC) && hw->recving) {
+               skb_trim(hw->recving, hw->rx_ptr - 1);
+               if (ch->debug_flags & DEBUG_HW_RX) {
+                       comx_debug_skb(dev, hw->recving,
+                               "MIXCOM_interrupt receiving");
+               }
+               hw->recving->dev = dev;
+               if (ch->LINE_rx) {
+                       ch->LINE_rx(dev, hw->recving);
+               }
+       }
+       else if(hw->recving) {
+               kfree_skb(hw->recving);
+       }
+       hw->recving = NULL; 
+       hw->rx_ptr = 0;
+}
+
+
+static inline void mixcom_extended_interrupt(struct device *dev) 
+{
+       struct comx_channel *ch=dev->priv;
+       struct mixcom_privdata *hw=ch->HW_privdata;
+       register byte exir;
+
+       exir = rd_hscx(dev, HSCX_EXIR) & (HSCX_XDU | HSCX_RFO | HSCX_CSC );
+
+       if (exir & HSCX_RFO) {
+               ch->stats.rx_over_errors++;
+               if (hw->rx_ptr) {
+                       kfree_skb(hw->recving);
+                       hw->recving = NULL; hw->rx_ptr = 0;
+               }
+               printk(KERN_ERR "MIXCOM: rx overrun\n");
+               hscx_cmd(dev, HSCX_RHR);
+       }
+
+       if (exir & HSCX_XDU) { // xmit underrun
+               ch->stats.tx_errors++;
+               ch->stats.tx_aborted_errors++;
+               if (hw->tx_ptr) {
+                       kfree_skb(hw->sending);
+                       hw->sending = NULL; 
+                       hw->tx_ptr = 0;
+               }
+               hscx_cmd(dev, HSCX_XRES);
+               clear_bit(0, &hw->txbusy);
+               if (ch->LINE_tx) {
+                       ch->LINE_tx(dev);
+               }
+               printk(KERN_ERR "MIXCOM: tx underrun\n");
+       }
+
+       if (exir & HSCX_CSC) {        
+               ch->stats.tx_carrier_errors++;
+               if ((rd_hscx(dev, HSCX_STAR) & HSCX_CTS) == 0) { // Vonal le
+                       if (test_and_clear_bit(0, &ch->lineup_pending)) {
+                                       del_timer(&ch->lineup_timer);
+                       } else if (ch->line_status & LINE_UP) {
+                               ch->line_status &= ~LINE_UP;
+                               if (ch->LINE_status) {
+                                       ch->LINE_status(dev,ch->line_status);
+                               }
+                       }
+               }
+               if (!(ch->line_status & LINE_UP) && (rd_hscx(dev, HSCX_STAR) & 
+                   HSCX_CTS)) { // Vonal fol
+                       if (!test_and_set_bit(0,&ch->lineup_pending)) {
+                               ch->lineup_timer.function = comx_lineup_func;
+                               ch->lineup_timer.data = (unsigned long)dev;
+                               ch->lineup_timer.expires = jiffies + HZ * 
+                                       ch->lineup_delay;
+                               add_timer(&ch->lineup_timer);
+                               hscx_cmd(dev, HSCX_XRES);
+                               clear_bit(0, &hw->txbusy);
+                               if (hw->sending) {
+                                       kfree_skb(hw->sending);
+                               }
+                               hw->sending=NULL;
+                               hw->tx_ptr = 0;
+                       }
+               }
+       }
+}
+
+
+static void MIXCOM_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+       unsigned long flags;
+       struct device *dev = (struct device *)dev_id;
+       struct comx_channel *ch, *twin_ch;
+       struct mixcom_privdata *hw, *twin_hw;
+       register unsigned char ista;
+
+       if (dev==NULL) {
+               printk(KERN_ERR "comx_interrupt: irq %d for unknown device\n",irq);
+               return;
+       }
+
+       ch = dev->priv; 
+       hw = ch->HW_privdata;
+
+       save_flags(flags); cli(); 
+
+       while((ista = (rd_hscx(dev, HSCX_ISTA) & (HSCX_RME | HSCX_RPF | 
+           HSCX_XPR | HSCX_EXB | HSCX_EXA | HSCX_ICA)))) {
+               register byte ista2 = 0;
+
+               if (ista & HSCX_RME) {
+                       mixcom_receive_frame(dev);
+               }
+               if (ista & HSCX_RPF) {
+                       hscx_empty_fifo(dev, 32);
+               }
+               if (ista & HSCX_XPR) {
+                       if (hw->tx_ptr) {
+                               hscx_fill_fifo(dev);
+                       } else {
+                               clear_bit(0, &hw->txbusy);
+                                       ch->LINE_tx(dev);
+                       }
+               }
+               
+               if (ista & HSCX_EXB) {
+                       mixcom_extended_interrupt(dev);
+               }
+               
+               if ((ista & HSCX_EXA) && ch->twin)  {
+                       mixcom_extended_interrupt(ch->twin);
+               }
+       
+               if ((ista & HSCX_ICA) && ch->twin &&
+                   (ista2 = rd_hscx(ch->twin, HSCX_ISTA) &
+                   (HSCX_RME | HSCX_RPF | HSCX_XPR ))) {
+                       if (ista2 & HSCX_RME) {
+                               mixcom_receive_frame(ch->twin);
+                       }
+                       if (ista2 & HSCX_RPF) {
+                               hscx_empty_fifo(ch->twin, 32);
+                       }
+                       if (ista2 & HSCX_XPR) {
+                               twin_ch=ch->twin->priv;
+                               twin_hw=twin_ch->HW_privdata;
+                               if (twin_hw->tx_ptr) {
+                                       hscx_fill_fifo(ch->twin);
+                               } else {
+                                       clear_bit(0, &twin_hw->txbusy);
+                                       ch->LINE_tx(ch->twin);
+                               }
+                       }
+               }
+       }
+
+       restore_flags(flags);
+       return;
+}
+
+static int MIXCOM_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+       struct proc_dir_entry *procfile = ch->procdir->subdir;
+       unsigned long flags; 
+
+       if (!dev->base_addr || !dev->irq) return -ENODEV;
+
+
+       if(hw->channel==1) {
+               if(!TWIN(dev) || !(COMX_CHANNEL(TWIN(dev))->init_status & 
+                   IRQ_ALLOCATED)) {
+                       printk(KERN_ERR "%s: channel 0 not yet initialized\n",dev->name);
+                       return -EAGAIN;
+               }
+       }
+
+
+       /* Is our hw present at all ? Not checking for channel 0 if it is already 
+          open */
+       if(hw->channel!=0 || !(ch->init_status & IRQ_ALLOCATED)) {
+               if (check_region(dev->base_addr, MIXCOM_IO_EXTENT)) {
+                       return -EAGAIN;
+               }
+               if (mixcom_probe(dev)) {
+                       return -ENODEV;
+               }
+       }
+
+       save_flags(flags); cli();
+
+       if(hw->channel==1) {
+               request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+       } 
+
+       if(hw->channel==0 && !(ch->init_status & IRQ_ALLOCATED)) {
+               if (request_irq(dev->irq, MIXCOM_interrupt, 0, 
+                   dev->name, (void *)dev)) {
+                       printk(KERN_ERR "MIXCOM: unable to obtain irq %d\n", dev->irq);
+                       return -EAGAIN;
+               }
+               ch->init_status|=IRQ_ALLOCATED;
+               request_region(dev->base_addr, MIXCOM_IO_EXTENT, dev->name);
+               mixcom_board_on(dev);
+       }
+
+       mixcom_on(dev);
+
+       restore_flags(flags);
+
+       hw->status=inb(MIXCOM_BOARD_BASE(dev) + MIXCOM_STATUS_OFFSET);
+       if(hw->status != 0xff) {
+               printk(KERN_DEBUG "%s: board has status register, good\n", dev->name);
+               hw->card_has_status=1;
+       }
+
+       hw->txbusy = 0;
+       ch->init_status |= HW_OPEN;
+       
+       if (rd_hscx(dev, HSCX_STAR) & HSCX_CTS) {
+               ch->line_status |= LINE_UP;
+       } else {
+               ch->line_status &= ~LINE_UP;
+       }
+
+       ch->LINE_status(dev, ch->line_status);
+
+       for (; procfile ; procfile = procfile->next) {
+               if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+                   strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+                   strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+                   strcmp(procfile->name, FILENAME_IRQ) == 0) {
+                       procfile->mode = S_IFREG |  0444;
+               }
+       }
+
+       return 0;
+}
+
+static int MIXCOM_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+       struct proc_dir_entry *procfile = ch->procdir->subdir;
+       unsigned long flags;
+
+
+       save_flags(flags); cli();
+
+       mixcom_off(dev);
+
+       /* This is channel 0, twin is not open, we can safely turn off everything */
+       if(hw->channel==0 && (!(TWIN(dev)) || 
+           !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN))) {
+               mixcom_board_off(dev);
+               free_irq(dev->irq, dev);
+               release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+               ch->init_status &= ~IRQ_ALLOCATED;
+       }
+
+       /* This is channel 1, channel 0 has already been shutdown, we can release
+          this one too */
+       if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+               if(COMX_CHANNEL(TWIN(dev))->init_status & IRQ_ALLOCATED) {
+                       mixcom_board_off(TWIN(dev));
+                       free_irq(TWIN(dev)->irq, TWIN(dev));
+                       release_region(TWIN(dev)->base_addr, MIXCOM_IO_EXTENT);
+                       COMX_CHANNEL(TWIN(dev))->init_status &= ~IRQ_ALLOCATED;
+               }
+       }
+
+       /* the ioports for channel 1 can be safely released */
+       if(hw->channel==1) {
+               release_region(dev->base_addr, MIXCOM_IO_EXTENT);
+       }
+
+       restore_flags(flags);
+
+       /* If we don't hold any hardware open */
+       if(!(ch->init_status & IRQ_ALLOCATED)) {
+               for (; procfile ; procfile = procfile->next) {
+                       if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+                           strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+                           strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+                           strcmp(procfile->name, FILENAME_IRQ) == 0) {
+                               procfile->mode = S_IFREG |  0644;
+                       }
+               }
+       }
+
+       /* channel 0 was only waiting for us to close channel 1 
+          close it completely */
+   
+       if(hw->channel==1 && !(COMX_CHANNEL(TWIN(dev))->init_status & HW_OPEN)) {
+               for (procfile=COMX_CHANNEL(TWIN(dev))->procdir->subdir; 
+                   procfile ; procfile = procfile->next) {
+                       if (strcmp(procfile->name, FILENAME_IO) == 0 ||
+                           strcmp(procfile->name, FILENAME_CHANNEL) == 0 ||
+                           strcmp(procfile->name, FILENAME_CLOCK) == 0 ||
+                           strcmp(procfile->name, FILENAME_IRQ) == 0) {
+                               procfile->mode = S_IFREG |  0644;
+                       }
+               }
+       }
+       
+       ch->init_status &= ~HW_OPEN;
+       return 0;
+}
+
+static int MIXCOM_statistics(struct device *dev,char *page)
+{
+       struct comx_channel *ch = dev->priv;
+       // struct mixcom_privdata *hw = ch->HW_privdata;
+       int len = 0;
+
+       if(ch->init_status && IRQ_ALLOCATED) {
+               len += sprintf(page + len, "Mixcom board: hardware open\n");
+       }
+
+       return len;
+}
+
+static int MIXCOM_dump(struct device *dev) {
+       return 0;
+}
+
+static int mixcom_read_proc(char *page, char **start, off_t off, int count,
+       int *eof, void *data)
+{
+       struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+       struct device *dev = file->parent->data;
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+       int len = 0;
+
+       if (strcmp(file->name, FILENAME_IO) == 0) {
+               len = sprintf(page, "0x%x\n", 
+                       (unsigned int)MIXCOM_BOARD_BASE(dev));
+       } else if (strcmp(file->name, FILENAME_IRQ) == 0) {
+               len = sprintf(page, "%d\n", (unsigned int)dev->irq);
+       } else if (strcmp(file->name, FILENAME_CLOCK) == 0) {
+               if (hw->clock) len = sprintf(page, "%d\n", hw->clock);
+                       else len = sprintf(page, "external\n");
+       } else if (strcmp(file->name, FILENAME_CHANNEL) == 0) {
+               len = sprintf(page, "%01d\n", hw->channel);
+       } else if (strcmp(file->name, FILENAME_TWIN) == 0) {
+               if (ch->twin) {
+                       len = sprintf(page, "%s\n",ch->twin->name);
+               } else {
+                       len = sprintf(page, "none\n");
+               }
+       } else {
+               printk(KERN_ERR "mixcom_read_proc: internal error, filename %s\n", file->name);
+               return -EBADF;
+       }
+
+       if (off >= len) {
+               *eof = 1;
+               return 0;
+       }
+       *start = page + off;
+       if (count >= len - off) *eof = 1;
+       return ( min(count, len - off) );
+}
+
+
+static struct device *mixcom_twin_check(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct proc_dir_entry *procfile = ch->procdir->parent->subdir;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+
+       struct device *twin;
+       struct comx_channel *ch_twin;
+       struct mixcom_privdata *hw_twin;
+
+
+       for ( ; procfile ; procfile = procfile->next) {
+               if(!S_ISDIR(procfile->mode)) continue;
+                
+               twin = procfile->data;
+               ch_twin = twin->priv;
+               hw_twin = ch_twin->HW_privdata;
+
+
+               if (twin != dev && dev->irq && dev->base_addr && 
+                   dev->irq == twin->irq && 
+                   ch->hardware == ch_twin->hardware &&
+                   dev->base_addr == twin->base_addr + 
+                   (1-2*hw->channel)*MIXCOM_CHANNEL_OFFSET &&
+                   hw->channel == (1 - hw_twin->channel)) {
+                       if  (!TWIN(twin) || TWIN(twin)==dev) {
+                               return twin;
+                       }
+               }
+        }
+       return NULL;
+}
+
+
+static void setup_twin(struct device* dev) 
+{
+
+       if(TWIN(dev) && TWIN(TWIN(dev))) {
+               TWIN(TWIN(dev))=NULL;
+       }
+       if ((TWIN(dev) = mixcom_twin_check(dev)) != NULL) {
+               if (TWIN(TWIN(dev)) && TWIN(TWIN(dev)) != dev) {
+                       TWIN(dev)=NULL;
+               } else {
+                       TWIN(TWIN(dev))=dev;
+               }
+       }       
+}
+
+static int mixcom_write_proc(struct file *file, const char *buffer,
+       u_long count, void *data)
+{
+       struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+       struct device *dev = (struct device *)entry->parent->data;
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+       char *page;
+       int value;
+
+       if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+               printk(KERN_ERR "mixcom_write_proc: file <-> data internal error\n");
+               return -EIO;
+       }
+
+       if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+               return -ENOMEM;
+       }
+
+       copy_from_user(page, buffer, count = min(count, PAGE_SIZE));
+       if (*(page + count - 1) == '\n') {
+               *(page + count - 1) = 0;
+       }
+
+       if (strcmp(entry->name, FILENAME_IO) == 0) {
+               value = simple_strtoul(page, NULL, 0);
+               if (value != 0x180 && value != 0x280 && value != 0x380) {
+                       printk(KERN_ERR "MIXCOM: incorrect io address!\n");
+               } else {
+                       dev->base_addr = MIXCOM_DEV_BASE(value,hw->channel);
+               }
+       } else if (strcmp(entry->name, FILENAME_IRQ) == 0) {
+               value = simple_strtoul(page, NULL, 0); 
+               if (value < 0 || value > 15 || mixcom_set_irq[value]==0xFF) {
+                       printk(KERN_ERR "MIXCOM: incorrect irq value!\n");
+               } else {
+                       dev->irq = value;       
+               }
+       } else if (strcmp(entry->name, FILENAME_CLOCK) == 0) {
+               if (strncmp("ext", page, 3) == 0) {
+                       hw->clock = 0;
+               } else {
+                       int kbps;
+
+                       kbps = simple_strtoul(page, NULL, 0);
+                       if (!kbps) {
+                               hw->clock = 0;
+                       } else {
+                               hw->clock = kbps;
+                       }
+                       if (hw->clock < 32 || hw->clock > 2000) {
+                               hw->clock = 0;
+                               printk(KERN_ERR "MIXCOM: invalid clock rate!\n");
+                       }
+               }
+               if (ch->init_status & HW_OPEN && ch->HW_set_clock) {
+                       ch->HW_set_clock(dev);
+               }
+       } else if (strcmp(entry->name, FILENAME_CHANNEL) == 0) {
+               value = simple_strtoul(page, NULL, 0);
+               if (value > 2) {
+                       printk(KERN_ERR "Invalid channel number\n");
+               } else {
+                       dev->base_addr+=(hw->channel - value) * MIXCOM_CHANNEL_OFFSET;
+                       hw->channel = value;
+               }               
+       } else {
+               printk(KERN_ERR "hw_read_proc: internal error, filename %s\n", 
+                       entry->name);
+               return -EBADF;
+       }
+
+       setup_twin(dev);
+
+       free_page((unsigned long)page);
+       return count;
+}
+
+static int MIXCOM_init(struct device *dev) {
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw;
+       struct proc_dir_entry *new_file;
+
+       if ((ch->HW_privdata = kmalloc(sizeof(struct mixcom_privdata), 
+           GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+
+       memset(hw = ch->HW_privdata, 0, sizeof(struct mixcom_privdata));
+
+       if ((new_file = create_proc_entry(FILENAME_IO, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &mixcom_read_proc;
+       new_file->write_proc = &mixcom_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_IRQ, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &mixcom_read_proc;
+       new_file->write_proc = &mixcom_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+#if 0
+       if ((new_file = create_proc_entry(FILENAME_CLOCK, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &mixcom_read_proc;
+       new_file->write_proc = &mixcom_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+#endif
+
+       if ((new_file = create_proc_entry(FILENAME_CHANNEL, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &mixcom_read_proc;
+       new_file->write_proc = &mixcom_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_TWIN, S_IFREG | 0444, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &mixcom_read_proc;
+       new_file->write_proc = &mixcom_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->nlink = 1;
+
+       setup_twin(dev);
+
+       /* Fill in ch_struct hw specific pointers */
+       ch->HW_access_board = NULL;
+       ch->HW_release_board = NULL;
+       ch->HW_txe = MIXCOM_txe;
+       ch->HW_open = MIXCOM_open;
+       ch->HW_close = MIXCOM_close;
+       ch->HW_send_packet = MIXCOM_send_packet;
+       ch->HW_statistics = MIXCOM_statistics;
+       ch->HW_set_clock = NULL;
+
+       dev->base_addr = MIXCOM_DEV_BASE(MIXCOM_DEFAULT_IO,0);
+       dev->irq = MIXCOM_DEFAULT_IRQ;
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int MIXCOM_exit(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct mixcom_privdata *hw = ch->HW_privdata;
+
+       if(hw->channel==0 && TWIN(dev)) {
+               return -EBUSY;
+       }
+
+       if(hw->channel==1 && TWIN(dev)) {
+               TWIN(TWIN(dev))=NULL;
+       }
+
+       kfree(ch->HW_privdata);
+       remove_proc_entry(FILENAME_IO, ch->procdir);
+       remove_proc_entry(FILENAME_IRQ, ch->procdir);
+#if 0
+       remove_proc_entry(FILENAME_CLOCK, ch->procdir);
+#endif
+       remove_proc_entry(FILENAME_CHANNEL, ch->procdir);
+       remove_proc_entry(FILENAME_TWIN, ch->procdir);
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static struct comx_hardware mixcomhw = {
+       "mixcom",
+       VERSION,
+       MIXCOM_init, 
+       MIXCOM_exit,
+       MIXCOM_dump,
+       NULL
+};
+       
+/* Module management */
+
+#ifdef MODULE
+#define comx_hw_mixcom_init init_module
+#endif
+
+__initfunc(int comx_hw_mixcom_init(void))
+{
+       return(comx_register_hardware(&mixcomhw));
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+       comx_unregister_hardware("mixcom");
+}
+#endif
diff --git a/drivers/net/comx-proto-fr.c b/drivers/net/comx-proto-fr.c
new file mode 100644 (file)
index 0000000..8477071
--- /dev/null
@@ -0,0 +1,1012 @@
+/*
+ * Frame-relay protocol module for the COMX driver 
+ * for Linux 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1998-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.70 (99/06/14):
+ *             - cleaned up the source code a bit
+ *             - ported back to kernel, now works as builtin code 
+ *
+ * Version 0.71 (99/06/25):
+ *             - use skb priorities and queues for sending keepalive
+ *             - use device queues for slave->master data transmit
+ *             - set IFF_RUNNING only line protocol up
+ *             - fixes on slave device flags
+ * 
+ * Version 0.72 (99/07/09):
+ *             - handle slave tbusy with master tbusy (should be fixed)
+ *             - fix the keepalive timer addition/deletion
+ */
+
+#define VERSION "0.72"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <linux/pkt_sched.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include "comx.h"
+#include "comxhw.h"
+
+MODULE_AUTHOR("Author: Tivadar Szemethy <tiv@itc.hu>");
+MODULE_DESCRIPTION("Frame Relay protocol implementation for the COMX drivers"
+       "for Linux kernel 2.2.X");
+
+#define        FRAD_UI         0x03
+#define        NLPID_IP        0xcc
+#define        NLPID_Q933_LMI  0x08
+#define        NLPID_CISCO_LMI 0x09    
+#define Q933_ENQ       0x75
+#define        Q933_LINESTAT   0x51
+#define        Q933_COUNTERS   0x53
+
+#define        MAXALIVECNT     3               /* No. of failures */
+
+struct fr_data {
+       u16     dlci;
+       struct  device *master;
+       char    keepa_pend;
+       char    keepa_freq;
+       char    keepalivecnt, keeploopcnt;
+       struct  timer_list keepa_timer;
+       u8      local_cnt, remote_cnt;
+};
+
+static struct comx_protocol fr_master_protocol;
+static struct comx_protocol fr_slave_protocol;
+static struct comx_hardware fr_dlci;
+
+static void fr_keepalive_send(struct device *dev) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct sk_buff *skb;
+       u8 *fr_packet;
+       
+       skb=alloc_skb(dev->hard_header_len + 13, GFP_ATOMIC);
+       
+       if(skb==NULL)
+               return;
+               
+        skb_reserve(skb, dev->hard_header_len);
+        
+        fr_packet=(u8*)skb_put(skb, 13);
+                 
+       fr_packet[0] = (fr->dlci & (1024 - 15)) >> 2;
+       fr_packet[1] = (fr->dlci & 15) << 4 | 1;        // EA bit 1
+       fr_packet[2] = FRAD_UI;
+       fr_packet[3] = NLPID_Q933_LMI;
+       fr_packet[4] = 0;
+       fr_packet[5] = Q933_ENQ;
+       fr_packet[6] = Q933_LINESTAT;
+       fr_packet[7] = 0x01;
+       fr_packet[8] = 0x01;
+       fr_packet[9] = Q933_COUNTERS;
+       fr_packet[10] = 0x02;
+       fr_packet[11] = ++fr->local_cnt;
+       fr_packet[12] = fr->remote_cnt;
+
+       skb->dev = dev;
+       skb->priority = TC_PRIO_CONTROL;
+       dev_queue_xmit(skb);
+}
+
+static void fr_keepalive_timerfun(unsigned long d) 
+{
+       struct device *dev = (struct device *)d;
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+       struct comx_channel *sch;
+       struct fr_data *sfr;
+       struct device *sdev;
+
+       if (ch->init_status & LINE_OPEN) {
+               if (fr->keepalivecnt == MAXALIVECNT) {
+                       comx_status(dev, ch->line_status & ~PROTO_UP);
+                       dev->flags &= ~IFF_RUNNING;
+                       for (; dir ; dir = dir->next) {
+                               if(!S_ISDIR(dir->mode)) {
+                                   continue;
+                               }
+       
+                               if ((sdev = dir->data) && (sch = sdev->priv) && 
+                                   (sdev->type == ARPHRD_DLCI) && 
+                                   (sfr = sch->LINE_privdata) 
+                                   && (sfr->master == dev) && 
+                                   (sdev->flags & IFF_UP)) {
+                                       sdev->flags &= ~IFF_RUNNING;
+                                       comx_status(sdev, 
+                                               sch->line_status & ~PROTO_UP);
+                               }
+                       }
+               }
+               if (fr->keepalivecnt <= MAXALIVECNT) {
+                       ++fr->keepalivecnt;
+               }
+               fr_keepalive_send(dev);
+       }
+       mod_timer(&fr->keepa_timer, jiffies + HZ * fr->keepa_freq);
+}
+
+static void fr_rx_lmi(struct device *dev, struct sk_buff *skb, 
+       u16 dlci, u8 nlpid) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+       struct comx_channel *sch;
+       struct fr_data *sfr;
+       struct device *sdev;
+
+       if (dlci != fr->dlci || nlpid != NLPID_Q933_LMI || !fr->keepa_freq) {
+               return;
+       }
+
+       fr->remote_cnt = skb->data[7];
+       if (skb->data[8] == fr->local_cnt) { // keepalive UP!
+               fr->keepalivecnt = 0;
+               if ((ch->line_status & LINE_UP) && 
+                   !(ch->line_status & PROTO_UP)) {
+                       comx_status(dev, ch->line_status |= PROTO_UP);
+                       dev->flags |= IFF_RUNNING;
+                       for (; dir ; dir = dir->next) {
+                               if(!S_ISDIR(dir->mode)) {
+                                   continue;
+                               }
+       
+                               if ((sdev = dir->data) && (sch = sdev->priv) && 
+                                   (sdev->type == ARPHRD_DLCI) && 
+                                   (sfr = sch->LINE_privdata) 
+                                   && (sfr->master == dev) && 
+                                   (sdev->flags & IFF_UP)) {
+                                       sdev->flags |= IFF_RUNNING;
+                                       comx_status(sdev, 
+                                               sch->line_status | PROTO_UP);
+                               }
+                       }
+               }
+       }
+}
+
+static void fr_set_keepalive(struct device *dev, int keepa) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+
+       if (!keepa && fr->keepa_freq) { // switch off
+               fr->keepa_freq = 0;
+               if (ch->line_status & LINE_UP) {
+                       comx_status(dev, ch->line_status | PROTO_UP);
+                       dev->flags |= IFF_RUNNING;
+                       del_timer(&fr->keepa_timer);
+               }
+               return;
+       }
+
+       if (keepa) { // bekapcs
+               if(fr->keepa_freq && (ch->line_status & LINE_UP)) {
+                       del_timer(&fr->keepa_timer);
+               }
+               fr->keepa_freq = keepa;
+               fr->local_cnt = fr->remote_cnt = 0;
+               fr->keepa_timer.expires = jiffies + HZ;
+               fr->keepa_timer.function = fr_keepalive_timerfun;
+               fr->keepa_timer.data = (unsigned long)dev;
+               ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+               dev->flags &= ~IFF_RUNNING;
+               comx_status(dev, ch->line_status);
+               if(ch->line_status & LINE_UP) {
+                       add_timer(&fr->keepa_timer);
+               }
+       }
+}
+
+static void fr_rx(struct device *dev, struct sk_buff *skb) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+       struct device *sdev = dev;
+       struct comx_channel *sch;
+       struct fr_data *sfr;
+       u16 dlci;
+       u8 nlpid;
+
+       if(skb->len <= 4 || skb->data[2] != FRAD_UI) {
+               kfree_skb(skb);
+               return;
+       }
+
+       /* Itt majd ki kell talalni, melyik slave kapja a csomagot */
+       dlci = ((skb->data[0] & 0xfc) << 2) | ((skb->data[1] & 0xf0) >> 4);
+       if ((nlpid = skb->data[3]) == 0) { // Optional padding 
+               nlpid = skb->data[4];
+               skb_pull(skb, 1);
+       }
+       skb_pull(skb, 4);       /* DLCI and header throw away */
+
+       if (ch->debug_flags & DEBUG_COMX_DLCI) {
+               comx_debug(dev, "Frame received, DLCI: %d, NLPID: 0x%02x\n", 
+                       dlci, nlpid);
+               comx_debug_skb(dev, skb, "Contents");
+       }
+
+       /* Megkeressuk, kihez tartozik */
+       for (; dir ; dir = dir->next) {
+               if(!S_ISDIR(dir->mode)) {
+                       continue;
+               }
+               if ((sdev = dir->data) && (sch = sdev->priv) && 
+                   (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+                   (sfr->master == dev) && (sfr->dlci == dlci)) {
+                       skb->dev = sdev;        
+                       if (ch->debug_flags & DEBUG_COMX_DLCI) {
+                               comx_debug(dev, "Passing it to %s\n",sdev->name);
+                       }
+                       if (dev != sdev) {
+                               sch->stats.rx_packets++;
+                               sch->stats.rx_bytes += skb->len;
+                       }
+                       break;
+               }
+       }
+       switch(nlpid) {
+               case NLPID_IP:
+                       skb->protocol = htons(ETH_P_IP);
+                       skb->mac.raw = skb->data;
+                       comx_rx(sdev, skb);
+                       break;
+               case NLPID_Q933_LMI:
+                       fr_rx_lmi(dev, skb, dlci, nlpid);
+               default:
+                       kfree_skb(skb);
+                       break;
+       }
+}
+
+static int fr_tx(struct device *dev) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+       struct device *sdev;
+       struct comx_channel *sch;
+       struct fr_data *sfr;
+       int cnt = 1;
+
+       /* Ha minden igaz, 2 helyen fog allni a tbusy: a masternel, 
+          es annal a slave-nel aki eppen kuldott.
+          Egy helyen akkor all, ha a master kuldott.
+          Ez megint jo lesz majd, ha utemezni akarunk */
+          
+       /* This should be fixed, the slave tbusy should be set when 
+          the masters queue is full and reset when not */
+
+       for (; dir ; dir = dir->next) {
+               if(!S_ISDIR(dir->mode)) {
+                   continue;
+               }
+               if ((sdev = dir->data) && (sch = sdev->priv) && 
+                   (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+                   (sfr->master == dev) && (sdev->tbusy)) {
+                       clear_bit(0, &sdev->tbusy);
+//                     printk("%s: clearing tbusy\n",sdev->name);
+                       cnt++;
+               }
+       }
+
+       clear_bit(0, &dev->tbusy);      // ezt nem talalja meg, mert ez FRAD
+//     printk("%s: clearing tbusy\n", dev->name);
+
+
+       mark_bh(NET_BH);
+       return 0;
+}
+
+static void fr_status(struct device *dev, unsigned short status)
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+       struct device *sdev;
+       struct comx_channel *sch;
+       struct fr_data *sfr;
+
+       if (status & LINE_UP) {
+               if (!fr->keepa_freq) {
+                       status |= PROTO_UP;
+               }
+       } else {
+               status &= ~(PROTO_UP | PROTO_LOOP);
+       }
+
+       if (dev == fr->master && fr->keepa_freq) {
+               if (status & LINE_UP) {
+                       fr->keepa_timer.expires = jiffies + HZ;
+                       add_timer(&fr->keepa_timer);
+                       fr->keepalivecnt = MAXALIVECNT + 1;
+                       fr->keeploopcnt = 0;
+               } else {
+                       del_timer(&fr->keepa_timer);
+               }
+       }
+               
+       /* Itt a status valtozast vegig kell vinni az osszes slave-n */
+       for (; dir ; dir = dir->next) {
+               if(!S_ISDIR(dir->mode)) {
+                   continue;
+               }
+       
+               if ((sdev = dir->data) && (sch = sdev->priv) && 
+                   (sdev->type == ARPHRD_FRAD || sdev->type == ARPHRD_DLCI) && 
+                   (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+                       if(status & LINE_UP) {
+                               sdev->tbusy = 0; 
+//                             printk("%s: clearing tbusy\n",sdev->name);
+                       }
+                       comx_status(sdev, status);
+                       if(status & (PROTO_UP | PROTO_LOOP)) {
+                               dev->flags |= IFF_RUNNING;
+                       } else {
+                               dev->flags &= ~IFF_RUNNING;
+                       }
+               }
+       }
+}
+
+static int fr_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct proc_dir_entry *comxdir = ch->procdir;
+       struct comx_channel *mch;
+
+       if (!(ch->init_status & HW_OPEN)) {
+               return -ENODEV;
+       }
+
+       if ((ch->hardware == &fr_dlci && ch->protocol != &fr_slave_protocol) ||
+           (ch->protocol == &fr_slave_protocol && ch->hardware != &fr_dlci)) {
+               printk(KERN_ERR "Trying to open an improperly set FR interface, giving up\n");
+               return -EINVAL;
+       }
+
+       if (!fr->master) {
+               return -ENODEV;
+       }
+       mch = fr->master->priv;
+       if (fr->master != dev && (!(mch->init_status & LINE_OPEN) 
+          || (mch->protocol != &fr_master_protocol))) {
+               printk(KERN_ERR "Master %s is inactive, or incorrectly set up, "
+                       "unable to open %s\n", fr->master->name, dev->name);
+               return -ENODEV;
+       }
+
+       ch->init_status |= LINE_OPEN;
+       ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+       dev->flags &= ~IFF_RUNNING;
+
+       if (fr->master == dev) {
+               if (fr->keepa_freq) {
+                       fr->keepa_timer.function = fr_keepalive_timerfun;
+                       fr->keepa_timer.data = (unsigned long)dev;
+                       add_timer(&fr->keepa_timer);
+               } else {
+                       if (ch->line_status & LINE_UP) {
+                               ch->line_status |= PROTO_UP;
+                               dev->flags |= IFF_RUNNING;
+                       }
+               }
+       } else {
+               ch->line_status = mch->line_status;
+               if(fr->master->flags & IFF_RUNNING) {
+                       dev->flags |= IFF_RUNNING;
+               }
+       }
+
+       for (; comxdir ; comxdir = comxdir->next) {
+               if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+                  strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+                  strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+                       comxdir->mode = S_IFREG | 0444;
+               }
+       }
+//     comx_status(dev, ch->line_status);
+       return 0;
+}
+
+static int fr_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct proc_dir_entry *comxdir = ch->procdir;
+
+       if (fr->master == dev) { // Ha master 
+               struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+               struct device *sdev = dev;
+               struct comx_channel *sch;
+               struct fr_data *sfr;
+
+               if (!(ch->init_status & HW_OPEN)) {
+                       return -ENODEV;
+               }
+
+               if (fr->keepa_freq) {
+                       del_timer(&fr->keepa_timer);
+               }
+               
+               for (; dir ; dir = dir->next) {
+                       if(!S_ISDIR(dir->mode)) {
+                               continue;
+                       }
+                       if ((sdev = dir->data) && (sch = sdev->priv) && 
+                           (sdev->type == ARPHRD_DLCI) && 
+                           (sfr = sch->LINE_privdata) &&
+                           (sfr->master == dev) && 
+                           (sch->init_status & LINE_OPEN)) {
+                               dev_close(sdev);
+                       }
+               }
+       }
+
+       ch->init_status &= ~LINE_OPEN;
+       ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+       dev->flags &= ~IFF_RUNNING;
+
+       for (; comxdir ; comxdir = comxdir->next) {
+               if (strcmp(comxdir->name, FILENAME_DLCI) == 0 ||
+                   strcmp(comxdir->name, FILENAME_MASTER) == 0 ||
+                   strcmp(comxdir->name, FILENAME_KEEPALIVE) == 0) {
+                       comxdir->mode = S_IFREG | 0444;
+               }
+       }
+
+       return 0;
+}
+
+static int fr_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct comx_channel *sch, *mch;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct fr_data *sfr;
+       struct device *sdev;
+       struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+       if (!fr->master) {
+               printk(KERN_ERR "BUG: fr_xmit without a master!!! dev: %s\n", dev->name);
+               return 0;
+       }
+
+       mch = fr->master->priv;
+
+       /* Ennek majd a slave utemezeskor lesz igazan jelentosege */
+       if (ch->debug_flags & DEBUG_COMX_DLCI) {
+               comx_debug_skb(dev, skb, "Sending frame");
+       }
+
+       if (dev != fr->master) {
+               struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+               newskb->dev=fr->master;
+               dev_queue_xmit(newskb);
+               dev_kfree_skb(skb);
+               ch->stats.tx_packets++;
+               ch->stats.tx_bytes += skb->len;
+       } else {
+               set_bit(0, &dev->tbusy);
+               for (; dir ; dir = dir->next) {
+                       if(!S_ISDIR(dir->mode)) {
+                           continue;
+                       }
+                       if ((sdev = dir->data) && (sch = sdev->priv) && 
+                           (sdev->type == ARPHRD_DLCI) && (sfr = sch->LINE_privdata) &&
+                           (sfr->master == dev) && (sdev->tbusy)) {
+                               set_bit(0, &sdev->tbusy);
+//                             printk("%s: clearing tbusy\n",sdev->name);
+                       }
+               }
+                       
+//             printk("%s: set tbusy\n", dev->name);
+               switch(mch->HW_send_packet(dev, skb)) {
+                       case FRAME_QUEUED:
+                               clear_bit(0, &dev->tbusy);
+//                             printk("%s: clear tbusy\n", dev->name);
+                               break;
+                       case FRAME_ACCEPTED:
+                       case FRAME_DROPPED:
+                               break;
+                       case FRAME_ERROR:
+                               printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
+                                       dev->name, skb->len);
+                               break;
+               }
+       }
+       return 0;
+}
+
+static int fr_header(struct sk_buff *skb, struct device *dev, 
+       unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+
+       skb_push(skb, dev->hard_header_len);      
+       /* Put in DLCI */
+       skb->data[0] = (fr->dlci & (1024 - 15)) >> 2;
+       skb->data[1] = (fr->dlci & 15) << 4 | 1;        // EA bit 1
+       skb->data[2] = FRAD_UI;
+       skb->data[3] = NLPID_IP;
+
+       return dev->hard_header_len;  
+}
+
+static int fr_statistics(struct device *dev, char *page) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       int len = 0;
+
+       if (fr->master == dev) {
+               struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+               struct device *sdev;
+               struct comx_channel *sch;
+               struct fr_data *sfr;
+               int slaves = 0;
+
+               len += sprintf(page + len, 
+                       "This is a Frame Relay master device\nSlaves: ");
+               for (; dir ; dir = dir->next) {
+                       if(!S_ISDIR(dir->mode)) {
+                               continue;
+                       }
+                       if ((sdev = dir->data) && (sch = sdev->priv) && 
+                           (sdev->type == ARPHRD_DLCI) &&
+                           (sfr = sch->LINE_privdata) && 
+                           (sfr->master == dev) && (sdev != dev)) {
+                               slaves++;
+                               len += sprintf(page + len, "%s ", sdev->name);
+                       }
+               }
+               len += sprintf(page + len, "%s\n", slaves ? "" : "(none)");
+               if (fr->keepa_freq) {
+                       len += sprintf(page + len, "Line keepalive (value %d) "
+                               "status %s [%d]\n", fr->keepa_freq, 
+                               ch->line_status & PROTO_LOOP ? "LOOP" :
+                               ch->line_status & PROTO_UP ? "UP" : "DOWN", 
+                               fr->keepalivecnt);
+               } else {
+                       len += sprintf(page + len, "Line keepalive protocol "
+                               "is not set\n");
+               }
+       } else {                // if slave
+               len += sprintf(page + len, 
+                       "This is a Frame Relay slave device, master: %s\n",
+                       fr->master ? fr->master->name : "(not set)");
+       }
+       return len;
+}
+
+static int fr_read_proc(char *page, char **start, off_t off, int count,
+       int *eof, void *data)
+{
+       struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+       struct device *dev = file->parent->data;
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = NULL;
+       int len = 0;
+
+       if (ch) {
+               fr = ch->LINE_privdata;
+       }
+
+       if (strcmp(file->name, FILENAME_DLCI) == 0) {
+               len = sprintf(page, "%04d\n", fr->dlci);
+       } else if (strcmp(file->name, FILENAME_MASTER) == 0) {
+               len = sprintf(page, "%-9s\n", fr->master ? fr->master->name :
+                       "(none)");
+       } else if (strcmp(file->name, FILENAME_KEEPALIVE) == 0) {
+               len = fr->keepa_freq ? sprintf(page, "% 3d\n", fr->keepa_freq) 
+                       : sprintf(page, "off\n");
+       } else {
+               printk(KERN_ERR "comxfr: internal error, filename %s\n", file->name);
+               return -EBADF;
+       }
+
+       if (off >= len) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+       if (count >= len - off) *eof = 1;
+       return ( min(count, len - off) );
+}
+
+static int fr_write_proc(struct file *file, const char *buffer, 
+       u_long count, void *data)
+{
+       struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+       struct device *dev = entry->parent->data;
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = NULL; 
+       char *page;
+
+       if (ch) {
+               fr = ch->LINE_privdata;
+       }
+
+       if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+               printk(KERN_ERR "comxfr_write_proc: file <-> data internal error\n");
+               return -EIO;
+       }
+
+       if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+               return -ENOMEM;
+       }
+
+       copy_from_user(page, buffer, count);
+       if (*(page + count - 1) == '\n') {
+               *(page + count - 1) = 0;
+       }
+
+       if (strcmp(entry->name, FILENAME_DLCI) == 0) {
+               u16 dlci_new = simple_strtoul(page, NULL, 10);
+
+               if (dlci_new > 1023) {
+                       printk(KERN_ERR "Invalid DLCI value\n");
+               }
+               else fr->dlci = dlci_new;
+       } else if (strcmp(entry->name, FILENAME_MASTER) == 0) {
+               struct device *new_master = dev_get(page);
+
+               if (new_master && new_master->type == ARPHRD_FRAD) {
+                       struct comx_channel *sch = new_master->priv;
+                       struct fr_data *sfr = sch->LINE_privdata;
+
+                       if (sfr && sfr->master == new_master) {
+                               fr->master = new_master;
+                               /* Megorokli a master statuszat */
+                               ch->line_status = sch->line_status;
+                       }
+               }
+       } else if (strcmp(entry->name, FILENAME_KEEPALIVE) == 0) {
+               int keepa_new = -1;
+
+               if (strcmp(page, KEEPALIVE_OFF) == 0) {
+                       keepa_new = 0;
+               } else {
+                       keepa_new = simple_strtoul(page, NULL, 10);
+               }
+
+               if (keepa_new < 0 || keepa_new > 100) {
+                       printk(KERN_ERR "invalid keepalive\n");
+               } else {
+                       if (fr->keepa_freq && keepa_new != fr->keepa_freq) {
+                               fr_set_keepalive(dev, 0);
+                       }
+                       if (keepa_new) {
+                               fr_set_keepalive(dev, keepa_new);
+                       }
+               }
+       } else {
+               printk(KERN_ERR "comxfr_write_proc: internal error, filename %s\n", 
+                       entry->name);
+               return -EBADF;
+       }
+
+       free_page((unsigned long)page);
+       return count;
+}
+
+static int fr_exit(struct device *dev) 
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+       struct device *sdev = dev;
+       struct comx_channel *sch;
+       struct fr_data *sfr;
+       struct proc_dir_entry *dir = ch->procdir->parent->subdir;
+
+       /* Ha lezarunk egy master-t, le kell kattintani a slave-eket is */
+       if (fr->master && fr->master == dev) {
+               for (; dir ; dir = dir->next) {
+                       if(!S_ISDIR(dir->mode)) {
+                               continue;
+                       }
+                       if ((sdev = dir->data) && (sch = sdev->priv) && 
+                           (sdev->type == ARPHRD_DLCI) && 
+                           (sfr = sch->LINE_privdata) && (sfr->master == dev)) {
+                               dev_close(sdev);
+                               sfr->master = NULL;
+                       }
+               }
+       }
+       dev->flags              = 0;
+       dev->type               = 0;
+       dev->mtu                = 0;
+       dev->hard_header_len    = 0;
+
+       ch->LINE_rx     = NULL;
+       ch->LINE_tx     = NULL;
+       ch->LINE_status = NULL;
+       ch->LINE_open   = NULL;
+       ch->LINE_close  = NULL;
+       ch->LINE_xmit   = NULL;
+       ch->LINE_header = NULL;
+       ch->LINE_rebuild_header = NULL;
+       ch->LINE_statistics = NULL;
+
+       ch->LINE_status = 0;
+
+       if (fr->master != dev) { // if not master, remove dlci
+               remove_proc_entry(FILENAME_DLCI, ch->procdir);
+               remove_proc_entry(FILENAME_MASTER, ch->procdir);
+       } else {
+               if (fr->keepa_freq) {
+                       fr_set_keepalive(dev, 0);
+               }
+               remove_proc_entry(FILENAME_KEEPALIVE, ch->procdir);
+               remove_proc_entry(FILENAME_DLCI, ch->procdir);
+       }
+
+       kfree(fr);
+       ch->LINE_privdata = NULL;
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int fr_master_init(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr;
+       struct proc_dir_entry *new_file;
+
+       if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
+           GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+       memset(fr, 0, sizeof(struct fr_data));
+       fr->master = dev;       // this means master
+       fr->dlci = 0;           // let's say default
+
+       dev->flags      = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+       dev->type       = ARPHRD_FRAD;
+       dev->mtu        = 1500;
+       dev->hard_header_len    = 4;            
+       dev->addr_len   = 0;
+
+       ch->LINE_rx     = fr_rx;
+       ch->LINE_tx     = fr_tx;
+       ch->LINE_status = fr_status;
+       ch->LINE_open   = fr_open;
+       ch->LINE_close  = fr_close;
+       ch->LINE_xmit   = fr_xmit;
+       ch->LINE_header = fr_header;
+       ch->LINE_rebuild_header = NULL;
+       ch->LINE_statistics = fr_statistics;
+
+       if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -ENOMEM;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &fr_read_proc;
+       new_file->write_proc = &fr_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 5;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_KEEPALIVE, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -ENOMEM;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &fr_read_proc;
+       new_file->write_proc = &fr_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 4;
+       new_file->nlink = 1;
+
+       fr_set_keepalive(dev, 0);
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int fr_slave_init(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr;
+       struct proc_dir_entry *new_file;
+
+       if ((fr = ch->LINE_privdata = kmalloc(sizeof(struct fr_data), 
+           GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+       memset(fr, 0, sizeof(struct fr_data));
+
+       dev->flags      = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+       dev->type       = ARPHRD_DLCI;
+       dev->mtu        = 1500;
+       dev->hard_header_len    = 4;            
+       dev->addr_len   = 0;
+
+       ch->LINE_rx     = fr_rx;
+       ch->LINE_tx     = fr_tx;
+       ch->LINE_status = fr_status;
+       ch->LINE_open   = fr_open;
+       ch->LINE_close  = fr_close;
+       ch->LINE_xmit   = fr_xmit;
+       ch->LINE_header = fr_header;
+       ch->LINE_rebuild_header = NULL;
+       ch->LINE_statistics = fr_statistics;
+
+       if ((new_file = create_proc_entry(FILENAME_DLCI, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -ENOMEM;
+       }
+       
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &fr_read_proc;
+       new_file->write_proc = &fr_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 5;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_MASTER, S_IFREG | 0644, 
+           ch->procdir)) == NULL) {
+               return -EIO;
+       }
+       new_file->data = (void *)new_file;
+       new_file->read_proc = &fr_read_proc;
+       new_file->write_proc = &fr_write_proc;
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->size = 10;
+       new_file->nlink = 1;
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int dlci_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+
+       ch->init_status |= HW_OPEN;
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int dlci_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+
+       ch->init_status &= ~HW_OPEN;
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int dlci_txe(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct fr_data *fr = ch->LINE_privdata;
+
+       if (!fr->master) {
+               return 0;
+       }
+
+       ch = fr->master->priv;
+       fr = ch->LINE_privdata;
+       return ch->HW_txe(fr->master);
+}
+
+static int dlci_statistics(struct device *dev, char *page) 
+{
+       return 0;
+}
+
+static int dlci_init(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+
+       ch->HW_open = dlci_open;
+       ch->HW_close = dlci_close;
+       ch->HW_txe = dlci_txe;
+       ch->HW_statistics = dlci_statistics;
+
+       /* Nincs egyeb hw info, mert ugyis a fr->master-bol fog minden kiderulni */
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int dlci_exit(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+
+       ch->HW_open = NULL;
+       ch->HW_close = NULL;
+       ch->HW_txe = NULL;
+       ch->HW_statistics = NULL;
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int dlci_dump(struct device *dev)
+{
+       printk(KERN_INFO "dlci_dump %s, HOGY MI ???\n", dev->name);
+       return -1;
+}
+
+static struct comx_protocol fr_master_protocol = {
+       "frad", 
+       VERSION,
+       ARPHRD_FRAD, 
+       fr_master_init, 
+       fr_exit, 
+       NULL 
+};
+
+static struct comx_protocol fr_slave_protocol = {
+       "ietf-ip", 
+       VERSION,
+       ARPHRD_DLCI, 
+       fr_slave_init, 
+       fr_exit, 
+       NULL 
+};
+
+static struct comx_hardware fr_dlci = { 
+       "dlci", 
+       VERSION,
+       dlci_init, 
+       dlci_exit, 
+       dlci_dump, 
+       NULL 
+};
+
+#ifdef MODULE
+#define comx_proto_fr_init init_module
+#endif
+
+__initfunc(int comx_proto_fr_init(void))
+{
+       int ret; 
+
+       if ((ret = comx_register_hardware(&fr_dlci))) {
+               return ret;
+       }
+       if ((ret = comx_register_protocol(&fr_master_protocol))) {
+               return ret;
+       }
+       return comx_register_protocol(&fr_slave_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       comx_unregister_hardware(fr_dlci.name);
+       comx_unregister_protocol(fr_master_protocol.name);
+       comx_unregister_protocol(fr_slave_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/comx-proto-lapb.c b/drivers/net/comx-proto-lapb.c
new file mode 100644 (file)
index 0000000..658ae4d
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * LAPB protocol module for the COMX driver 
+ * for Linux kernel 2.2.X
+ *
+ * Original author: Tivadar Szemethy <tiv@itc.hu>
+ * Maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1997-1999 (C) ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ * Version 0.80 (99/06/14):
+ *             - cleaned up the source code a bit
+ *             - ported back to kernel, now works as non-module
+ * 
+ */
+
+#define VERSION "0.80"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/lapb.h>
+#include <linux/init.h>
+
+#include       "comx.h"
+#include       "comxhw.h"
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+       int size, struct proc_dir_entry *dir);
+
+static void comxlapb_rx(struct device *dev, struct sk_buff *skb) 
+{
+       if (!dev || !dev->priv) {
+               dev_kfree_skb(skb);
+       } else {
+               lapb_data_received(dev->priv, skb);
+       }
+}
+
+static int comxlapb_tx(struct device *dev) 
+{
+       clear_bit(0, &dev->tbusy);
+       mark_bh(NET_BH);
+       return 0;
+}
+
+static int comxlapb_header(struct sk_buff *skb, struct device *dev, 
+       unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+       return dev->hard_header_len;  
+}
+
+static void comxlapb_status(struct device *dev, unsigned short status)
+{
+       struct comx_channel *ch;
+
+       if (!dev || !(ch = dev->priv)) {
+               return;
+       }
+       if (status & LINE_UP) {
+               clear_bit(0, &dev->tbusy);
+       }
+       comx_status(dev, status);
+}
+
+static int comxlapb_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       int err = 0;
+
+       if (!(ch->init_status & HW_OPEN)) {
+               return -ENODEV;
+       }
+
+       err = lapb_connect_request(ch);
+
+       if (ch->debug_flags & DEBUG_COMX_LAPB) {
+               comx_debug(dev, "%s: lapb opened, error code: %d\n", 
+                       dev->name, err);
+       }
+
+       if (!err) {
+               ch->init_status |= LINE_OPEN;
+               MOD_INC_USE_COUNT;
+       }
+       return err;
+}
+
+static int comxlapb_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+
+       if (!(ch->init_status & HW_OPEN)) {
+               return -ENODEV;
+       }
+
+       if (ch->debug_flags & DEBUG_COMX_LAPB) {
+               comx_debug(dev, "%s: lapb closed\n", dev->name);
+       }
+
+       lapb_disconnect_request(ch);
+
+       ch->init_status &= ~LINE_OPEN;
+       ch->line_status &= ~PROTO_UP;
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int comxlapb_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct sk_buff *skb2;
+
+       if (!dev || !(ch = dev->priv) || !(dev->flags & (IFF_UP | IFF_RUNNING))) {
+               return -ENODEV;
+       }
+
+       if (dev->type == ARPHRD_X25) { // first byte tells what to do 
+               switch(skb->data[0]) {
+                       case 0x00:      
+                               break;  // transmit
+                       case 0x01:      
+                               lapb_connect_request(ch);
+                               kfree_skb(skb);
+                               return 0;
+                       case 0x02:      
+                               lapb_disconnect_request(ch);
+                       default:
+                               kfree_skb(skb);
+                               return 0;
+               }
+               skb_pull(skb,1);
+       }
+
+       if (test_and_set_bit(0, &dev->tbusy)) {
+               ch->stats.tx_errors++;
+               return 1;
+       }
+
+       if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+               lapb_data_request(ch, skb2);
+       }
+
+       return FRAME_ACCEPTED;
+}
+
+static int comxlapb_statistics(struct device *dev, char *page) 
+{
+       struct lapb_parms_struct parms;
+       int len = 0;
+
+       len += sprintf(page + len, "Line status: ");
+       if (lapb_getparms(dev->priv, &parms) != LAPB_OK) {
+               len += sprintf(page + len, "not initialized\n");
+               return len;
+       }
+       len += sprintf(page + len, "%s (%s), T1: %d/%d, T2: %d/%d, N2: %d/%d, "
+               "window: %d\n", parms.mode & LAPB_DCE ? "DCE" : "DTE", 
+               parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD",
+               parms.t1timer, parms.t1, parms.t2timer, parms.t2, 
+               parms.n2count, parms.n2, parms.window);
+
+       return len;
+}
+
+static int comxlapb_read_proc(char *page, char **start, off_t off, int count,
+       int *eof, void *data)
+{
+       struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+       struct device *dev = file->parent->data;
+       struct lapb_parms_struct parms;
+       int len = 0;
+
+       if (lapb_getparms(dev->priv, &parms)) {
+               return -ENODEV;
+       }
+
+       if (strcmp(file->name, FILENAME_T1) == 0) {
+               len += sprintf(page + len, "%02u / %02u\n", 
+                       parms.t1timer, parms.t1);
+       } else if (strcmp(file->name, FILENAME_T2) == 0) {
+               len += sprintf(page + len, "%02u / %02u\n", 
+                       parms.t2timer, parms.t2);
+       } else if (strcmp(file->name, FILENAME_N2) == 0) {
+               len += sprintf(page + len, "%02u / %02u\n", 
+                       parms.n2count, parms.n2);
+       } else if (strcmp(file->name, FILENAME_WINDOW) == 0) {
+               len += sprintf(page + len, "%u\n", parms.window);
+       } else if (strcmp(file->name, FILENAME_MODE) == 0) {
+               len += sprintf(page + len, "%s, %s\n", 
+                       parms.mode & LAPB_DCE ? "DCE" : "DTE",
+                       parms.mode & LAPB_EXTENDED ? "EXTENDED" : "STANDARD");
+       } else {
+               printk(KERN_ERR "comxlapb: internal error, filename %s\n", file->name);
+               return -EBADF;
+       }
+
+       if (off >= len) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+       if (count >= len - off) {
+               *eof = 1;
+       }
+       return ( min(count, len - off) );
+}
+
+static int comxlapb_write_proc(struct file *file, const char *buffer, 
+       u_long count, void *data)
+{
+       struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+       struct device *dev = entry->parent->data;
+       struct lapb_parms_struct parms;
+       unsigned long parm;
+       char *page;
+
+       if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+               printk(KERN_ERR "comxlapb_write_proc: file <-> data internal error\n");
+               return -EIO;
+       }
+
+       if (lapb_getparms(dev->priv, &parms)) {
+               return -ENODEV;
+       }
+
+       if (!(page = (char *)__get_free_page(GFP_KERNEL))) {
+               return -ENOMEM;
+       }
+
+       copy_from_user(page, buffer, count);
+       if (*(page + count - 1) == '\n') {
+               *(page + count - 1) = 0;
+       }
+
+       if (strcmp(entry->name, FILENAME_T1) == 0) {
+               parm=simple_strtoul(page,NULL,10);
+               if (parm > 0 && parm < 100) {
+                       parms.t1=parm;
+                       lapb_setparms(dev->priv, &parms);
+               }
+       } else if (strcmp(entry->name, FILENAME_T2) == 0) {
+               parm=simple_strtoul(page, NULL, 10);
+               if (parm > 0 && parm < 100) {
+                       parms.t2=parm;
+                       lapb_setparms(dev->priv, &parms);
+               }
+       } else if (strcmp(entry->name, FILENAME_N2) == 0) {
+               parm=simple_strtoul(page, NULL, 10);
+               if (parm > 0 && parm < 100) {
+                       parms.n2=parm;
+                       lapb_setparms(dev->priv, &parms);
+               }
+       } else if (strcmp(entry->name, FILENAME_WINDOW) == 0) {
+               parms.window = simple_strtoul(page, NULL, 10);
+               lapb_setparms(dev->priv, &parms);
+       } else if (strcmp(entry->name, FILENAME_MODE) == 0) {
+               if (comx_strcasecmp(page, "dte") == 0) {
+                       parms.mode &= ~(LAPB_DCE | LAPB_DTE); 
+                       parms.mode |= LAPB_DTE;
+               } else if (comx_strcasecmp(page, "dce") == 0) {
+                       parms.mode &= ~(LAPB_DTE | LAPB_DCE); 
+                       parms.mode |= LAPB_DCE;
+               } else if (comx_strcasecmp(page, "std") == 0 || 
+                   comx_strcasecmp(page, "standard") == 0) {
+                       parms.mode &= ~LAPB_EXTENDED; 
+                       parms.mode |= LAPB_STANDARD;
+               } else if (comx_strcasecmp(page, "ext") == 0 || 
+                   comx_strcasecmp(page, "extended") == 0) {
+                       parms.mode &= ~LAPB_STANDARD; 
+                       parms.mode |= LAPB_EXTENDED;
+               }
+               lapb_setparms(dev->priv, &parms);
+       } else {
+               printk(KERN_ERR "comxlapb_write_proc: internal error, filename %s\n", 
+                       entry->name);
+               return -EBADF;
+       }
+
+       free_page((unsigned long)page);
+       return count;
+}
+
+static void comxlapb_connected(void *token, int reason)
+{
+       struct comx_channel *ch = token; 
+       struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+       if (ch->debug_flags & DEBUG_COMX_LAPB) {
+               comx_debug(ch->dev, "%s: lapb connected, reason: %d\n", 
+                       ch->dev->name, reason);
+       }
+
+       if (ch->dev->type == ARPHRD_X25) {
+               unsigned char *p;
+               struct sk_buff *skb;
+
+               if ((skb = dev_alloc_skb(1)) == NULL) {
+                       printk(KERN_ERR "comxlapb: out of memory!\n");
+                       return;
+               }
+               p = skb_put(skb,1);
+               *p = 0x01;              // link established
+               skb->dev = ch->dev;
+               skb->protocol = htons(ETH_P_X25);
+               skb->mac.raw = skb->data;
+               skb->pkt_type = PACKET_HOST;
+
+               netif_rx(skb);
+       }
+
+       for (; comxdir; comxdir = comxdir->next) {
+               if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+                       comxdir->mode = S_IFREG | 0444;
+               }
+       }
+
+
+       ch->line_status |= PROTO_UP;
+       comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_disconnected(void *token, int reason)
+{
+       struct comx_channel *ch = token; 
+       struct proc_dir_entry *comxdir = ch->procdir->subdir;
+
+       if (ch->debug_flags & DEBUG_COMX_LAPB) {
+               comx_debug(ch->dev, "%s: lapb disconnected, reason: %d\n", 
+                       ch->dev->name, reason);
+       }
+
+       if (ch->dev->type == ARPHRD_X25) {
+               unsigned char *p;
+               struct sk_buff *skb;
+
+               if ((skb = dev_alloc_skb(1)) == NULL) {
+                       printk(KERN_ERR "comxlapb: out of memory!\n");
+                       return;
+               }
+               p = skb_put(skb,1);
+               *p = 0x02;              // link disconnected
+               skb->dev = ch->dev;
+               skb->protocol = htons(ETH_P_X25);
+               skb->mac.raw = skb->data;
+               skb->pkt_type = PACKET_HOST;
+
+               netif_rx(skb);
+       }
+
+       for (; comxdir; comxdir = comxdir->next) {
+               if (strcmp(comxdir->name, FILENAME_MODE) == 0) {
+                       comxdir->mode = S_IFREG | 0644;
+               }
+       }
+       
+       ch->line_status &= ~PROTO_UP;
+       comx_status(ch->dev, ch->line_status);
+}
+
+static void comxlapb_data_indication(void *token, struct sk_buff *skb)
+{
+       struct comx_channel *ch = token; 
+
+       if (ch->dev->type == ARPHRD_X25) {
+               skb_push(skb, 1);
+               skb->data[0] = 0;       // indicate data for X25
+               skb->protocol = htons(ETH_P_X25);
+       } else {
+               skb->protocol = htons(ETH_P_IP);
+       }
+
+       skb->dev = ch->dev;
+       skb->mac.raw = skb->data;
+       comx_rx(ch->dev, skb);
+}
+
+static void comxlapb_data_transmit(void *token, struct sk_buff *skb)
+{
+       struct comx_channel *ch = token; 
+
+       if (ch->HW_send_packet) {
+               ch->HW_send_packet(ch->dev, skb);
+       }
+}
+
+static int comxlapb_exit(struct device *dev) 
+{
+       struct comx_channel *ch = dev->priv;
+
+       dev->flags              = 0;
+       dev->type               = 0;
+       dev->mtu                = 0;
+       dev->hard_header_len    = 0;
+
+       ch->LINE_rx     = NULL;
+       ch->LINE_tx     = NULL;
+       ch->LINE_status = NULL;
+       ch->LINE_open   = NULL;
+       ch->LINE_close  = NULL;
+       ch->LINE_xmit   = NULL;
+       ch->LINE_header = NULL;
+       ch->LINE_statistics = NULL;
+
+       if (ch->debug_flags & DEBUG_COMX_LAPB) {
+               comx_debug(dev, "%s: unregistering lapb\n", dev->name);
+       }
+       lapb_unregister(dev->priv);
+
+       remove_proc_entry(FILENAME_T1, ch->procdir);
+       remove_proc_entry(FILENAME_T2, ch->procdir);
+       remove_proc_entry(FILENAME_N2, ch->procdir);
+       remove_proc_entry(FILENAME_MODE, ch->procdir);
+       remove_proc_entry(FILENAME_WINDOW, ch->procdir);
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int comxlapb_init(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct lapb_register_struct lapbreg;
+
+       dev->mtu                = 1500;
+       dev->hard_header_len    = 4;
+       dev->addr_len           = 0;
+
+       ch->LINE_rx     = comxlapb_rx;
+       ch->LINE_tx     = comxlapb_tx;
+       ch->LINE_status = comxlapb_status;
+       ch->LINE_open   = comxlapb_open;
+       ch->LINE_close  = comxlapb_close;
+       ch->LINE_xmit   = comxlapb_xmit;
+       ch->LINE_header = comxlapb_header;
+       ch->LINE_statistics = comxlapb_statistics;
+
+       lapbreg.connect_confirmation = comxlapb_connected;
+       lapbreg.connect_indication = comxlapb_connected;
+       lapbreg.disconnect_confirmation = comxlapb_disconnected;
+       lapbreg.disconnect_indication = comxlapb_disconnected;
+       lapbreg.data_indication = comxlapb_data_indication;
+       lapbreg.data_transmit = comxlapb_data_transmit;
+       if (lapb_register(dev->priv, &lapbreg)) {
+               return -ENOMEM;
+       }
+       if (ch->debug_flags & DEBUG_COMX_LAPB) {
+               comx_debug(dev, "%s: lapb registered\n", dev->name);
+       }
+
+       if (!create_comxlapb_proc_entry(FILENAME_T1, 0644, 8, ch->procdir)) {
+               return -ENOMEM;
+       }
+       if (!create_comxlapb_proc_entry(FILENAME_T2, 0644, 8, ch->procdir)) {
+               return -ENOMEM;
+       }
+       if (!create_comxlapb_proc_entry(FILENAME_N2, 0644, 8, ch->procdir)) {
+               return -ENOMEM;
+       }
+       if (!create_comxlapb_proc_entry(FILENAME_MODE, 0644, 14, ch->procdir)) {
+               return -ENOMEM;
+       }
+       if (!create_comxlapb_proc_entry(FILENAME_WINDOW, 0644, 0, ch->procdir)) {
+               return -ENOMEM;
+       }
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int comxlapb_init_lapb(struct device *dev) 
+{
+       dev->flags      = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+       dev->type       = ARPHRD_LAPB;
+
+       return(comxlapb_init(dev));
+}
+
+static int comxlapb_init_x25(struct device *dev)
+{
+       dev->flags              = IFF_NOARP;
+       dev->type               = ARPHRD_X25;
+
+       return(comxlapb_init(dev));
+}
+
+static struct proc_dir_entry *create_comxlapb_proc_entry(char *name, int mode,
+       int size, struct proc_dir_entry *dir)
+{
+       struct proc_dir_entry *new_file;
+
+       if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+               new_file->data = (void *)new_file;
+               new_file->read_proc = &comxlapb_read_proc;
+               new_file->write_proc = &comxlapb_write_proc;
+               new_file->ops = &comx_normal_inode_ops;
+               new_file->size = size;
+               new_file->nlink = 1;
+       }
+       return(new_file);
+}
+
+static struct comx_protocol comxlapb_protocol = {
+       "lapb", 
+       VERSION,
+       ARPHRD_LAPB, 
+       comxlapb_init_lapb, 
+       comxlapb_exit, 
+       NULL 
+};
+
+static struct comx_protocol comx25_protocol = {
+       "x25", 
+       VERSION,
+       ARPHRD_X25, 
+       comxlapb_init_x25, 
+       comxlapb_exit, 
+       NULL 
+};
+
+#ifdef MODULE
+#define comx_proto_lapb_init init_module
+#endif
+
+__initfunc(int comx_proto_lapb_init(void))
+{
+       int ret;
+
+       if ((ret = comx_register_protocol(&comxlapb_protocol)) != 0) {
+               return ret;
+       }
+       return comx_register_protocol(&comx25_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       comx_unregister_protocol(comxlapb_protocol.name);
+       comx_unregister_protocol(comx25_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/comx-proto-ppp.c b/drivers/net/comx-proto-ppp.c
new file mode 100644 (file)
index 0000000..c80fedf
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Synchronous PPP / Cisco-HDLC driver for the COMX boards
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ *
+ * Version 0.10 (99/06/10):
+ *             - written the first code :)
+ *
+ * Version 0.20 (99/06/16):
+ *             - added hdlc protocol 
+ *             - protocol up is IFF_RUNNING
+ *
+ * Version 0.21 (99/07/15):
+ *             - some small fixes with the line status
+ *
+ * Version 0.22 (99/08/05):
+ *             - don't test IFF_RUNNING but the pp_link_state of the sppp
+ *
+ */
+
+#define VERSION "0.22"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/inetdevice.h>
+#include <asm/uaccess.h>
+#include <linux/init.h>
+
+#include       "syncppp.h"
+#include       "comx.h"
+
+MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
+
+static struct comx_protocol syncppp_protocol;
+static struct comx_protocol hdlc_protocol;
+
+struct syncppp_data {
+       struct timer_list status_timer;
+};
+
+static void syncppp_status_timerfun(unsigned long d) {
+       struct device *dev=(struct device *)d;
+       struct comx_channel *ch=dev->priv;
+       struct syncppp_data *spch=ch->LINE_privdata;
+       struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+        
+       if(!(ch->line_status & PROTO_UP) && 
+           (sp->pp_link_state==SPPP_LINK_UP)) {
+               comx_status(dev, ch->line_status | PROTO_UP);
+       }
+       if((ch->line_status & PROTO_UP) &&
+           (sp->pp_link_state==SPPP_LINK_DOWN)) {
+               comx_status(dev, ch->line_status & ~PROTO_UP);
+       }
+       mod_timer(&spch->status_timer,jiffies + HZ*3);
+}
+
+static int syncppp_tx(struct device *dev) 
+{
+       clear_bit(0, &dev->tbusy);
+       mark_bh(NET_BH);
+       return 0;
+}
+
+static void syncppp_status(struct device *dev, unsigned short status)
+{
+       status &= ~(PROTO_UP | PROTO_LOOP);
+       if(status & LINE_UP) {
+               dev->tbusy = 0; /* Just to be sure */
+               sppp_open(dev);
+       } else  {
+               /* Line went down */
+               sppp_close(dev);
+       }
+       comx_status(dev, status);
+}
+
+static int syncppp_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct syncppp_data *spch = ch->LINE_privdata;
+
+       if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+
+       ch->init_status |= LINE_OPEN;
+       ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+       if(ch->line_status & LINE_UP) {
+               sppp_open(dev);
+       }
+
+       init_timer(&spch->status_timer);
+       spch->status_timer.function=syncppp_status_timerfun;
+       spch->status_timer.data=(unsigned long)dev;
+       spch->status_timer.expires=jiffies + HZ*3;
+       add_timer(&spch->status_timer);
+       
+       return 0;
+}
+
+static int syncppp_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct syncppp_data *spch = ch->LINE_privdata;
+
+       if (!(ch->init_status & HW_OPEN)) return -ENODEV;
+       del_timer(&spch->status_timer);
+       
+       sppp_close(dev);
+
+       ch->init_status &= ~LINE_OPEN;
+       ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
+
+       return 0;
+}
+
+static int syncppp_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+
+       if (test_and_set_bit(0, &dev->tbusy)) {
+               ch->stats.tx_errors++;
+               return 0;
+       }       
+       switch(ch->HW_send_packet(dev, skb)) {
+               case FRAME_QUEUED:
+                       clear_bit(0, &dev->tbusy);
+                       break;
+               case FRAME_ACCEPTED:
+               case FRAME_DROPPED:
+                       break;
+               case FRAME_ERROR:
+                       printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
+                               dev->name, skb->len);
+               break;
+       }
+       return 0;
+}
+
+
+static int syncppp_statistics(struct device *dev, char *page) 
+{
+       int len = 0;
+
+       len += sprintf(page + len, " ");
+       return len;
+}
+
+
+static int syncppp_exit(struct device *dev) 
+{
+       struct comx_channel *ch = dev->priv;
+
+       sppp_detach(dev);
+
+       dev->flags = 0;
+       dev->type = 0;
+       dev->mtu = 0;
+
+       ch->LINE_rx = NULL;
+       ch->LINE_tx = NULL;
+       ch->LINE_status = NULL;
+       ch->LINE_open = NULL;
+       ch->LINE_close = NULL;
+       ch->LINE_xmit = NULL;
+       ch->LINE_header = NULL;
+       ch->LINE_rebuild_header = NULL;
+       ch->LINE_statistics = NULL;
+
+       kfree(ch->LINE_privdata);
+       ch->LINE_privdata = NULL;
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static int syncppp_init(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct ppp_device *pppdev = (struct ppp_device*)dev;
+
+       ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
+
+       sppp_attach(pppdev);
+
+       if(ch->protocol == &hdlc_protocol) {
+               pppdev->sppp.pp_flags |= PP_CISCO;
+               dev->type = ARPHRD_HDLC;
+       } else {
+               pppdev->sppp.pp_flags &= ~PP_CISCO;
+               dev->type = ARPHRD_PPP;
+       }
+
+       ch->LINE_rx = sppp_input;
+       ch->LINE_tx = syncppp_tx;
+       ch->LINE_status = syncppp_status;
+       ch->LINE_open = syncppp_open;
+       ch->LINE_close = syncppp_close;
+       ch->LINE_xmit = syncppp_xmit;
+       ch->LINE_header = NULL;
+       ch->LINE_statistics = syncppp_statistics;
+
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static struct comx_protocol syncppp_protocol = {
+       "ppp", 
+       VERSION,
+       ARPHRD_PPP, 
+       syncppp_init, 
+       syncppp_exit, 
+       NULL 
+};
+
+static struct comx_protocol hdlc_protocol = {
+       "hdlc", 
+       VERSION,
+       ARPHRD_PPP, 
+       syncppp_init, 
+       syncppp_exit, 
+       NULL 
+};
+
+
+#ifdef MODULE
+#define comx_proto_ppp_init init_module
+#endif
+
+__initfunc(int comx_proto_ppp_init(void))
+{
+       int ret;
+
+       if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
+               return ret;
+       }
+       return comx_register_protocol(&syncppp_protocol);
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       comx_unregister_protocol(syncppp_protocol.name);
+       comx_unregister_protocol(hdlc_protocol.name);
+}
+#endif /* MODULE */
+
diff --git a/drivers/net/comx.c b/drivers/net/comx.c
new file mode 100644 (file)
index 0000000..00f252f
--- /dev/null
@@ -0,0 +1,1230 @@
+/*
+ * Device driver framework for the COMX line of synchronous serial boards
+ * 
+ * for Linux kernel 2.2.X
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co.
+ *
+ * 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.
+ *
+ * Version 0.80 (99/06/11):
+ *             - clean up source code (playing a bit of indent)
+ *             - port back to kernel, add support for non-module versions
+ *             - add support for board resets when channel protocol is down
+ *             - reset the device structure after protocol exit
+ *               the syncppp driver needs it
+ *             - add support for /proc/comx/protocols and 
+ *               /proc/comx/boardtypes
+ *
+ * Version 0.81 (99/06/21):
+ *             - comment out the board reset support code, the locomx
+ *               driver seems not buggy now
+ *             - printk() levels fixed
+ *
+ * Version 0.82 (99/07/08):
+ *             - Handle stats correctly if the lowlevel driver is
+ *               is not a comx one (locomx - z85230)
+ *
+ * Version 0.83 (99/07/15):
+ *             - reset line_status when interface is down
+ *
+ */
+
+#define VERSION "0.82"
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
+#include "comx.h"
+#include "syncppp.h"
+
+MODULE_AUTHOR("Gergely Madarasz <gorgo@itc.hu>");
+MODULE_DESCRIPTION("Common code for the COMX synchronous serial adapters");
+
+extern int comx_hw_comx_init(void);
+extern int comx_hw_locomx_init(void);
+extern int comx_hw_mixcom_init(void);
+extern int comx_proto_hdlc_init(void);
+extern int comx_proto_ppp_init(void);
+extern int comx_proto_syncppp_init(void);
+extern int comx_proto_lapb_init(void);
+extern int comx_proto_fr_init(void);
+
+static struct comx_hardware *comx_channels = NULL;
+static struct comx_protocol *comx_lines = NULL;
+
+struct inode_operations comx_normal_inode_ops; 
+static struct inode_operations comx_root_inode_ops; // for mkdir
+static struct inode_operations comx_debug_inode_ops; // mas a file_ops
+static struct file_operations comx_normal_file_ops;  // with open/relase
+static struct file_operations comx_debug_file_ops;  // with lseek+read
+
+static void comx_delete_dentry(struct dentry *dentry);
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+       int size, struct proc_dir_entry *dir);
+
+static void comx_fill_inode(struct inode *inode, int fill);
+
+static struct dentry_operations comx_dentry_operations = {
+       NULL,                   /* revalidate */
+       NULL,                   /* d_hash */
+       NULL,                   /* d_compare */
+       &comx_delete_dentry     /* d_delete */
+};
+
+
+struct proc_dir_entry comx_root_dir = {
+       0, 4, "comx",
+       S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0,
+       0, &comx_root_inode_ops,
+       NULL, comx_fill_inode,
+       NULL, &proc_root, NULL 
+};
+
+struct comx_debugflags_struct  comx_debugflags[] = {
+       { "comx_rx",            DEBUG_COMX_RX           },
+       { "comx_tx",            DEBUG_COMX_TX           },
+       { "hw_tx",              DEBUG_HW_TX             },
+       { "hw_rx",              DEBUG_HW_RX             },
+       { "hdlc_keepalive",     DEBUG_HDLC_KEEPALIVE    },
+       { "comxppp",            DEBUG_COMX_PPP          },
+       { "comxlapb",           DEBUG_COMX_LAPB         },
+       { "dlci",               DEBUG_COMX_DLCI         },
+       { NULL,                 0                       } 
+};
+
+static void comx_fill_inode(struct inode *inode, int fill)
+{
+       if (fill)
+               MOD_INC_USE_COUNT;
+       else
+               MOD_DEC_USE_COUNT;
+}
+
+
+int comx_debug(struct device *dev, char *fmt, ...)
+{
+       struct comx_channel *ch = dev->priv;
+       char *page,*str;
+       va_list args;
+       int len;
+
+       if (!ch->debug_area) return 0;
+
+       if (!(page = (char *)__get_free_page(GFP_ATOMIC))) return -ENOMEM;
+
+       va_start(args, fmt);
+       len = vsprintf(str = page, fmt, args);
+       va_end(args);
+
+       if (len >= PAGE_SIZE) {
+               printk(KERN_ERR "comx_debug: PANIC! len = %d !!!\n", len);
+               free_page((unsigned long)page);
+               return -EINVAL;
+       }
+
+       while (len) {
+               int to_copy;
+               int free = (ch->debug_start - ch->debug_end + ch->debug_size) 
+                       % ch->debug_size;
+
+               to_copy = min( free ? free : ch->debug_size, 
+                       min (ch->debug_size - ch->debug_end, len) );
+               memcpy(ch->debug_area + ch->debug_end, str, to_copy);
+               str += to_copy;
+               len -= to_copy;
+               ch->debug_end = (ch->debug_end + to_copy) % ch->debug_size;
+               if (ch->debug_start == ch->debug_end) // Full ? push start away
+                       ch->debug_start = (ch->debug_start + len + 1) % 
+                                       ch->debug_size;
+               ch->debug_file->size = (ch->debug_end - ch->debug_start +
+                                       ch->debug_size) % ch->debug_size;
+       } 
+
+       free_page((unsigned long)page);
+       return 0;
+}
+
+int comx_debug_skb(struct device *dev, struct sk_buff *skb, char *msg)
+{
+       struct comx_channel *ch = dev->priv;
+
+       if (!ch->debug_area) return 0;
+       if (!skb) comx_debug(dev, "%s: %s NULL skb\n\n", dev->name, msg);
+       if (!skb->len) comx_debug(dev, "%s: %s empty skb\n\n", dev->name, msg);
+
+       return comx_debug_bytes(dev, skb->data, skb->len, msg);
+}
+
+int comx_debug_bytes(struct device *dev, unsigned char *bytes, int len, 
+               char *msg)
+{
+       int pos = 0;
+       struct comx_channel *ch = dev->priv;
+
+       if (!ch->debug_area) return 0;
+
+       comx_debug(dev, "%s: %s len %d\n", dev->name, msg, len);
+
+       while (pos != len) {
+               char line[80];
+               int i = 0;
+
+               memset(line, 0, 80);
+               sprintf(line,"%04d ", pos);
+               do {
+                       sprintf(line + 5 + (pos % 16) * 3, "%02x", bytes[pos]);
+                       sprintf(line + 60 + (pos % 16), "%c", 
+                               isprint(bytes[pos]) ? bytes[pos] : '.');
+                       pos++;
+               } while (pos != len && pos % 16);
+
+               while ( i++ != 78 ) if (line[i] == 0) line[i] = ' ';
+               line[77] = '\n';
+               line[78] = 0;
+       
+               comx_debug(dev, "%s", line);
+       }
+       comx_debug(dev, "\n");
+       return 0;
+}
+
+static void comx_loadavg_timerfun(unsigned long d)
+{
+       struct device *dev = (struct device *)d;
+       struct comx_channel *ch = dev->priv;
+
+       ch->avg_bytes[ch->loadavg_counter] = ch->current_stats->rx_bytes;
+       ch->avg_bytes[ch->loadavg_counter + ch->loadavg_size] = 
+               ch->current_stats->tx_bytes;
+
+       ch->loadavg_counter = (ch->loadavg_counter + 1) % ch->loadavg_size;
+
+       mod_timer(&ch->loadavg_timer,jiffies + HZ * ch->loadavg[0]);
+}
+
+#if 0
+static void comx_reset_timerfun(unsigned long d)
+{ 
+       struct device *dev = (struct device *)d;
+       struct comx_channel *ch = dev->priv;
+
+       if(!(ch->line_status & (PROTO_LOOP | PROTO_UP))) {
+               if(test_and_set_bit(0,&ch->reset_pending) && ch->HW_reset) {
+                       ch->HW_reset(dev);
+               }
+       }
+
+       mod_timer(&ch->reset_timer, jiffies + HZ * ch->reset_timeout);
+}
+#endif                                            
+
+static int comx_open(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct proc_dir_entry *comxdir = ch->procdir->subdir;
+       int ret=0;
+
+       if (!ch->protocol || !ch->hardware) return -ENODEV;
+
+       if ((ret = ch->HW_open(dev))) return ret;
+       if ((ret = ch->LINE_open(dev))) { 
+               ch->HW_close(dev); 
+               return ret; 
+       };
+
+       for (; comxdir ; comxdir = comxdir->next) {
+               if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+                  strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+                       comxdir->mode = S_IFREG | 0444;
+       }
+
+#if 0
+       ch->reset_pending = 1;
+       ch->reset_timeout = 30;
+       ch->reset_timer.function = comx_reset_timerfun;
+       ch->reset_timer.data = (unsigned long)dev;
+       ch->reset_timer.expires = jiffies + HZ * ch->reset_timeout;
+       add_timer(&ch->reset_timer);
+#endif
+
+       return 0;
+}
+
+static int comx_close(struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       struct proc_dir_entry *comxdir = ch->procdir->subdir;
+       int ret = -ENODEV;
+
+       if (test_and_clear_bit(0, &ch->lineup_pending)) {
+               del_timer(&ch->lineup_timer);
+       }
+
+#if 0  
+       del_timer(&ch->reset_timer);
+#endif
+
+       if (ch->init_status & LINE_OPEN && ch->protocol && ch->LINE_close) {
+               ret = ch->LINE_close(dev);
+       }
+
+       if (ret) return ret;
+
+       if (ch->init_status & HW_OPEN && ch->hardware && ch->HW_close) {
+               ret = ch->HW_close(dev);
+       }
+       
+       ch->line_status=0;
+
+       for (; comxdir ; comxdir = comxdir->next) {
+               if (strcmp(comxdir->name, FILENAME_HARDWARE) == 0 ||
+                   strcmp(comxdir->name, FILENAME_PROTOCOL) == 0)
+                       comxdir->mode = S_IFREG | 0644;
+       }
+
+       return ret;
+}
+
+void comx_status(struct device *dev, int status)
+{
+       struct comx_channel *ch = dev->priv;
+
+#if 0
+       if(status & (PROTO_UP | PROTO_LOOP)) {
+               clear_bit(0,&ch->reset_pending);
+       }
+#endif
+
+       if (dev->flags & IFF_UP) {
+               printk(KERN_NOTICE "Interface %s: modem status %s, line protocol %s\n",
+                   dev->name, status & LINE_UP ? "UP" : "DOWN", 
+                   status & PROTO_LOOP ? "LOOP" : status & PROTO_UP ? 
+                   "UP" : "DOWN");
+       }
+       
+       ch->line_status = status;
+}
+
+static int comx_xmit(struct sk_buff *skb, struct device *dev)
+{
+       struct comx_channel *ch = dev->priv;
+       int rc;
+
+       if (skb->len > dev->mtu + dev->hard_header_len) {
+               printk(KERN_ERR "comx_xmit: %s: skb->len %d > dev->mtu %d\n", dev->name,
+               (int)skb->len, dev->mtu);
+       }
+       
+       if (ch->debug_flags & DEBUG_COMX_TX) {
+               comx_debug_skb(dev, skb, "comx_xmit skb");
+       }
+       
+       rc=ch->LINE_xmit(skb, dev);
+//     if (!rc) dev_kfree_skb(skb);
+
+       return rc;
+}
+
+static int comx_header(struct sk_buff *skb, struct device *dev, 
+       unsigned short type, void *daddr, void *saddr, unsigned len) 
+{
+       struct comx_channel *ch = dev->priv;
+
+       if (ch->LINE_header) {
+               return (ch->LINE_header(skb, dev, type, daddr, saddr, len));
+       } else {
+               return 0;
+       }
+}
+
+static int comx_rebuild_header(struct sk_buff *skb) 
+{
+       struct device *dev = skb->dev;
+       struct comx_channel *ch = dev->priv;
+
+       if (ch->LINE_rebuild_header) {
+               return(ch->LINE_rebuild_header(skb));
+       } else {
+               return 0;
+       }
+}
+
+int comx_rx(struct device *dev, struct sk_buff *skb)
+{
+       struct comx_channel *ch = dev->priv;
+
+       if (ch->debug_flags & DEBUG_COMX_RX) {
+               comx_debug_skb(dev, skb, "comx_rx skb");
+       }
+       if (skb) {
+               netif_rx(skb);
+       }
+       return 0;
+}
+
+static struct net_device_stats *comx_stats(struct device *dev)
+{
+       struct comx_channel *ch = (struct comx_channel *)dev->priv;
+
+       return ch->current_stats;
+}
+
+void comx_lineup_func(unsigned long d)
+{
+       struct device *dev = (struct device *)d;
+       struct comx_channel *ch = dev->priv;
+
+       del_timer(&ch->lineup_timer);
+       clear_bit(0, &ch->lineup_pending);
+
+       if (ch->LINE_status) {
+               ch->LINE_status(dev, ch->line_status |= LINE_UP);
+       }
+}
+
+#define LOADAVG(avg, off) (int) \
+       ((ch->avg_bytes[(ch->loadavg_counter - 1 + ch->loadavg_size * 2) \
+       % ch->loadavg_size + off] -  ch->avg_bytes[(ch->loadavg_counter - 1 \
+               - ch->loadavg[avg] / ch->loadavg[0] + ch->loadavg_size * 2) \
+               % ch->loadavg_size + off]) / ch->loadavg[avg] * 8)
+
+static int comx_statistics(struct device *dev, char *page)
+{
+       struct comx_channel *ch = dev->priv;
+       int len = 0;
+       int tmp;
+       int i = 0;
+       char tmpstr[20];
+       int tmpstrlen = 0;
+
+       len += sprintf(page + len, "Interface administrative status is %s, "
+               "modem status is %s, protocol is %s\n", 
+               dev->flags & IFF_UP ? "UP" : "DOWN",
+               ch->line_status & LINE_UP ? "UP" : "DOWN",
+               ch->line_status & PROTO_LOOP ? "LOOP" : 
+               ch->line_status & PROTO_UP ? "UP" : "DOWN");
+       len += sprintf(page + len, "Modem status changes: %lu, Transmitter status "
+               "is %s, tbusy: %d\n", ch->current_stats->tx_carrier_errors, ch->HW_txe ? 
+               ch->HW_txe(dev) ? "IDLE" : "BUSY" : "NOT READY", (int)dev->tbusy);
+       len += sprintf(page + len, "Interface load (input): %d / %d / %d bits/s (",
+               LOADAVG(0,0), LOADAVG(1, 0), LOADAVG(2, 0));
+       tmpstr[0] = 0;
+       for (i=0; i != 3; i++) {
+               char tf;
+
+               tf = ch->loadavg[i] % 60 == 0 && 
+                       ch->loadavg[i] / 60 > 0 ? 'm' : 's';
+               tmpstrlen += sprintf(tmpstr + tmpstrlen, "%d%c%s", 
+                       ch->loadavg[i] / (tf == 'm' ? 60 : 1), tf, 
+                       i == 2 ? ")\n" : "/");
+       }
+       len += sprintf(page + len, 
+               "%s              (output): %d / %d / %d bits/s (%s", tmpstr, 
+               LOADAVG(0,ch->loadavg_size), LOADAVG(1, ch->loadavg_size), 
+               LOADAVG(2, ch->loadavg_size), tmpstr);
+
+       len += sprintf(page + len, "Debug flags: ");
+       tmp = len; i = 0;
+       while (comx_debugflags[i].name) {
+               if (ch->debug_flags & comx_debugflags[i].value) 
+                       len += sprintf(page + len, "%s ", 
+                               comx_debugflags[i].name);
+               i++;
+       }
+       len += sprintf(page + len, "%s\n", tmp == len ? "none" : "");
+
+       len += sprintf(page + len, "RX errors: len: %lu, overrun: %lu, crc: %lu, "
+               "aborts: %lu\n           buffer overrun: %lu, pbuffer overrun: %lu\n"
+               "TX errors: underrun: %lu\n",
+               ch->current_stats->rx_length_errors, ch->current_stats->rx_over_errors, 
+               ch->current_stats->rx_crc_errors, ch->current_stats->rx_frame_errors, 
+               ch->current_stats->rx_missed_errors, ch->current_stats->rx_fifo_errors,
+               ch->current_stats->tx_fifo_errors);
+
+       if (ch->LINE_statistics && (ch->init_status & LINE_OPEN)) {
+               len += ch->LINE_statistics(dev, page + len);
+       } else {
+               len += sprintf(page+len, "Line status: driver not initialized\n");
+       }
+       if (ch->HW_statistics && (ch->init_status & HW_OPEN)) {
+               len += ch->HW_statistics(dev, page + len);
+       } else {
+               len += sprintf(page+len, "Board status: driver not initialized\n");
+       }
+
+       return len;
+}
+
+static int comx_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+       struct comx_channel *ch = dev->priv;
+
+       if (ch->LINE_ioctl) {
+               return(ch->LINE_ioctl(dev, ifr, cmd));
+       }
+       return -EINVAL;
+}
+
+static void comx_reset_dev(struct device *dev)
+{
+       dev->open = comx_open;
+       dev->stop = comx_close;
+       dev->hard_start_xmit = comx_xmit;
+       dev->hard_header = comx_header;
+       dev->rebuild_header = comx_rebuild_header;
+       dev->get_stats = comx_stats;
+       dev->do_ioctl = comx_ioctl;
+       dev->change_mtu = NULL;
+       dev->tx_queue_len = 20;
+       dev->flags = IFF_NOARP;
+}
+
+static int comx_init_dev(struct device *dev)
+{
+       struct comx_channel *ch;
+
+       if ((ch = kmalloc(sizeof(struct comx_channel), GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+       memset(ch, 0, sizeof(struct comx_channel));
+
+       ch->loadavg[0] = 5;
+       ch->loadavg[1] = 300;
+       ch->loadavg[2] = 900;
+       ch->loadavg_size = ch->loadavg[2] / ch->loadavg[0] + 1; 
+       if ((ch->avg_bytes = kmalloc(ch->loadavg_size * 
+               sizeof(unsigned long) * 2, GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+
+       memset(ch->avg_bytes, 0, ch->loadavg_size * sizeof(unsigned long) * 2);
+       ch->loadavg_counter = 0;
+       ch->loadavg_timer.function = comx_loadavg_timerfun;
+       ch->loadavg_timer.data = (unsigned long)dev;
+       ch->loadavg_timer.expires = jiffies + HZ * ch->loadavg[0];
+       add_timer(&ch->loadavg_timer);
+
+       dev->priv = (void *)ch;
+       ch->dev = dev;
+       ch->line_status &= ~LINE_UP;
+
+       ch->current_stats = &ch->stats;
+
+       comx_reset_dev(dev);
+       return 0;
+}
+
+static int comx_read_proc(char *page, char **start, off_t off, int count, 
+       int *eof, void *data)
+{
+       struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+       struct device *dev = file->parent->data;
+       struct comx_channel *ch=(struct comx_channel *)dev->priv;
+       int len = 0;
+
+       if (strcmp(file->name, FILENAME_STATUS) == 0) {
+               len = comx_statistics(dev, page);
+       } else if (strcmp(file->name, FILENAME_HARDWARE) == 0) {
+               len = sprintf(page, "%s\n", ch->hardware ? 
+                       ch->hardware->name : HWNAME_NONE);
+       } else if (strcmp(file->name, FILENAME_PROTOCOL) == 0) {
+               len = sprintf(page, "%s\n", ch->protocol ? 
+                       ch->protocol->name : PROTONAME_NONE);
+       } else if (strcmp(file->name, FILENAME_LINEUPDELAY) == 0) {
+               len = sprintf(page, "%01d\n", ch->lineup_delay);
+       }
+
+       if (off >= len) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+       if (count >= len - off) {
+               *eof = 1;
+       }
+       return( min(count, len - off) );
+}
+
+
+static int comx_root_read_proc(char *page, char **start, off_t off, int count, 
+       int *eof, void *data)
+{
+       struct proc_dir_entry *file = (struct proc_dir_entry *)data;
+       struct comx_hardware *hw;
+       struct comx_protocol *line;
+
+       int len = 0;
+
+       if (strcmp(file->name, FILENAME_HARDWARELIST) == 0) {
+               for(hw=comx_channels;hw;hw=hw->next) 
+                       len+=sprintf(page+len, "%s\n", hw->name);
+       } else if (strcmp(file->name, FILENAME_PROTOCOLLIST) == 0) {
+               for(line=comx_lines;line;line=line->next)
+                       len+=sprintf(page+len, "%s\n", line->name);
+       }
+
+       if (off >= len) {
+               *eof = 1;
+               return 0;
+       }
+
+       *start = page + off;
+       if (count >= len - off) {
+               *eof = 1;
+       }
+       return( min(count, len - off) );
+}
+
+
+
+static int comx_write_proc(struct file *file, const char *buffer, u_long count,
+       void *data)
+{
+       struct proc_dir_entry *entry = (struct proc_dir_entry *)data;
+       struct device *dev = (struct device *)entry->parent->data;
+       struct comx_channel *ch=(struct comx_channel *)dev->priv;
+       char *page;
+       struct comx_hardware *hw = comx_channels;
+       struct comx_protocol *line = comx_lines;
+       char str[30];
+       int ret=0;
+
+       if (file->f_dentry->d_inode->i_ino != entry->low_ino) {
+               printk(KERN_ERR "comx_write_proc: file <-> data internal error\n");
+               return -EIO;
+       }
+
+       if (count > PAGE_SIZE) {
+               printk(KERN_ERR "count is %lu > %d!!!\n", count, (int)PAGE_SIZE);
+               return -ENOSPC;
+       }
+
+       if (!(page = (char *)__get_free_page(GFP_KERNEL))) return -ENOMEM;
+
+       copy_from_user(page, buffer, count);
+
+       if (*(page + count - 1) == '\n') *(page + count - 1) = 0;
+
+       if (strcmp(entry->name, FILENAME_DEBUG) == 0) {
+               int i;
+               int ret = 0;
+
+               if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+                       unsigned long flags;
+
+                       save_flags(flags); cli();
+                       if (ch->debug_area) kfree(ch->debug_area);
+                       if ((ch->debug_area = kmalloc(ch->debug_size = i, 
+                               GFP_KERNEL)) == NULL) {
+                               ret = -ENOMEM;
+                       }
+                       ch->debug_start = ch->debug_end = 0;
+                       restore_flags(flags);
+                       free_page((unsigned long)page);
+                       return count;
+               }
+               
+               if (*page != '+' && *page != '-') {
+                       free_page((unsigned long)page);
+                       return -EINVAL;
+               }
+               while (comx_debugflags[i].value && 
+                       strncmp(comx_debugflags[i].name, page + 1, 
+                       strlen(comx_debugflags[i].name))) {
+                       i++;
+               }
+       
+               if (comx_debugflags[i].value == 0) {
+                       printk(KERN_ERR "Invalid debug option\n");
+                       free_page((unsigned long)page);
+                       return -EINVAL;
+               }
+               if (*page == '+') {
+                       ch->debug_flags |= comx_debugflags[i].value;
+               } else {
+                       ch->debug_flags &= ~comx_debugflags[i].value;
+               }
+       } else if (strcmp(entry->name, FILENAME_HARDWARE) == 0) {
+               if(strlen(page)>10) {
+                       free_page((unsigned long)page);
+                       return -EINVAL;
+               }
+               while (hw) { 
+                       if (strcmp(hw->name, page) == 0) {
+                               break;
+                       } else {
+                               hw = hw->next;
+                       }
+               }
+#ifdef CONFIG_KMOD
+               if(!hw && comx_strcasecmp(HWNAME_NONE,page) != 0){
+                       sprintf(str,"comx-hw-%s",page);
+                       request_module(str);
+               }               
+               hw=comx_channels;
+               while (hw) {
+                       if (comx_strcasecmp(hw->name, page) == 0) {
+                               break;
+                       } else {
+                               hw = hw->next;
+                       }
+               }
+#endif
+
+               if (comx_strcasecmp(HWNAME_NONE, page) != 0 && !hw)  {
+                       free_page((unsigned long)page);
+                       return -ENODEV;
+               }
+               if (ch->init_status & HW_OPEN) {
+                       free_page((unsigned long)page);
+                       return -EBUSY;
+               }
+               if (ch->hardware && ch->hardware->hw_exit && 
+                  (ret=ch->hardware->hw_exit(dev))) {
+                       free_page((unsigned long)page);
+                       return ret;
+               }
+               ch->hardware = hw;
+               entry->size = strlen(page) + 1;
+               if (hw && hw->hw_init) hw->hw_init(dev);
+       } else if (strcmp(entry->name, FILENAME_PROTOCOL) == 0) {
+               if(strlen(page)>10) {
+                       free_page((unsigned long)page);
+                       return -EINVAL;
+               }
+               while (line) {
+                       if (comx_strcasecmp(line->name, page) == 0) {
+                               break;
+                       } else {
+                               line = line->next;
+                       }
+               }
+#ifdef CONFIG_KMOD
+               if(!line && comx_strcasecmp(PROTONAME_NONE, page) != 0) {
+                       sprintf(str,"comx-proto-%s",page);
+                       request_module(str);
+               }               
+               line=comx_lines;
+               while (line) {
+                       if (comx_strcasecmp(line->name, page) == 0) {
+                               break;
+                       } else {
+                               line = line->next;
+                       }
+               }
+#endif
+               
+               if (comx_strcasecmp(PROTONAME_NONE, page) != 0 && !line) {
+                       free_page((unsigned long)page);
+                       return -ENODEV;
+               }
+               
+               if (ch->init_status & LINE_OPEN) {
+                       free_page((unsigned long)page);
+                       return -EBUSY;
+               }
+               
+               if (ch->protocol && ch->protocol->line_exit && 
+                   (ret=ch->protocol->line_exit(dev))) {
+                       free_page((unsigned long)page);
+                       return ret;
+               }
+               ch->protocol = line;
+               entry->size = strlen(page) + 1;
+               comx_reset_dev(dev);
+               if (line && line->line_init) line->line_init(dev);
+       } else if (strcmp(entry->name, FILENAME_LINEUPDELAY) == 0) {
+               int i;
+
+               if ((i = simple_strtoul(page, NULL, 10)) != 0) {
+                       if (i >=0 && i < 10) { 
+                               ch->lineup_delay = i; 
+                       } else {
+                               printk(KERN_ERR "comx: invalid lineup_delay value\n");
+                       }
+               }
+       }
+
+       free_page((unsigned long)page);
+       return count;
+}
+
+static loff_t comx_debug_lseek(struct file *file, loff_t offset, int orig)
+{
+       switch(orig) {
+               case 0: 
+                       file->f_pos = max(0, min(offset, 
+                               file->f_dentry->d_inode->i_size));
+                       return(file->f_pos);
+               case 1: 
+                       file->f_pos = max(0, min(offset + file->f_pos, 
+                               file->f_dentry->d_inode->i_size));
+                       return(file->f_pos);
+               case 2: 
+                       file->f_pos = max(0, 
+                               min(offset + file->f_dentry->d_inode->i_size, 
+                               file->f_dentry->d_inode->i_size));
+                       return(file->f_pos);
+       }
+       return(file->f_pos);
+}
+
+static int comx_file_open(struct inode *inode, struct file *file)
+{
+
+       if((file->f_mode & FMODE_WRITE) && !(inode->i_mode & 0200)) {
+               return -EACCES;
+       }
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int comx_file_release(struct inode *inode, struct file *file)
+{
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static ssize_t comx_debug_read(struct file *file, char *buffer, size_t count,
+       loff_t *ppos)
+{
+       struct proc_dir_entry *de = file->f_dentry->d_inode->u.generic_ip;
+       struct device *dev = de->parent->data;
+       struct comx_channel *ch = dev->priv;
+       loff_t copied = 0;
+       unsigned long flags;
+
+       save_flags(flags); cli(); // We may run into trouble when debug_area is filled
+                                 // from irq inside read. no problem if the buffer is
+                                 // large enough
+
+       while (count > 0 && ch->debug_start != ch->debug_end) {
+               int len;
+
+               len = min( (ch->debug_end - ch->debug_start + ch->debug_size)
+                       %ch->debug_size, min (ch->debug_size - 
+                       ch->debug_start, count));
+
+               if (len) copy_to_user(buffer + copied, 
+                       ch->debug_area + ch->debug_start, len);
+               ch->debug_start = (ch->debug_start + len) % ch->debug_size;
+
+               de->size -= len;
+               count -= len;
+               copied += len;
+       }
+
+       restore_flags(flags);
+       return copied;
+}
+
+static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       struct proc_dir_entry *new_dir, *debug_file;
+       struct device *dev;
+       struct comx_channel *ch;
+
+       if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+       if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, 
+               &comx_root_dir)) == NULL) {
+               return -EIO;
+       }
+
+       new_dir->ops = &proc_dir_inode_operations;  // ez egy normalis /proc konyvtar
+       new_dir->nlink = 2;
+       new_dir->data = NULL; // ide jon majd a struct dev
+
+       /* Ezek kellenek */
+       if (!create_comx_proc_entry(FILENAME_HARDWARE, 0644, 
+           strlen(HWNAME_NONE) + 1, new_dir)) {
+               return -ENOMEM;
+       }
+       if (!create_comx_proc_entry(FILENAME_PROTOCOL, 0644, 
+           strlen(PROTONAME_NONE) + 1, new_dir)) {
+               return -ENOMEM;
+       }
+       if (!create_comx_proc_entry(FILENAME_STATUS, 0444, 0, new_dir)) {
+               return -ENOMEM;
+       }
+       if (!create_comx_proc_entry(FILENAME_LINEUPDELAY, 0644, 2, new_dir)) {
+               return -ENOMEM;
+       }
+
+       if ((debug_file = create_proc_entry(FILENAME_DEBUG, 
+           S_IFREG | 0644, new_dir)) == NULL) {
+               return -ENOMEM;
+       }
+       debug_file->ops = &comx_debug_inode_ops;
+       debug_file->data = (void *)debug_file; 
+       debug_file->read_proc = NULL; // see below
+       debug_file->write_proc = &comx_write_proc;
+       debug_file->nlink = 1;
+
+       /* struct ppp_device is a bit larger then struct device and the
+          syncppp driver needs it */
+       if ((dev = kmalloc(sizeof(struct ppp_device), GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+       memset(dev, 0, sizeof(struct ppp_device));
+       dev->name = (char *)new_dir->name;
+       dev->init = comx_init_dev;
+
+       if (register_netdevice(dev)) {
+               return -EIO;
+       }
+       ch=dev->priv;
+       ch->debug_file = debug_file; 
+       ch->procdir = new_dir;
+       new_dir->data = dev;
+
+       ch->debug_start = ch->debug_end = 0;
+       if ((ch->debug_area = kmalloc(ch->debug_size = DEFAULT_DEBUG_SIZE, 
+           GFP_KERNEL)) == NULL) {
+               return -ENOMEM;
+       }
+
+       ch->lineup_delay = DEFAULT_LINEUP_DELAY;
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+static int comx_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       struct proc_dir_entry *entry = dentry->d_inode->u.generic_ip;
+       struct device *dev = entry->data;
+       struct comx_channel *ch = dev->priv;
+       int ret;
+
+       /* Egyelore miert ne ? */
+       if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR;
+
+       if (dev->flags & IFF_UP) {
+               printk(KERN_ERR "%s: down interface before removing it\n", dev->name);
+               return -EBUSY;
+       }
+
+       if (ch->protocol && ch->protocol->line_exit && 
+           (ret=ch->protocol->line_exit(dev))) {
+               return ret;
+       }
+       if (ch->hardware && ch->hardware->hw_exit && 
+          (ret=ch->hardware->hw_exit(dev))) { 
+               if(ch->protocol && ch->protocol->line_init) {
+                       ch->protocol->line_init(dev);
+               }
+               return ret;
+       }
+       ch->protocol = NULL;
+       ch->hardware = NULL;
+
+       del_timer(&ch->loadavg_timer);
+       kfree(ch->avg_bytes);
+
+       unregister_netdev(dev);
+       if (ch->debug_area) {
+               kfree(ch->debug_area);
+       }
+       if (dev->priv) {
+               kfree(dev->priv);
+       }
+       kfree(dev);
+
+       remove_proc_entry(FILENAME_DEBUG, entry);
+       remove_proc_entry(FILENAME_LINEUPDELAY, entry);
+       remove_proc_entry(FILENAME_STATUS, entry);
+       remove_proc_entry(FILENAME_HARDWARE, entry);
+       remove_proc_entry(FILENAME_PROTOCOL, entry);
+       remove_proc_entry(dentry->d_name.name, &comx_root_dir);
+//     proc_unregister(&comx_root_dir, dentry->d_inode->i_ino);
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+static struct dentry *comx_lookup(struct inode *dir, struct dentry *dentry)
+{
+       struct proc_dir_entry *de;
+       struct inode *inode = NULL;
+
+       if (!dir || !S_ISDIR(dir->i_mode)) {
+               return ERR_PTR(-ENOTDIR);
+       }
+
+       if ((de = (struct proc_dir_entry *) dir->u.generic_ip) != NULL) {
+               for (de = de->subdir ; de ; de = de->next) {
+                       if ((de && de->low_ino) && 
+                           (de->namelen == dentry->d_name.len) &&
+                           (memcmp(dentry->d_name.name, de->name, 
+                           de->namelen) == 0)) {
+                               if ((inode = proc_get_inode(dir->i_sb, 
+                                   de->low_ino, de)) == NULL) { 
+                                       printk(KERN_ERR "COMX: lookup error\n"); 
+                                       return ERR_PTR(-EINVAL); 
+                               }
+                               break;
+                       }
+               }
+       }
+       dentry->d_op = &comx_dentry_operations;
+       d_add(dentry, inode);
+       return NULL;
+}
+
+int comx_strcasecmp(const char *cs, const char *ct)
+{
+       register signed char __res;
+
+       while (1) {
+               if ((__res = toupper(*cs) - toupper(*ct++)) != 0 || !*cs++) {
+                       break;
+               }
+       }
+       return __res;
+}
+
+static void comx_delete_dentry(struct dentry *dentry)
+{
+       d_drop(dentry);
+}
+
+static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode,
+       int size, struct proc_dir_entry *dir)
+{
+       struct proc_dir_entry *new_file;
+
+       if ((new_file = create_proc_entry(name, S_IFREG | mode, dir)) != NULL) {
+               new_file->ops = &comx_normal_inode_ops;
+               new_file->data = (void *)new_file;
+               new_file->read_proc = &comx_read_proc;
+               new_file->write_proc = &comx_write_proc;
+               new_file->size = size;
+               new_file->nlink = 1;
+       }
+       return(new_file);
+}
+
+int comx_register_hardware(struct comx_hardware *comx_hw)
+{
+       struct comx_hardware *hw = comx_channels;
+
+       if (!hw) {
+               comx_channels = comx_hw;
+       } else {
+               while (hw->next != NULL && strcmp(comx_hw->name, hw->name) != 0) {
+                       hw = hw->next;
+               }
+               if (strcmp(comx_hw->name, hw->name) == 0) {
+                       return -1;
+               }
+               hw->next = comx_hw;
+       }
+
+       printk(KERN_INFO "COMX: driver for hardware type %s, version %s\n", comx_hw->name, comx_hw->version);
+       return 0;
+}
+
+int comx_unregister_hardware(char *name)
+{
+       struct comx_hardware *hw = comx_channels;
+
+       if (!hw) {
+               return -1;
+       }
+
+       if (strcmp(hw->name, name) == 0) {
+               comx_channels = comx_channels->next;
+               return 0;
+       }
+
+       while (hw->next != NULL && strcmp(hw->next->name,name) != 0) {
+               hw = hw->next;
+       }
+
+       if (hw->next != NULL && strcmp(hw->next->name, name) == 0) {
+               hw->next = hw->next->next;
+               return 0;
+       }
+       return -1;
+}
+
+int comx_register_protocol(struct comx_protocol *comx_line)
+{
+       struct comx_protocol *pr = comx_lines;
+
+       if (!pr) {
+               comx_lines = comx_line;
+       } else {
+               while (pr->next != NULL && strcmp(comx_line->name, pr->name) !=0) {
+                       pr = pr->next;
+               }
+               if (strcmp(comx_line->name, pr->name) == 0) {
+                       return -1;
+               }
+               pr->next = comx_line;
+       }
+
+       printk(KERN_INFO "COMX: driver for protocol type %s, version %s\n", comx_line->name, comx_line->version);
+       return 0;
+}
+
+int comx_unregister_protocol(char *name)
+{
+       struct comx_protocol *pr = comx_lines;
+
+       if (!pr) {
+               return -1;
+       }
+
+       if (strcmp(pr->name, name) == 0) {
+               comx_lines = comx_lines->next;
+               return 0;
+       }
+
+       while (pr->next != NULL && strcmp(pr->next->name,name) != 0) {
+               pr = pr->next;
+       }
+
+       if (pr->next != NULL && strcmp(pr->next->name, name) == 0) {
+               pr->next = pr->next->next;
+               return 0;
+       }
+       return -1;
+}
+
+#ifdef MODULE
+#define comx_init init_module
+#endif
+
+__initfunc(int comx_init(void))
+{
+       struct proc_dir_entry *new_file;
+
+       memcpy(&comx_root_inode_ops, &proc_dir_inode_operations, 
+               sizeof(struct inode_operations));
+       comx_root_inode_ops.lookup = &comx_lookup;
+       comx_root_inode_ops.mkdir = &comx_mkdir;
+       comx_root_inode_ops.rmdir = &comx_rmdir;
+
+       memcpy(&comx_normal_inode_ops, &proc_net_inode_operations, 
+               sizeof(struct inode_operations));
+       comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops;
+       comx_normal_inode_ops.lookup = &comx_lookup;
+
+       memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops, 
+               sizeof(struct inode_operations));
+       comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops;
+
+       memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops,
+               sizeof(struct file_operations));
+       comx_normal_file_ops.open = &comx_file_open;
+       comx_normal_file_ops.release = &comx_file_release;
+
+       memcpy(&comx_debug_file_ops, &comx_normal_file_ops, 
+               sizeof(struct file_operations));
+       comx_debug_file_ops.llseek = &comx_debug_lseek;
+       comx_debug_file_ops.read = &comx_debug_read;
+
+       if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM;
+
+
+       if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, 
+          S_IFREG | 0444, &comx_root_dir)) == NULL) {
+               return -ENOMEM;
+       }
+       
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->data = new_file;
+       new_file->read_proc = &comx_root_read_proc;
+       new_file->write_proc = NULL;
+       new_file->nlink = 1;
+
+       if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, 
+          S_IFREG | 0444, &comx_root_dir)) == NULL) {
+               return -ENOMEM;
+       }
+       
+       new_file->ops = &comx_normal_inode_ops;
+       new_file->data = new_file;
+       new_file->read_proc = &comx_root_read_proc;
+       new_file->write_proc = NULL;
+       new_file->nlink = 1;
+
+
+       printk(KERN_INFO "COMX: driver version %s (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>\n", 
+               VERSION);
+
+#ifndef MODULE
+#ifdef CONFIG_COMX_HW_COMX
+       comx_hw_comx_init();
+#endif
+#ifdef CONFIG_COMX_HW_LOCOMX
+       comx_hw_locomx_init();
+#endif
+#ifdef CONFIG_COMX_HW_MIXCOM
+       comx_hw_mixcom_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_HDLC
+       comx_proto_hdlc_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_PPP
+       comx_proto_ppp_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_LAPB
+       comx_proto_lapb_init();
+#endif
+#ifdef CONFIG_COMX_PROTO_FR
+       comx_proto_fr_init();
+#endif
+#endif
+
+       return 0;
+}
+
+#ifdef MODULE
+void cleanup_module(void)
+{
+       remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir);
+       remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir);
+       proc_unregister(&proc_root, comx_root_dir.low_ino);
+}
+#endif
+
+EXPORT_SYMBOL(comx_register_hardware);
+EXPORT_SYMBOL(comx_unregister_hardware);
+EXPORT_SYMBOL(comx_register_protocol);
+EXPORT_SYMBOL(comx_unregister_protocol);
+EXPORT_SYMBOL(comx_debug_skb);
+EXPORT_SYMBOL(comx_debug_bytes);
+EXPORT_SYMBOL(comx_debug);
+EXPORT_SYMBOL(comx_lineup_func);
+EXPORT_SYMBOL(comx_status);
+EXPORT_SYMBOL(comx_rx);
+EXPORT_SYMBOL(comx_strcasecmp);
+EXPORT_SYMBOL(comx_normal_inode_ops);
+EXPORT_SYMBOL(comx_root_dir);
diff --git a/drivers/net/comx.h b/drivers/net/comx.h
new file mode 100644 (file)
index 0000000..061791f
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * General definitions for the COMX driver 
+ * 
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Currently maintained by: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ *
+ * net_device_stats:
+ *     rx_length_errors        rec_len < 4 || rec_len > 2000
+ *     rx_over_errors          receive overrun (OVR)
+ *     rx_crc_errors           rx crc error
+ *     rx_frame_errors         aborts rec'd (ABO)
+ *     rx_fifo_errors          status fifo overrun (PBUFOVR)
+ *     rx_missed_errors        receive buffer overrun (BUFOVR)
+ *     tx_aborted_errors       ?
+ *     tx_carrier_errors       modem line status changes
+ *     tx_fifo_errors          tx underrun (locomx)
+ */
+
+struct comx_protocol {
+       char    *name;
+       char    *version;
+       unsigned short encap_type;
+       int     (*line_init)(struct device *dev);
+       int     (*line_exit)(struct device *dev);
+       struct comx_protocol *next;
+       };
+
+struct comx_hardware {
+       char *name; 
+       char *version;
+       int     (*hw_init)(struct device *dev);
+       int     (*hw_exit)(struct device *dev);
+       int     (*hw_dump)(struct device *dev);
+       struct comx_hardware *next;
+       };
+
+struct comx_channel {
+       struct device   *dev;           // Where we belong to
+       struct device   *twin;          // On dual-port cards
+       struct proc_dir_entry *procdir; // the directory
+
+       unsigned char   init_status;
+       unsigned char   line_status;
+
+       struct timer_list lineup_timer; // against line jitter
+       int             lineup_pending;
+       unsigned char   lineup_delay;
+
+#if 0
+       struct timer_list reset_timer; // for board resetting
+       int             reset_pending;
+       int             reset_timeout;
+#endif
+
+       struct net_device_stats stats;  
+       struct net_device_stats *current_stats;
+#if 0
+       unsigned long   board_resets;
+#endif
+       unsigned long   *avg_bytes;
+       int             loadavg_counter, loadavg_size;
+       int             loadavg[3];
+       struct timer_list loadavg_timer;
+       int             debug_flags;
+       char            *debug_area;
+       int             debug_start, debug_end, debug_size;
+       struct proc_dir_entry *debug_file;
+#ifdef CONFIG_COMX_DEBUG_RAW
+       char            *raw;
+       int             raw_len;
+#endif
+       // LINE specific        
+       struct comx_protocol *protocol;
+       void            (*LINE_rx)(struct device *dev, struct sk_buff *skb);
+       int             (*LINE_tx)(struct device *dev);
+       void            (*LINE_status)(struct device *dev, u_short status);
+       int             (*LINE_open)(struct device *dev);
+       int             (*LINE_close)(struct device *dev);
+       int             (*LINE_xmit)(struct sk_buff *skb, struct device *dev);
+       int             (*LINE_header)(struct sk_buff *skb, struct device *dev,
+                               u_short type,void *daddr, void *saddr, 
+                               unsigned len);
+       int             (*LINE_rebuild_header)(struct sk_buff *skb);
+       int             (*LINE_statistics)(struct device *dev, char *page);
+       int             (*LINE_parameter_check)(struct device *dev);
+       int             (*LINE_ioctl)(struct device *dev, struct ifreq *ifr,
+                               int cmd);
+       void            (*LINE_mod_use)(int);
+       void *          LINE_privdata;
+
+       // HW specific
+
+       struct comx_hardware *hardware;
+       void    (*HW_board_on)(struct device *dev);
+       void    (*HW_board_off)(struct device *dev);
+       struct device *(*HW_access_board)(struct device *dev);
+       void    (*HW_release_board)(struct device *dev, struct device *savep);
+       int     (*HW_txe)(struct device *dev);
+       int     (*HW_open)(struct device *dev);
+       int     (*HW_close)(struct device *dev);
+       int     (*HW_send_packet)(struct device *dev,struct sk_buff *skb);
+       int     (*HW_statistics)(struct device *dev, char *page);
+#if 0
+       int     (*HW_reset)(struct device *dev, char *page);
+#endif
+       int     (*HW_load_board)(struct device *dev);
+       void    (*HW_set_clock)(struct device *dev);
+       void    *HW_privdata;
+       };
+
+struct comx_debugflags_struct {
+       char *name;
+       int  value;
+       };
+
+#define        COMX_ROOT_DIR_NAME      "comx"
+
+#define        FILENAME_HARDWARE       "boardtype"
+#define FILENAME_HARDWARELIST  "boardtypes"
+#define FILENAME_PROTOCOL      "protocol"
+#define FILENAME_PROTOCOLLIST  "protocols"
+#define FILENAME_DEBUG         "debug"
+#define FILENAME_CLOCK         "clock"
+#define        FILENAME_STATUS         "status"
+#define        FILENAME_IO             "io"
+#define FILENAME_IRQ           "irq"
+#define        FILENAME_KEEPALIVE      "keepalive"
+#define FILENAME_LINEUPDELAY   "lineup_delay"
+#define FILENAME_CHANNEL       "channel"
+#define FILENAME_FIRMWARE      "firmware"
+#define FILENAME_MEMADDR       "memaddr"
+#define        FILENAME_TWIN           "twin"
+#define FILENAME_T1            "t1"
+#define FILENAME_T2            "t2"
+#define FILENAME_N2            "n2"
+#define FILENAME_WINDOW                "window"
+#define FILENAME_MODE          "mode"
+#define        FILENAME_DLCI           "dlci"
+#define        FILENAME_MASTER         "master"
+#ifdef CONFIG_COMX_DEBUG_RAW
+#define        FILENAME_RAW            "raw"
+#endif
+
+#define PROTONAME_NONE         "none"
+#define HWNAME_NONE            "none"
+#define KEEPALIVE_OFF          "off"
+
+#define FRAME_ACCEPTED         0               /* sending and xmitter busy */
+#define FRAME_DROPPED          1
+#define FRAME_ERROR            2               /* xmitter error */
+#define        FRAME_QUEUED            3               /* sending but more can come */
+
+#define        LINE_UP                 1               /* Modem UP */
+#define PROTO_UP               2
+#define PROTO_LOOP             4
+
+#define        HW_OPEN                 1
+#define        LINE_OPEN               2
+#define FW_LOADED              4
+#define IRQ_ALLOCATED          8
+
+#define DEBUG_COMX_RX          2
+#define        DEBUG_COMX_TX           4
+#define        DEBUG_HW_TX             16
+#define        DEBUG_HW_RX             32
+#define        DEBUG_HDLC_KEEPALIVE    64
+#define        DEBUG_COMX_PPP          128
+#define DEBUG_COMX_LAPB                256
+#define        DEBUG_COMX_DLCI         512
+
+#define        DEBUG_PAGESIZE          3072
+#define DEFAULT_DEBUG_SIZE     4096
+#define        DEFAULT_LINEUP_DELAY    1
+#define        FILE_PAGESIZE           3072
+
+#ifndef        COMX_PPP_MAJOR
+#define        COMX_PPP_MAJOR          88
+#endif
+
+
+#ifndef min
+#define min(a,b)               ((a) > (b) ? (b) : (a))
+#endif
+#ifndef max
+#define max(a,b)               ((a) > (b) ? (a) : (b))
+#endif
+
+
+#define COMX_CHANNEL(dev) ((struct comx_channel*)dev->priv)
+
+#define TWIN(dev) (COMX_CHANNEL(dev)->twin)
+
+
+#ifndef byte
+typedef u8     byte;
+#endif
+#ifndef word
+typedef u16    word;
+#endif
+
+#ifndef        SEEK_SET
+#define        SEEK_SET        0
+#endif
+#ifndef        SEEK_CUR
+#define        SEEK_CUR        1
+#endif
+#ifndef        SEEK_END
+#define        SEEK_END        2
+#endif
+
+extern struct proc_dir_entry comx_root_dir;
+
+extern int     comx_register_hardware(struct comx_hardware *comx_hw);
+extern int     comx_unregister_hardware(char *name);
+extern int     comx_register_protocol(struct comx_protocol *comx_line);
+extern int     comx_unregister_protocol(char *name);
+
+extern int     comx_rx(struct device *dev, struct sk_buff *skb);
+extern void    comx_status(struct device *dev, int status);
+extern void    comx_lineup_func(unsigned long d);
+
+extern int     comx_debug(struct device *dev, char *fmt, ...);
+extern int     comx_debug_skb(struct device *dev, struct sk_buff *skb, char *msg);
+extern int     comx_debug_bytes(struct device *dev, unsigned char *bytes, int len,
+               char *msg);
+extern int     comx_strcasecmp(const char *cs, const char *ct);
+
+extern struct inode_operations comx_normal_inode_ops;
diff --git a/drivers/net/comxhw.h b/drivers/net/comxhw.h
new file mode 100644 (file)
index 0000000..5749c34
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Defines for comxhw.c
+ *
+ * Original authors:  Arpad Bakay <bakay.arpad@synergon.hu>,
+ *                    Peter Bajan <bajan.peter@synergon.hu>,
+ * Previous maintainer: Tivadar Szemethy <tiv@itc.hu>
+ * Current maintainer: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1995-1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ */
+
+#define        LOCOMX_IO_EXTENT        8
+#define COMX_IO_EXTENT         4
+#define        HICOMX_IO_EXTENT        16
+
+#define COMX_MAX_TX_SIZE       1600
+
+#define COMX_JAIL_OFFSET       0xffff
+#define COMX_JAIL_VALUE                0xfe
+#define        COMX_MEMORY_SIZE        65536
+#define HICOMX_MEMORY_SIZE     16384
+#define COMX_MEM_MIN           0xa0000
+#define COMX_MEM_MAX           0xf0000
+
+#define        COMX_DEFAULT_IO         0x360
+#define        COMX_DEFAULT_IRQ        10
+#define        COMX_DEFAULT_MEMADDR    0xd0000
+#define        HICOMX_DEFAULT_IO       0x320
+#define        HICOMX_DEFAULT_IRQ      10
+#define        HICOMX_DEFAULT_MEMADDR  0xd0000
+#define        LOCOMX_DEFAULT_IO       0x368
+#define        LOCOMX_DEFAULT_IRQ      7
+
+#define MAX_CHANNELNO          2
+
+#define        COMX_CHANNEL_OFFSET     0x2000
+
+#define COMX_ENABLE_BOARD_IT    0x40
+#define COMX_BOARD_RESET               0x20
+#define COMX_ENABLE_BOARD_MEM   0x10
+#define COMX_DISABLE_BOARD_MEM  0
+#define COMX_DISABLE_ALL       0x00
+
+#define HICOMX_DISABLE_ALL     0x00
+#define HICOMX_ENABLE_BOARD_MEM        0x02
+#define HICOMX_DISABLE_BOARD_MEM 0x0
+#define HICOMX_BOARD_RESET     0x01
+#define HICOMX_PRG_MEM         4
+#define HICOMX_DATA_MEM                0
+#define HICOMX_ID_BYTE         0x55
+
+#define CMX_ID_BYTE            0x31
+#define COMX_CLOCK_CONST       8000
+
+#define        LINKUP_READY            3
+
+#define        OFF_FW_L1_ID    0x01e    /* ID bytes */
+#define OFF_FW_L2_ID   0x1006
+#define        FW_L1_ID_1      0xab
+#define FW_L1_ID_2_COMX                0xc0
+#define FW_L1_ID_2_HICOMX      0xc1
+#define        FW_L2_ID_1      0xab
+
+#define OFF_A_L2_CMD     0x130   /* command register for L2 */
+#define OFF_A_L2_CMDPAR  0x131   /* command parameter byte */
+#define OFF_A_L1_STATB   0x122   /* stat. block for L1 */
+#define OFF_A_L1_ABOREC  0x122   /* receive ABORT counter */
+#define OFF_A_L1_OVERRUN 0x123   /* receive overrun counter */
+#define OFF_A_L1_CRCREC  0x124   /* CRC error counter */
+#define OFF_A_L1_BUFFOVR 0x125   /* buffer overrun counter */
+#define OFF_A_L1_PBUFOVR 0x126   /* priority buffer overrun counter */
+#define OFF_A_L1_MODSTAT 0x127   /* current state of modem ctrl lines */
+#define OFF_A_L1_STATE   0x127   /* end of stat. block for L1 */
+#define OFF_A_L1_TXPC    0x128   /* Tx counter for the PC */
+#define OFF_A_L1_TXZ80   0x129   /* Tx counter for the Z80 */
+#define OFF_A_L1_RXPC    0x12a   /* Rx counter for the PC */
+#define OFF_A_L1_RXZ80   0x12b   /* Rx counter for the Z80 */
+#define OFF_A_L1_REPENA  0x12c   /* IT rep disable */
+#define OFF_A_L1_CHNR    0x12d   /* L1 channel logical number */
+#define OFF_A_L1_CLKINI  0x12e   /* Timer Const */
+#define OFF_A_L2_LINKUP         0x132   /* Linkup byte */
+#define OFF_A_L2_DAV    0x134   /* Rx DAV */
+#define OFF_A_L2_RxBUFP  0x136  /* Rx buff relative to membase */
+#define OFF_A_L2_TxEMPTY 0x138   /* Tx Empty */
+#define OFF_A_L2_TxBUFP  0x13a   /* Tx Buf */
+#define OFF_A_L2_NBUFFS         0x144   /* Number of buffers to fetch */
+
+#define OFF_A_L2_SABMREC 0x164  /* LAPB no. of SABMs received */
+#define OFF_A_L2_SABMSENT 0x165         /* LAPB no. of SABMs sent */
+#define OFF_A_L2_REJREC  0x166  /* LAPB no. of REJs received */
+#define OFF_A_L2_REJSENT 0x167  /* LAPB no. of REJs sent */
+#define OFF_A_L2_FRMRREC 0x168  /* LAPB no. of FRMRs received */
+#define OFF_A_L2_FRMRSENT 0x169         /* LAPB no. of FRMRs sent */
+#define OFF_A_L2_PROTERR 0x16A  /* LAPB no. of protocol errors rec'd */
+#define OFF_A_L2_LONGREC 0x16B  /* LAPB no. of long frames */
+#define OFF_A_L2_INVNR   0x16C  /* LAPB no. of invalid N(R)s rec'd */
+#define OFF_A_L2_UNDEFFR 0x16D  /* LAPB no. of invalid frames */
+
+#define        OFF_A_L2_T1     0x174    /* T1 timer */
+#define        OFF_A_L2_ADDR   0x176    /* DCE = 1, DTE = 3 */
+
+#define        COMX_CMD_INIT   1
+#define COMX_CMD_EXIT  2
+#define COMX_CMD_OPEN  16
+#define COMX_CMD_CLOSE 17
+
diff --git a/drivers/net/hscx.h b/drivers/net/hscx.h
new file mode 100644 (file)
index 0000000..675b7b1
--- /dev/null
@@ -0,0 +1,103 @@
+#define        HSCX_MTU        1600
+
+#define        HSCX_ISTA       0x00
+#define HSCX_MASK      0x00
+#define HSCX_STAR      0x01
+#define HSCX_CMDR      0x01
+#define HSCX_MODE      0x02
+#define HSCX_TIMR      0x03
+#define HSCX_EXIR      0x04
+#define HSCX_XAD1      0x04
+#define HSCX_RBCL      0x05
+#define HSCX_SAD2      0x05
+#define HSCX_RAH1      0x06
+#define HSCX_RSTA      0x07
+#define HSCX_RAH2      0x07
+#define HSCX_RAL1      0x08
+#define HSCX_RCHR      0x09
+#define HSCX_RAL2      0x09
+#define HSCX_XBCL      0x0a
+#define HSCX_BGR       0x0b
+#define HSCX_CCR2      0x0c
+#define HSCX_RBCH      0x0d
+#define HSCX_XBCH      0x0d
+#define HSCX_VSTR      0x0e
+#define HSCX_RLCR      0x0e
+#define HSCX_CCR1      0x0f
+#define HSCX_FIFO      0x1e
+
+#define HSCX_HSCX_CHOFFS       0x400
+#define HSCX_SEROFFS   0x1000
+
+#define HSCX_RME       0x80
+#define HSCX_RPF       0x40
+#define HSCX_RSC       0x20
+#define HSCX_XPR       0x10
+#define HSCX_TIN       0x08
+#define HSCX_ICA       0x04
+#define HSCX_EXA       0x02
+#define HSCX_EXB       0x01
+
+#define HSCX_XMR       0x80
+#define HSCX_XDU       0x40
+#define HSCX_EXE       0x40
+#define HSCX_PCE       0x20
+#define HSCX_RFO       0x10
+#define HSCX_CSC       0x08
+#define HSCX_RFS       0x04
+
+#define HSCX_XDOV      0x80
+#define HSCX_XFW       0x40
+#define HSCX_XRNR      0x20
+#define HSCX_RRNR      0x10
+#define HSCX_RLI       0x08
+#define HSCX_CEC       0x04
+#define HSCX_CTS       0x02
+#define HSCX_WFA       0x01
+
+#define HSCX_RMC       0x80
+#define HSCX_RHR       0x40
+#define HSCX_RNR       0x20
+#define HSCX_XREP      0x20
+#define HSCX_STI       0x10
+#define HSCX_XTF       0x08
+#define HSCX_XIF       0x04
+#define HSCX_XME       0x02
+#define HSCX_XRES      0x01
+
+#define HSCX_AUTO      0x00
+#define HSCX_NONAUTO   0x40
+#define HSCX_TRANS     0x80
+#define HSCX_XTRANS    0xc0
+#define HSCX_ADM16     0x20
+#define HSCX_ADM8      0x00
+#define HSCX_TMD_EXT   0x00
+#define HSCX_TMD_INT   0x10
+#define HSCX_RAC       0x08
+#define HSCX_RTS       0x04
+#define HSCX_TLP       0x01
+
+#define HSCX_VFR       0x80
+#define HSCX_RDO       0x40
+#define HSCX_CRC       0x20
+#define HSCX_RAB       0x10
+
+#define HSCX_CIE       0x04
+#define HSCX_RIE       0x02
+
+#define HSCX_DMA       0x80
+#define HSCX_NRM       0x40
+#define HSCX_CAS       0x20
+#define HSCX_XC        0x10
+
+#define HSCX_OV        0x10
+
+#define HSCX_CD        0x80
+
+#define HSCX_RC        0x80
+
+#define HSCX_PU        0x80
+#define HSCX_NRZ       0x00
+#define HSCX_NRZI      0x40
+#define HSCX_ODS       0x10
+#define HSCX_ITF       0x08
diff --git a/drivers/net/mixcom.h b/drivers/net/mixcom.h
new file mode 100644 (file)
index 0000000..1815eef
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Defines for the mixcom board
+ *
+ * Author: Gergely Madarasz <gorgo@itc.hu>
+ *
+ * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
+ *
+ * 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.
+ *
+ */
+
+#define        MIXCOM_IO_EXTENT        0x20
+
+#define        MIXCOM_DEFAULT_IO       0x180
+#define        MIXCOM_DEFAULT_IRQ      5
+
+#define MIXCOM_ID              0x11
+#define MIXCOM_SERIAL_OFFSET   0x1000
+#define MIXCOM_CHANNEL_OFFSET  0x400
+#define MIXCOM_IT_OFFSET       0xc14
+#define MIXCOM_STATUS_OFFSET   0xc14
+#define MIXCOM_ID_OFFSET       0xc10
+#define MIXCOM_ON              0x1
+#define MIXCOM_OFF             0x0
+
+/* Status register bits */
+
+#define MIXCOM_CTSB            0x1
+#define MIXCOM_CTSA            0x2
+#define MIXCOM_CHANNELNO       0x20
+#define MIXCOM_POWERFAIL       0x40
+#define MIXCOM_BOOT            0x80
index b7fca44e1c07eb9d602b8357cf4ba1314425abb0..57a6a0c224a435070e595ea3c4aa32360b5d3c9a 100644 (file)
@@ -18,6 +18,7 @@
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm
    
+   Rev 1.06 Nov. 4 1999 Ollie Lho (ollie@sis.com.tw) Second release
    Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx  
    Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support
    Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release
@@ -46,7 +47,7 @@
 #include "sis900.h"
 
 static const char *version =
-"sis900.c:v1.05.05  10/29/99\n";
+"sis900.c: v1.06  11/04/99\n";
 
 static int max_interrupt_work = 20;
 #define sis900_debug debug
@@ -75,15 +76,19 @@ static struct mac_chip_info  mac_chip_table[] = {
        {0,},                                          /* 0 terminated list. */
 };
 
+static void sis900_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex);
+static void amd79c901_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex);
+
 static struct mii_chip_info {
        const char * name;
        u16 phy_id0;
        u16 phy_id1;
+       void (*read_mode) (struct device *net_dev, int phy_addr, int *speed, int *duplex);
 } mii_chip_table[] = {
-       {"SiS 900 Internal MII PHY", 0x001d, 0x8000},
-       {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830},
-       {"AMD 79C901 10BASE-T PHY", 0x0000, 0x35b9},
-       {"AMD 79C901 HomePNA PHY",  0x0000, 0x35c8},
+       {"SiS 900 Internal MII PHY", 0x001d, 0x8000, sis900_read_mode},
+       {"SiS 7014 Physical Layer Solution", 0x0016, 0xf830,sis900_read_mode},
+       {"AMD 79C901 10BASE-T PHY",  0x0000, 0x35b9, amd79c901_read_mode},
+       {"AMD 79C901 HomePNA PHY",   0x0000, 0x35c8, amd79c901_read_mode},
        {0,},
 };
 
@@ -107,63 +112,53 @@ struct sis900_private {
 
        struct mac_chip_info * mac;
        struct mii_phy * mii;
+       unsigned int cur_phy;
 
         struct timer_list timer;                       /* Link status detection timer. */
         unsigned int cur_rx, dirty_rx;         
         unsigned int cur_tx, dirty_tx;
 
-        /* The saved address of a sent/receive-in-place packet/buffer */
+        /* The saved address of a sent/receive-in-place packet buffer */
         struct sk_buff *tx_skbuff[NUM_TX_DESC];
        struct sk_buff *rx_skbuff[NUM_RX_DESC];
        BufferDesc tx_ring[NUM_TX_DESC];        
         BufferDesc rx_ring[NUM_RX_DESC];
         unsigned int tx_full;                  /* The Tx queue is full.    */
 
-       int MediaSpeed;                                 /* user force speed         */
-       int MediaDuplex;                                /* user force duplex        */
-        int full_duplex;                               /* Full/Half-duplex.        */
-        int speeds;                                    /* 100/10 Mbps.             */
-        u16 LinkOn;
-        u16 LinkChange;
+        int LinkOn;
 };
 
 #ifdef MODULE
 #if LINUX_VERSION_CODE > 0x20115
 MODULE_AUTHOR("Jim Huang <cmhuang@sis.com.tw>");
 MODULE_DESCRIPTION("SiS 900 PCI Fast Ethernet driver");
-MODULE_PARM(speeds, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM(multicast_filter_limit, "i");
 MODULE_PARM(max_interrupt_work, "i");
 MODULE_PARM(debug, "i");
 #endif
 #endif
 
-static int sis900_open(struct device *dev);
-static int sis900_mii_probe (struct device * dev);
-static void sis900_init_rxfilter (struct device * dev);
+static int sis900_open(struct device *net_dev);
+static int sis900_mii_probe (struct device * net_dev);
+static void sis900_init_rxfilter (struct device * net_dev);
 static u16 read_eeprom(long ioaddr, int location);
-static u16 mdio_read(struct device *dev, int phy_id, int location);
-static void mdio_write(struct device *dev, int phy_id, int location, int val);
+static u16 mdio_read(struct device *net_dev, int phy_id, int location);
+static void mdio_write(struct device *net_dev, int phy_id, int location, int val);
 static void sis900_timer(unsigned long data);
-static void sis900_tx_timeout(struct device *dev);
-static void sis900_init_tx_ring(struct device *dev);
-static void sis900_init_rx_ring(struct device *dev);
-static int sis900_start_xmit(struct sk_buff *skb, struct device *dev);
-static int sis900_rx(struct device *dev);
-static void sis900_finish_xmit (struct device *dev);
+static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy);
+static void sis900_tx_timeout(struct device *net_dev);
+static void sis900_init_tx_ring(struct device *net_dev);
+static void sis900_init_rx_ring(struct device *net_dev);
+static int sis900_start_xmit(struct sk_buff *skb, struct device *net_dev);
+static int sis900_rx(struct device *net_dev);
+static void sis900_finish_xmit (struct device *net_dev);
 static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int sis900_close(struct device *dev);
-static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd);
-static struct enet_statistics *sis900_get_stats(struct device *dev);
-static void set_rx_mode(struct device *dev);
-static void sis900_reset(struct device *dev);
-static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed);
-static void elSetCapability(struct device *dev, int phy_id, int duplex, int speed);
-static u16 elPMDreadMode(struct device *dev, int phy_id, int *speed, int *duplex);
-static u16 elMIIpollBit(struct device *dev, int phy_id, int location, u16 mask, 
-                                       u16 polarity, u16 *value);
-static void elSetMediaType(struct device *dev, int speed, int duplex);
+static int sis900_close(struct device *net_dev);
+static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd);
+static struct enet_statistics *sis900_get_stats(struct device *net_dev);
+static u16 sis900_compute_hashtable_index(u8 *addr);
+static void set_rx_mode(struct device *net_dev);
+static void sis900_reset(struct device *net_dev);
 
 /* A list of all installed SiS900 devices, for removing the driver module. */
 static struct device *root_sis900_dev = NULL;
@@ -323,7 +318,7 @@ static int sis900_mii_probe (struct device * net_dev)
                                        sis_priv->mii = mii_phy;
                                }
                                /* the current mii is on our mii_info_table, 
-                                  quit searching (table) */
+                                  try next address */
                                break;
                        }
        }
@@ -334,21 +329,16 @@ static int sis900_mii_probe (struct device * net_dev)
                return 0;
        }
 
-       /* FIXME: AMD stuff should be added */
-       /* auto negotiate FIXME: not completed */
-       elSetCapability(net_dev, sis_priv->mii->phy_addr, 1, 100);
-       sis_priv->mii->status = elAutoNegotiate(net_dev, 
-                                               sis_priv->mii->phy_addr,
-                                               &sis_priv->full_duplex, 
-                                               &sis_priv->speeds);
-       
-       if (sis_priv->mii->status & MIISTAT_LINK) 
+       /* arbitrary choose that last PHY and current PHY */
+       sis_priv->cur_phy = sis_priv->mii->phy_addr;
+       printk(KERN_INFO "%s: Using %s as default\n", net_dev->name,
+              sis_priv->mii->chip_info->name);
+
+       if (sis_priv->mii->status & MII_STAT_LINK) 
                sis_priv->LinkOn = TRUE;
        else
                sis_priv->LinkOn = FALSE;
-       
-       sis_priv->LinkChange = FALSE;
-       
+
        return 1;
 }
 
@@ -424,9 +414,9 @@ static void mdio_reset(long mdio_addr)
         return;
 }
 
-static u16 mdio_read(struct device *dev, int phy_id, int location)
+static u16 mdio_read(struct device *net_dev, int phy_id, int location)
 {
-        long mdio_addr = dev->base_addr + mear;
+        long mdio_addr = net_dev->base_addr + mear;
         int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
         u16 retval = 0;
         int i;
@@ -453,9 +443,9 @@ static u16 mdio_read(struct device *dev, int phy_id, int location)
         return retval;
 }
 
-static void mdio_write(struct device *dev, int phy_id, int location, int value)
+static void mdio_write(struct device *net_dev, int phy_id, int location, int value)
 {
-        long mdio_addr = dev->base_addr + mear;
+        long mdio_addr = net_dev->base_addr + mear;
         int mii_cmd = MIIwrite|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
         int i;
 
@@ -493,48 +483,43 @@ static void mdio_write(struct device *dev, int phy_id, int location, int value)
 }
 
 static int
-sis900_open(struct device *dev)
+sis900_open(struct device *net_dev)
 {
-        struct sis900_private *sis_priv = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
 
         /* Soft reset the chip. */
-       sis900_reset(dev);
+       sis900_reset(net_dev);
 
-        if (request_irq(dev->irq, &sis900_interrupt, SA_SHIRQ, dev->name, dev)) {
+        if (request_irq(net_dev->irq, &sis900_interrupt, SA_SHIRQ, net_dev->name, net_dev)) {
                 return -EAGAIN;
         }
 
         MOD_INC_USE_COUNT;
 
-       /* FIXME: should this be move to set_rx_mode() ? */
-       sis900_init_rxfilter(dev);
-
-       sis900_init_tx_ring(dev);
-       sis900_init_rx_ring(dev);
+       sis900_init_rxfilter(net_dev);
 
-       /* FIXME: should be removed, and replaced by AutoNeogotiate stuff */
-        outl((RX_DMA_BURST << RxMXDMA_shift) | (RxDRNT_10 << RxDRNT_shift), 
-            ioaddr + rxcfg);
-        outl(TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift) | TxDRNT_10,
-            ioaddr + txcfg);
+       sis900_init_tx_ring(net_dev);
+       sis900_init_rx_ring(net_dev);
 
-        set_rx_mode(dev);
+        set_rx_mode(net_dev);
 
-        dev->tbusy = 0;
-        dev->interrupt = 0;
-        dev->start = 1;
+        net_dev->tbusy = 0;
+        net_dev->interrupt = 0;
+        net_dev->start = 1;
 
         /* Enable all known interrupts by setting the interrupt mask. */
        outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr);
         outl(RxENA, ioaddr + cr);
         outl(IE, ioaddr + ier);
 
+       sis900_check_mode(net_dev, sis_priv->mii);
+
         /* Set the timer to switch to check for link beat and perhaps switch
            to an alternate media type. */
         init_timer(&sis_priv->timer);
         sis_priv->timer.expires = jiffies + HZ;
-        sis_priv->timer.data = (unsigned long)dev;
+        sis_priv->timer.data = (unsigned long)net_dev;
         sis_priv->timer.function = &sis900_timer;
         add_timer(&sis_priv->timer);
 
@@ -574,51 +559,51 @@ sis900_init_rxfilter (struct device * net_dev)
 
 /* Initialize the Tx ring. */
 static void
-sis900_init_tx_ring(struct device *dev)
+sis900_init_tx_ring(struct device *net_dev)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr; 
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr; 
         int i;
        
-        tp->tx_full = 0;
-        tp->dirty_tx = tp->cur_tx = 0;
+        sis_priv->tx_full = 0;
+        sis_priv->dirty_tx = sis_priv->cur_tx = 0;
        
         for (i = 0; i < NUM_TX_DESC; i++) {
-               tp->tx_skbuff[i] = NULL;
+               sis_priv->tx_skbuff[i] = NULL;
 
-               tp->tx_ring[i].link = (u32) virt_to_bus(&tp->tx_ring[i+1]);
-                tp->tx_ring[i].cmdsts = 0;
-               tp->tx_ring[i].bufptr = 0;
+               sis_priv->tx_ring[i].link = (u32) virt_to_bus(&sis_priv->tx_ring[i+1]);
+                sis_priv->tx_ring[i].cmdsts = 0;
+               sis_priv->tx_ring[i].bufptr = 0;
         }
-       tp->tx_ring[i-1].link = (u32) virt_to_bus(&tp->tx_ring[0]);
+       sis_priv->tx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->tx_ring[0]);
 
        /* load Transmit Descriptor Register */
-        outl(virt_to_bus(&tp->tx_ring[0]), ioaddr + txdp); 
+        outl(virt_to_bus(&sis_priv->tx_ring[0]), ioaddr + txdp); 
        if (sis900_debug > 2)
                 printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", 
-                      dev->name, inl(ioaddr + txdp));
+                      net_dev->name, inl(ioaddr + txdp));
 }
 
 /* Initialize the Rx descriptor ring, pre-allocate recevie buffers */ 
 static void 
-sis900_init_rx_ring(struct device *dev) 
+sis900_init_rx_ring(struct device *net_dev) 
 { 
-        struct sis900_private *tp = (struct sis900_private *)dev->priv; 
-        long ioaddr = dev->base_addr; 
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; 
+        long ioaddr = net_dev->base_addr; 
         int i;
  
-        tp->cur_rx = 0; 
-       tp->dirty_rx = 0;
+        sis_priv->cur_rx = 0; 
+       sis_priv->dirty_rx = 0;
 
        /* init RX descriptor */
        for (i = 0; i < NUM_RX_DESC; i++) {
-               tp->rx_skbuff[i] = NULL;
+               sis_priv->rx_skbuff[i] = NULL;
 
-               tp->rx_ring[i].link = (u32) virt_to_bus(&tp->rx_ring[i+1]);
-               tp->rx_ring[i].cmdsts = 0;
-               tp->rx_ring[i].bufptr = 0;
+               sis_priv->rx_ring[i].link = (u32) virt_to_bus(&sis_priv->rx_ring[i+1]);
+               sis_priv->rx_ring[i].cmdsts = 0;
+               sis_priv->rx_ring[i].bufptr = 0;
        }
-       tp->rx_ring[i-1].link = (u32) virt_to_bus(&tp->rx_ring[0]);
+       sis_priv->rx_ring[i-1].link = (u32) virt_to_bus(&sis_priv->rx_ring[0]);
 
         /* allocate sock buffers */
        for (i = 0; i < NUM_RX_DESC; i++) {
@@ -631,69 +616,187 @@ sis900_init_rx_ring(struct device *dev)
                           buffer */
                        break;
                }
-               skb->dev = dev;
-               tp->rx_skbuff[i] = skb;
-               tp->rx_ring[i].cmdsts = RX_BUF_SIZE;
-               tp->rx_ring[i].bufptr = virt_to_bus(skb->tail);
+               skb->dev = net_dev;
+               sis_priv->rx_skbuff[i] = skb;
+               sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE;
+               sis_priv->rx_ring[i].bufptr = virt_to_bus(skb->tail);
        }
-       tp->dirty_rx = (unsigned int) (i - NUM_RX_DESC);
+       sis_priv->dirty_rx = (unsigned int) (i - NUM_RX_DESC);
 
        /* load Receive Descriptor Register */
-        outl(virt_to_bus(&tp->rx_ring[0]), ioaddr + rxdp);
+        outl(virt_to_bus(&sis_priv->rx_ring[0]), ioaddr + rxdp);
        if (sis900_debug > 2)
                 printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", 
-                      dev->name, inl(ioaddr + rxdp));
+                      net_dev->name, inl(ioaddr + rxdp));
 }
-
+/* on each timer ticks we check two things, Link Status (ON/OFF) and 
+   Link Mode (10/100/Full/Half)
+ */
 static void sis900_timer(unsigned long data)
 {
-        struct device *dev = (struct device *)data;
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        int next_tick = 2*HZ;
+        struct device *net_dev = (struct device *)data;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+       struct mii_phy *mii_phy = sis_priv->mii;
+        static int next_tick = 5*HZ;
         u16 status;
 
-       /* FIXME: Should we check transmission time out here ? */
-       /* FIXME: call auto negotiate routine to detect link status */
-        if (!tp->LinkOn) {
-                status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS);
-               if (status & MIISTAT_LINK) {
-                       elPMDreadMode(dev, tp->mii->phy_addr,
-                                     &tp->speeds, &tp->full_duplex);
-                       tp->LinkOn = TRUE;
-                        printk(KERN_INFO "%s: Media Link On %s%s-duplex \n", 
-                              dev->name,
-                              tp->speeds == HW_SPEED_100_MBPS ? 
-                              "100mbps " : "10mbps ",
-                              tp->full_duplex == FDX_CAPABLE_FULL_SELECTED ?
-                              "full" : "half");
+       status = mdio_read(net_dev, sis_priv->cur_phy, MII_STATUS);
+
+       /* current mii phy is failed to link, try another one */
+       while (!(status & MII_STAT_LINK)) {             
+               if (mii_phy->next == NULL) { 
+                       if (sis_priv->LinkOn) {
+                               /* link stat change from ON to OFF */
+                               next_tick = HZ;
+                               sis_priv->LinkOn = FALSE;
+                               printk(KERN_INFO "%s: Media Link Off\n",
+                                      net_dev->name);
+                       }
+                       sis_priv->timer.expires = jiffies + next_tick;
+                       add_timer(&sis_priv->timer);
+                       return;
                }
-        } else { // previous link on
-                status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS);
-               if (!(status & MIISTAT_LINK)) {
-                       tp->LinkOn = FALSE;
-                        printk(KERN_INFO "%s: Media Link Off\n", dev->name);
+               mii_phy = mii_phy->next;
+               status = mdio_read(net_dev, mii_phy->phy_addr, MII_STATUS);
+       }
+
+       if (!sis_priv->LinkOn) {
+               /* link stat change forn OFF to ON, read and report link mode */
+               sis_priv->LinkOn = TRUE;
+               next_tick = 5*HZ;
+               /* change what cur_phy means */
+               if (mii_phy->phy_addr != sis_priv->cur_phy) {
+                       printk(KERN_INFO "%s: Changing transceiver to %s\n", net_dev->name,
+                              mii_phy->chip_info->name);
+                       status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
+                       mdio_write(net_dev, sis_priv->cur_phy, 
+                                  MII_CONTROL, status | MII_CNTL_ISOLATE);
+                       status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL);
+                       mdio_write(net_dev, mii_phy->phy_addr, 
+                                  MII_CONTROL, status & ~MII_CNTL_ISOLATE);
+                       sis_priv->cur_phy = mii_phy->phy_addr;
                }
-        }
+               sis900_check_mode(net_dev, mii_phy);
+       }
 
-       tp->timer.expires = jiffies + next_tick;
-       add_timer(&tp->timer);
+       sis_priv->timer.expires = jiffies + next_tick;
+       add_timer(&sis_priv->timer);
 }
+static void sis900_check_mode (struct device *net_dev, struct mii_phy *mii_phy)
+{
+       struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
+       int speed, duplex;
+       u32 tx_flags = 0, rx_flags = 0;
+
+       mii_phy->chip_info->read_mode(net_dev, sis_priv->cur_phy, &speed, &duplex);
 
-static void sis900_tx_timeout(struct device *dev)
+       tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);
+       rx_flags = RX_DMA_BURST << RxMXDMA_shift;
+
+       if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) {
+               rx_flags |= (RxDRNT_10 << RxDRNT_shift);
+               tx_flags |= (TxDRNT_10 << TxDRNT_shift);
+       }
+       else {
+               rx_flags |= (RxDRNT_100 << RxDRNT_shift);
+               tx_flags |= (TxDRNT_100 << TxDRNT_shift);
+       }
+
+       if (duplex == FDX_CAPABLE_FULL_SELECTED) {
+               tx_flags |= (TxCSI | TxHBI);
+               rx_flags |= RxATX;
+       }
+
+       outl (tx_flags, ioaddr + txcfg);
+       outl (rx_flags, ioaddr + rxcfg);
+}
+static void sis900_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
+       int i = 0;
+       u32 status;
+       
+       /* STSOUT register is Latched on Transition, read operation updates it */
+       while (i++ < 2)
+               status = mdio_read(net_dev, phy_addr, MII_STSOUT);
+
+       if (status & MII_STSOUT_SPD)
+               *speed = HW_SPEED_100_MBPS;
+       else
+               *speed = HW_SPEED_10_MBPS;
+
+       if (status & MII_STSOUT_DPLX)
+               *duplex = FDX_CAPABLE_FULL_SELECTED;
+       else
+               *duplex = FDX_CAPABLE_HALF_SELECTED;
 
-        if (sis900_debug > 2)
-                printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n",
-                      dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
+       if (status & MII_STSOUT_LINK_FAIL)
+               printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+       else
+               printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", 
+                      net_dev->name,
+                      *speed == HW_SPEED_100_MBPS ? 
+                      "100mbps" : "10mbps",
+                      *duplex == FDX_CAPABLE_FULL_SELECTED ?
+                      "full" : "half");
+}
+static void amd79c901_read_mode(struct device *net_dev, int phy_addr, int *speed, int *duplex)
+{
+       int i;
+       u16 status;
+       
+       for (i = 0; i < 2; i++)
+               status = mdio_read(net_dev, phy_addr, MII_STATUS);
+
+       if (status & MII_STAT_CAN_AUTO) {
+               /* 10BASE-T PHY */
+               for (i = 0; i < 2; i++)
+                       status = mdio_read(net_dev, phy_addr, MII_STATUS_SUMMARY);
+               if (status & MII_STSSUM_SPD)
+                       *speed = HW_SPEED_100_MBPS;
+               else
+                       *speed = HW_SPEED_10_MBPS;
+               if (status & MII_STSSUM_DPLX)
+                       *duplex = FDX_CAPABLE_FULL_SELECTED;
+               else
+                       *duplex = FDX_CAPABLE_HALF_SELECTED;
+
+               if (status & MII_STSSUM_LINK)
+                       printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", 
+                              net_dev->name,
+                              *speed == HW_SPEED_100_MBPS ? 
+                              "100mbps" : "10mbps",
+                              *duplex == FDX_CAPABLE_FULL_SELECTED ?
+                              "full" : "half");
+               else
+                       printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+                       
+       }
+       else {
+               /* HomePNA */
+               *speed = HW_SPEED_HOME;
+               *duplex = FDX_CAPABLE_HALF_SELECTED;
+               if (status & MII_STAT_LINK)
+                       printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n", 
+                              net_dev->name);
+               else
+                       printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);
+       }
+}
+static void sis900_tx_timeout(struct device *net_dev)
+{
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
+
+       printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",
+              net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));
        
         /* Disable interrupts by clearing the interrupt mask. */
         outl(0x0000, ioaddr + imr);
 
-        tp->cur_rx = 0;
-        dev->trans_start = jiffies;
-        tp->stats.tx_errors++;
+        sis_priv->cur_rx = 0;
+        net_dev->trans_start = jiffies;
+        sis_priv->stats.tx_errors++;
 
        /* FIXME: Should we restart the transmission thread here  ?? */
 
@@ -703,43 +806,43 @@ static void sis900_tx_timeout(struct device *dev)
 }
 
 static int
-sis900_start_xmit(struct sk_buff *skb, struct device *dev)
+sis900_start_xmit(struct sk_buff *skb, struct device *net_dev)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
         unsigned int  entry;
 
        /* test tbusy to see if we have timeout situation then set it */
-        if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
-                if (jiffies - dev->trans_start > TX_TIMEOUT)
-                       sis900_tx_timeout(dev);
+        if (test_and_set_bit(0, (void*)&net_dev->tbusy) != 0) {
+                if (jiffies - net_dev->trans_start > TX_TIMEOUT)
+                       sis900_tx_timeout(net_dev);
                 return 1;
         }
 
         /* Calculate the next Tx descriptor entry. */
-        entry = tp->cur_tx % NUM_TX_DESC;
-        tp->tx_skbuff[entry] = skb;
+        entry = sis_priv->cur_tx % NUM_TX_DESC;
+        sis_priv->tx_skbuff[entry] = skb;
 
        /* set the transmit buffer descriptor and enable Transmit State Machine */
-       tp->tx_ring[entry].bufptr = virt_to_bus(skb->data);
-        tp->tx_ring[entry].cmdsts = (OWN | skb->len);
+       sis_priv->tx_ring[entry].bufptr = virt_to_bus(skb->data);
+        sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);
         outl(TxENA, ioaddr + cr);
 
-        if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {
+        if (++sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC) {
                /* Typical path, clear tbusy to indicate more 
                   transmission is possible */
-                clear_bit(0, (void*)&dev->tbusy);
+                clear_bit(0, (void*)&net_dev->tbusy);
         } else {
                /* no more transmit descriptor avaiable, tbusy remain set */
-                tp->tx_full = 1;
+                sis_priv->tx_full = 1;
         }
 
-        dev->trans_start = jiffies;
+        net_dev->trans_start = jiffies;
 
         if (sis900_debug > 3)
                 printk(KERN_INFO "%s: Queued Tx packet at %p size %d "
                       "to slot %d.\n",
-                      dev->name, skb->data, (int)skb->len, entry);
+                      net_dev->name, skb->data, (int)skb->len, entry);
 
         return 0;
 }
@@ -748,26 +851,26 @@ sis900_start_xmit(struct sk_buff *skb, struct device *dev)
    after the Tx thread. */
 static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
 {
-        struct device *dev = (struct device *)dev_instance;
+        struct device *net_dev = (struct device *)dev_instance;
         int boguscnt = max_interrupt_work;
-       long ioaddr = dev->base_addr;
+       long ioaddr = net_dev->base_addr;
        u32 status;
 
 #if defined(__i386__)
         /* A lock to prevent simultaneous entry bug on Intel SMP machines. */
-        if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+        if (test_and_set_bit(0, (void*)&net_dev->interrupt)) {
                 printk(KERN_INFO "%s: SMP simultaneous entry of "
-                      "an interrupt handler.\n", dev->name);
-                dev->interrupt = 0;     /* Avoid halting machine. */
+                      "an interrupt handler.\n", net_dev->name);
+                net_dev->interrupt = 0;     /* Avoid halting machine. */
                 return;
         }
 #else
-        if (dev->interrupt) {
+        if (net_dev->interrupt) {
                 printk(KERN_INFO "%s: Re-entering the interrupt handler.\n", 
-                      dev->name);
+                      net_dev->name);
                 return;
         }
-        dev->interrupt = 1;
+        net_dev->interrupt = 1;
 #endif
 
         do {
@@ -777,7 +880,7 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
                        printk(KERN_INFO "%s: entering interrupt, "
                               "original status = %#8.8x, "
                               "new status = %#8.8x.\n",
-                              dev->name, status, inl(ioaddr + isr));
+                              net_dev->name, status, inl(ioaddr + isr));
 
                 if ((status & (HIBERR|TxURN|TxERR|TxOK|RxORN|RxERR|RxOK)) == 0)
                        /* nothing intresting happened */
@@ -786,22 +889,22 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
                /* why dow't we break after Tx/Rx case ?? keyword: full-duplex */
                 if (status & (RxORN | RxERR | RxOK))
                        /* Rx interrupt */
-                        sis900_rx(dev);
+                        sis900_rx(net_dev);
 
                 if (status & (TxURN | TxERR | TxOK))
                        /* Tx interrupt */
-                       sis900_finish_xmit(dev);
+                       sis900_finish_xmit(net_dev);
 
                 /* something strange happened !!! */
                 if (status & HIBERR) {
                        printk(KERN_INFO "%s: Abnormal interrupt,"
-                              "status %#8.8x.\n", dev->name, status);
+                              "status %#8.8x.\n", net_dev->name, status);
                        break;
                }
                 if (--boguscnt < 0) {
                         printk(KERN_INFO "%s: Too much work at interrupt, "
-                              "interrupt Status = %#8.8x.\n",
-                              dev->name, status);
+                              "interrupt status = %#8.8x.\n",
+                              net_dev->name, status);
                         break;
                 }
         } while (1);
@@ -809,89 +912,90 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
         if (sis900_debug > 3)
                 printk(KERN_INFO "%s: exiting interrupt, "
                       "interrupt status = 0x%#8.8x.\n",
-                      dev->name, inl(ioaddr + isr));
+                      net_dev->name, inl(ioaddr + isr));
        
 #if defined(__i386__)
-        clear_bit(0, (void*)&dev->interrupt);
+        clear_bit(0, (void*)&net_dev->interrupt);
 #else
-        dev->interrupt = 0;
+        net_dev->interrupt = 0;
 #endif
         return;
 }
 
 /* Process receive interrupt events, put buffer to higher layer and refill buffer pool 
    Note: This fucntion is called by interrupt handler, don't do "too much" work here */
-static int sis900_rx(struct device *dev)
+static int sis900_rx(struct device *net_dev)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-        long ioaddr = dev->base_addr;
-       unsigned int entry = tp->cur_rx % NUM_RX_DESC;
-        u32 rx_status = tp->rx_ring[entry].cmdsts;
-
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+        long ioaddr = net_dev->base_addr;
+       unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;
+        u32 rx_status = sis_priv->rx_ring[entry].cmdsts;
+       
         if (sis900_debug > 3)
                 printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "
-                       "status:0x%8.8x\n",
-                       tp->cur_rx, tp->dirty_rx,rx_status);
+                      "status:0x%8.8x\n",
+                      sis_priv->cur_rx, sis_priv->dirty_rx,rx_status);
        
         while (rx_status & OWN) {
                 unsigned int rx_size;
                
                rx_size = (rx_status & DSIZE) - CRC_SIZE;
-
+               
                if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {
                        /* corrupted packet received */
                         if (sis900_debug > 3)
                                 printk(KERN_INFO "%s: Corrupted packet "
                                       "received, buffer status = 0x%8.8x.\n",
-                                      dev->name, rx_status);
-                        tp->stats.rx_errors++;
+                                      net_dev->name, rx_status);
+                        sis_priv->stats.rx_errors++;
                        if (rx_status & OVERRUN)
-                               tp->stats.rx_over_errors++;
+                               sis_priv->stats.rx_over_errors++;
                        if (rx_status & (TOOLONG|RUNT))
-                                tp->stats.rx_length_errors++;
+                                sis_priv->stats.rx_length_errors++;
                         if (rx_status & (RXISERR | FAERR))
-                                tp->stats.rx_frame_errors++;
+                                sis_priv->stats.rx_frame_errors++;
                        if (rx_status & CRCERR) 
-                               tp->stats.rx_crc_errors++;
+                               sis_priv->stats.rx_crc_errors++;
                        /* reset buffer descriptor state */
-                       tp->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+                       sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
                 } else {
                        struct sk_buff * skb;
                        
-                       if (tp->rx_skbuff[entry] == NULL) {
+                       if (sis_priv->rx_skbuff[entry] == NULL) {
                                printk(KERN_INFO "%s: NULL pointer " 
                                       "encountered in Rx ring, skipping\n",
-                                      dev->name);
+                                      net_dev->name);
                                break;                  
                        }
-                       skb = tp->rx_skbuff[entry];
-                       tp->rx_skbuff[entry] = NULL;
+                       skb = sis_priv->rx_skbuff[entry];
+                       sis_priv->rx_skbuff[entry] = NULL;
                        /* reset buffer descriptor state */
-                       tp->rx_ring[entry].cmdsts = 0;
-                       tp->rx_ring[entry].bufptr = 0;
-
+                       sis_priv->rx_ring[entry].cmdsts = 0;
+                       sis_priv->rx_ring[entry].bufptr = 0;
+                       
                        skb_put(skb, rx_size);
-                        skb->protocol = eth_type_trans(skb, dev);
+                        skb->protocol = eth_type_trans(skb, net_dev);
                         netif_rx(skb);
-
-                       if (rx_status & MCAST)
-                               tp->stats.multicast++;
-                        tp->stats.rx_bytes += rx_size;
-                        tp->stats.rx_packets++;
+                       
+                       if ((rx_status & BCAST) == MCAST)
+                               sis_priv->stats.multicast++;
+                       net_dev->last_rx = jiffies;
+                        sis_priv->stats.rx_bytes += rx_size;
+                        sis_priv->stats.rx_packets++;
                 }
-                tp->cur_rx++;
-               entry = tp->cur_rx % NUM_RX_DESC;
-                rx_status = tp->rx_ring[entry].cmdsts;
+                sis_priv->cur_rx++;
+               entry = sis_priv->cur_rx % NUM_RX_DESC;
+                rx_status = sis_priv->rx_ring[entry].cmdsts;
         } // while
-
+       
        /* refill the Rx buffer, what if the rate of refilling is slower than 
           consuming ?? */
-       for (;tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) {
+       for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; sis_priv->dirty_rx++) {
                struct sk_buff *skb;
-
-               entry = tp->dirty_rx % NUM_RX_DESC;
-
-               if (tp->rx_skbuff[entry] == NULL) {
+               
+               entry = sis_priv->dirty_rx % NUM_RX_DESC;
+               
+               if (sis_priv->rx_skbuff[entry] == NULL) {
                        if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) {
                                /* not enough memory for skbuff, this makes a "hole"
                                   on the buffer ring, it is not clear how the 
@@ -899,89 +1003,89 @@ static int sis900_rx(struct device *dev)
                                   buffer */
                                printk(KERN_INFO "%s: Memory squeeze,"
                                       "deferring packet.\n",
-                                      dev->name);
-                               tp->stats.rx_dropped++;
+                                      net_dev->name);
+                               sis_priv->stats.rx_dropped++;
                                break;
                        }
-                       skb->dev = dev;
-                       tp->rx_skbuff[entry] = skb;
-                       tp->rx_ring[entry].cmdsts = RX_BUF_SIZE;
-                       tp->rx_ring[entry].bufptr = virt_to_bus(skb->tail);
+                       skb->dev = net_dev;
+                       sis_priv->rx_skbuff[entry] = skb;
+                       sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;
+                       sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail);
                }
        }
        /* re-enable the potentially idle receive state matchine */
        outl(RxENA , ioaddr + cr );
-
+       
         return 0;
 }
 
 /* finish up transmission of packets, check for error condition and free skbuff etc.
    Note: This fucntion is called by interrupt handler, don't do "too much" work here */
-static void sis900_finish_xmit (struct device *dev)
+static void sis900_finish_xmit (struct device *net_dev)
 {
-       struct sis900_private *tp = (struct sis900_private *)dev->priv;
-
-       for (; tp->dirty_tx < tp->cur_tx; tp->dirty_tx++) {
+       struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
+       
+       for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) {
                unsigned int entry;
                u32 tx_status;
-
-               entry = tp->dirty_tx % NUM_TX_DESC;
-               tx_status = tp->tx_ring[entry].cmdsts;
-
+               
+               entry = sis_priv->dirty_tx % NUM_TX_DESC;
+               tx_status = sis_priv->tx_ring[entry].cmdsts;
+               
                if (tx_status & OWN) {
                        /* The packet is not transmited yet (owned by hardware) ! */
                        break;
                }
-
+               
                if (tx_status & (ABORT | UNDERRUN | OWCOLL)) {
                        /* packet unsuccessfully transmited */
                        if (sis900_debug > 3)
                                printk(KERN_INFO "%s: Transmit "
                                       "error, Tx status %8.8x.\n",
-                                      dev->name, tx_status);
-                       tp->stats.tx_errors++;
+                                      net_dev->name, tx_status);
+                       sis_priv->stats.tx_errors++;
                        if (tx_status & UNDERRUN)
-                               tp->stats.tx_fifo_errors++;
+                               sis_priv->stats.tx_fifo_errors++;
                        if (tx_status & ABORT)
-                               tp->stats.tx_aborted_errors++;
+                               sis_priv->stats.tx_aborted_errors++;
                        if (tx_status & NOCARRIER)
-                               tp->stats.tx_carrier_errors++;
+                               sis_priv->stats.tx_carrier_errors++;
                        if (tx_status & OWCOLL)
-                               tp->stats.tx_window_errors++;
+                               sis_priv->stats.tx_window_errors++;
                } else {
                        /* packet successfully transmited */
                        if (sis900_debug > 3)
                                printk(KERN_INFO "Tx Transmit OK\n");
-                       tp->stats.collisions += (tx_status & COLCNT) >> 16;
-                       tp->stats.tx_bytes += tx_status & DSIZE;
-                       tp->stats.tx_packets++;
+                       sis_priv->stats.collisions += (tx_status & COLCNT) >> 16;
+                       sis_priv->stats.tx_bytes += tx_status & DSIZE;
+                       sis_priv->stats.tx_packets++;
                }
                /* Free the original skb. */
-               dev_kfree_skb(tp->tx_skbuff[entry]);
-               tp->tx_skbuff[entry] = NULL;
-               tp->tx_ring[entry].bufptr = 0;
-               tp->tx_ring[entry].cmdsts = 0;
+               dev_kfree_skb(sis_priv->tx_skbuff[entry]);
+               sis_priv->tx_skbuff[entry] = NULL;
+               sis_priv->tx_ring[entry].bufptr = 0;
+               sis_priv->tx_ring[entry].cmdsts = 0;
        }
-
-       if (tp->tx_full && dev->tbusy && 
-           tp->cur_tx - tp->dirty_tx < NUM_TX_DESC - 4) {
+       
+       if (sis_priv->tx_full && net_dev->tbusy && 
+           sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) {
                /* The ring is no longer full, clear tbusy, tx_full and schedule 
                   more transmission by marking NET_BH */
-               tp->tx_full = 0;
-               clear_bit(0, (void *)&dev->tbusy);
+               sis_priv->tx_full = 0;
+               clear_bit(0, (void *)&net_dev->tbusy);
                mark_bh(NET_BH);
        }
 }
 
 static int
-sis900_close(struct device *dev)
+sis900_close(struct device *net_dev)
 {
-        long ioaddr = dev->base_addr;
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        long ioaddr = net_dev->base_addr;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
         int i;
-
-        dev->start = 0;
-        dev->tbusy = 1;
+       
+        net_dev->start = 0;
+        net_dev->tbusy = 1;
 
         /* Disable interrupts by clearing the interrupt mask. */
         outl(0x0000, ioaddr + imr);
@@ -990,20 +1094,20 @@ sis900_close(struct device *dev)
         /* Stop the chip's Tx and Rx Status Machine */
         outl(RxDIS | TxDIS, ioaddr + cr);
 
-        del_timer(&tp->timer);
+        del_timer(&sis_priv->timer);
 
-        free_irq(dev->irq, dev);
+        free_irq(net_dev->irq, net_dev);
 
        /* Free Tx and RX skbuff */
        for (i = 0; i < NUM_RX_DESC; i++) {
-               if (tp->rx_skbuff[i] != NULL)
-                       dev_kfree_skb(tp->rx_skbuff[i]);
-               tp->rx_skbuff[i] = 0;
+               if (sis_priv->rx_skbuff[i] != NULL)
+                       dev_kfree_skb(sis_priv->rx_skbuff[i]);
+               sis_priv->rx_skbuff[i] = 0;
        }
         for (i = 0; i < NUM_TX_DESC; i++) {
-                if (tp->tx_skbuff[i] != NULL)
-                        dev_kfree_skb(tp->tx_skbuff[i]);
-                tp->tx_skbuff[i] = 0;
+                if (sis_priv->tx_skbuff[i] != NULL)
+                        dev_kfree_skb(sis_priv->tx_skbuff[i]);
+                sis_priv->tx_skbuff[i] = 0;
         }
 
         /* Green! Put the chip in low-power mode. */
@@ -1013,22 +1117,22 @@ sis900_close(struct device *dev)
         return 0;
 }
 
-static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+static int mii_ioctl(struct device *net_dev, struct ifreq *rq, int cmd)
 {
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
         u16 *data = (u16 *)&rq->ifr_data;
 
         switch(cmd) {
         case SIOCDEVPRIVATE:                   /* Get the address of the PHY in use. */
-                data[0] = tp->mii->phy_addr;
+                data[0] = sis_priv->mii->phy_addr;
                 /* Fall Through */
         case SIOCDEVPRIVATE+1:                 /* Read the specified MII register. */
-                data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f);
+                data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f);
                 return 0;
         case SIOCDEVPRIVATE+2:                 /* Write the specified MII register */
                 if (!suser())
                         return -EPERM;
-                mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+                mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]);
                 return 0;
         default:
                 return -EOPNOTSUPP;
@@ -1036,280 +1140,100 @@ static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd)
 }
 
 static struct enet_statistics *
-sis900_get_stats(struct device *dev)
-{
-        struct sis900_private *tp = (struct sis900_private *)dev->priv;
-
-        return &tp->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
-   This routine is not state sensitive and need not be SMP locked. */
-
-static u16 elComputeHashTableIndex(u8 *addr)
-{
-#define POLYNOMIAL 0x04C11DB6L
-    u32      crc = 0xffffffff, msb;
-    int      i, j;
-    u8       byte;
-
-    for( i=0; i<6; i++ ) {
-        byte = *addr++;
-        for( j=0; j<8; j++ ) {
-            msb = crc >> 31;
-            crc <<= 1;
-            if( msb ^ ( byte & 1 )) {
-                crc ^= POLYNOMIAL;
-                crc |= 1;
-            }
-            byte >>= 1;
-        }
-    }
-    // 7 bit crc for 128 bit hash table
-    return( (int)(crc >> 25) );
-}
-
-static u16 elMIIpollBit(struct device *dev,
-                         int phy_id,
-                         int location,
-                         u16 mask,
-                         u16 polarity,
-                         u16 *value)
-{
-        u32 i;
-        i=0;
-        while (1) {
-                *value = mdio_read(dev, phy_id, location);
-                if (polarity) {
-                        if (mask & *value) return(TRUE);
-                } else {
-                        if (mask & ~(*value)) return(TRUE);
-                }
-                if (++i == 1200) break;
-        }
-        return(FALSE);
-}
-
-static u16 elPMDreadMode(struct device *dev,
-                         int phy_id,
-                         int *speed,
-                         int *duplex)
-{
-        u16 status, OurCap;
-
-        *speed = HW_SPEED_10_MBPS;
-        *duplex = FDX_CAPABLE_HALF_SELECTED;
-
-        status = mdio_read(dev, phy_id, MII_ANLPAR);
-        OurCap = mdio_read(dev, phy_id, MII_ANADV);
-       if (sis900_debug > 1) {
-               printk(KERN_INFO "Link Part Status %4X\n", status);
-               printk(KERN_INFO "Our Status %4X\n", OurCap);
-               printk(KERN_INFO "Status Reg %4X\n",
-                                       mdio_read(dev, phy_id, MII_STATUS));
-       }
-       status &= OurCap;
-
-        if ( !( status &
-                (MII_NWAY_T|MII_NWAY_T_FDX | MII_NWAY_TX | MII_NWAY_TX_FDX ))) {
-               if (sis900_debug > 1) {
-                       printk(KERN_INFO "The other end NOT support NWAY...\n");
-               }
-                while (( status = mdio_read(dev, phy_id, MII_STSOUT)) & 0x4000) ;
-                while (( status = mdio_read(dev, phy_id, MII_STSOUT)) & 0x0020) ;
-                if (status & 0x80)
-                        *speed = HW_SPEED_100_MBPS;
-                if (status & 0x40)
-                        *duplex = FDX_CAPABLE_FULL_SELECTED;
-                if (sis900_debug > 3) {
-                        printk(KERN_INFO"%s: Setting %s%s-duplex.\n",
-                                dev->name,
-                                *speed == HW_SPEED_100_MBPS ?
-                                        "100mbps " : "10mbps ",
-                                *duplex == FDX_CAPABLE_FULL_SELECTED ?
-                                        "full" : "half");
-                }
-        } else {
-               if (sis900_debug > 1) {
-                       printk(KERN_INFO "The other end support NWAY...\n");
-               }
-
-                if (status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) {
-                        *duplex = FDX_CAPABLE_FULL_SELECTED;
-                }
-                if (status & (MII_NWAY_TX_FDX | MII_NWAY_TX)) {
-                        *speed = HW_SPEED_100_MBPS;
-                }
-                if (sis900_debug > 3) {
-                        printk(KERN_INFO"%s: Setting %s%s-duplex based on"
-                                " auto-negotiated partner ability.\n",
-                                dev->name,
-                                *speed == HW_SPEED_100_MBPS ?
-                                        "100mbps " : "10mbps ",
-                                *duplex == FDX_CAPABLE_FULL_SELECTED ?
-                                        "full" : "half");
-                }
-        }
-        return (status);
-}
-
-static u16 elAutoNegotiate(struct device *dev, int phy_id, int *duplex, int *speed)
+sis900_get_stats(struct device *net_dev)
 {
-        u16 status, retnVal;
+        struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv;
 
-       if (sis900_debug > 1) {
-               printk(KERN_INFO "AutoNegotiate...\n");
-       }
-        mdio_write(dev, phy_id, MII_CONTROL, 0);
-        mdio_write(dev, phy_id, MII_CONTROL, MIICNTL_AUTO | MIICNTL_RST_AUTO);
-        retnVal = elMIIpollBit(dev, phy_id, MII_CONTROL, MIICNTL_RST_AUTO,
-                               FALSE,&status);
-       if (!retnVal) {
-               printk(KERN_INFO "%s: Not wait for Reset Complete\n", dev->name);
-       }
-        retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_AUTO_DONE,
-                               TRUE, &status);
-       if (!retnVal) {
-               printk(KERN_INFO "%s: Not wait for AutoNego Complete\n", dev->name);
-       }
-        retnVal = elMIIpollBit(dev, phy_id, MII_STATUS, MIISTAT_LINK,
-                               TRUE, &status);
-       if (!retnVal) {
-               printk(KERN_INFO "%s: Not wait for Link Complete\n", dev->name);
-       }
-        if (status & MIISTAT_LINK) {
-                elPMDreadMode(dev, phy_id, speed, duplex);
-                elSetMediaType(dev, *speed, *duplex);
-        }
-        return(status);
+        return &sis_priv->stats;
 }
 
-static void elSetCapability(struct device *dev, int phy_id,
-                           int duplex, int speed)
+/* SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast hash table, which makes
+   this function a little bit different from other drivers */
+static u16 sis900_compute_hashtable_index(u8 *addr)
 {
-        u16 cap = ( MII_NWAY_T  | MII_NWAY_T_FDX  |
-                   MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_CSMA_CD );
 
-       if (speed != 100) {
-               cap &= ~( MII_NWAY_TX | MII_NWAY_TX_FDX );
-               if (sis900_debug > 1) {
-                       printk(KERN_INFO "UNSET 100Mbps\n");
-               }
-       }
-
-       if (!duplex) {
-               cap &= ~( MII_NWAY_T_FDX | MII_NWAY_TX_FDX );
-               if (sis900_debug > 1) {
-                       printk(KERN_INFO "UNSET full-duplex\n");
+/* what is the correct value of the POLYNOMIAL ??
+   Donald Becker use 0x04C11DB7U */
+#define POLYNOMIAL 0x04C11DB6L
+       u32 crc = 0xffffffff, msb;
+       int  i, j;
+       u8  byte;
+
+       for (i = 0; i < 6; i++) {
+               byte = *addr++;
+               for (j = 0; j < 8; j++) {
+                       msb = crc >> 31;
+                       crc <<= 1;
+                       if (msb ^ (byte & 1)) {
+                               crc ^= POLYNOMIAL;
+                               crc |= 1;
+                       }
+                       byte >>= 1;
                }
        }
-
-        mdio_write(dev, phy_id, MII_ANADV, cap);
-}
-
-static void elSetMediaType(struct device *dev, int speed, int duplex)
-{
-        long ioaddr = dev->base_addr;
-        u32     txCfgOn = 0, txCfgOff = TxDRNT;
-        u32     rxCfgOn = 0, rxCfgOff = 0;
-
-        if (speed == HW_SPEED_100_MBPS) {
-                txCfgOn |= (TxDRNT_100 | TxHBI);
-        } else {
-                txCfgOn |= TxDRNT_10;
-        }
-
-        if (duplex == FDX_CAPABLE_FULL_SELECTED) {
-                txCfgOn |= (TxCSI | TxHBI);
-                rxCfgOn |= RxATP;
-        } else {
-                txCfgOff |= (TxCSI | TxHBI);
-                rxCfgOff |= RxATP;
-        }
-        outl( (inl(ioaddr + txcfg) & ~txCfgOff) | txCfgOn, ioaddr + txcfg);
-        outl( (inl(ioaddr + rxcfg) & ~rxCfgOff) | rxCfgOn, ioaddr + rxcfg);
+       /* leave 7 most siginifant bits */ 
+       return ((int)(crc >> 25));
 }
 
-static void set_rx_mode(struct device *dev)
+static void set_rx_mode(struct device *net_dev)
 {
-        long ioaddr = dev->base_addr;
-        u16 mc_filter[8];
+        long ioaddr = net_dev->base_addr;
+        u16 mc_filter[8];                      /* 128 bits multicast hash table */
         int i;
-        int rx_mode;
-        u32 rxCfgOn = 0, rxCfgOff = 0;
-        u32 txCfgOn = 0, txCfgOff = 0;
-
-        if (sis900_debug > 3)
-                printk(KERN_INFO "%s: set_rx_mode (%4.4x) done--"
-                                "RxCfg %8.8x.\n",
-                                dev->name, dev->flags, inl(ioaddr + rxcfg));
-
-        /* Note: do not reorder, GCC is clever about common statements. */
-        if (dev->flags & IFF_PROMISC) {
-                printk(KERN_NOTICE"%s: Promiscuous mode enabled.\n", dev->name);
-                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
-                                ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_PHYS;
-                for (i=0 ; i<8 ; i++)
-                        mc_filter[i]=0xffff;
-        } else if ((dev->mc_count > multicast_filter_limit)
-                                  ||  (dev->flags & IFF_ALLMULTI)) {
-                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS | ACCEPT_CAM_QUALIFIED;
-                for (i=0 ; i<8 ; i++)
-                        mc_filter[i]=0xffff;
+        u32 rx_mode;
+
+        if (net_dev->flags & IFF_PROMISC) {
+               /* Accept any kinds of packets */
+               rx_mode = RFPromiscuous;
+                for (i = 0; i < 8; i++)
+                        mc_filter[i] = 0xffff;
+        } else if ((net_dev->mc_count > multicast_filter_limit) ||
+                  (net_dev->flags & IFF_ALLMULTI)) {
+               /* too many multicast addresses or accept all multicast packet */
+                rx_mode = RFAAB | RFAAM;
+                for (i = 0; i < 8; i++)
+                        mc_filter[i] = 0xffff;
         } else {
+               /* Accept Broadcast packet, destination address matchs our MAC address,
+                  use Receive Filter to reject unwanted MCAST packet */
                 struct dev_mc_list *mclist;
-                rx_mode = ACCEPT_ALL_BCASTS | ACCEPT_ALL_MCASTS |
-                                ACCEPT_CAM_QUALIFIED;
-                for (i=0 ; i<8 ; i++)
+                rx_mode = RFAAB;
+                for (i = 0; i < 8; i++)
                         mc_filter[i]=0;
-                for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-                         i++, mclist = mclist->next)
-                        set_bit(elComputeHashTableIndex(mclist->dmi_addr),
-                                                mc_filter);
+                for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count;
+                    i++, mclist = mclist->next)
+                        set_bit(sis900_compute_hashtable_index(mclist->dmi_addr),
+                               mc_filter);
         }
 
-        for (i=0 ; i<8 ; i++) {
-                outl((u32)(0x00000004+i) << 16, ioaddr + rfcr);
+       /* update Multicast Hash Table in Receive Filter */
+        for (i = 0; i < 8; i++) {
+               /* why plus 0x04 ??, I don't know, UNDOCUMENT FEATURE ?? */
+                outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr);
                 outl(mc_filter[i], ioaddr + rfdr);
         }
 
-        outl(RFEN | ((rx_mode & (ACCEPT_ALL_MCASTS | ACCEPT_ALL_BCASTS |
-                          ACCEPT_ALL_PHYS)) << RFAA_shift), ioaddr + rfcr);
+        outl(RFEN | rx_mode, ioaddr + rfcr);
+
+       /* sis900 is capatable of looping back packet at MAC level for debugging purpose */
+       if (net_dev->flags & IFF_LOOPBACK) {
+               u32 cr_saved;
+               /* We must disable Tx/Rx before setting loopback mode */
+               cr_saved = inl(ioaddr + cr);
+               outl(cr_saved | TxDIS | RxDIS, ioaddr + cr);
+               /* enable loopback */
+               outl(inl(ioaddr + txcfg) | TxMLB, ioaddr + txcfg);
+               outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg);
+               /* restore cr */
+               outl(cr_saved, ioaddr + cr);
+       }               
 
-        if (rx_mode & ACCEPT_ALL_ERRORS) {
-                rxCfgOn = RxAEP | RxARP | RxAJAB;
-        } else {
-                rxCfgOff = RxAEP | RxARP | RxAJAB;
-        }
-        if (rx_mode & MAC_LOOPBACK) {
-                rxCfgOn |= RxATP;
-                txCfgOn |= TxMLB;
-        } else {
-                if (!(( (struct sis900_private *)(dev->priv) )->full_duplex))
-                        rxCfgOff |= RxATP;
-                txCfgOff |= TxMLB;
-        }
-
-        if (sis900_debug > 2) {
-                printk(KERN_INFO "Before Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
-                printk(KERN_INFO "Before Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
-        }
-
-        outl((inl(ioaddr + rxcfg) | rxCfgOn) & ~rxCfgOff, ioaddr + rxcfg);
-        outl((inl(ioaddr + txcfg) | txCfgOn) & ~txCfgOff, ioaddr + txcfg);
-
-        if (sis900_debug > 2) {
-                printk(KERN_INFO "After Set TxCfg=%8.8x\n",inl(ioaddr+txcfg));
-                printk(KERN_INFO "After Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg));
-                printk(KERN_INFO "Receive Filter Register:%8.8x\n", 
-                      inl(ioaddr + rfcr));
-        }
         return;
 }
 
-static void sis900_reset(struct device *dev)
+static void sis900_reset(struct device *net_dev)
 {
-        long ioaddr = dev->base_addr;
+        long ioaddr = net_dev->base_addr;
        int i = 0;
        u32 status = TxRCMP | RxRCMP;
 
@@ -1338,14 +1262,14 @@ cleanup_module(void)
 {
        /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
        while (root_sis900_dev) {
-               struct sis900_private *tp =
+               struct sis900_private *sis_priv =
                        (struct sis900_private *)root_sis900_dev->priv;
-               struct device *next_dev = tp->next_module;
+               struct device *next_dev = sis_priv->next_module;
                
                unregister_netdev(root_sis900_dev);
                release_region(root_sis900_dev->base_addr,
-                              tp->mac->io_size);
-               kfree(tp);
+                              sis_priv->mac->io_size);
+               kfree(sis_priv);
                kfree(root_sis900_dev);
 
                root_sis900_dev = next_dev;
index 335d094e25a26b8cf62db6bfe61c51f46a0a2c23..818e193322b8da2fb689f5c2cdb7ed581a53cebc 100644 (file)
@@ -78,11 +78,11 @@ enum sis900_interrupt_enable_reigster_bits {
 #define RX_DMA_BURST           0
 
 /* transmit FIFO threshholds */
-#define TX_FILL_THRESH   16
+#define TX_FILL_THRESH   16    /* 1/4 FIFO size */
 #define TxFILLT_shift          8
 #define TxDRNT_shift           0
-#define TxDRNT_100      (1536>>5)
-#define TxDRNT_10              16 
+#define TxDRNT_100             48      /* 3/4 FIFO size */
+#define TxDRNT_10              16      /* 1/2 FIFO size */
 
 enum sis900_transmit_config_register_bits {
        TxCSI = 0x80000000, TxHBI = 0x40000000, TxMLB = 0x20000000,
@@ -90,13 +90,13 @@ enum sis900_transmit_config_register_bits {
        TxDRNT = 0x0000003F
 };
 
-/* recevie FFIFO thresholds */
+/* recevie FIFO thresholds */
 #define RxDRNT_shift     1
-#define RxDRNT_100     8
-#define RxDRNT_10              
+#define RxDRNT_100     24      /* 3/4 FIFO size */
+#define RxDRNT_10              16      /* 1/2 FIFO size */
 
 enum sis900_reveive_config_register_bits {
-       RxAEP  = 0x80000000, RxARP = 0x40000000, RxATP   = 0x10000000,
+       RxAEP  = 0x80000000, RxARP = 0x40000000, RxATX = 0x10000000,
        RxAJAB = 0x08000000, RxDRNT = 0x0000007F
 };
 
@@ -158,68 +158,73 @@ enum sis900_rx_bufer_status {
 enum mii_registers {
        MII_CONTROL = 0x0000, MII_STATUS = 0x0001, MII_PHY_ID0 = 0x0002,
        MII_PHY_ID1 = 0x0003, MII_ANADV  = 0x0004, MII_ANLPAR  = 0x0005,
+       MII_ANEXT   = 0x0006
+};
+
+/* mii registers specific to SiS 900 */
+enum sis_mii_registers {
        MII_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012,
        MII_MASK    = 0x0013
 };
 
+/* mii registers specific to AMD 79C901 */
+enum amd_mii_registers {
+       MII_STATUS_SUMMARY = 0x0018
+};
+
 /* MII Control register bit definitions. */
-#define MIICNTL_FDX             0x0100
-#define MIICNTL_RST_AUTO        0x0200
-#define MIICNTL_ISOLATE         0x0400
-#define MIICNTL_PWRDWN          0x0800
-#define MIICNTL_AUTO            0x1000
-#define MIICNTL_SPEED           0x2000
-#define MIICNTL_LPBK            0x4000
-#define MIICNTL_RESET           0x8000
-/* MII Status register bit significance. */
-#define MIISTAT_EXT             0x0001
-#define MIISTAT_JAB             0x0002
-#define MIISTAT_LINK            0x0004
-#define MIISTAT_CAN_AUTO        0x0008
-#define MIISTAT_FAULT           0x0010
-#define MIISTAT_AUTO_DONE       0x0020
-#define MIISTAT_CAN_T           0x0800
-#define MIISTAT_CAN_T_FDX       0x1000
-#define MIISTAT_CAN_TX          0x2000
-#define MIISTAT_CAN_TX_FDX      0x4000
-#define MIISTAT_CAN_T4          0x8000
+enum mii_control_register_bits {
+       MII_CNTL_FDX     = 0x0100, MII_CNTL_RST_AUTO = 0x0200, 
+       MII_CNTL_ISOLATE = 0x0400, MII_CNTL_PWRDWN   = 0x0800,
+       MII_CNTL_AUTO    = 0x1000, MII_CNTL_SPEED    = 0x2000,
+       MII_CNTL_LPBK    = 0x4000, MII_CNTL_RESET    = 0x8000
+};
+
+/* MII Status register bit  */
+enum mii_status_register_bits {
+       MII_STAT_EXT    = 0x0001, MII_STAT_JAB        = 0x0002, 
+       MII_STAT_LINK   = 0x0004, MII_STAT_CAN_AUTO   = 0x0008, 
+       MII_STAT_FAULT  = 0x0010, MII_STAT_AUTO_DONE  = 0x0020,
+       MII_STAT_CAN_T  = 0x0800, MII_STAT_CAN_T_FDX  = 0x1000,
+       MII_STAT_CAN_TX = 0x2000, MII_STAT_CAN_TX_FDX = 0x4000,
+       MII_STAT_CAN_T4 = 0x8000
+};
+
+#define                MII_ID1_OUI_LO          0xFC00  /* low bits of OUI mask */
+#define                MII_ID1_MODEL           0x03F0  /* model number */
+#define                MII_ID1_REV             0x000F  /* model number */
+
 /* MII NWAY Register Bits ...
-** valid for the ANAR (Auto-Negotiation Advertisement) and
-** ANLPAR (Auto-Negotiation Link Partner) registers */
-#define MII_NWAY_NODE_SEL       0x001f
-#define MII_NWAY_CSMA_CD        0x0001
-#define MII_NWAY_T              0x0020
-#define MII_NWAY_T_FDX          0x0040
-#define MII_NWAY_TX             0x0080
-#define MII_NWAY_TX_FDX         0x0100
-#define MII_NWAY_T4             0x0200
-#define MII_NWAY_RF             0x2000
-#define MII_NWAY_ACK            0x4000
-#define MII_NWAY_NP             0x8000
-
-/* MII Auto-Negotiation Expansion Register Bits */
-#define MII_ANER_PDF            0x0010
-#define MII_ANER_LP_NP_ABLE     0x0008
-#define MII_ANER_NP_ABLE        0x0004
-#define MII_ANER_RX_PAGE        0x0002
-#define MII_ANER_LP_AN_ABLE     0x0001
-#define HALF_DUPLEX                     1
-#define FDX_CAPABLE_DUPLEX_UNKNOWN      2
-#define FDX_CAPABLE_HALF_SELECTED       3
-#define FDX_CAPABLE_FULL_SELECTED       4
-#define HW_SPEED_UNCONFIG       0
-#define HW_SPEED_10_MBPS        10
-#define HW_SPEED_100_MBPS       100
-#define HW_SPEED_DEFAULT        (HW_SPEED_10_MBPS)
-
-#define ACCEPT_ALL_PHYS         0x01
-#define ACCEPT_ALL_MCASTS       0x02
-#define ACCEPT_ALL_BCASTS       0x04
-
-#define ACCEPT_ALL_ERRORS       0x08
-#define ACCEPT_CAM_QUALIFIED    0x10
-
-#define MAC_LOOPBACK            0x20
+   valid for the ANAR (Auto-Negotiation Advertisement) and
+   ANLPAR (Auto-Negotiation Link Partner) registers */
+enum mii_nway_register_bits {
+       MII_NWAY_NODE_SEL = 0x001f, MII_NWAY_CSMA_CD = 0x0001,
+       MII_NWAY_T        = 0x0020, MII_NWAY_T_FDX   = 0x0040,
+       MII_NWAY_TX       = 0x0080, MII_NWAY_TX_FDX  = 0x0100,
+       MII_NWAY_T4       = 0x0200, MII_NWAY_PAUSE   = 0x0400,
+       MII_NWAY_RF       = 0x2000, MII_NWAY_ACK     = 0x4000,
+       MII_NWAY_NP       = 0x8000
+};
+
+enum mii_stsout_register_bits {
+       MII_STSOUT_LINK_FAIL = 0x4000,
+       MII_STSOUT_SPD       = 0x0080, MII_STSOUT_DPLX = 0x0040
+};
+
+enum mii_stssum_register_bits {
+       MII_STSSUM_LINK = 0x0008, MII_STSSUM_DPLX = 0x0004,
+       MII_STSSUM_AUTO = 0x0002, MII_STSSUM_SPD  = 0x0001
+};
+
+#define FDX_CAPABLE_DUPLEX_UNKNOWN      0
+#define FDX_CAPABLE_HALF_SELECTED       1
+#define FDX_CAPABLE_FULL_SELECTED       2
+
+#define HW_SPEED_UNCONFIG              0
+#define HW_SPEED_HOME          1
+#define HW_SPEED_10_MBPS               10
+#define HW_SPEED_100_MBPS              100
+#define HW_SPEED_DEFAULT               (HW_SPEED_100_MBPS)
 
 #define CRC_SIZE                4
 #define MAC_HEADER_SIZE         14
index 3388f10c3de1cf38139ff9c75b74f2fd1c4c8a9e..79b8ee0e10383d15f6a5b3c1f5be7523d36edbea 100644 (file)
@@ -151,7 +151,9 @@ static int debug = 0;
 
 static void if_down(struct device *dev)
 {
-       ;
+       struct sppp *sp = &((struct ppp_device *)dev)->sppp;
+
+       sp->pp_link_state=SPPP_LINK_DOWN;
 }
 
 /*
@@ -192,7 +194,7 @@ void sppp_input (struct device *dev, struct sk_buff *skb)
        skb->dev=dev;
        skb->mac.raw=skb->data;
        
-       if (dev->flags & IFF_UP)
+       if (dev->flags & IFF_RUNNING)
        {
                /* Count received bytes, add FCS and one flag */
                sp->ibytes+= skb->len + 3;
@@ -368,7 +370,7 @@ static void sppp_keepalive (unsigned long dummy)
 
                /* Keepalive mode disabled or channel down? */
                if (! (sp->pp_flags & PP_KEEPALIVE) ||
-                   ! (dev->flags & IFF_RUNNING))
+                   ! (dev->flags & IFF_UP))
                        continue;
 
                /* No keepalive in PPP mode if LCP not opened yet. */
@@ -528,10 +530,10 @@ badreq:
                if (h->ident != sp->lcp.confid)
                        break;
                sppp_clear_timeout (sp);
-               if (! (dev->flags & IFF_UP) &&
-                   (dev->flags & IFF_RUNNING)) {
+               if ((sp->pp_link_state != SPPP_LINK_UP) &&
+                   (dev->flags & IFF_UP)) {
                        /* Coming out of loopback mode. */
-                       dev->flags |= IFF_UP;
+                       sp->pp_link_state=SPPP_LINK_UP;
                        printk (KERN_INFO "%s: up\n", dev->name);
                }
                switch (sp->lcp.state) {
@@ -696,9 +698,9 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
                        break;
                }
                sp->pp_loopcnt = 0;
-               if (! (dev->flags & IFF_UP) &&
-                   (dev->flags & IFF_RUNNING)) {
-                       dev->flags |= IFF_UP;
+               if (sp->pp_link_state==SPPP_LINK_DOWN &&
+                   (dev->flags & IFF_UP)) {
+                       sp->pp_link_state=SPPP_LINK_UP;
                        printk (KERN_INFO "%s: up\n", dev->name);
                }
                break;
@@ -823,7 +825,7 @@ static void sppp_cisco_send (struct sppp *sp, int type, long par1, long par2)
 int sppp_close (struct device *dev)
 {
        struct sppp *sp = &((struct ppp_device *)dev)->sppp;
-       dev->flags &= ~IFF_RUNNING;
+       sp->pp_link_state = SPPP_LINK_DOWN;
        sp->lcp.state = LCP_STATE_CLOSED;
        sp->ipcp.state = IPCP_STATE_CLOSED;
        sppp_clear_timeout (sp);
@@ -837,9 +839,10 @@ int sppp_open (struct device *dev)
 {
        struct sppp *sp = &((struct ppp_device *)dev)->sppp;
        sppp_close(dev);
-       dev->flags |= IFF_RUNNING;
-       if (!(sp->pp_flags & PP_CISCO))
+       if (!(sp->pp_flags & PP_CISCO)) {
                sppp_lcp_open (sp);
+       }
+       sp->pp_link_state = SPPP_LINK_DOWN;
        return 0;
 }
 
@@ -849,7 +852,6 @@ int sppp_reopen (struct device *dev)
 {
        struct sppp *sp = &((struct ppp_device *)dev)->sppp;
        sppp_close(dev);
-       dev->flags |= IFF_RUNNING;
        if (!(sp->pp_flags & PP_CISCO))
        {
                sp->lcp.magic = jiffies;
@@ -858,7 +860,8 @@ int sppp_reopen (struct device *dev)
                sp->ipcp.state = IPCP_STATE_CLOSED;
                /* Give it a moment for the line to settle then go */
                sppp_set_timeout (sp, 1);
-       }
+       } 
+       sp->pp_link_state=SPPP_LINK_DOWN;
        return 0;
 }
 
@@ -1176,7 +1179,7 @@ static void sppp_cp_timeout (unsigned long arg)
        cli();
 
        sp->pp_flags &= ~PP_TIMO;
-       if (! (sp->pp_if->flags & IFF_RUNNING) || (sp->pp_flags & PP_CISCO)) {
+       if (! (sp->pp_if->flags & IFF_UP) || (sp->pp_flags & PP_CISCO)) {
                restore_flags(flags);
                return;
        }
index 98f16aa0c40ad7163a4830287c3c183b6f6c5456..859f26e57a88dac0f36157ec2ccbb4f4cd5d8deb 100644 (file)
@@ -47,6 +47,7 @@ struct sppp
        u32             ipkts,opkts;    /* Packets in/out */
        struct timer_list       pp_timer;
        struct device   *pp_if;
+       char            pp_link_state;
 };
 
 struct ppp_device
@@ -72,6 +73,9 @@ struct ppp_device
 #define IPCP_STATE_ACK_SENT     2       /* IPCP state: conf-ack sent */
 #define IPCP_STATE_OPENED       3       /* IPCP state: opened */
 
+#define SPPP_LINK_DOWN         0       /* link down - no keepalive */
+#define SPPP_LINK_UP           1       /* link is up - keepalive ok */
+
 void sppp_attach (struct ppp_device *pd);
 void sppp_detach (struct device *dev);
 void sppp_input (struct device *dev, struct sk_buff *m);
index 91cdfed220812b07feab89d6b78eea3b807b96af..c0e614d8c217e0b82ab82c3019b863622297a60d 100644 (file)
@@ -1910,18 +1910,18 @@ wavelan_ioctl(struct device *   dev,    /* device on which the ioctl is applied */
 
     case SIOCSIWNWID:
       /* Set NWID in WaveLAN. */
-      if(wrq->u.nwid.on)
+      if(!wrq->u.nwid.disabled)
        {
-         /* Set NWID in psa. */
-         psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
-         psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
+         /* Set NWID in psa */
+         psa.psa_nwid[0] = (wrq->u.nwid.value & 0xFF00) >> 8;
+         psa.psa_nwid[1] = wrq->u.nwid.value & 0xFF;
          psa.psa_nwid_select = 0x01;
          psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
                    (unsigned char *)psa.psa_nwid, 3);
 
          /* Set NWID in mmc. */
-         m.w.mmw_netw_id_l = wrq->u.nwid.nwid & 0xFF;
-         m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+         m.w.mmw_netw_id_l = psa.psa_nwid[1];
+         m.w.mmw_netw_id_h = psa.psa_nwid[0];
          mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m,
                    (unsigned char *)&m.w.mmw_netw_id_l, 2);
          mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
@@ -1945,8 +1945,9 @@ wavelan_ioctl(struct device *     dev,    /* device on which the ioctl is applied */
       /* Read the NWID. */
       psa_read(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
               (unsigned char *)psa.psa_nwid, 3);
-      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
-      wrq->u.nwid.on = psa.psa_nwid_select;
+      wrq->u.nwid.value = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.disabled = !(psa.psa_nwid_select);
+      wrq->u.nwid.fixed = 1;   /* Superfluous */
       break;
 
     case SIOCSIWFREQ:
@@ -2006,83 +2007,96 @@ wavelan_ioctl(struct device *   dev,    /* device on which the ioctl is applied */
       wrq->u.sens.fixed = 1;
       break;
 
-     case SIOCSIWENCODE:
-       /* Set encryption key. */
-       if(!mmc_encr(ioaddr))
-        {
-          ret = -EOPNOTSUPP;
-          break;
-        }
-
-       if(wrq->u.encoding.method)
-        {      /* Enable encryption. */
-          int          i;
-          long long    key = wrq->u.encoding.code;
-
-          for(i = 7; i >= 0; i--)
-            {
-              psa.psa_encryption_key[i] = key & 0xFF;
-              key >>= 8;
-            }
-           psa.psa_encryption_select = 1;
-          psa_write(ioaddr, lp->hacr,
-                    (char *) &psa.psa_encryption_select - (char *) &psa,
-                    (unsigned char *) &psa.psa_encryption_select, 8+1);
-
-           mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
-                  MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
-           mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
-                    (unsigned char *) &psa.psa_encryption_key, 8);
-        }
-       else
-        {      /* Disable encryption. */
-          psa.psa_encryption_select = 0;
-          psa_write(ioaddr, lp->hacr,
-                    (char *) &psa.psa_encryption_select - (char *) &psa,
-                    (unsigned char *) &psa.psa_encryption_select, 1);
-
-          mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
-        }
-       /* update the Wavelan checksum */
-       update_psa_checksum(dev, ioaddr, lp->hacr);
-       break;
-
-     case SIOCGIWENCODE:
-       /* Read the encryption key. */
-       if(!mmc_encr(ioaddr))
-        {
-          ret = -EOPNOTSUPP;
-          break;
-        }
-
-       /* Only super-user can see encryption key. */
-       if(!suser())
-        {
-          ret = -EPERM;
-          break;
-        }
-       else
-        {
-          int          i;
-          long long    key = 0;
-
-          psa_read(ioaddr, lp->hacr,
+    case SIOCSIWENCODE:
+      /* Set encryption key */
+      if(!mmc_encr(ioaddr))
+       {
+         ret = -EOPNOTSUPP;
+         break;
+       }
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+       {
+         /* Check the size of the key */
+         if(wrq->u.encoding.length != 8)
+           {
+             ret = -EINVAL;
+             break;
+           }
+
+         /* Copy the key in the driver */
+         if(copy_from_user(psa.psa_encryption_key, wrq->u.encoding.pointer,
+                           wrq->u.encoding.length))
+           {
+             ret = -EFAULT;
+             break;
+           }
+
+         psa.psa_encryption_select = 1;
+         psa_write(ioaddr, lp->hacr,
+                   (char *) &psa.psa_encryption_select - (char *) &psa,
+                   (unsigned char *) &psa.psa_encryption_select, 8+1);
+
+         mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
+                 MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+         mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
+                   (unsigned char *) &psa.psa_encryption_key, 8);
+       }
+
+      if(wrq->u.encoding.flags & IW_ENCODE_DISABLED)
+       {       /* disable encryption */
+         psa.psa_encryption_select = 0;
+         psa_write(ioaddr, lp->hacr,
                    (char *) &psa.psa_encryption_select - (char *) &psa,
-                   (unsigned char *) &psa.psa_encryption_select, 1+8);
-          for(i = 0; i < 8; i++)
-            {
-              key <<= 8;
-              key += psa.psa_encryption_key[i];
-            }
-          wrq->u.encoding.code = key;
-
-          /* encryption is enabled */
-          if(psa.psa_encryption_select)
-            wrq->u.encoding.method = mmc_encr(ioaddr);
-          else
-            wrq->u.encoding.method = 0;
-        }
-       break;
+                   (unsigned char *) &psa.psa_encryption_select, 1);
+
+         mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
+       }
+      /* update the Wavelan checksum */
+      update_psa_checksum(dev, ioaddr, lp->hacr);
+      break;
+
+    case SIOCGIWENCODE:
+      /* Read the encryption key */
+      if(!mmc_encr(ioaddr))
+       {
+         ret = -EOPNOTSUPP;
+         break;
+       }
+
+      /* only super-user can see encryption key */
+      if(!suser())
+       {
+         ret = -EPERM;
+         break;
+       }
+
+      /* Basic checking... */
+      if(wrq->u.encoding.pointer != (caddr_t) 0)
+       {
+         /* Verify the user buffer */
+         ret = verify_area(VERIFY_WRITE, wrq->u.encoding.pointer, 8);
+         if(ret)
+           break;
+
+         psa_read(ioaddr, lp->hacr,
+                  (char *) &psa.psa_encryption_select - (char *) &psa,
+                  (unsigned char *) &psa.psa_encryption_select, 1+8);
+
+         /* encryption is enabled ? */
+         if(psa.psa_encryption_select)
+           wrq->u.encoding.flags = IW_ENCODE_ENABLED;
+         else
+           wrq->u.encoding.flags = IW_ENCODE_DISABLED;
+         wrq->u.encoding.flags |= mmc_encr(ioaddr);
+
+         /* Copy the key to the user buffer */
+         wrq->u.encoding.length = 8;
+         if(copy_to_user(wrq->u.encoding.pointer, psa.psa_encryption_key, 8))
+           ret = -EFAULT;
+       }
+      break;
 
     case SIOCGIWRANGE:
       /* basic checking */
@@ -2117,6 +2131,19 @@ wavelan_ioctl(struct device *    dev,    /* device on which the ioctl is applied */
          range.num_bitrates = 1;
          range.bitrate[0] = 2000000;   /* 2 Mb/s */
 
+         /* Encryption supported ? */
+         if(mmc_encr(ioaddr))
+           {
+             range.encoding_size[0] = 8;       /* DES = 64 bits key */
+             range.num_encoding_sizes = 1;
+             range.max_encoding_tokens = 1;    /* Only one key possible */
+           }
+         else
+           {
+             range.num_encoding_sizes = 0;
+             range.max_encoding_tokens = 0;
+           }
+
          /* Copy structure to the user buffer. */
          if (copy_to_user(wrq->u.data.pointer, &range, sizeof(struct iw_range)))
                ret = -EFAULT;
index 5e859136610738c7219dd51e69befdf1be3474a1..b12c09c3ac031a8590cf67294bb68b18ad7f28ee 100644 (file)
  *     - Add the (short) list of bit-rates in range
  *     - Developp a new sensitivity... (sens.value & sens.fixed)
  *
+ * Changes made for release in 2.2.14 & 2.3.23 :
+ * -------------------------------------------
+ *     - Fix check for root permission (break instead of exit)
+ *     - New nwid & encoding setting (Wireless Extension 9)
+ *
  * Wishes & dreams:
  * ----------------
  *     - roaming (see Pcmcia driver)
 /************************ CONSTANTS & MACROS ************************/
 
 #ifdef DEBUG_VERSION_SHOW
-static const char      *version        = "wavelan.c : v20 (wireless extensions) 29/7/99\n";
+static const char      *version        = "wavelan.c : v21 (wireless extensions) 16/10/99\n";
 #endif
 
 /* Watchdog temporisation */
index dcceb8e53ddac4ae461ecb1903528fc16932a621..99b2d6aa580adb2c88cb43b36b70371bb301ada8 100644 (file)
  *     Asynchronous mode dropped for 2.2. For 2.3 we will attempt the
  *     unification of all the Z85x30 asynchronous drivers for real.
  *
- *     To Do:
- *     
- *     Finish DMA mode support.
- *
  *     Performance
  *
  *     Z85230:
@@ -169,7 +165,7 @@ EXPORT_SYMBOL(z8530_hdlc_kilostream);
 /*
  *     As above but for enhanced chips.
  */
-
 u8 z8530_hdlc_kilostream_85230[]=
 {
        4,      SYNC_ENAB|SDLC|X1CLK,
@@ -354,13 +350,15 @@ static void z8530_status(struct z8530_channel *chan)
                z8530_tx_done(chan);
        }
                
-       if(altered&DCD)
+       if(altered&chan->dcdcheck)
        {
-               if(status&DCD)
+               if(status&chan->dcdcheck)
                {
                        printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);
                        write_zsreg(chan, R3, chan->regs[3]|RxENABLE);
-                       if(chan->netdevice)
+                       if(chan->netdevice &&
+                           ((chan->netdevice->type == ARPHRD_HDLC) ||
+                           (chan->netdevice->type == ARPHRD_PPP)))
                                sppp_reopen(chan->netdevice);
                }
                else
@@ -440,7 +438,6 @@ static void z8530_dma_status(struct z8530_channel *chan)
                if(status&TxEOM)
                {
                        flags=claim_dma_lock();
-                       /* Transmit underrun */
                        disable_dma(chan->txdma);
                        clear_dma_ff(chan->txdma);      
                        chan->txdma_on=0;
@@ -448,13 +445,15 @@ static void z8530_dma_status(struct z8530_channel *chan)
                        z8530_tx_done(chan);
                }
        }
-       if(altered&DCD)
+       if(altered&chan->dcdcheck)
        {
-               if(status&DCD)
+               if(status&chan->dcdcheck)
                {
                        printk(KERN_INFO "%s: DCD raised\n", chan->dev->name);
                        write_zsreg(chan, R3, chan->regs[3]|RxENABLE);
-                       if(chan->netdevice)
+                       if(chan->netdevice &&
+                           ((chan->netdevice->type == ARPHRD_HDLC) ||
+                           (chan->netdevice->type == ARPHRD_PPP)))
                                sppp_reopen(chan->netdevice);
                }
                else
@@ -1012,6 +1011,8 @@ int z8530_init(struct z8530_dev *dev)
           floating IRQ transition when we reset the chip */
        dev->chanA.irqs=&z8530_nop;
        dev->chanB.irqs=&z8530_nop;
+       dev->chanA.dcdcheck=DCD;
+       dev->chanB.dcdcheck=DCD;
        /* Reset the chip */
        write_zsreg(&dev->chanA, R9, 0xC0);
        udelay(200);
@@ -1104,7 +1105,7 @@ int z8530_channel_load(struct z8530_channel *c, u8 *rtable)
        c->mtu=1500;
        c->max=0;
        c->count=0;
-       c->status=0;    /* Fixme - check DCD now */
+       c->status=read_zsreg(c, R0);
        c->sync=1;
        write_zsreg(c, R3, c->regs[R3]|RxENABLE);
        return 0;
@@ -1251,7 +1252,7 @@ static void z8530_rx_done(struct z8530_channel *c)
                 *      Save the ready state and the buffer currently
                 *      being used as the DMA target
                 */
-
+                
                int ready=c->dma_ready;
                unsigned char *rxb=c->rx_buf[c->dma_num];
                unsigned long flags;
index 0b3850061308c44091c9f411472afe5ea5c2cb57..1562d1cab687200a9cf06a7c466101280ee355ae 100644 (file)
@@ -270,6 +270,7 @@ struct z8530_channel
        struct sk_buff  *skb;           /* Buffer dptr points into */
        struct sk_buff  *skb2;          /* Pending buffer */
        u8              status;         /* Current DCD */
+       u8              dcdcheck;       /* which bit to check for line */
        u8              sync;           /* Set if in sync mode */
 
        u8              regs[32];       /* Register map for the chip */
@@ -413,7 +414,7 @@ extern void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb);
  *     Standard interrupt vector sets
  */
  
-struct z8530_irqhandler z8530_sync, z8530_async, z8530_nop;
+extern struct z8530_irqhandler z8530_sync, z8530_async, z8530_nop;
 
 /*
  *     Asynchronous Interfacing
index 1c50e1fbf2a720f8408441ceea804d8939840b23..bce089b6ad2f1dac5a2d00248a5d2c7917712b9d 100644 (file)
@@ -1,6 +1,13 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *      16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
+ *        + Updated to the new __setup interface for boot command line options.
+ *        + When loaded as a module, accepts the new parameter boot_options
+ *          which value is a string with the same format of the kernel boot
+ *          command line options. A valid example is:
+ *          modprobe eata 'boot_options=\"0x7410,0x230,lc:y,tc:n,mq:4\"'
+ *
  *       9 Sep 1999 Rev. 5.10 for linux 2.2.12 and 2.3.17
  *        + 64bit cleanup for Linux/Alpha platform support
  *          (contribution from H.J. Lu).
 #if defined(MODULE)
 #include <linux/module.h>
 
+MODULE_PARM(boot_options, "s");
 MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
 MODULE_PARM(linked_comm, "i");
 MODULE_PARM(tagged_comm, "i");
@@ -401,7 +409,13 @@ MODULE_AUTHOR("Dario Ballabio");
 #include <linux/config.h>
 #include <linux/pci.h>
 #include <linux/init.h>
+#include <linux/ctype.h>
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
 #include <asm/spinlock.h>
+#else
+#include <linux/spinlock.h>
+#endif
 
 #define SPIN_FLAGS unsigned long spin_flags;
 #define SPIN_LOCK spin_lock_irq(&io_request_lock);
@@ -410,8 +424,6 @@ MODULE_AUTHOR("Dario Ballabio");
 #define SPIN_UNLOCK_RESTORE \
                   spin_unlock_irqrestore(&io_request_lock, spin_flags);
 
-static int use_new_eh_code = TRUE;
-
 struct proc_dir_entry proc_scsi_eata2x = {
     PROC_SCSI_EATA2X, 6, "eata2x",
     S_IFDIR | S_IRUGO | S_IXUGO, 2
@@ -494,7 +506,7 @@ struct proc_dir_entry proc_scsi_eata2x = {
 #define ASOK              0x00
 #define ASST              0x01
 
-#if !defined(ARRAY_SIZE)
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
 #define ARRAY_SIZE(x) (sizeof (x) / sizeof((x)[0]))
 #endif
 
@@ -646,7 +658,7 @@ static char sha[MAX_BOARDS];
 /* Initialize num_boards so that ihdlr can work while detect is in progress */
 static unsigned int num_boards = MAX_BOARDS;
 
-static unsigned long io_port[] __initdata = {
+static unsigned long io_port[] = {
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -686,6 +698,8 @@ static int link_statistics = 0;
 static int tag_mode = TAG_MIXED;
 static int ext_tran = FALSE;
 static int rev_scan = TRUE;
+static int use_new_eh_code = TRUE;
+static char *boot_options = NULL;
 
 #if defined(CONFIG_SCSI_EATA_TAGGED_QUEUE)
 static int tagged_comm = TRUE;
@@ -820,12 +834,6 @@ static inline void tune_pci_port(unsigned long port_base) {
 
       if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break;
 
-#if 0
-      /* Don't bother if PCI vendor and/or device don't match. */
-      if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT)
-               continue;
-#endif
-
       if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
 
 #if defined(DEBUG_PCI_DETECT)
@@ -857,12 +865,6 @@ static inline int
 
    while((dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) {
 
-#if 0
-      /* Don't bother if PCI vendor and/or device don't match. */
-      if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT)
-               continue;
-#endif
-
       if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
 
 #if defined(DEBUG_PCI_DETECT)
@@ -913,7 +915,7 @@ static inline int port_detect \
    if (info.sign != EATA_SIGNATURE) return FALSE;
 
    if (DEV2H(info.data_len) < EATA_2_0A_SIZE) {
-      printk("%s: config structure size (%d bytes) too short, detaching.\n",
+      printk("%s: config structure size (%ld bytes) too short, detaching.\n",
              name, DEV2H(info.data_len));
       return FALSE;
       }
@@ -1166,7 +1168,7 @@ static inline int port_detect \
    return TRUE;
 }
 
-void eata2x_setup(char *str, int *ints) {
+static void internal_setup(char *str, int *ints) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -1202,6 +1204,22 @@ void eata2x_setup(char *str, int *ints) {
    return;
 }
 
+static int option_setup(char *str) {
+   int ints[MAX_INT_PARAM];
+   char *cur = str;
+   int i = 1;
+
+   while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+      ints[i++] = simple_strtoul(cur, NULL, 0);
+
+      if ((cur = strchr(cur, ',')) != NULL) cur++;
+   }
+
+   ints[0] = i - 1;
+   internal_setup(cur, ints);
+   return 0;
+}
+
 static void add_pci_ports(void) {
 
 #if defined(CONFIG_PCI)
@@ -1216,12 +1234,6 @@ static void add_pci_ports(void) {
 
       if (!(dev = pci_find_class(PCI_CLASS_STORAGE_SCSI << 8, dev))) break;
 
-#if 0
-      /* Don't bother if PCI vendor and/or device don't match. */
-      if (dev->vendor != PCI_VENDOR_ID_DPT || dev->device != PCI_DEVICE_ID_DPT)
-               continue;
-#endif
-
       if (pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &addr)) continue;
 
 #if defined(DEBUG_PCI_DETECT)
@@ -1247,6 +1259,8 @@ int eata2x_detect(Scsi_Host_Template *tpnt) {
 
    tpnt->proc_dir = &proc_scsi_eata2x;
 
+   if(boot_options) option_setup(boot_options);
+
 #if defined(MODULE)
    /* io_port could have been modified when loading as a module */
    if(io_port[0] != SKIP) {
@@ -2281,4 +2295,15 @@ int eata2x_release(struct Scsi_Host *shpnt) {
 Scsi_Host_Template driver_template = EATA;
 
 #include "scsi_module.c"
+
+#else
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+void eata2x_setup(char *str, int *ints) {
+   internal_setup(str, ints);
+}
+#else
+__setup("eata=", option_setup);
 #endif
+
+#endif /* end MODULE */
index 99d61dba7b435069dcc2bf3825e820d71797f067..d1fd870b5f8c14fd270580ccbbde12b1f7988ea7 100644 (file)
@@ -16,9 +16,7 @@ int eata2x_reset(Scsi_Cmnd *);
 int eata2x_old_reset(Scsi_Cmnd *, unsigned int);
 int eata2x_biosparam(Disk *, kdev_t, int *);
 
-#define EATA_VERSION "5.10.00"
-
-#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define EATA_VERSION "5.11.01"
 
 #define EATA {                                                               \
                 name:              "EATA/DMA 2.0x rev. " EATA_VERSION " ",   \
index 6196f51ad167bf9d69dec898cf72d426e605109e..fa6f2825557f853a3c080b6595b0ca2fd11b7b7a 100644 (file)
  *
  *  Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book.
  */
- static char * sg_version_str = "Version: 2.1.34 (990603)";
- static int sg_version_num = 20134; /* 2 digits for each component */
+ static char * sg_version_str = "Version: 2.1.36 (991008)";
+ static int sg_version_num = 20136; /* 2 digits for each component */
 /*
  *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
  *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
  *        the kernel/module needs to be built with CONFIG_SCSI_LOGGING
- *        (otherwise the macros compile to empty statements), then do
- *        something like: 'echo "scsi log all" > /proc/scsi/scsi' to log
- *        everything or 'echo "scsi log {token} #N" > /proc/scsi/scsi'
- *        where {token} is one of [error,timeout,scan,mlqueue,mlcomplete,
- *        llqueue,llcomplete,hlqueue,hlcomplete,ioctl] and #N is 0...7
- *        (with 0 meaning off). For example: 'scsi log timeout 7 > 
- *        /proc/scsi/scsi' to get all logging messages from this driver.
- *        Should use hlcomplete but it is too "noisy" (sd uses it).
- *
- *      - This driver obtains memory (heap) for the low-level driver to
- *        transfer/dma to and from. It is obtained from up to 3 sources:
- *              - obtain heap via get_free_pages()
- *              - obtain heap from the shared scsi dma pool
- *              - obtain heap from kernel directly (kmalloc) [last choice]
- *        Each open() attempts to obtain a "reserve" buffer of
- *        SG_DEF_RESERVED_SIZE bytes (or 0 bytes if opened O_RDONLY). The
- *        amount actually obtained [which could be 0 bytes] can be found from
- *        the SG_GET_RESERVED_SIZE ioctl(). This reserved buffer size can
- *        be changed by calling the SG_SET_RESERVED_SIZE ioctl(). Since this
- *        is an ambit claim, it should be followed by a SG_GET_RESERVED_SIZE
- *        ioctl() to find out how much was actually obtained.
- *        A subsequent write() to this file descriptor will use the
- *        reserved buffer unless:
- *              - it is already in use (eg during command queuing)
- *              - or the write() needs a buffer size larger than the
- *                reserved size
- *        In these cases the write() will attempt to get the required memory
- *        for the duration of this request but, if memory is low, it may
- *        fail with ENOMEM.
+ *        (otherwise the macros compile to empty statements). 
+ *        Then before running the program to be debugged enter: 
+ *          # echo "scsi log timeout 7" > /proc/scsi/scsi 
+ *        This will send copious output to the console and the log which
+ *        is usually /var/log/messages. To turn off debugging enter:
+ *          # echo "scsi log timeout 0" > /proc/scsi/scsi 
+ *        The 'timeout' token was chosen because it is relatively unused.
+ *        The token 'hlcomplete' should be used but that triggers too
+ *        much output from the sd device driver. To dump the current
+ *        state of the SCSI mid level data structures enter:
+ *          # echo "scsi dump 1" > /proc/scsi/scsi 
+ *        To dump the state of sg's data structures get the 'sg_debug'
+ *        program from the utilities and enter:
+ *          # sg_debug /dev/sga 
+ *        or any valid sg device name. The state of _all_ sg devices
+ *        will be sent to the console and the log.
  *
  *      - The 'alt_address' field in the scatter_list structure and the
  *        related 'mem_src' indicate the source of the heap allocation.
@@ -158,7 +145,7 @@ typedef struct sg_fd /* holds the state of a file descriptor */
     char closed;        /* 1 -> fd closed but request(s) outstanding */
     char my_mem_src;    /* heap whereabouts of this Sg_fd object */
     char cmd_q;         /* 1 -> allow command queuing, 0 -> don't */
-    char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */
+    char underrun_flag; /* 1 -> flag underruns, 0 -> don't flag underruns */
     char next_cmd_len;  /* 0 -> automatic (def), >0 -> use on next write() */
 } Sg_fd; /* 1208 bytes long on i386 */
 
@@ -212,7 +199,6 @@ static Sg_device * sg_dev_arr = NULL;
 static const int size_sg_header = sizeof(struct sg_header);
 
 
-
 static int sg_open(struct inode * inode, struct file * filp)
 {
     int dev = MINOR(inode->i_rdev);
@@ -316,7 +302,6 @@ static ssize_t sg_read(struct file * filp, char * buf,
     Sg_fd * sfp;
     Sg_request * srp;
     int req_pack_id = -1;
-    struct sg_header * shp = (struct sg_header *)buf;
 
     if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)))
         return -ENXIO;
@@ -329,8 +314,11 @@ static ssize_t sg_read(struct file * filp, char * buf,
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
     if ((k = verify_area(VERIFY_WRITE, buf, count)))
         return k;
-    if (sfp->force_packid && (count >= size_sg_header))
-        req_pack_id = shp->pack_id;
+    if (sfp->force_packid && (count >= size_sg_header)) {
+        struct sg_header hdr;
+        copy_from_user(&hdr, buf, size_sg_header);
+        req_pack_id = hdr.pack_id;
+    }
     srp = sg_get_request(sfp, req_pack_id);
     if (! srp) { /* now wait on packet to arrive */
         if (filp->f_flags & O_NONBLOCK)
@@ -342,8 +330,7 @@ static ssize_t sg_read(struct file * filp, char * buf,
         if (res)
             return res; /* -ERESTARTSYS because signal hit process */
     }
-    if (2 != sfp->underrun_flag)
-        srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
+    srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
 
     /* Now copy the result back to the user buffer.  */
     if (count >= size_sg_header) {
@@ -391,9 +378,8 @@ static ssize_t sg_write(struct file * filp, const char * buf,
     if (count < (size_sg_header + 6))
         return -EIO;   /* The minimum scsi command length is 6 bytes. */ 
 
-    srp = sg_add_request(sfp);
-    if (! srp) {
-        SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full, domain error\n"));
+    if (! (srp = sg_add_request(sfp))) {
+        SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n"));
         return -EDOM;
     }
     __copy_from_user(&srp->header, buf, size_sg_header); 
@@ -404,7 +390,8 @@ static ssize_t sg_write(struct file * filp, const char * buf,
         if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {
             SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));
             sfp->next_cmd_len = 0;
-            return -EDOM;
+            sg_remove_request(sfp, srp);
+            return -EIO;
         }
         cmd_size = sfp->next_cmd_len;
         sfp->next_cmd_len = 0; /* reset so only this write() effected */
@@ -422,9 +409,9 @@ static ssize_t sg_write(struct file * filp, const char * buf,
                                                     srp->header.reply_len;
     mxsize -= size_sg_header;
     input_size -= size_sg_header;
-    if (input_size < 0) {
+    if ((input_size < 0)  || (srp->header.reply_len < 0)) {
         sg_remove_request(sfp, srp);
-        return -EIO; /* User did not pass enough bytes for this command. */
+        return -EIO; /* Count too small or reply_len negative. */
     }
     if ((k = sg_start_req(srp, mxsize, buf + cmd_size, input_size))) {
         SCSI_LOG_TIMEOUT(1, printk("sg_write: build err=%d\n", k));
@@ -432,8 +419,10 @@ static ssize_t sg_write(struct file * filp, const char * buf,
         return k;    /* probably out of space --> ENOMEM */
     }
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */
-    if (! (SCpnt = scsi_allocate_device(NULL, sdp->device, 
-                                        !(filp->f_flags & O_NONBLOCK)))) {
+    spin_lock_irqsave(&io_request_lock, flags);
+    SCpnt = scsi_allocate_device(NULL, sdp->device, ! (filp->f_flags & O_NONBLOCK));
+    spin_unlock_irqrestore(&io_request_lock, flags);
+    if (! SCpnt) {
         sg_finish_rem_req(srp, NULL, 0);
         return -EAGAIN;   /* No available command blocks at the moment */
     }
@@ -490,7 +479,12 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
     switch(cmd_in)
     {
     case SG_SET_TIMEOUT:
-        return get_user(sfp->timeout, (int *)arg);
+        result =  get_user(val, (int *)arg);
+        if (result) return result;
+        if (val < 0)
+            return -EIO;
+        sfp->timeout = val;
+        return 0;
     case SG_GET_TIMEOUT:  /* N.B. User receives timeout as return value */
         return sfp->timeout; /* strange ..., for backward compatibility */
     case SG_SET_FORCE_LOW_DMA:
@@ -514,14 +508,19 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
         if (result) return result;
         else {
             Sg_scsi_id * sg_idp = (Sg_scsi_id *)arg;
-            __put_user((int)sdp->device->host->host_no, &sg_idp->host_no);
+            struct Scsi_Host * hostp = sdp->device->host;
+
+            __put_user((int)hostp->host_no, &sg_idp->host_no);
             __put_user((int)sdp->device->channel, &sg_idp->channel);
             __put_user((int)sdp->device->id, &sg_idp->scsi_id);
             __put_user((int)sdp->device->lun, &sg_idp->lun);
             __put_user((int)sdp->device->type, &sg_idp->scsi_type);
+            __put_user(hostp->cmd_per_lun ? hostp->cmd_per_lun :
+                       hostp->hostt->cmd_per_lun, &sg_idp->h_cmd_per_lun);
+            __put_user((short)sdp->device->queue_depth, 
+                       &sg_idp->d_queue_depth);
             __put_user(0, &sg_idp->unused1);
             __put_user(0, &sg_idp->unused2);
-            __put_user(0, &sg_idp->unused3);
             return 0;
         }
     case SG_SET_FORCE_PACK_ID:
@@ -605,6 +604,13 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
         return put_user(sg_version_num, (int *)arg);
     case SG_EMULATED_HOST:
         return put_user(sdp->device->host->hostt->emulated, (int *)arg);
+    case SG_SCSI_RESET:
+        if (! scsi_block_when_processing_errors(sdp->device))
+            return -EBUSY;
+        result = get_user(val, (int *)arg);
+        if (result) return result;
+        /* Don't do anything till scsi mod level visibility */
+        return 0;
     case SCSI_IOCTL_SEND_COMMAND:
         /* Allow SCSI_IOCTL_SEND_COMMAND without checking suser() since the
            user already has read/write access to the generic device and so
@@ -686,9 +692,13 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
     Sg_fd * sfp;
     Sg_request * srp = NULL;
     int closed = 0;
+    static const int min_sb_len = 
+                SG_MAX_SENSE > sizeof(SCpnt->sense_buffer) ? 
+                        sizeof(SCpnt->sense_buffer) : SG_MAX_SENSE;
 
     if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max)) {
         SCSI_LOG_TIMEOUT(1, printk("sg__done: bad args dev=%d\n", dev));
+        wake_up(&SCpnt->device->device_wait);
         scsi_release_command(SCpnt);
         SCpnt = NULL;
         return;
@@ -711,6 +721,7 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
     }
     if (! srp) {
         SCSI_LOG_TIMEOUT(1, printk("sg__done: req missing, dev=%d\n", dev));
+        wake_up(&SCpnt->device->device_wait);
         scsi_release_command(SCpnt);
         SCpnt = NULL;
         return;
@@ -720,15 +731,12 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
     srp->data.sglist_len = SCpnt->sglist_len;
     srp->data.bufflen = SCpnt->bufflen;
     srp->data.buffer = SCpnt->buffer;
-    if (2 == sfp->underrun_flag)
-        srp->header.pack_len = SCpnt->underflow;
     sg_clr_scpnt(SCpnt);
     srp->my_cmdp = NULL;
 
     SCSI_LOG_TIMEOUT(4, printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", 
                 dev, (int)status_byte(SCpnt->result), (int)SCpnt->result));
-    memcpy(srp->header.sense_buffer, SCpnt->sense_buffer,
-           sizeof(SCpnt->sense_buffer));
+    memcpy(srp->header.sense_buffer, SCpnt->sense_buffer, min_sb_len);
     switch (host_byte(SCpnt->result)) 
     { /* This setup of 'result' is for backward compatibility and is best
          ignored by the user who should use target, host + driver status */
@@ -781,6 +789,7 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
     srp->header.host_status = host_byte(SCpnt->result);
     srp->header.driver_status = driver_byte(SCpnt->result);
 
+    wake_up(&SCpnt->device->device_wait);
     scsi_release_command(SCpnt);
     SCpnt = NULL;
     if (sfp->closed) { /* whoops this fd already released, cleanup */
index 3493fb40c215edc305f91c459d843b9428a87c93..4912846c0527f65079732d872ef1a39299337176 100644 (file)
@@ -1,6 +1,16 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *      16 Sep 1999 Rev. 5.11 for linux 2.2.12 and 2.3.18
+ *        + Updated to the new __setup interface for boot command line options.
+ *        + When loaded as a module, accepts the new parameter boot_options
+ *          which value is a string with the same format of the kernel boot
+ *          command line options. A valid example is:
+ *          modprobe u14-34f 'boot_options=\"0x230,0x340,lc:y,mq:4\"'
+ *
+ *      22 Jul 1999 Rev. 5.00 for linux 2.2.10 and 2.3.11
+ *        + Removed pre-2.2 source code compatibility.
+ *
  *      26 Jul 1998 Rev. 4.33 for linux 2.0.35 and 2.1.111
  *          Added command line option (et:[y|n]) to use the existing
  *          translation (returned by scsicam_bios_param) as disk geometry.
  *
  *          Multiple U14F and/or U34F host adapters are supported.
  *
- *  Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
+ *  Copyright (C) 1994-1999 Dario Ballabio (dario@milano.europe.dg.com)
  *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that redistributions of source
  *  After the optional list of detection probes, other possible command line
  *  options are:
  *
- *  eh:y  use new scsi code (linux 2.2 only);
+ *  eh:y  use new scsi code;
  *  eh:n  use old scsi code;
  *  et:y  use disk geometry returned by scsicam_bios_param;
  *  et:n  use disk geometry jumpered on the board;
 #if defined(MODULE)
 #include <linux/module.h>
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(boot_options, "s");
 MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
 MODULE_PARM(linked_comm, "i");
 MODULE_PARM(have_old_firmware, "i");
@@ -327,7 +337,6 @@ MODULE_PARM(max_queue_depth, "i");
 MODULE_PARM(use_new_eh_code, "i");
 MODULE_PARM(ext_tran, "i");
 MODULE_AUTHOR("Dario Ballabio");
-#endif
 
 #endif
 
@@ -349,42 +358,21 @@ MODULE_AUTHOR("Dario Ballabio");
 #include "u14-34f.h"
 #include <linux/stat.h>
 #include <linux/config.h>
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,36)
 #include <linux/init.h>
+#include <linux/ctype.h>
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#include <asm/spinlock.h>
 #else
-#define __initfunc(A) A
-#define __initdata
-#define __init
+#include <linux/spinlock.h>
 #endif
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
-#include <asm/spinlock.h>
-#define IRQ_FLAGS
-#define IRQ_LOCK
-#define IRQ_LOCK_SAVE
-#define IRQ_UNLOCK
-#define IRQ_UNLOCK_RESTORE
 #define SPIN_FLAGS unsigned long spin_flags;
 #define SPIN_LOCK spin_lock_irq(&io_request_lock);
 #define SPIN_LOCK_SAVE spin_lock_irqsave(&io_request_lock, spin_flags);
 #define SPIN_UNLOCK spin_unlock_irq(&io_request_lock);
 #define SPIN_UNLOCK_RESTORE \
                   spin_unlock_irqrestore(&io_request_lock, spin_flags);
-static int use_new_eh_code = TRUE;
-#else
-#define IRQ_FLAGS unsigned long irq_flags;
-#define IRQ_LOCK cli();
-#define IRQ_LOCK_SAVE do {save_flags(irq_flags); cli();} while (0);
-#define IRQ_UNLOCK sti();
-#define IRQ_UNLOCK_RESTORE do {restore_flags(irq_flags);} while (0);
-#define SPIN_FLAGS
-#define SPIN_LOCK
-#define SPIN_LOCK_SAVE
-#define SPIN_UNLOCK
-#define SPIN_UNLOCK_RESTORE
-static int use_new_eh_code = FALSE;
-#endif
 
 struct proc_dir_entry proc_scsi_u14_34f = {
     PROC_SCSI_U14_34F, 6, "u14_34f",
@@ -467,7 +455,10 @@ struct proc_dir_entry proc_scsi_u14_34f = {
 #define ASOK              0x00
 #define ASST              0x91
 
-#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+#define ARRAY_SIZE(x) (sizeof (x) / sizeof((x)[0]))
+#endif
+
 #define YESNO(a) ((a) ? 'y' : 'n')
 #define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
 
@@ -532,7 +523,7 @@ static char sha[MAX_BOARDS];
 /* Initialize num_boards so that ihdlr can work while detect is in progress */
 static unsigned int num_boards = MAX_BOARDS;
 
-static unsigned long io_port[] __initdata = {
+static unsigned long io_port[] = {
 
    /* Space for MAX_INT_PARAM ports usable while loading as a module */
    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
@@ -570,6 +561,8 @@ static int do_trace = FALSE;
 static int setup_done = FALSE;
 static int link_statistics = 0;
 static int ext_tran = FALSE;
+static int use_new_eh_code = TRUE;
+static char *boot_options = NULL;
 
 #if defined(HAVE_OLD_UX4F_FIRMWARE)
 static int have_old_firmware = TRUE;
@@ -592,9 +585,7 @@ static int max_queue_depth = MAX_CMD_PER_LUN;
 static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
    Scsi_Device *dev;
    int j, ntag = 0, nuntag = 0, tqd, utqd;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    j = ((struct hostdata *) host->hostdata)->board_number;
 
    for(dev = devlist; dev; dev = dev->next) {
@@ -642,7 +633,6 @@ static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
              dev->queue_depth, link_suffix, tag_suffix);
       }
 
-   IRQ_UNLOCK_RESTORE
    return;
 }
 
@@ -686,10 +676,8 @@ static int board_inquiry(unsigned int j) {
    outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
 
    SPIN_UNLOCK
-   IRQ_UNLOCK
    time = jiffies;
    while ((jiffies - time) < HZ && limit++ < 20000) udelay(100L);
-   IRQ_LOCK
    SPIN_LOCK
 
    if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) {
@@ -701,8 +689,8 @@ static int board_inquiry(unsigned int j) {
    return FALSE;
 }
 
-__initfunc (static inline int port_detect \
-      (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+static inline int port_detect \
+      (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char in_byte;
    char *bus_type, dma_name[16];
@@ -747,7 +735,9 @@ __initfunc (static inline int port_detect \
    sprintf(name, "%s%d", driver_name, j);
 
    if(check_region(port_base, REGION_SIZE)) {
+#if defined(DEBUG_DETECT)
       printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
+#endif
       return FALSE;
       }
 
@@ -781,11 +771,7 @@ __initfunc (static inline int port_detect \
 
    if (have_old_firmware) tpnt->use_clustering = DISABLE_CLUSTERING;
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
    tpnt->use_new_eh_code = use_new_eh_code;
-#else
-   use_new_eh_code = FALSE;
-#endif
 
    sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
 
@@ -894,7 +880,7 @@ __initfunc (static inline int port_detect \
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
 
    if (j == 0) {
-      printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n");
+      printk("UltraStor 14F/34F: Copyright (C) 1994-1999 Dario Ballabio.\n");
       printk("%s config options -> of:%c, lc:%c, mq:%d, eh:%c, et:%c.\n",
              driver_name, YESNO(have_old_firmware), YESNO(linked_comm),
              max_queue_depth, YESNO(use_new_eh_code), YESNO(ext_tran));
@@ -915,7 +901,7 @@ __initfunc (static inline int port_detect \
    return TRUE;
 }
 
-__initfunc (void u14_34f_setup(char *str, int *ints)) {
+static void internal_setup(char *str, int *ints) {
    int i, argc = ints[0];
    char *cur = str, *pc;
 
@@ -949,13 +935,30 @@ __initfunc (void u14_34f_setup(char *str, int *ints)) {
    return;
 }
 
-__initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
+static int option_setup(char *str) {
+   int ints[MAX_INT_PARAM];
+   char *cur = str;
+   int i = 1;
+
+   while (cur && isdigit(*cur) && i <= MAX_INT_PARAM) {
+      ints[i++] = simple_strtoul(cur, NULL, 0);
+
+      if ((cur = strchr(cur, ',')) != NULL) cur++;
+   }
+
+   ints[0] = i - 1;
+   internal_setup(cur, ints);
+   return 0;
+}
+
+int u14_34f_detect(Scsi_Host_Template *tpnt)
+{
    unsigned int j = 0, k;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    tpnt->proc_dir = &proc_scsi_u14_34f;
 
+   if(boot_options) option_setup(boot_options);
+
 #if defined(MODULE)
    /* io_port could have been modified when loading as a module */
    if(io_port[0] != SKIP) {
@@ -974,7 +977,6 @@ __initfunc (int u14_34f_detect(Scsi_Host_Template *tpnt)) {
       }
 
    num_boards = j;
-   IRQ_UNLOCK_RESTORE
    return j;
 }
 
@@ -1110,11 +1112,8 @@ static inline int do_qcomm(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
 
 int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    int rtn;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    rtn = do_qcomm(SCpnt, done);
-   IRQ_UNLOCK_RESTORE
    return rtn;
 }
 
@@ -1186,16 +1185,11 @@ static inline int do_old_abort(Scsi_Cmnd *SCarg) {
 
 int u14_34f_old_abort(Scsi_Cmnd *SCarg) {
    int rtn;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    rtn = do_old_abort(SCarg);
-   IRQ_UNLOCK_RESTORE
    return rtn;
 }
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
-
 static inline int do_abort(Scsi_Cmnd *SCarg) {
    unsigned int i, j;
 
@@ -1273,8 +1267,6 @@ int u14_34f_abort(Scsi_Cmnd *SCarg) {
    return do_abort(SCarg);
 }
 
-#endif /* new_eh_code */
-
 static inline int do_old_reset(Scsi_Cmnd *SCarg) {
    unsigned int i, j, time, k, c, limit = 0;
    int arg_done = FALSE;
@@ -1362,10 +1354,8 @@ static inline int do_old_reset(Scsi_Cmnd *SCarg) {
 
    HD(j)->in_reset = TRUE;
    SPIN_UNLOCK
-   IRQ_UNLOCK
    time = jiffies;
    while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
-   IRQ_LOCK
    SPIN_LOCK
    printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
 
@@ -1401,7 +1391,6 @@ static inline int do_old_reset(Scsi_Cmnd *SCarg) {
          continue;
 
       SCpnt->scsi_done(SCpnt);
-      IRQ_LOCK
       }
 
    HD(j)->in_reset = FALSE;
@@ -1419,16 +1408,11 @@ static inline int do_old_reset(Scsi_Cmnd *SCarg) {
 
 int u14_34f_old_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
    int rtn;
-   IRQ_FLAGS
 
-   IRQ_LOCK_SAVE
    rtn = do_old_reset(SCarg);
-   IRQ_UNLOCK_RESTORE
    return rtn;
 }
 
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
-
 static inline int do_reset(Scsi_Cmnd *SCarg) {
    unsigned int i, j, time, k, c, limit = 0;
    int arg_done = FALSE;
@@ -1510,10 +1494,8 @@ static inline int do_reset(Scsi_Cmnd *SCarg) {
 
    HD(j)->in_reset = TRUE;
    SPIN_UNLOCK
-   IRQ_UNLOCK
    time = jiffies;
    while ((jiffies - time) < (10 * HZ) && limit++ < 200000) udelay(100L);
-   IRQ_LOCK
    SPIN_LOCK
    printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
 
@@ -1549,7 +1531,6 @@ static inline int do_reset(Scsi_Cmnd *SCarg) {
          continue;
 
       SCpnt->scsi_done(SCpnt);
-      IRQ_LOCK
       }
 
    HD(j)->in_reset = FALSE;
@@ -1566,8 +1547,6 @@ int u14_34f_reset(Scsi_Cmnd *SCarg) {
    return do_reset(SCarg);
 }
 
-#endif /* new_eh_code */
-
 int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) {
    unsigned int j = 0;
    int size = disk->capacity;
@@ -1956,24 +1935,18 @@ static inline void ihdlr(int irq, unsigned int j) {
 
 static void do_interrupt_handler(int irq, void *shap, struct pt_regs *regs) {
    unsigned int j;
-   IRQ_FLAGS
    SPIN_FLAGS
 
    /* Check if the interrupt must be processed by this handler */
    if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
 
    SPIN_LOCK_SAVE
-   IRQ_LOCK_SAVE
    ihdlr(irq, j);
-   IRQ_UNLOCK_RESTORE
    SPIN_UNLOCK_RESTORE
 }
 
 int u14_34f_release(struct Scsi_Host *shpnt) {
    unsigned int i, j;
-   IRQ_FLAGS
-
-   IRQ_LOCK_SAVE
 
    for (j = 0; sh[j] != NULL && sh[j] != shpnt; j++);
 
@@ -1989,7 +1962,6 @@ int u14_34f_release(struct Scsi_Host *shpnt) {
 
    release_region(sh[j]->io_port, sh[j]->n_io_port);
    scsi_unregister(sh[j]);
-   IRQ_UNLOCK_RESTORE
    return FALSE;
 }
 
@@ -1997,4 +1969,15 @@ int u14_34f_release(struct Scsi_Host *shpnt) {
 Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
 
 #include "scsi_module.c"
+
+#else
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,3,18)
+void u14_34f_setup(char *str, int *ints) {
+   internal_setup(str, ints);
+}
+#else
+__setup("u14-34f=", option_setup);
 #endif
+
+#endif /* end MODULE */
index c97d29bcb6d54517f81a0c234150a558ea9b6f00..241836c1e7a2e1040d12a8a5fcdd041480396752 100644 (file)
@@ -16,13 +16,7 @@ int u14_34f_reset(Scsi_Cmnd *);
 int u14_34f_old_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "4.33.00"
-
-#ifndef LinuxVersionCode
-#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
-#endif
-
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,101)
+#define U14_34F_VERSION "5.11.01"
 
 #define ULTRASTOR_14_34F {                                                   \
                 name:         "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
@@ -42,21 +36,4 @@ int u14_34f_biosparam(Disk *, kdev_t, int *);
                 use_new_eh_code:         1    /* Enable new error code */    \
                 }
 
-#else /* Use old scsi code */
-
-#define ULTRASTOR_14_34F {                                                   \
-                name:         "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
-                detect:                  u14_34f_detect,                     \
-                release:                 u14_34f_release,                    \
-                queuecommand:            u14_34f_queuecommand,               \
-                abort:                   u14_34f_old_abort,                  \
-                reset:                   u14_34f_old_reset,                  \
-                bios_param:              u14_34f_biosparam,                  \
-                this_id:                 7,                                  \
-                unchecked_isa_dma:       1,                                  \
-                use_clustering:          ENABLE_CLUSTERING                   \
-                }
-
-#endif
-
 #endif
index 8741a20513b3457f10933e5fb1ec8d456733f51d..42ac74a0f5a8e048815ce4667315172a162e240c 100644 (file)
 /*****************************************************************************/
 
 #include <linux/version.h>
+#include <linux/module.h>
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
 
- #ifdef MODULE
-  #include <linux/module.h>
-  #ifdef MODVERSIONS
-   #include <linux/modversions.h>
-  #endif
- #endif
  #define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL}
  #define wait_queue_head_t struct wait_queue *
  #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK)
index 3ecb9bfa630e44c713f67dd45124323a25d6be66..4e32fd4d1373c94b6ebaa11c1df5ba4e911a58b5 100644 (file)
 # define NFSDBG_FACILITY       NFSDBG_ROOT
 #endif
 
+#ifndef MAX
+# define MAX(a, b)     (((a) > (b))? (a) : (b))
+#endif
+
+
 /*
 #define MOUNT_PROGRAM          100005
 #define MOUNT_VERSION          1
@@ -119,7 +124,7 @@ static struct rpc_procinfo  mnt_procedures[2] = {
        { "mnt_mount",
                (kxdrproc_t) xdr_encode_dirpath,        
                (kxdrproc_t) xdr_decode_fhstatus,
-               MNT_dirpath_sz, MNT_fhstatus_sz },
+               MAX(MNT_dirpath_sz, MNT_fhstatus_sz)<<2, 0},
 };
 
 static struct rpc_version      mnt_version1 = {
index 77f98d496d43404c403f4d25090cd6141120a50f..d965f1fb309c1b34651cad89493504fbd2fec7a5 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -712,7 +712,7 @@ repeat:
 
        /* Do we need to expand the fdset array? */
        if (fd >= current->files->max_fdset) {
-               error = expand_fdset(files, 0);
+               error = expand_fdset(files, fd + 1);
                if (!error)
                        goto repeat;
                goto out;
@@ -722,7 +722,7 @@ repeat:
         * Check whether we need to expand the fd array.
         */
        if (fd >= files->max_fds) {
-               error = expand_fd_array(files, 0);
+               error = expand_fd_array(files, fd + 1);
                if (!error)
                        goto repeat;
                goto out;
index f674884d82139bc8308eb9afdeb76f87108fd73e..8b355d9ea0783f31eca2a4e74034df892037f7d0 100644 (file)
@@ -38,9 +38,9 @@ struct i2c_device;
 
 #define I2C_BUSID_BT848                1       /* I2C bus on a BT848 */
        /* 2 is used in 2.3.x */
-#define I2C_BUSID_SGIVWFB      1       /* From the SGI official patch */
 #define I2C_BUSID_BUZ          3       /* I2C bus on a BUZ */
 #define I2C_BUSID_ZORAN                4       /* I2C bus on a Zoran */
+#define I2C_BUSID_SGIVWFB      5       /* Moved to be unique */
 
 /*
  * struct for a driver for a i2c chip (tuner, soundprocessor,
index a52c57d9bb3024f4dca93ab40c193585d237ba39..446866db150eb44e3c43a10a64982ec0c1eecf64 100644 (file)
@@ -657,12 +657,12 @@ static inline int expand_files(struct files_struct *files, int nr)
        
        if (nr >= files->max_fdset) {
                expand = 1;
-               if ((err = expand_fdset(files, nr)))
+               if ((err = expand_fdset(files, nr + 1)))
                        goto out;
        }
        if (nr >= files->max_fds) {
                expand = 1;
-               if ((err = expand_fd_array(files, nr)))
+               if ((err = expand_fd_array(files, nr + 1)))
                        goto out;
        }
        err = expand;
index acc0619eb5e575c446611822f1a2d9a6fcc459d8..868f812acbfbd49c09d9bcfcb647770869f405a6 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file define a set of standard wireless extensions
  *
- * Version :   8       28.7.99
+ * Version :   9       16.10.99
  *
  * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
  */
@@ -63,7 +63,7 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT   8
+#define WIRELESS_EXT   9
 
 /*
  * Changes :
  *     - Changed my e-mail address
  *     - More 802.11 support (nickname, rate, rts, frag)
  *     - List index in frequencies
+ *
+ * V8 to V9
+ * --------
+ *     - Support for 'mode of operation' (ad-hoc, managed...)
+ *     - Support for unicast and multicast power saving
+ *     - Change encoding to support larger tokens (>64 bits)
+ *     - Updated iw_params (disable, flags) and use it for NWID
+ *     - Extracted iw_point from iwreq for clarity
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
 /* Basic operations */
 #define SIOCSIWNAME    0x8B00          /* Unused ??? */
 #define SIOCGIWNAME    0x8B01          /* get name */
-#define SIOCSIWNWID    0x8B02          /* set network id */
+#define SIOCSIWNWID    0x8B02          /* set network id (the cell) */
 #define SIOCGIWNWID    0x8B03          /* get network id */
 #define SIOCSIWFREQ    0x8B04          /* set channel/frequency */
 #define SIOCGIWFREQ    0x8B05          /* get channel/frequency */
-#define SIOCSIWENCODE  0x8B06          /* set encoding info */
-#define SIOCGIWENCODE  0x8B07          /* get encoding info */
+#define SIOCSIWMODE    0x8B06          /* set operation mode */
+#define SIOCGIWMODE    0x8B07          /* get operation mode */
 #define SIOCSIWSENS    0x8B08          /* set sensitivity */
 #define SIOCGIWSENS    0x8B09          /* get sensitivity */
 
 #define SIOCSIWFRAG    0x8B24          /* set fragmentation thr (bytes) */
 #define SIOCGIWFRAG    0x8B25          /* get fragmentation thr (bytes) */
 
+/* Encoding stuff (scrambling, hardware security, WEP...) */
+#define SIOCSIWENCODE  0x8B2A          /* set encoding token & mode */
+#define SIOCGIWENCODE  0x8B2B          /* get encoding token & mode */
+/* Power saving stuff (power management, unicast and multicast) */
+#define SIOCSIWPOWER   0x8B2C          /* set Power Management settings */
+#define SIOCGIWPOWER   0x8B2D          /* get Power Management settings */
+
 /* ------------------------- IOCTL STUFF ------------------------- */
 
 /* The first and the last (range) */
 #define SIOCIWFIRST    0x8B00
-#define SIOCIWLAST     0x8B25
+#define SIOCIWLAST     0x8B30
 
 /* Even : get (world access), odd : set (root access) */
 #define IW_IS_SET(cmd) (!((cmd) & 0x1))
 /* Maximum size of the ESSID and NICKN strings */
 #define IW_ESSID_MAX_SIZE      32
 
+/* Modes of operation */
+#define IW_MODE_AUTO   0       /* Let the driver decides */
+#define IW_MODE_ADHOC  1       /* Single cell network */
+#define IW_MODE_INFRA  2       /* Multi cell network, roaming, ... */
+#define IW_MODE_MASTER 3       /* Synchronisation master or Access Point */
+#define IW_MODE_REPEAT 4       /* Wireless Repeater (forwarder) */
+#define IW_MODE_SECOND 5       /* Secondary master/repeater (backup) */
+
+/* Maximum number of size of encoding token available
+ * they are listed in the range structure */
+#define IW_MAX_ENCODING_SIZES  8
+
+/* Maximum size of the encoding token in bytes */
+#define IW_ENCODING_TOKEN_MAX  32      /* 256 bits (for now) */
+
+/* Flags for encoding (along with the token) */
+#define IW_ENCODE_INDEX                0x00FF  /* Token index (if needed) */
+#define IW_ENCODE_FLAGS                0xF000  /* Flags defined below */
+#define IW_ENCODE_DISABLED     0x8000  /* Encoding disabled */
+#define IW_ENCODE_ENABLED      0x0000  /* Encoding enabled */
+#define IW_ENCODE_RESTRICTED   0x4000  /* Refuse non-encoded packets */
+#define IW_ENCODE_OPEN         0x2000  /* Accept non-encoded packets */
+
+/* Power management flags available (along with the value, if any) */
+#define IW_POWER_ON            0x0000  /* No details... */
+#define IW_POWER_TYPE          0xF000  /* Type of parameter */
+#define IW_POWER_PERIOD                0x1000  /* Value is a period/duration of  */
+#define IW_POWER_TIMEOUT       0x2000  /* Value is a timeout (to go asleep) */
+#define IW_POWER_MODE          0x0F00  /* Power Management mode */
+#define IW_POWER_UNICAST_R     0x0100  /* Receive only unicast messages */
+#define IW_POWER_MULTICAST_R   0x0200  /* Receive only multicast messages */
+#define IW_POWER_ALL_R         0x0300  /* Receive all messages though PM */
+#define IW_POWER_FORCE_S       0x0400  /* Force PM procedure for sending unicast */
+#define IW_POWER_REPEATER      0x0800  /* Repeat broadcast messages in PM period */
+
 /****************************** TYPES ******************************/
 
 /* --------------------------- SUBTYPES --------------------------- */
+/*
+ *     Generic format for most parameters that fit in an int
+ */
+struct iw_param
+{
+  __s32                value;          /* The value of the parameter itself */
+  __u8         fixed;          /* Hardware should not use auto select */
+  __u8         disabled;       /* Disable the feature */
+  __u16                flags;          /* Various specifc flags (if any) */
+};
+
+/*
+ *     For all data larger than 16 octets, we need to use a
+ *     pointer to memory alocated in user space.
+ */
+struct iw_point
+{
+  caddr_t      pointer;        /* Pointer to the data  (in user space) */
+  __u16                length;         /* number of fields or size in bytes */
+  __u16                flags;          /* Optional params */
+};
+
 /*
  *     A frequency
  *     For numbers lower than 10^9, we encode the number in 'm' and
@@ -223,7 +295,7 @@ struct      iw_freq
  */
 struct iw_quality
 {
-       __u8            qual;           /* link quality (SNR or better...) */
+       __u8            qual;           /* link quality (%retries, SNR or better...) */
        __u8            level;          /* signal level */
        __u8            noise;          /* noise level */
        __u8            updated;        /* Flags to know if updated */
@@ -240,33 +312,13 @@ struct    iw_discarded
        __u32           misc;           /* Others cases */
 };
 
-/*
- *     Encoding information (setting and so on)
- *     Encoding might be hardware encryption, scrambing or others
- */
-struct iw_encoding
-{
-  __u8 method;                 /* Algorithm number / key used */
-  __u64        code;                   /* Data/key used for algorithm */
-};
-
-/*
- *     Generic format for parameters
- */
-struct iw_param
-{
-  __s32                value;          /* The value of the parameter itself */
-  __u8         fixed;          /* Hardware should not use auto select */
-};
-
-
 /* ------------------------ WIRELESS STATS ------------------------ */
 /*
  * Wireless statistics (used for /proc/net/wireless)
  */
 struct iw_statistics
 {
-       __u           status;         /* Status
+       __u16           status;         /* Status
                                         * - device dependent for now */
 
        struct iw_quality       qual;           /* Quality of the link
@@ -295,37 +347,28 @@ struct    iwreq
        union
        {
                /* Config - generic */
-               char    name[IFNAMSIZ];
+               char            name[IFNAMSIZ];
                /* Name : used to verify the presence of  wireless extensions.
                 * Name of the protocol/provider... */
 
-               struct          /* network id (or domain) : used to to */
-               {               /* create logical channels on the air */
-                       __u32   nwid;           /* value */
-                       __u8    on;             /* active/unactive nwid */
-               }       nwid;
-
+               struct iw_point essid;  /* Extended network name */
+               struct iw_param nwid;   /* network id (or domain - the cell) */
                struct iw_freq  freq;   /* frequency or channel :
                                         * 0-1000 = channel
                                         * > 1000 = frequency in Hz */
 
-               struct iw_encoding      encoding;       /* Encoding stuff */
-
-               __u32   sensitivity;            /* Obsolete, but compatible */
                struct iw_param sens;           /* signal level threshold */
                struct iw_param bitrate;        /* default bit rate */
                struct iw_param rts;            /* RTS threshold threshold */
                struct iw_param frag;           /* Fragmentation threshold */
+               __u32           mode;           /* Operation mode */
+
+               struct iw_point encoding;       /* Encoding stuff : tokens */
+               struct iw_param power;          /* PM duration/timeout */
 
                struct sockaddr ap_addr;        /* Access point address */
 
-               struct          /* For all data bigger than 16 octets */
-               {
-                       caddr_t pointer;        /* Pointer to the data
-                                                * (in user space) */
-                       __u16   length;         /* fields or byte size */
-                       __u16   flags;          /* Optional params */
-               }       data;
+               struct iw_point data;           /* Other large parameters */
        }       u;
 };
 
@@ -366,9 +409,6 @@ struct      iw_range
        /* Quality of link & SNR stuff */
        struct iw_quality       max_qual;       /* Quality of the link */
 
-       /* Encoder stuff */
-       struct iw_encoding      max_encoding;   /* Encoding max range */
-
        /* Rates */
        __u8            num_bitrates;   /* Number of entries in the list */
        __s32           bitrate[IW_MAX_BITRATES];       /* list, in bps */
@@ -380,6 +420,17 @@ struct     iw_range
        /* Frag threshold */
        __s32           min_frag;       /* Minimal frag threshold */
        __s32           max_frag;       /* Maximal frag threshold */
+
+       /* Power Management duration & timeout */
+       __s32           min_pmd;        /* Minimal PM duration */
+       __s32           max_pmd;        /* Maximal PM duration */
+       __s32           min_pmt;        /* Minimal PM timeout */
+       __s32           max_pmt;        /* Maximal PM timeout */
+
+       /* Encoder stuff */
+       __u16   encoding_size[IW_MAX_ENCODING_SIZES];   /* Different token sizes */
+       __u8    num_encoding_sizes;     /* Number of entry in the list */
+       __u8    max_encoding_tokens;    /* Max number of tokens */
 };
 
 /*
index 8b50bb3da939203f14340869cdaf5ea6107d4995..67320db695cdc68be025724de2e75f2c848714f0 100644 (file)
@@ -12,10 +12,17 @@ Original driver (sg.h):
 *       Copyright (C) 1998, 1999 Douglas Gilbert
 
 
-    Version: 2.1.34 (990603)
-    This version for later 2.1.x and 2.2.x series kernels
+    Version: 2.1.36 (991008)
+    This version for 2.2.x series kernels
     D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au)
 
+    Changes since 2.1.34 (990603)
+        - skipped 2.1.35 (never fully released)
+        - add queuing info into struct sg_scsi_id
+        - block negative timeout and reply_len values
+        - protect scsi_allocate_device() call
+        - fix pack_id input to sg_read()
+        - add wake_up to mid-level on command release
     Changes since 2.1.33 (990521)
         - implement SG_SET_RESERVED_SIZE and associated memory re-org.
         - add SG_NEXT_CMD_LEN to override SCSI command lengths
@@ -144,9 +151,10 @@ typedef struct sg_scsi_id {
     int scsi_id;        /* scsi id of target device */
     int lun;
     int scsi_type;      /* TYPE_... defined in scsi/scsi.h */
+    short h_cmd_per_lun;/* host (adapter) maximum commands per lun */
+    short d_queue_depth;/* device (or adapter) maximum queue length */
     int unused1;        /* probably find a good use, set 0 for now */
     int unused2;        /* ditto */
-    int unused3;  
 } Sg_scsi_id;
 
 /* IOCTLs: ( _GET_s yield result via 'int *' 3rd argument unless 
@@ -205,6 +213,10 @@ typedef struct sg_scsi_id {
 #define SG_NEXT_CMD_LEN 0x2283  /* override SCSI command length with given
                    number on the next write() on this file descriptor */
 
+/* Returns -EBUSY if occupied else takes as input: 0 -> do nothing,
+   1 -> device reset or  2 -> bus reset (not operational yet) */
+#define SG_SCSI_RESET 0x2284
+
 
 #define SG_SCATTER_SZ (8 * 4096)  /* PAGE_SIZE not available to user */
 /* Largest size (in bytes) a single scatter-gather list element can have.
index faabb183a26e783433ebcfa4677a05c880020cc5..8343731c951f3e332f9528bd4d8dde116d244b5f 100644 (file)
@@ -232,18 +232,17 @@ void exit_fs(struct task_struct *tsk)
 static inline void __exit_sighand(struct task_struct *tsk)
 {
        struct signal_struct * sig = tsk->sig;
+       unsigned long flags;
 
+       spin_lock_irqsave(&tsk->sigmask_lock, flags);
        if (sig) {
-               unsigned long flags;
-
-               spin_lock_irqsave(&tsk->sigmask_lock, flags);
                tsk->sig = NULL;
-               spin_unlock_irqrestore(&tsk->sigmask_lock, flags);
                if (atomic_dec_and_test(&sig->count))
                        kfree(sig);
        }
 
        flush_signals(tsk);
+       spin_unlock_irqrestore(&tsk->sigmask_lock, flags);
 }
 
 void exit_sighand(struct task_struct *tsk)
index 1f9e480f9a4e303c18d2c37b98c9a42f470f5336..d2ded68379bf8de39d9ef53fc97befd54136c298 100644 (file)
@@ -44,6 +44,8 @@ void __init signals_init(void)
 
 /*
  * Flush all pending signals for a task.
+ * Callers must hold the sigmask_lock so that we do not race
+ * with dequeue_signal or send_sig_info.
  */
 
 void
@@ -270,12 +272,16 @@ printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
                goto out_nolock;
 
        /* The null signal is a permissions and process existance probe.
-          No signal is actually delivered.  Same goes for zombies. */
+          No signal is actually delivered.  Same goes for zombies.
+          We have to grab the spinlock now so that we do not race
+          with flush_signals. */
        ret = 0;
-       if (!sig || !t->sig)
+       spin_lock_irqsave(&t->sigmask_lock, flags);
+       if (!sig || !t->sig) {
+               spin_unlock_irqrestore(&t->sigmask_lock, flags);
                goto out_nolock;
+       }
 
-       spin_lock_irqsave(&t->sigmask_lock, flags);
        switch (sig) {
        case SIGKILL: case SIGCONT:
                /* Wake up the process if stopped.  */
index aa5e2e8dbd5195adf2f1784b9f5213dc511d8443..6a2fd63588014b296bea00f7c3c0e82f51bff1ff 100644 (file)
@@ -1258,7 +1258,7 @@ static struct sk_buff *alloc_bridge_skb(int port_no, int pdu_size, char *pdu_nam
        memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
  
        if (br_stats.flags & BR_DEBUG)
-               printk("send_%s_bpdu: port %i src %02x:%02x:%02x:%02x:%02x:%02x\n",
+               printk(KERN_DEBUG "send_%s_bpdu: port %i src %02x:%02x:%02x:%02x:%02x:%02x\n",
                        pdu_name,
                        port_no,
                        eth->h_source[0],
@@ -1693,7 +1693,7 @@ int br_tx_frame(struct sk_buff *skb)      /* 3.5 */
        eth = skb->mac.ethernet;
        port = 0;       /* an impossible port (locally generated) */    
        if (br_stats.flags & BR_DEBUG)
-               printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x"
+               printk(KERN_DEBUG "br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x"
                        " dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
                        port,
                        eth->h_source[0],
@@ -1935,7 +1935,7 @@ static int br_forward(struct sk_buff *skb, int port)      /* 3.7 */
                                /* timer expired, invalidate entry */
                                f->flags &= ~FDB_ENT_VALID;
                                if (br_stats.flags & BR_DEBUG)
-                                       printk("fdb entry expired...\n");
+                                       printk(KERN_DEBUG "fdb entry expired...\n");
                                /*
                                 *      Send flood and drop original
                                 */
@@ -1977,7 +1977,7 @@ static int br_forward(struct sk_buff *skb, int port)      /* 3.7 */
                                /* timer expired, invalidate entry */
                                f->flags &= ~FDB_ENT_VALID;
                                if (br_stats.flags & BR_DEBUG)
-                                       printk("fdb entry expired...\n");
+                                       printk(KERN_DEBUG "fdb entry expired...\n");
                                ++br_stats_cnt.drop_same_port_aged;
                        }
                        else ++br_stats_cnt.drop_same_port;
@@ -2019,7 +2019,7 @@ static int br_flood(struct sk_buff *skb, int port)
                        /* To get here we must have done ARP already,
                           or have a received valid MAC header */
                        
-/*                     printk("Flood to port %d\n",i);*/
+/*                     printk(KERN_DEBUG "Flood to port %d\n",i);*/
                        nskb->nh.raw = nskb->data + ETH_HLEN;
                        nskb->priority = 1;
                        dev_queue_xmit(nskb);
@@ -2578,7 +2578,7 @@ static int brg_start_xmit(struct sk_buff *skb, struct device *dev)
        port = 0;       /* an impossible port (locally generated) */    
 
         if (br_stats.flags & BR_DEBUG)
-               printk("%s: brg_start_xmit - src %02x:%02x:%02x:%02x:%02x:%02x"
+               printk(KERN_DEBUG "%s: brg_start_xmit - src %02x:%02x:%02x:%02x:%02x:%02x"
                        " dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
                       dev->name,
                       eth->h_source[0],
@@ -2679,7 +2679,7 @@ static int brg_rx(struct sk_buff *skb, int port)
        }
 
        if (br_stats.flags & BR_DEBUG)
-         printk("%s: brg_rx - src %02x:%02x:%02x:%02x:%02x:%02x"
+         printk(KERN_DEBUG "%s: brg_rx - src %02x:%02x:%02x:%02x:%02x:%02x"
                 " dest %02x:%02x:%02x:%02x:%02x:%02x\n", 
                 dev->name,
                 eth->h_source[0],
index 0b6268f43403677b5f7f4f2ac1f7ac715f15b940..930bda229ecbb3d22a4838761c919b9565b46b6a 100644 (file)
@@ -1278,8 +1278,9 @@ static int sprintf_wireless_stats(char *buffer, struct device *dev)
        int size;
 
        if(stats != (struct iw_statistics *) NULL)
+       {
                size = sprintf(buffer,
-                              "%6s: %02x  %3d%c %3d%c  %3d%c %5d %5d %5d\n",
+                              "%6s: %04x  %3d%c  %3d%c  %3d%c  %6d %6d %6d\n",
                               dev->name,
                               stats->status,
                               stats->qual.qual,
@@ -1291,6 +1292,8 @@ static int sprintf_wireless_stats(char *buffer, struct device *dev)
                               stats->discard.nwid,
                               stats->discard.code,
                               stats->discard.misc);
+               stats->qual.updated = 0;
+       }
        else
                size = 0;
 
@@ -1312,8 +1315,9 @@ int dev_get_wireless_info(char * buffer, char **start, off_t offset,
        struct device * dev;
 
        size = sprintf(buffer,
-                      "Inter-|sta|  Quality       |  Discarded packets\n"
-                      " face |tus|link level noise| nwid crypt  misc\n");
+                      "Inter-| sta-|   Quality        |   Discarded packets\n"
+                      " face | tus | link level noise |  nwid  crypt   misc\n"
+                       );
        
        pos+=size;
        len+=size;
@@ -1858,6 +1862,7 @@ extern int baycom_ser_hdx_init(void);
 extern int baycom_par_init(void);
 
 extern int lapbeth_init(void);
+extern int comx_init(void);
 extern void arcnet_init(void);
 extern void ip_auto_config(void);
 #ifdef CONFIG_8xx
@@ -1950,6 +1955,9 @@ __initfunc(int net_dev_init(void))
 #endif
 #if defined(CONFIG_8xx)
         cpm_enet_init();
+#endif
+#if defined(CONFIG_COMX)
+       comx_init();
 #endif
        /*
         *      SLHC if present needs attaching so other people see it
index 1042684f388a2a478bedc4b5eae0c275ead9d3ce..0614e22cda7dfa8ca6e596e796bf6081e0c334f7 100644 (file)
@@ -176,8 +176,7 @@ int ip_forward(struct sk_buff *skb)
                            (icmph->type==ICMP_TIME_EXCEEDED))
                        {
 #endif
-                               u32 maddr = rt->rt_src;
-                               fw_res = ip_fw_masq_icmp(&skb, maddr);
+                               fw_res = ip_fw_masquerade(&skb, 0);
                                if (fw_res < 0) {
                                        kfree_skb(skb);
                                        return -1;
@@ -219,15 +218,11 @@ skip_call_fw_firewall:
         */
        if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
            (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
-               u32 maddr;
+               u32 maddr = 0;
 
 #ifdef CONFIG_IP_ROUTE_NAT
                maddr = (rt->rt_flags&RTCF_MASQ) ? rt->rt_src_map : 0;
-
-               if (maddr == 0)
 #endif
-                       maddr = rt->rt_src;
-
                        if (ip_fw_masquerade(&skb, maddr) < 0) {
                                kfree_skb(skb);
                                return -1;
index 4edd083c3d781004e9de6a58cc2024eea7b2b8b0..a310d348589749ccd087ac8fa9825bc84a75e34e 100644 (file)
  * 23-Jul-1999: Fixed small fragment security exposure opened on 15-May-1998.
  *              John McDonald <jm@dataprotect.com>
  *              Thomas Lopatic <tl@dataprotect.com>
+ * 21-Oct-1999: Use bh, not interrupt locking. --RR
+ *              Applied count fix by Emanuele Caratti <wiz@iol.it>
  */
 
 /*
  *
- * The origina Linux port was done Alan Cox, with changes/fixes from
+ * The original Linux port was done Alan Cox, with changes/fixes from
  * Pauline Middlelink, Jos Vos, Thomas Quinot, Wouter Gadeyne, Juan
  * Jose Ciarlante, Bernd Eckenfels, Keith Owens and others.
  * 
@@ -86,6 +88,7 @@
 #include <net/udp.h>
 #include <net/sock.h>
 #include <net/icmp.h>
+#include <net/ip_masq.h>
 #include <linux/netlink.h>
 #include <linux/init.h>
 #include <linux/firewall.h>
 static struct sock *ipfwsk;
 #endif
 
+/* Don't call SLOT_NUMBER when you have a write lock. */
 #ifdef __SMP__
 #define SLOT_NUMBER() (cpu_number_map[smp_processor_id()]*2 + !in_interrupt())
 #else
@@ -190,21 +194,26 @@ do {                                                      \
               __FILE__, __LINE__, SLOT_NUMBER());      \
 } while (0)
 
+#define FWC_NOINT()                                                        \
+do {                                                                       \
+       if (in_interrupt())                                                 \
+               printk("Rusty, you promised! %s %u\n", __FILE__, __LINE__); \
+} while(0)
 #else
 #define FWC_DEBUG_LOCK(d) do { } while(0)
 #define FWC_DEBUG_UNLOCK(d) do { } while(0)
 #define FWC_DONT_HAVE_LOCK(d) do { } while(0)
 #define FWC_HAVE_LOCK(d) do { } while(0)
+#define FWC_NOINT() do { } while(0)
 #endif /*DEBUG_IP_FIRWALL_LOCKING*/
 
+/* We never to a write lock in bh, so we only need write_lock_bh */
 #define FWC_READ_LOCK(l) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock(l); } while (0)
-#define FWC_WRITE_LOCK(l) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock(l); } while (0)
-#define FWC_READ_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_rlocks); read_lock_irqsave(l,f); } while (0)
-#define FWC_WRITE_LOCK_IRQ(l,f) do { FWC_DEBUG_LOCK(fwc_wlocks); write_lock_irqsave(l,f); } while (0)
+/* Debug after lock obtained, (in_interrupt() will be true there), so
+   SLOT_NUMBER consistent. */
+#define FWC_WRITE_LOCK(l) do { FWC_NOINT(); write_lock_bh(l); FWC_DEBUG_LOCK(fwc_wlocks); } while (0)
 #define FWC_READ_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock(l); } while (0)
-#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock(l); } while (0)
-#define FWC_READ_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_rlocks); read_unlock_irqrestore(l,f); } while (0)
-#define FWC_WRITE_UNLOCK_IRQ(l,f) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_irqrestore(l,f); } while (0)
+#define FWC_WRITE_UNLOCK(l) do { FWC_DEBUG_UNLOCK(fwc_wlocks); write_unlock_bh(l); } while (0)
 
 struct ip_chain;
 
@@ -228,6 +237,7 @@ struct ip_reent
 {
        struct ip_chain *prevchain;     /* Pointer to referencing chain */
        struct ip_fwkernel *prevrule;   /* Pointer to referencing rule */
+       unsigned int count;
        struct ip_counters counters;
 };
 
@@ -732,8 +742,8 @@ ip_fw_check(struct iphdr *ip,
        else FWC_HAVE_LOCK(fwc_rlocks);
 
        f = chain->chain;
+       count = 0;
        do {
-               count = 0;
                for (; f; f = f->next) {
                        count++;
                        if (ip_rule_match(f,rif,ip,
@@ -771,10 +781,12 @@ ip_fw_check(struct iphdr *ip,
                                else {
                                        f->branch->reent[slot].prevchain 
                                                = chain;
+                                       f->branch->reent[slot].count = count;
                                        f->branch->reent[slot].prevrule 
                                                = f->next;
                                        chain = f->branch;
                                        f = chain->chain;
+                                       count = 0;
                                }
                        }
                        else if (f->simplebranch == FW_SKIP) 
@@ -793,6 +805,7 @@ ip_fw_check(struct iphdr *ip,
                        if (chain->reent[slot].prevchain) {
                                struct ip_chain *tmp = chain;
                                f = chain->reent[slot].prevrule;
+                               count = chain->reent[slot].count;
                                chain = chain->reent[slot].prevchain;
                                tmp->reent[slot].prevchain = NULL;
                        }
@@ -1303,9 +1316,8 @@ int ip_fw_ctl(int cmd, void *m, int len)
 {
        int ret;
        struct ip_chain *chain;
-       unsigned long flags;
 
-       FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+       FWC_WRITE_LOCK(&ip_fw_lock);
 
        switch (cmd) {
        case IP_FW_FLUSH:
@@ -1329,7 +1341,7 @@ int ip_fw_ctl(int cmd, void *m, int len)
                struct iphdr *ip;
 
                /* Don't need write lock. */
-               FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+               FWC_WRITE_UNLOCK(&ip_fw_lock);
                
                if (len != sizeof(struct ip_fwtest) || !check_label(m))
                        return EINVAL;
@@ -1524,7 +1536,7 @@ int ip_fw_ctl(int cmd, void *m, int len)
                ret = EINVAL;
        }
 
-       FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+       FWC_WRITE_UNLOCK(&ip_fw_lock);
        return ret;
 }
 
@@ -1585,7 +1597,6 @@ static int ip_chain_procinfo(char *buffer, char **start,
 {
        struct ip_chain *i;
        struct ip_fwkernel *j = ip_fw_chains->chain;
-       unsigned long flags;
        int len = 0;
        int last_len = 0;
        off_t upto = 0;
@@ -1594,7 +1605,7 @@ static int ip_chain_procinfo(char *buffer, char **start,
        duprintf("ip_fw_chains is 0x%0lX\n", (unsigned long int)ip_fw_chains);
 
        /* Need a write lock to lock out ``readers'' which update counters. */
-       FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+       FWC_WRITE_LOCK(&ip_fw_lock);
 
        for (i = ip_fw_chains; i; i = i->next) {
            for (j = i->chain; j; j = j->next) {
@@ -1625,7 +1636,7 @@ static int ip_chain_procinfo(char *buffer, char **start,
                }
        }
 outside:
-       FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+       FWC_WRITE_UNLOCK(&ip_fw_lock);
        buffer[len] = '\0';
 
        duprintf("ip_chain_procinfo: Length = %i (of %i).  Offset = %li.\n",
@@ -1641,10 +1652,9 @@ static int ip_chain_name_procinfo(char *buffer, char **start,
        struct ip_chain *i;
        int len = 0,last_len = 0;
        off_t pos = 0,begin = 0;
-       unsigned long flags;
 
        /* Need a write lock to lock out ``readers'' which update counters. */
-       FWC_WRITE_LOCK_IRQ(&ip_fw_lock, flags);
+       FWC_WRITE_LOCK(&ip_fw_lock);
 
        for (i = ip_fw_chains; i; i = i->next)
        {
@@ -1676,7 +1686,7 @@ static int ip_chain_name_procinfo(char *buffer, char **start,
                
                last_len = len;
        }
-       FWC_WRITE_UNLOCK_IRQ(&ip_fw_lock, flags);
+       FWC_WRITE_UNLOCK(&ip_fw_lock);
 
        *start = buffer+(offset-begin);
        len-=(offset-begin);
index e9e88a675c98a998c968dfaef8dd2d434defc6e2..a52e7ed7a5d33f88abba7ef4722cbb67518a952a 100644 (file)
@@ -1152,6 +1152,7 @@ int ip_fw_masquerade(struct sk_buff **skb_p, __u32 maddr)
 
                if (ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos)|RTO_CONN, skb_dev?skb_dev->ifindex:0)) {
                        /* Fallback on old method */
+                       /* This really shouldn't happen... */
                        maddr = inet_select_addr(skb_dev, skb_rt->rt_gateway, RT_SCOPE_UNIVERSE);
                } else {
                        /* Route lookup succeeded */
index ca82fe4385811d153eddd4bab689118a08e502d8..a3f3f575a5514e6be054ed6a0eba108edf4101f9 100644 (file)
@@ -1,6 +1,6 @@
 To join in the work on Linux IPv6 send mail to:
 
-        majordomo@nuclecu.unam.mx
+        majordomo@oss.sgi.com
 
 and in the body of the message include:
 
index 08ce2adced8899bf7e6240419176e30168a45593..3daf33793afb8455d83f861d1f9c36b22c9334b2 100644 (file)
@@ -526,13 +526,13 @@ static int irda_device_net_ioctl(struct device *dev, /* ioctl device */
                break;
        case SIOCSIWNWID:
                /* Set domain */
-               if (wrq->u.nwid.on) {
+               if (!wrq->u.nwid.disabled) {
                        
                } break;
        case SIOCGIWNWID:
                /* Read domain*/
 /*             wrq->u.nwid.nwid = domain; */
-/*             wrq->u.nwid.on = 1; */
+/*             wrq->u.nwid.disabled = 0; */
                break;
        case SIOCGIWENCODE:
                /* Get scramble key */