From f9290a20d0fd418bbab03ab54a1e08dce892776f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:12:49 -0500 Subject: [PATCH] Import 2.1.20 --- Documentation/Configure.help | 46 +- Documentation/networking/00-INDEX | 6 + Documentation/networking/lapb-module.txt | 257 +++ Documentation/networking/x25-iface.txt | 64 + Documentation/networking/x25.txt | 17 +- Makefile | 2 +- arch/i386/defconfig | 1 + arch/i386/kernel/ldt.c | 13 +- arch/i386/kernel/process.c | 195 +- arch/i386/kernel/ptrace.c | 12 +- arch/i386/kernel/signal.c | 62 +- arch/i386/kernel/sys_i386.c | 39 +- arch/sparc/mm/srmmu.c | 1025 ++++----- drivers/block/loop.c | 4 +- drivers/char/Config.in | 21 +- drivers/char/Makefile | 8 +- drivers/char/esp.c | 431 ++-- drivers/char/esp.h | 7 +- drivers/char/tpqic02.c | 2436 +++++++++++----------- drivers/char/tty_io.c | 2 +- drivers/char/wdt.c | 16 + drivers/net/Config.in | 4 + drivers/net/Makefile | 8 + drivers/net/Space.c | 19 - drivers/net/arcnet.c | 5 +- drivers/net/bpqether.c | 3 +- drivers/net/bsd_comp.c | 2 +- drivers/net/de4x5.c | 46 +- drivers/net/defxx.c | 22 +- drivers/net/dlci.c | 5 +- drivers/net/eql.c | 8 +- drivers/net/hdlcdrv.c | 4 +- drivers/net/lapbether.c | 629 ++++++ drivers/net/loopback.c | 5 +- drivers/net/mkiss.c | 7 +- drivers/net/net_init.c | 27 +- drivers/net/pi2.c | 4 +- drivers/net/ppp.c | 5 +- drivers/net/pt.c | 4 +- drivers/net/scc.c | 162 +- drivers/net/sdla.c | 6 +- drivers/net/shaper.c | 5 +- drivers/net/slip.c | 18 +- drivers/net/strip.c | 5 +- drivers/net/tunnel.c | 6 +- drivers/net/wic.c | 81 +- drivers/sbus/char/sunserial.c | 47 +- drivers/scsi/ChangeLog.ncr53c8xx | 52 + drivers/scsi/Config.in | 5 - drivers/scsi/README.ncr53c8xx | 207 +- drivers/scsi/README.st | 19 +- drivers/scsi/ncr53c8xx.c | 774 +++---- drivers/scsi/ncr53c8xx.h | 167 +- drivers/scsi/st.c | 871 ++++---- drivers/scsi/st.h | 23 +- fs/buffer.c | 8 +- fs/dquot.c | 20 +- fs/exec.c | 2 + fs/ext2/ioctl.c | 25 +- fs/ext2/symlink.c | 15 +- fs/isofs/inode.c | 7 +- fs/isofs/rock.c | 2 +- fs/isofs/symlink.c | 11 +- fs/locks.c | 21 +- fs/nfs/symlink.c | 10 +- fs/open.c | 14 +- fs/pipe.c | 11 +- fs/stat.c | 50 +- fs/super.c | 21 +- include/asm-i386/string.h | 4 +- include/asm-i386/uaccess.h | 61 +- include/linux/fddidevice.h | 19 +- include/linux/ipv6.h | 60 +- include/linux/lapb.h | 56 + include/linux/lapbether.h | 19 + include/linux/mm.h | 2 +- include/linux/mtio.h | 3 + include/linux/net_alias.h | 163 +- include/linux/netdevice.h | 74 +- include/linux/netrom.h | 2 + include/linux/tpqic02.h | 85 +- include/linux/tty.h | 2 +- include/net/lapb.h | 121 ++ include/net/lapbcall.h | 2 + include/net/llc.h | 46 +- include/net/ndisc.h | 2 +- include/net/neighbour.h | 6 +- include/net/netbeui.h | 8 +- include/net/sock.h | 4 + include/net/x25.h | 12 +- init/main.c | 11 +- kernel/fork.c | 4 + mm/memory.c | 28 +- mm/mmap.c | 19 +- mm/mremap.c | 2 +- net/802/cl2llc.c | 79 +- net/802/cl2llc.pre | 83 +- net/802/fddi.c | 67 +- net/802/llc_macinit.c | 32 +- net/802/p8022.c | 36 +- net/802/p8022tr.c | 29 +- net/802/p8023.c | 39 +- net/802/sysctl_net_802.c | 11 +- net/802/tr.c | 251 ++- net/Config.in | 1 + net/Makefile | 10 +- net/README | 1 + net/appletalk/aarp.c | 8 +- net/appletalk/ddp.c | 105 +- net/ax25/af_ax25.c | 77 +- net/ax25/ax25_in.c | 10 +- net/ax25/ax25_out.c | 4 +- net/ax25/ax25_route.c | 4 +- net/ax25/ax25_subr.c | 4 +- net/ax25/ax25_timer.c | 4 +- net/core/datagram.c | 5 +- net/core/dev.c | 167 +- net/core/dev_mcast.c | 43 +- net/core/firewall.c | 4 + net/core/neighbour.c | 6 +- net/core/net_alias.c | 1910 +++++++++-------- net/core/scm.c | 18 +- net/core/skbuff.c | 38 +- net/core/sock.c | 113 +- net/ipv4/devinet.c | 4 - net/ipv4/tcp.c | 55 +- net/ipv4/tcp_input.c | 9 +- net/ipv6/addrconf.c | 64 +- net/ipv6/ndisc.c | 73 +- net/ipv6/reassembly.c | 9 +- net/ipx/af_ipx.c | 498 +++-- net/lapb/Makefile | 20 + net/lapb/lapb_iface.c | 409 ++++ net/lapb/lapb_in.c | 799 +++++++ net/lapb/lapb_out.c | 252 +++ net/lapb/lapb_subr.c | 236 +++ net/lapb/lapb_timer.c | 183 ++ net/netbeui/netbeui.c | 214 +- net/netbeui/netbeui_llc.c | 263 ++- net/netrom/af_netrom.c | 172 +- net/netrom/nr_dev.c | 72 +- net/netrom/nr_in.c | 4 +- net/netrom/nr_out.c | 4 +- net/netrom/nr_route.c | 56 +- net/netrom/nr_subr.c | 4 +- net/netrom/nr_timer.c | 4 +- net/protocols.c | 8 + net/rose/af_rose.c | 252 ++- net/rose/rose_dev.c | 67 +- net/rose/rose_in.c | 2 +- net/rose/rose_link.c | 2 +- net/rose/rose_out.c | 2 +- net/rose/rose_route.c | 2 +- net/rose/rose_subr.c | 2 +- net/rose/rose_timer.c | 2 +- net/socket.c | 29 +- net/unix/af_unix.c | 61 +- net/unix/sysctl_net_unix.c | 14 +- net/x25/af_x25.c | 82 +- net/x25/x25_dev.c | 143 +- net/x25/x25_link.c | 62 +- net/x25/x25_route.c | 14 +- 162 files changed, 10302 insertions(+), 5956 deletions(-) create mode 100644 Documentation/networking/lapb-module.txt create mode 100644 Documentation/networking/x25-iface.txt create mode 100644 drivers/net/lapbether.c create mode 100644 include/linux/lapb.h create mode 100644 include/linux/lapbether.h create mode 100644 include/net/lapb.h create mode 100644 include/net/lapbcall.h create mode 100644 net/lapb/Makefile create mode 100644 net/lapb/lapb_iface.c create mode 100644 net/lapb/lapb_in.c create mode 100644 net/lapb/lapb_out.c create mode 100644 net/lapb/lapb_subr.c create mode 100644 net/lapb/lapb_timer.c diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 03fb817f352c..ed2233d79668 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -1638,9 +1638,10 @@ CONFIG_SCSI_NCR53C8XX of PCI-SCSI controllers. This driver supports parity checking, tagged command queuing, fast scsi II transfer up to 10 MB/s with narrow scsi devices and 20 MB/s with wide scsi devices. - Linux/i386 and Linux/Alpha are supported by this driver. - Memory mapped io is currently untested under Linux/Alpha. + Support of Ultra SCSI data transfers with NCR53C860 and NCR53C875 + controllers has been recently added to the driver. Please read drivers/scsi/README.ncr53c8xx for more information. + Linux/i386 and Linux/Alpha are supported by this driver. synchronous data transfers frequency CONFIG_SCSI_NCR53C8XX_SYNC @@ -1702,43 +1703,11 @@ CONFIG_SCSI_NCR53C8XX_MAX_TAGS possible. The default value is 4. Minimum is 2, maximum is 12. The normal answer therefore is the default one. -force asynchronous transfer mode -CONFIG_SCSI_NCR53C8XX_FORCE_ASYNCHRONOUS - This option allows you to force asynchronous transfer mode for all - SCSI devices at linux startup. You can enable synchronous - negotiation with the "setsync" control command after boot-up, for - example: - echo "setsync 2 25" >/proc/scsi/ncr53c8xx/0 - asks the driver to set the period to 25 ns (10MB/sec) for target 2 - of controller 0 (please read drivers/scsi/README.ncr53c8xx for more - information). The safe answer therefore is Y. The normal answer - therefore is N. - -force synchronous negotiation -CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO - Some scsi-2 devices support synchronous negotiations but do not - report this feature in byte 7 of inquiry data. - Answer Y only if you suspect some device to be so humble. - The normal answer therefore is N. - IBMMCA SCSI support CONFIG_SCSI_IBMMCA If your computer sports an MCA bus system architecture (IBM PS/2) with an SCSI harddrive, say Y here. -disable master parity checking -CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK - Some hardware may have problems with parity during master cycles on - PCI bus. Only seen once. Answer Y if you suspect such problem. The - normal answer therefore is N. - -disable scsi parity checking -CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK - Parity on scsi bus is a system option. If one device checks parity, - then all devices on the scsi bus must generate parity. However, the - parity can be ignored by the scsi devices. Answer Y only if you - know what you are doing. The normal answer therefore is N. - Always IN2000 SCSI support CONFIG_SCSI_IN2000 This is support for an ISA bus SCSI host adaptor. You'll find @@ -3697,9 +3666,12 @@ CONFIG_QIC02_TAPE Do you want runtime configuration for QIC-02 CONFIG_QIC02_DYNCONF You can either configure this driver once and for all by editing a - header file, in which case you should say N, or you can fetch a - program via anonymous ftp which is able to configure this driver - during runtime. If you want this, say Y. + header file (include/linux/tpqic02.h), in which case you should + say N, or you can fetch a program via anonymous ftp which is able + to configure this driver during runtime. The program to do this is + called 'qic02conf' and it is part of the 'tpqic02-support-X.Y.tar.gz' + support package. + If you want to use the qic02conf program, say Y. Ftape (QIC-80/Travan) support CONFIG_FTAPE diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index d376f9640fb1..de4382f0d0b8 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -14,6 +14,8 @@ ax25.txt - info on using AX.25 and NET/ROM code for Linux framerelay.txt - info on using Frame Relay/Data Link Connection Identifier (DLCI). +lapb-module.txt + - programming information on the LAPB module. ncsa-telnet - notes on how NCSA telnet (DOS) breaks with MTU discovery enabled. net-modules.txt @@ -28,6 +30,10 @@ tulip.txt - info on using DEC 21040/21041/21140 based PCI ethernet cards. vortex.txt - info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) e'net cards. +x25.txt + - general info on X.25 development. +x25-iface.txt + - description of the X.25 Packet Layer to LAPB device interface. z8530drv.txt - info about Linux driver for Z8530 based HDLC cards for AX.25 diff --git a/Documentation/networking/lapb-module.txt b/Documentation/networking/lapb-module.txt new file mode 100644 index 000000000000..938d09787b4e --- /dev/null +++ b/Documentation/networking/lapb-module.txt @@ -0,0 +1,257 @@ + The Linux LAPB Module Interface 1.3 + + Jonathan Naylor 29.12.96 + +The LAPB module will be a seperately compiled module for use by any parts of +the Linux operating system that require a LAPB service. This document +defines the interfaces to, and the services provided by this module. The +term module in this context does not imply that the LAPB module is a +seperately loadable module, although it may be. The term module is used in +its more standard meaning. + +The interface to the LAPB module consists of functions to the module, +callbacks from the module to indicate important state changes, and +structures for getting and setting information about the module. + +Structures +---------- + +Probably the most important structure is the skbuff structure for holding +received and transmitted data, however it is beyond the scope of this +document. + +The two LAPB specific structures are the LAPB initialisation structure and +the LAPB parameter structure. These will be defined in a standard header +file, . The header file is internal to the LAPB +module and is not for use. + +LAPB Initialisation Structure +----------------------------- + +This structure is used only once, in the call to lapb_register (see below). +It contains information about the device driver that requires the services +of the LAPB module. + +struct lapb_register_struct { + void (*connect_confirmation)(int token, int reason); + void (*connect_indication)(int token, int reason); + void (*disconnect_confirmation)(int token, int reason); + void (*disconnect_indication)(int token, int reason); + void (*data_indication)(int token, struct sk_buff *skb); + void (*data_transmit)(int token, struct sk_buff *skb); +}; + +Each member of this structure corresponds to a function in the device driver +that is called when a particular event in the LAPB module occurs. These will +be described in detail below. If a callback is not required (!!) then a NULL +may be substituted. + + +LAPB Parameter Structure +------------------------ + +This structure is used with the lapb_getparms and lapb_setparms functions +(see below). They are used to allow the device driver to get and set the +operational parameters of the LAPB implementation for a given connection. + +struct lapb_parms_struct { + unsigned int t1; + unsigned int t1timer; + unsigned int t2; + unsigned int t2timer; + unsigned int n2; + unsigned int n2count; + unsigned int window; + unsigned int state; + unsigned int mode; +}; + +T1 and T2 are protocol timing parameters and are given in units of 100ms. N2 +is the maximum number of tries on the link before it is declared a failure. +The window size is the maximum number of outstanding data packets allowed to +be unacknowledged by the remote end, the value of the window is between 1 +and 7 for a standard LAPB link, and between 1 and 127 for an extended LAPB +link. + +The mode variable is a bit field is used for setting (at present) three values. +The bit fields have the following meanings: + +Bit Meaning +0 LAPB operation (0=LAPB_STANDARD 1=LAPB_EXTENDED). +1 [SM]LP operation (0=LAPB_SLP 1=LAPB=MLP). +2 DTE/DCE operation (0=LAPB_DTE 1=LAPB_DCE) +3-31 Reserved, must be 0. + +Extended LAPB operation indicates the use of extended sequence numbers and +consequently larger window sizes, the default is standard LAPB operation. +MLP operation is the same as SLP operation except that the addresses used by +LAPB are different to indicate the mode of operation, the default is Single +Link Procedure. The difference between DCE and DTE operation is (i) the +addresses used for commands and responses, and (ii) when the DCE is not +connected, it sends DM without polls set, every T1. The upper case constant +names will be defined in the public LAPB header file. + + +Functions +--------- + +The LAPB module provides a number of function entry points. + + +int lapb_register(void *token, struct lapb_register_struct); + +This must be called before the LAPB module may be used. If the call is +successful then LAPB_OK is returned. The token must be a unique identifier +generated by the device driver to allow for the unique identification of the +instance of the LAPB link. It is returned by the LAPB module in all of the +callbacks, and is used by the device driver in all calls to the LAPB module. +For multiple LAPB links in a single device driver, multiple calls to +lapb_register must be made. The format of the lapb_register_struct is given +above. The return values are: + +LAPB_OK LAPB registered successfully. +LAPB_BADTOKEN Token is already registered. +LAPB_NOMEM Out of memory + + +int lapb_unregister(void *token); + +This releases all the resources associated with a LAPB link. Any current +LAPB link will be abandoned without further messages being passed. After +this call, the value of token is no longer valid for any calls to the LAPB +function. The valid return values are: + +LAPB_OK LAPB unregistered successfully. +LAPB_BADTOKEN Invalid/unknown LAPB token. + + +int lapb_getparms(void *token, struct lapb_parms_struct *parms); + +This allows the device driver to get the values of the current LAPB +variables, the lapb_parms_struct is described above. The valid return values +are: + +LAPB_OK LAPB getparms was successful. +LAPB_BADTOKEN Invalid/unknown LAPB token. + + +int lapb_setparms(void *token, struct lapb_parms_struct *parms); + +This allows the device driver to set the values of the current LAPB +variables, the lapb_parms_struct is described above. The values of t1timer, +t2timer and n2count are ignored, likewise changing the mode bits when +connected will be ignored. An error implies that none of the values have +been changed. The valid return values are: + +LAPB_OK LAPB getparms was successful. +LAPB_BADTOKEN Invalid/unknown LAPB token. +LAPB_INVALUE One of the values was out of its allowable range. + + +int lapb_connect_request(void *token); + +Initiate a connect using the current parameter settings. The valid return +values are: + +LAPB_OK LAPB is starting to connect. +LAPB_BADTOKEN Invalid/unknown LAPB token. +LAPB_CONNECTED LAPB module is already connected. + + +int lapb_disconnect_request(void *token); + +Initiate a disconnect. The valid return values are: + +LAPB_OK LAPB is starting to disconnect. +LAPB_BADTOKEN Invalid/unknown LAPB token. +LAPB_NOTCONNECTED LAPB module is not connected. + + +int lapb_data_request(void *token, struct sk_buff *skb); + +Queue data with the LAPB module for transmitting over the link. If the call +is successful then the skbuff is owned by the LAPB module and may not be +used by the device driver again. The valid return values are: + +LAPB_OK LAPB has accepted the data. +LAPB_BADTOKEN Invalid/unknown LAPB token. +LAPB_NOTCONNECTED LAPB module is not connected. + + +int lapb_data_received(void *token, struct sk_buff *skb); + +Queue data with the LAPB module which has been received from the device. It +is expected that the data passed to the LAPB module has skb->data pointing +to the beginning of the LAPB data. If the call is successful then the skbuff +is owned by the LAPB module and may not be used by the device driver again. +The valid return values are: + +LAPB_OK LAPB has accepted the data. +LAPB_BADTOKEN Invalid/unknown LAPB token. + + +Callbacks +--------- + +These callbacks are functions provided by the device driver for the LAPB +module to call when an event occurs. They are registered with the LAPB +module with lapb_register (see above) in the structure lapb_register_struct +(see above). + + +void (*connect_confirmation)(void *token, int reason); + +This is called by the LAPB module when a connection is established after +being requested by a call to lapb_connect_request (see above). The reason is +always LAPB_OK. + + +void (*connect_indication)(void *token, int reason); + +This is called by the LAPB module when the link is established by the remote +system. The value of reason is always LAPB_OK. + + +void (*disconnect_confirmation)(void *token, int reason); + +This is called by the LAPB module when an event occurs after the device +driver has called lapb_disconnect_request (see above). The reason indicates +what has happended. In all cases the LAPB link can be regarded as being +terminated. The values for reason are: + +LAPB_OK The LAPB link was terminated normally. +LAPB_NOTCONNECTED The remote system was not connected. +LAPB_TIMEDOUT No response was received in N2 tries from the remote + system. + + +void (*disconnect_indication)(void *token, int reason); + +This is called by the LAPB module when the link is terminated by the remote +system or another event has occurred to terminate the link. This may be +returned in response to a lapb_connect_request (see above) if the remote +system refused the request. The values for reason are: + +LAPB_OK The LAPB link was terminated normally by the remote + system. +LAPB_REFUSED The remote system refused the connect request. +LAPB_NOTCONNECTED The remote system was not connected. +LAPB_TIMEDOUT No response was received in N2 tries from the remote + system. + + +void (*data_indication)(void *token, struct sk_buff *skb); + +This is called by the LAPB module when data has been received from the +remote system that should be passed onto the next layer in the protocol +stack. The skbuff becomes the property of the device driver and the LAPB +module will not perform any more actions on it. The skb->data pointer will +be pointing to the first byte of data after the LAPB header. + + +void (*data_transmit)(void *token, struct sk_buff *skb); + +This is called by the LAPB module when data is to be transmitted to the +remote system by the device driver. The skbuff becomes the property of the +device driver and the LAPB module will not perform any more actions on it. +The skb->data pointer will be pointing to the first byte of the LAPB header. diff --git a/Documentation/networking/x25-iface.txt b/Documentation/networking/x25-iface.txt new file mode 100644 index 000000000000..fab57cf3240a --- /dev/null +++ b/Documentation/networking/x25-iface.txt @@ -0,0 +1,64 @@ + X.25 Device Driver Interface 1.1 + + Jonathan Naylor 26.12.96 + +This is a description of the messages to be passed between the X.25 Packet +Layer and the X.25 device driver. They are designed to allow for the easy +setting of the LAPB mode from within the Packet Layer. + +The X.25 device driver will be coded normally as per the Linux device driver +standards, most X.25 device drivers will be moderately similar to the +already existing Eethernet device drivers. However unlike those drivers, the +X.25 device driver has a state associated with it, and this information +needs to be passed to and from the Packet Layer for proper operation. + +All messages are held in sk_buff's just like real data to be transmitted +over the LAPB link. The first byte of the skbuff indicates the meaning of +the rest of the skbuff, if any more information does exist. + + +Packet Layer to Device Driver +----------------------------- + +First Byte = 0x00 + +This indicates that the rest of the skbuff contains data to be transmitted +over the LAPB link. The LAPB link should already exist before any data is +passed down. + +First Byte = 0x01 + +Establish the LAPB link. If the link is already established then the connect +confirmation message should be returned as soon as possible. + +First Byte = 0x02 + +Terminate the LAPB link. If it is already disconnected then the disconnect +confirmation message should be returned as soon as possible. + +First Byte = 0x03 + +LAPB parameters. To be defined. + + +Device Driver to Packet Layer +----------------------------- + +First Byte = 0x00 + +This indicates that the rest of the skbuff contains data that has been +received over the LAPB link. + +First Byte = 0x01 + +LAPB link has been established. The same message is used for both a LAPB +link connect_confirmation and a connect_indication. + +First Byte = 0x02 + +LAPB link has been terminated. This same message is used for both a LAPB +link disconnect_confirmation and a disconnect_indication. + +First Byte = 0x03 + +LAPB parameters. To be defined. diff --git a/Documentation/networking/x25.txt b/Documentation/networking/x25.txt index 91d9362c3219..09681d565f6c 100644 --- a/Documentation/networking/x25.txt +++ b/Documentation/networking/x25.txt @@ -25,12 +25,19 @@ by a different author. Just when you thought that it could not become more confusing, another option appeared, XOT. This allows X.25 Packet Layer frames to operate over -the Internet using TCP/IP as a reliable link layer. An RFC exists that -specifies the format and behaviour of the protocol. If time permits this -option will also be actively considered. +the Internet using TCP/IP as a reliable link layer. RFC1613 specifies the +format and behaviour of the protocol. If time permits this option will also +be actively considered. -It is hoped that a linux-x25 mailing list will be opened soon to allow for -the discussion of Linux X.25, but it hasn't appeared yet. +A linux-x25 mailing list has been created at vger.rutgers.edu to support the +development and use of Linux X.25. It is early days yet, but interested +parties are welcome to subscribe to it. Just send a message to +Majordomo@vger.rutgers.edu with the following in the message body: + +subscribe linux-x25 +end + +The contents of the Subject line are ignored. Jonathan diff --git a/Makefile b/Makefile index 4e38bc09ab0a..a93252e5b15b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 19 +SUBLEVEL = 20 ARCH = i386 diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 39d54ad7d2ec..09c6979364bc 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -117,6 +117,7 @@ CONFIG_EL3=y # CONFIG_PLIP is not set # CONFIG_PPP is not set # CONFIG_NET_RADIO is not set +# CONFIG_LAPBETHER is not set # CONFIG_SLIP is not set # CONFIG_TR is not set diff --git a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c index 5007d1db928f..5d46baae5d10 100644 --- a/arch/i386/kernel/ldt.c +++ b/arch/i386/kernel/ldt.c @@ -16,7 +16,6 @@ static int read_ldt(void * ptr, unsigned long bytecount) { - int error; void * address = current->ldt; unsigned long size; @@ -29,11 +28,7 @@ static int read_ldt(void * ptr, unsigned long bytecount) } if (size > bytecount) size = bytecount; - error = verify_area(VERIFY_WRITE, ptr, size); - if (error) - return error; - copy_to_user(ptr, address, size); - return size; + return copy_to_user(ptr, address, size) ? -EFAULT : size; } static inline int limits_ok(struct modify_ldt_ldt_s *ldt_info) @@ -69,11 +64,9 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode) if (bytecount != sizeof(ldt_info)) return -EINVAL; - error = verify_area(VERIFY_READ, ptr, sizeof(ldt_info)); + error = copy_from_user(&ldt_info, ptr, sizeof(ldt_info)); if (error) - return error; - - copy_from_user(&ldt_info, ptr, sizeof(ldt_info)); + return -EFAULT; if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES) return -EINVAL; diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 73e52b396394..ace895956d93 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -184,16 +184,90 @@ int cpu_idle(void *unused) */ static long no_idt[2] = {0, 0}; static int reboot_mode = 0; +static int reboot_thru_bios = 0; void reboot_setup(char *str, int *ints) { - int mode = 0; + while(1) { + switch (*str) { + case 'w': /* "warm" reboot (no memory testing etc) */ + reboot_mode = 0x1234; + break; + case 'c': /* "cold" reboot (with memory testing etc) */ + reboot_mode = 0x0; + break; + case 'b': /* "bios" reboot by jumping through the BIOS */ + reboot_thru_bios = 1; + break; + case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */ + reboot_thru_bios = 0; + break; + } + if((str = strchr(str,',')) != NULL) + str++; + else + break; + } +} + - /* "w" for "warm" reboot (no memory testing etc) */ - if (str[0] == 'w') - mode = 0x1234; - reboot_mode = mode; +/* The following code and data reboots the machine by switching to real + mode and jumping to the BIOS reset entry point, as if the CPU has + really been reset. The previous version asked the keyboard + controller to pulse the CPU reset line, which is more thorough, but + doesn't work with at least one type of 486 motherboard. It is easy + to stop this code working; hence the copious comments. */ + +unsigned long long +real_mode_gdt_entries [3] = +{ + 0x0000000000000000ULL, /* Null descriptor */ + 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */ + 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */ +}; + +struct +{ + unsigned short size __attribute__ ((packed)); + unsigned long long * base __attribute__ ((packed)); } +real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries }, +real_mode_idt = { 0x3ff, 0 }; + +/* This is 16-bit protected mode code to disable paging and the cache, + switch to real mode and jump to the BIOS reset code. + + The instruction that switches to real mode by writing to CR0 must be + followed immediately by a far jump instruction, which set CS to a + valid value for real mode, and flushes the prefetch queue to avoid + running instructions that have already been decoded in protected + mode. + + Clears all the flags except ET, especially PG (paging), PE + (protected-mode enable) and TS (task switch for coprocessor state + save). Flushes the TLB after paging has been disabled. Sets CD and + NW, to disable the cache on a 486, and invalidates the cache. This + is more like the state of a 486 after reset. I don't know if + something else should be done for other chips. + + More could be done here to set up the registers as if a CPU reset had + occurred; hopefully real BIOSes don't assume much. */ + +unsigned char real_mode_switch [] = +{ + 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */ + 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */ + 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */ + 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ + 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */ + 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */ + 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */ + 0x74, 0x02, /* jz f */ + 0x0f, 0x08, /* invd */ + 0x24, 0x10, /* f: andb $0x10,al */ + 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */ + 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */ +}; static inline void kb_wait(void) { @@ -204,22 +278,107 @@ static inline void kb_wait(void) break; } -void hard_reset_now(void) +void hard_reset_now (void) { - int i, j; - - sti(); - *((unsigned short *)__va(0x472)) = reboot_mode; - for (;;) { - for (i=0; i<100; i++) { - kb_wait(); - for(j = 0; j < 100000 ; j++) - /* nothing */; - outb(0xfe,0x64); /* pulse reset low */ - udelay(100); + + if(!reboot_thru_bios) { + sti(); + /* rebooting needs to touch the page at absolute addr 0 */ + pg0[0] = 7; + *((unsigned short *)0x472) = reboot_mode; + for (;;) { + int i; + for (i=0; i<100; i++) { + int j; + kb_wait(); + for(j = 0; j < 100000 ; j++) + /* nothing */; + outb(0xfe,0x64); /* pulse reset low */ + udelay(10); + } + __asm__ __volatile__("\tlidt %0": "=m" (no_idt)); } - __asm__ __volatile__("\tlidt %0": "=m" (no_idt)); } + + cli (); + + /* Write zero to CMOS register number 0x0f, which the BIOS POST + routine will recognize as telling it to do a proper reboot. (Well + that's what this book in front of me says -- it may only apply to + the Phoenix BIOS though, it's not clear). At the same time, + disable NMIs by setting the top bit in the CMOS address register, + as we're about to do peculiar things to the CPU. I'm not sure if + `outb_p' is needed instead of just `outb'. Use it to be on the + safe side. */ + + outb_p (0x8f, 0x70); + outb_p (0x00, 0x71); + + /* Remap the kernel at virtual address zero, as well as offset zero + from the kernel segment. This assumes the kernel segment starts at + virtual address 0xc0000000. */ + + memcpy (swapper_pg_dir, swapper_pg_dir + 768, + sizeof (swapper_pg_dir [0]) * 256); + + /* Make sure the first page is mapped to the start of physical memory. + It is normally not mapped, to trap kernel NULL pointer dereferences. */ + + pg0 [0] = 7; + + /* Use `swapper_pg_dir' as our page directory. Don't bother with + `SET_PAGE_DIR' because interrupts are disabled and we're rebooting. + This instruction flushes the TLB. */ + + __asm__ __volatile__ ("movl %0,%%cr3" : : "a" (swapper_pg_dir) : "memory"); + + /* Write 0x1234 to absolute memory location 0x472. The BIOS reads + this on booting to tell it to "Bypass memory test (also warm + boot)". This seems like a fairly standard thing that gets set by + REBOOT.COM programs, and the previous reset routine did this + too. */ + + *((unsigned short *)0x472) = reboot_mode; + + /* For the switch to real mode, copy some code to low memory. It has + to be in the first 64k because it is running in 16-bit mode, and it + has to have the same physical and virtual address, because it turns + off paging. Copy it near the end of the first page, out of the way + of BIOS variables. */ + + memcpy ((void *) (0x1000 - sizeof (real_mode_switch)), + real_mode_switch, sizeof (real_mode_switch)); + + /* Set up the IDT for real mode. */ + + __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt)); + + /* Set up a GDT from which we can load segment descriptors for real + mode. The GDT is not used in real mode; it is just needed here to + prepare the descriptors. */ + + __asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt)); + + /* Load the data segment registers, and thus the descriptors ready for + real mode. The base address of each segment is 0x100, 16 times the + selector value being loaded here. This is so that the segment + registers don't have to be reloaded after switching to real mode: + the values are consistent for real mode operation already. */ + + __asm__ __volatile__ ("movw $0x0010,%%ax\n" + "\tmovw %%ax,%%ds\n" + "\tmovw %%ax,%%es\n" + "\tmovw %%ax,%%fs\n" + "\tmovw %%ax,%%gs\n" + "\tmovw %%ax,%%ss" : : : "eax"); + + /* Jump to the 16-bit code that we copied earlier. It disables paging + and the cache, switches to real mode, and jumps to the BIOS reset + entry point. */ + + __asm__ __volatile__ ("ljmp $0x0008,%0" + : + : "i" ((void *) (0x1000 - sizeof (real_mode_switch)))); } void show_regs(struct pt_regs * regs) diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index 5ea636092761..e6f44243e7fe 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -421,24 +421,17 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) res = read_long(child, addr, &tmp); if (res < 0) return res; - res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); - if (!res) - put_user(tmp,(unsigned long *) data); - return res; + return put_user(tmp,(unsigned long *) data); } /* read the word at location addr in the USER area. */ case PTRACE_PEEKUSR: { unsigned long tmp; - int res; if ((addr & 3) || addr < 0 || addr > sizeof(struct user) - 3) return -EIO; - res = verify_area(VERIFY_WRITE, (void *) data, sizeof(long)); - if (res) - return res; tmp = 0; /* Default return condition */ if(addr < 17*sizeof(long)) tmp = getreg(child, addr); @@ -448,8 +441,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) addr = addr >> 2; tmp = child->debugreg[addr]; }; - put_user(tmp,(unsigned long *) data); - return 0; + return put_user(tmp,(unsigned long *) data); } /* when I and D space are separate, this will have to be fixed. */ diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index f90cbca05c37..14cd82f6642b 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c @@ -181,50 +181,56 @@ static void setup_frame(struct sigaction * sa, if ((regs->xss & 0xffff) != USER_DS && sa->sa_restorer) frame = (unsigned long *) sa->sa_restorer; frame -= 64; - if (verify_area(VERIFY_WRITE,frame,64*4)) + if (!access_ok(VERIFY_WRITE,frame,64*4)) do_exit(SIGSEGV); /* set up the "normal" stack seen by the signal handler (iBCS2) */ #define __CODE ((unsigned long)(frame+24)) #define CODE(x) ((unsigned long *) ((x)+__CODE)) - if (put_user(__CODE,frame)) + + /* XXX Can possible miss a SIGSEGV when frame crosses a page border + and a thread unmaps it while we are accessing it. + So either check all put_user() calls or don't do it at all. + We use __put_user() here because the access_ok() call was already + done earlier. */ + if (__put_user(__CODE,frame)) do_exit(SIGSEGV); if (current->exec_domain && current->exec_domain->signal_invmap) - put_user(current->exec_domain->signal_invmap[signr], frame+1); + __put_user(current->exec_domain->signal_invmap[signr], frame+1); else - put_user(signr, frame+1); + __put_user(signr, frame+1); { unsigned int tmp = 0; #define PUT_SEG(seg, mem) \ -__asm__("mov %%" #seg",%w0":"=r" (tmp):"0" (tmp)); put_user(tmp,mem); +__asm__("mov %%" #seg",%w0":"=r" (tmp):"0" (tmp)); __put_user(tmp,mem); PUT_SEG(gs, frame+2); PUT_SEG(fs, frame+3); } - put_user(regs->xes, frame+4); - put_user(regs->xds, frame+5); - put_user(regs->edi, frame+6); - put_user(regs->esi, frame+7); - put_user(regs->ebp, frame+8); - put_user(regs->esp, frame+9); - put_user(regs->ebx, frame+10); - put_user(regs->edx, frame+11); - put_user(regs->ecx, frame+12); - put_user(regs->eax, frame+13); - put_user(current->tss.trap_no, frame+14); - put_user(current->tss.error_code, frame+15); - put_user(regs->eip, frame+16); - put_user(regs->xcs, frame+17); - put_user(regs->eflags, frame+18); - put_user(regs->esp, frame+19); - put_user(regs->xss, frame+20); - put_user((unsigned long) save_i387((struct _fpstate *)(frame+32)),frame+21); + __put_user(regs->xes, frame+4); + __put_user(regs->xds, frame+5); + __put_user(regs->edi, frame+6); + __put_user(regs->esi, frame+7); + __put_user(regs->ebp, frame+8); + __put_user(regs->esp, frame+9); + __put_user(regs->ebx, frame+10); + __put_user(regs->edx, frame+11); + __put_user(regs->ecx, frame+12); + __put_user(regs->eax, frame+13); + __put_user(current->tss.trap_no, frame+14); + __put_user(current->tss.error_code, frame+15); + __put_user(regs->eip, frame+16); + __put_user(regs->xcs, frame+17); + __put_user(regs->eflags, frame+18); + __put_user(regs->esp, frame+19); + __put_user(regs->xss, frame+20); + __put_user((unsigned long) save_i387((struct _fpstate *)(frame+32)),frame+21); /* non-iBCS2 extensions.. */ - put_user(oldmask, frame+22); - put_user(current->tss.cr2, frame+23); + __put_user(oldmask, frame+22); + __put_user(current->tss.cr2, frame+23); /* set up the return code... */ - put_user(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */ - put_user(0x80cd0000, CODE(4)); /* int $0x80 */ - put_user(__NR_sigreturn, CODE(2)); + __put_user(0x0000b858, CODE(0)); /* popl %eax ; movl $,%eax */ + __put_user(0x80cd0000, CODE(4)); /* int $0x80 */ + __put_user(__NR_sigreturn, CODE(2)); #undef __CODE #undef CODE diff --git a/arch/i386/kernel/sys_i386.c b/arch/i386/kernel/sys_i386.c index 619b372d8722..8ff3753e0a4e 100644 --- a/arch/i386/kernel/sys_i386.c +++ b/arch/i386/kernel/sys_i386.c @@ -28,15 +28,12 @@ asmlinkage int sys_pipe(unsigned long * fildes) int fd[2]; int error; - error = verify_area(VERIFY_WRITE,fildes,8); - if (error) - return error; error = do_pipe(fd); - if (error) - return error; - put_user(fd[0],0+fildes); - put_user(fd[1],1+fildes); - return 0; + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; } /* @@ -60,10 +57,8 @@ asmlinkage int old_mmap(struct mmap_arg_struct *arg) struct file * file = NULL; struct mmap_arg_struct a; - error = verify_area(VERIFY_READ, arg, sizeof(*arg)); - if (error) - return error; - copy_from_user(&a, arg, sizeof(a)); + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; if (!(a.flags & MAP_ANONYMOUS)) { if (a.fd >= NR_OPEN || !(file = current->files->fd[a.fd])) return -EBADF; @@ -110,12 +105,10 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, return sys_semget (first, second, third); case SEMCTL: { union semun fourth; - int err; if (!ptr) return -EINVAL; - if ((err = verify_area (VERIFY_READ, ptr, sizeof(long)))) - return err; - get_user(fourth.__pad, (void **) ptr); + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; return sys_semctl (first, second, third, fourth); } default: @@ -130,12 +123,11 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, switch (version) { case 0: { struct ipc_kludge tmp; - int err; if (!ptr) return -EINVAL; - if ((err = verify_area (VERIFY_READ, ptr, sizeof(tmp)))) - return err; - copy_from_user(&tmp,(struct ipc_kludge *) ptr, sizeof (tmp)); + if (copy_from_user(&tmp,(struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; return sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); } case 1: default: @@ -155,14 +147,11 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, case 0: default: { ulong raddr; int err; - if ((err = verify_area(VERIFY_WRITE, (ulong*) third, sizeof(ulong)))) - return err; err = sys_shmat (first, (char *) ptr, second, &raddr); if (err) return err; - put_user (raddr, (ulong *) third); - return 0; - } + return put_user (raddr, (ulong *) third); + } case 1: /* iBCS2 emulator entry point */ if (get_fs() != get_ds()) return -EINVAL; diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 20efc8a1bf48..5e3cdb723a6c 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.122 1996/12/30 06:16:31 davem Exp $ +/* $Id: srmmu.c,v 1.123 1996/12/31 09:53:00 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -53,6 +53,14 @@ extern void smp_release(void); #define smp_release() #endif /* !(__SMP__) */ +#ifdef __SMP__ +#define FLUSH_BEGIN(mm) +#define FLUSH_END +#else +#define FLUSH_BEGIN(mm) if((mm)->context != NO_CONTEXT) { +#define FLUSH_END } +#endif + /* #define USE_CHUNK_ALLOC 1 */ static unsigned long (*mmu_getpage)(void); @@ -149,16 +157,12 @@ static inline unsigned long srmmu_p2v(unsigned long paddr) */ static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value) { - __asm__ __volatile__("swap [%2], %0\n\t" : - "=&r" (value) : - "0" (value), "r" (addr)); + __asm__ __volatile__("swap [%2], %0" : "=&r" (value) : "0" (value), "r" (addr)); return value; } /* Functions really use this, not srmmu_swap directly. */ -#define srmmu_set_entry(ptr, newentry) \ - srmmu_swap((unsigned long *) (ptr), (newentry)) - +#define srmmu_set_entry(ptr, newentry) srmmu_swap((unsigned long *) (ptr), (newentry)) /* The very generic SRMMU page table operations. */ static unsigned int srmmu_pmd_align(unsigned int addr) { return SRMMU_PMD_ALIGN(addr); } @@ -279,17 +283,15 @@ static pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address) static void srmmu_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { if(tsk->mm->context != NO_CONTEXT) { - flush_cache_mm(current->mm); + flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); - flush_tlb_mm(current->mm); + flush_tlb_mm(tsk->mm); } } static unsigned long srmmu_getpage(void) { - unsigned long page = get_free_page(GFP_KERNEL); - - return page; + return get_free_page(GFP_KERNEL); } static inline void srmmu_putpage(unsigned long page) @@ -688,15 +690,23 @@ static void srmmu_set_pte_cacheable(pte_t *ptep, pte_t pteval) static void srmmu_set_pte_nocache_hyper(pte_t *ptep, pte_t pteval) { - volatile unsigned long clear; - unsigned long flags; + unsigned long page = ((unsigned long)ptep) & PAGE_MASK; - save_and_cli(flags); srmmu_set_entry(ptep, pte_val(pteval)); - if(srmmu_hwprobe(((unsigned long)ptep)&PAGE_MASK)) - hyper_flush_cache_page(((unsigned long)ptep) & PAGE_MASK); - clear = srmmu_get_fstatus(); - restore_flags(flags); + __asm__ __volatile__(" + lda [%0] %2, %%g4 + orcc %%g4, 0x0, %%g0 + be 2f + sethi %%hi(%7), %%g5 +1: subcc %%g5, %6, %%g5 ! hyper_flush_cache_page + bne 1b + sta %%g0, [%1 + %%g5] %3 + lda [%4] %5, %%g0 +2:" : /* no outputs */ + : "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE), + "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS), + "r" (vac_line_size), "i" (PAGE_SIZE) + : "g4", "g5"); } static void srmmu_set_pte_nocache_cypress(pte_t *ptep, pte_t pteval) @@ -867,6 +877,9 @@ static void srmmu_unlockarea(char *vaddr, unsigned long len) { } +/* This is used in many routines below. */ +#define UWINMASK_OFFSET (const unsigned long)(&(((struct task_struct *)0)->tss.uwinmask)) + /* On the SRMMU we do not have the problems with limited tlb entries * for mapping kernel pages, so we just take things from the free page * pool. As a side effect we are putting a little too much pressure @@ -916,42 +929,29 @@ static void tsunami_flush_cache_all(void) static void tsunami_flush_cache_mm(struct mm_struct *mm) { -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - tsunami_flush_icache(); - tsunami_flush_dcache(); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(mm) + flush_user_windows(); + tsunami_flush_icache(); + tsunami_flush_dcache(); + FLUSH_END } static void tsunami_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - tsunami_flush_icache(); - tsunami_flush_dcache(); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(mm) + flush_user_windows(); + tsunami_flush_icache(); + tsunami_flush_dcache(); + FLUSH_END } static void tsunami_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { -#ifndef __SMP__ - struct mm_struct *mm = vma->vm_mm; - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - tsunami_flush_icache(); - tsunami_flush_dcache(); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(vma->vm_mm) + flush_user_windows(); + tsunami_flush_icache(); + tsunami_flush_dcache(); + FLUSH_END } static void tsunami_flush_cache_page_to_uncache(unsigned long page) @@ -982,55 +982,42 @@ static void tsunami_flush_sig_insns(struct mm_struct *mm, unsigned long insn_add static void tsunami_flush_tlb_all(void) { - module_stats.invall++; srmmu_flush_whole_tlb(); + module_stats.invall++; } static void tsunami_flush_tlb_mm(struct mm_struct *mm) { + FLUSH_BEGIN(mm) + srmmu_flush_whole_tlb(); module_stats.invmm++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - srmmu_flush_whole_tlb(); -#ifndef __SMP__ - } -#endif + FLUSH_END } static void tsunami_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { + FLUSH_BEGIN(mm) + srmmu_flush_whole_tlb(); module_stats.invrnge++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - srmmu_flush_whole_tlb(); -#ifndef __SMP__ - } -#endif + FLUSH_END } static void tsunami_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { - int octx; struct mm_struct *mm = vma->vm_mm; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - unsigned long flags; - - save_and_cli(flags); - octx = srmmu_get_context(); - - srmmu_set_context(mm->context); - srmmu_flush_tlb_page(page); - srmmu_set_context(octx); - restore_flags(flags); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(mm) + __asm__ __volatile__(" + lda [%0] %3, %%g5 + sta %1, [%0] %3 + sta %%g0, [%2] %4 + sta %%g5, [%0] %3" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK), + "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE) + : "g5"); module_stats.invpg++; + FLUSH_END } static void tsunami_flush_tlb_page_for_cbit(unsigned long page) @@ -1051,41 +1038,28 @@ static void swift_flush_cache_all(void) static void swift_flush_cache_mm(struct mm_struct *mm) { -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - swift_idflash_clear(); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(mm) + flush_user_windows(); + swift_idflash_clear(); + FLUSH_END } static void swift_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - swift_idflash_clear(); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(mm) + flush_user_windows(); + swift_idflash_clear(); + FLUSH_END } static void swift_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { -#ifndef __SMP__ - struct mm_struct *mm = vma->vm_mm; - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - if(vma->vm_flags & VM_EXEC) - swift_flush_icache(); - swift_flush_dcache(); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(vma->vm_mm) + flush_user_windows(); + if(vma->vm_flags & VM_EXEC) + swift_flush_icache(); + swift_flush_dcache(); + FLUSH_END } /* Not copy-back on swift. */ @@ -1115,36 +1089,32 @@ static void swift_flush_cache_page_to_uncache(unsigned long page) static void swift_flush_tlb_all(void) { - module_stats.invall++; srmmu_flush_whole_tlb(); + module_stats.invall++; } static void swift_flush_tlb_mm(struct mm_struct *mm) { + FLUSH_BEGIN(mm) + srmmu_flush_whole_tlb(); module_stats.invmm++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) -#endif - srmmu_flush_whole_tlb(); + FLUSH_END } static void swift_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { + FLUSH_BEGIN(mm) + srmmu_flush_whole_tlb(); module_stats.invrnge++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) -#endif - srmmu_flush_whole_tlb(); + FLUSH_END } static void swift_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { -#ifndef __SMP__ - struct mm_struct *mm = vma->vm_mm; - if(mm->context != NO_CONTEXT) -#endif - srmmu_flush_whole_tlb(); + FLUSH_BEGIN(vma->vm_mm) + srmmu_flush_whole_tlb(); module_stats.invpg++; + FLUSH_END } static void swift_flush_tlb_page_for_cbit(unsigned long page) @@ -1296,82 +1266,114 @@ static unsigned long viking_no_mxcc_getpage(void) static void viking_flush_tlb_all(void) { - module_stats.invall++; - flush_user_windows(); + register int ctr asm("g5"); + + ctr = 0; + __asm__ __volatile__(" + 1: ld [%%g6 + %2], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %0, 1, %0 + bne 1b + save %%sp, -64, %%sp + 2: subcc %0, 1, %0 + bne 2b + restore %%g0, %%g0, %%g0" + : "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4"); srmmu_flush_whole_tlb(); + module_stats.invall++; } static void viking_flush_tlb_mm(struct mm_struct *mm) { - int octx; + register int ctr asm("g5"); + + FLUSH_BEGIN(mm) + ctr = 0; + __asm__ __volatile__(" +1: ld [%%g6 + %7], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %0, 1, %0 + bne 1b + save %%sp, -64, %%sp +2: subcc %0, 1, %0 + bne 2b + restore %%g0, %%g0, %%g0 + lda [%1] %4, %0 + sta %3, [%1] %4 + sta %%g0, [%2] %5 + sta %0, [%1] %4" + : "=&r" (ctr) + : "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context), + "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE), "0" (ctr), + "i" (UWINMASK_OFFSET) + : "g4"); module_stats.invmm++; - -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - srmmu_flush_tlb_ctx(); - srmmu_set_context(octx); -#ifndef __SMP__ - } -#endif + FLUSH_END } static void viking_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - int octx; - module_stats.invrnge++; + register int ctr asm("g5"); + unsigned long size; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - if((end - start) < SRMMU_PMD_SIZE) { - start &= PAGE_MASK; - while(start < end) { - srmmu_flush_tlb_page(start); - start += PAGE_SIZE; - } - } else if((end - start) < SRMMU_PGDIR_SIZE) { - start &= SRMMU_PMD_MASK; - while(start < end) { - srmmu_flush_tlb_segment(start); - start += SRMMU_PMD_SIZE; - } - } else { - start &= SRMMU_PGDIR_MASK; - while(start < end) { - srmmu_flush_tlb_region(start); - start += SRMMU_PGDIR_SIZE; - } - } - srmmu_set_context(octx); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(mm) + ctr = 0; + __asm__ __volatile__(" + 1: ld [%%g6 + %2], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %0, 1, %0 + bne 1b + save %%sp, -64, %%sp + 2: subcc %0, 1, %0 + bne 2b + restore %%g0, %%g0, %%g0" + : "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4"); + start &= SRMMU_PGDIR_MASK; + size = SRMMU_PGDIR_ALIGN(end) - start; + __asm__ __volatile__(" + lda [%0] %5, %%g5 + sta %1, [%0] %5 + 1: subcc %3, %4, %3 + bne 1b + sta %%g0, [%2 + %3] %6 + sta %%g5, [%0] %5" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200), + "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS), + "i" (ASI_M_FLUSH_PROBE) + : "g5"); + module_stats.invrnge++; + FLUSH_END } static void viking_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { - int octx; struct mm_struct *mm = vma->vm_mm; - + register int ctr asm("g5"); + + FLUSH_BEGIN(mm) + ctr = 0; + __asm__ __volatile__(" + 1: ld [%%g6 + %2], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %0, 1, %0 + bne 1b + save %%sp, -64, %%sp + 2: subcc %0, 1, %0 + bne 2b + restore %%g0, %%g0, %%g0" + : "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4"); + __asm__ __volatile__(" + lda [%0] %3, %%g5 + sta %1, [%0] %3 + sta %%g0, [%2] %4 + sta %%g5, [%0] %3" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK), + "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE) + : "g5"); module_stats.invpg++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - srmmu_flush_tlb_page(page); - srmmu_set_context(octx); -#ifndef __SMP__ - } -#endif + FLUSH_END } static void viking_flush_tlb_page_for_cbit(unsigned long page) @@ -1400,21 +1402,57 @@ static void cypress_flush_cache_all(void) static void cypress_flush_cache_mm(struct mm_struct *mm) { + register unsigned long a, b, c, d, e, f, g; unsigned long flags, faddr; int octx; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - register unsigned long a, b, c, d, e, f, g; - flush_user_windows(); - save_and_cli(flags); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - a = 0x20; b = 0x40; c = 0x60; - d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; + FLUSH_BEGIN(mm) + flush_user_windows(); + save_and_cli(flags); + octx = srmmu_get_context(); + srmmu_set_context(mm->context); + a = 0x20; b = 0x40; c = 0x60; + d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; - faddr = (0x10000 - 0x100); + faddr = (0x10000 - 0x100); + goto inside; + do { + faddr -= 0x100; + inside: + __asm__ __volatile__("sta %%g0, [%0] %1\n\t" + "sta %%g0, [%0 + %2] %1\n\t" + "sta %%g0, [%0 + %3] %1\n\t" + "sta %%g0, [%0 + %4] %1\n\t" + "sta %%g0, [%0 + %5] %1\n\t" + "sta %%g0, [%0 + %6] %1\n\t" + "sta %%g0, [%0 + %7] %1\n\t" + "sta %%g0, [%0 + %8] %1\n\t" : : + "r" (faddr), "i" (ASI_M_FLUSH_CTX), + "r" (a), "r" (b), "r" (c), "r" (d), + "r" (e), "r" (f), "r" (g)); + } while(faddr); + srmmu_set_context(octx); + restore_flags(flags); + FLUSH_END +} + +static void cypress_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) +{ + register unsigned long a, b, c, d, e, f, g; + unsigned long flags, faddr; + int octx; + + FLUSH_BEGIN(mm) + flush_user_windows(); + save_and_cli(flags); + octx = srmmu_get_context(); + srmmu_set_context(mm->context); + a = 0x20; b = 0x40; c = 0x60; + d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; + + start &= SRMMU_PMD_MASK; + while(start < end) { + faddr = (start + (0x10000 - 0x100)); goto inside; do { faddr -= 0x100; @@ -1427,103 +1465,55 @@ static void cypress_flush_cache_mm(struct mm_struct *mm) "sta %%g0, [%0 + %6] %1\n\t" "sta %%g0, [%0 + %7] %1\n\t" "sta %%g0, [%0 + %8] %1\n\t" : : - "r" (faddr), "i" (ASI_M_FLUSH_CTX), + "r" (faddr), + "i" (ASI_M_FLUSH_SEG), "r" (a), "r" (b), "r" (c), "r" (d), "r" (e), "r" (f), "r" (g)); - } while(faddr); - srmmu_set_context(octx); - restore_flags(flags); -#ifndef __SMP__ - } -#endif -} - -static void cypress_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) -{ - unsigned long flags, faddr; - int octx; - -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - register unsigned long a, b, c, d, e, f, g; - flush_user_windows(); - save_and_cli(flags); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - a = 0x20; b = 0x40; c = 0x60; - d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; - - start &= SRMMU_PMD_MASK; - while(start < end) { - faddr = (start + (0x10000 - 0x100)); - goto inside; - do { - faddr -= 0x100; - inside: - __asm__ __volatile__("sta %%g0, [%0] %1\n\t" - "sta %%g0, [%0 + %2] %1\n\t" - "sta %%g0, [%0 + %3] %1\n\t" - "sta %%g0, [%0 + %4] %1\n\t" - "sta %%g0, [%0 + %5] %1\n\t" - "sta %%g0, [%0 + %6] %1\n\t" - "sta %%g0, [%0 + %7] %1\n\t" - "sta %%g0, [%0 + %8] %1\n\t" : : - "r" (faddr), - "i" (ASI_M_FLUSH_SEG), - "r" (a), "r" (b), "r" (c), "r" (d), - "r" (e), "r" (f), "r" (g)); - } while (faddr != start); - start += SRMMU_PMD_SIZE; - } - srmmu_set_context(octx); - restore_flags(flags); -#ifndef __SMP__ + } while (faddr != start); + start += SRMMU_PMD_SIZE; } -#endif + srmmu_set_context(octx); + restore_flags(flags); + FLUSH_END } static void cypress_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { + register unsigned long a, b, c, d, e, f, g; struct mm_struct *mm = vma->vm_mm; unsigned long flags, line; int octx; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - register unsigned long a, b, c, d, e, f, g; - flush_user_windows(); - save_and_cli(flags); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - a = 0x20; b = 0x40; c = 0x60; - d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; + FLUSH_BEGIN(mm) + flush_user_windows(); + save_and_cli(flags); + octx = srmmu_get_context(); + srmmu_set_context(mm->context); + a = 0x20; b = 0x40; c = 0x60; + d = 0x80; e = 0xa0; f = 0xc0; g = 0xe0; - page &= PAGE_MASK; - line = (page + PAGE_SIZE) - 0x100; - goto inside; - do { - line -= 0x100; - inside: - __asm__ __volatile__("sta %%g0, [%0] %1\n\t" - "sta %%g0, [%0 + %2] %1\n\t" - "sta %%g0, [%0 + %3] %1\n\t" - "sta %%g0, [%0 + %4] %1\n\t" - "sta %%g0, [%0 + %5] %1\n\t" - "sta %%g0, [%0 + %6] %1\n\t" - "sta %%g0, [%0 + %7] %1\n\t" - "sta %%g0, [%0 + %8] %1\n\t" : : - "r" (line), - "i" (ASI_M_FLUSH_PAGE), - "r" (a), "r" (b), "r" (c), "r" (d), - "r" (e), "r" (f), "r" (g)); - } while(line != page); - srmmu_set_context(octx); - restore_flags(flags); -#ifndef __SMP__ - } -#endif + page &= PAGE_MASK; + line = (page + PAGE_SIZE) - 0x100; + goto inside; + do { + line -= 0x100; + inside: + __asm__ __volatile__("sta %%g0, [%0] %1\n\t" + "sta %%g0, [%0 + %2] %1\n\t" + "sta %%g0, [%0 + %3] %1\n\t" + "sta %%g0, [%0 + %4] %1\n\t" + "sta %%g0, [%0 + %5] %1\n\t" + "sta %%g0, [%0 + %6] %1\n\t" + "sta %%g0, [%0 + %7] %1\n\t" + "sta %%g0, [%0 + %8] %1\n\t" : : + "r" (line), + "i" (ASI_M_FLUSH_PAGE), + "r" (a), "r" (b), "r" (c), "r" (d), + "r" (e), "r" (f), "r" (g)); + } while(line != page); + srmmu_set_context(octx); + restore_flags(flags); + FLUSH_END } /* Cypress is copy-back, at least that is how we configure it. */ @@ -1625,80 +1615,65 @@ static unsigned long cypress_getpage(void) static void cypress_flush_tlb_all(void) { - module_stats.invall++; srmmu_flush_whole_tlb(); + module_stats.invall++; } static void cypress_flush_tlb_mm(struct mm_struct *mm) { - int octx; - + FLUSH_BEGIN(mm) + __asm__ __volatile__(" + lda [%0] %3, %%g5 + sta %2, [%0] %3 + sta %%g0, [%1] %4 + sta %%g5, [%0] %3" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context), + "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE) + : "g5"); module_stats.invmm++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - srmmu_flush_tlb_ctx(); - srmmu_set_context(octx); -#ifndef __SMP__ - } -#endif + FLUSH_END } static void cypress_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - int octx; - module_stats.invrnge++; + unsigned long size; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - if((end - start) < SRMMU_PMD_SIZE) { - start &= PAGE_MASK; - while(start < end) { - srmmu_flush_tlb_page(start); - start += PAGE_SIZE; - } - } else if((end - start) < SRMMU_PGDIR_SIZE) { - start &= SRMMU_PMD_MASK; - while(start < end) { - srmmu_flush_tlb_segment(start); - start += SRMMU_PMD_SIZE; - } - } else { - start &= SRMMU_PGDIR_MASK; - while(start < end) { - srmmu_flush_tlb_region(start); - start += SRMMU_PGDIR_SIZE; - } - } - srmmu_set_context(octx); -#ifndef __SMP__ - } -#endif + FLUSH_BEGIN(mm) + start &= SRMMU_PGDIR_MASK; + size = SRMMU_PGDIR_ALIGN(end) - start; + __asm__ __volatile__(" + lda [%0] %5, %%g5 + sta %1, [%0] %5 + 1: subcc %3, %4, %3 + bne 1b + sta %%g0, [%2 + %3] %6 + sta %%g5, [%0] %5" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200), + "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS), + "i" (ASI_M_FLUSH_PROBE) + : "g5"); + module_stats.invrnge++; + FLUSH_END } static void cypress_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { - int octx; struct mm_struct *mm = vma->vm_mm; + FLUSH_BEGIN(mm) + __asm__ __volatile__(" + lda [%0] %3, %%g5 + sta %1, [%0] %3 + sta %%g0, [%2] %4 + sta %%g5, [%0] %3" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK), + "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE) + : "g5"); module_stats.invpg++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - srmmu_flush_tlb_page(page); - srmmu_set_context(octx); -#ifndef __SMP__ - } -#endif + FLUSH_END } static void cypress_flush_tlb_page_for_cbit(unsigned long page) @@ -1709,48 +1684,111 @@ static void cypress_flush_tlb_page_for_cbit(unsigned long page) /* Hypersparc flushes. Very nice chip... */ static void hypersparc_flush_cache_all(void) { - flush_user_windows(); - hyper_flush_unconditional_combined(); - hyper_flush_whole_icache(); + register int ctr asm("g5"); + unsigned long tmp1; + + ctr = 0; + __asm__ __volatile__(" + 1: ld [%%g6 + %6], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %1, 1, %1 + bne 1b + save %%sp, -64, %%sp + 2: subcc %1, 1, %1 + bne 2b + restore %%g0, %%g0, %%g0 + 1: subcc %0, %3, %0 ! hyper_flush_unconditional_combined + bne 1b + sta %%g0, [%0] %4 + sta %%g0, [%%g0] %5 ! hyper_flush_whole_icache" + : "=&r" (tmp1), "=&r" (ctr) + : "0" (vac_cache_size), "r" (vac_line_size), + "i" (ASI_M_FLUSH_CTX), "i" (ASI_M_FLUSH_IWHOLE), + "i" (UWINMASK_OFFSET), "1" (ctr) + : "g4"); } static void hypersparc_flush_cache_mm(struct mm_struct *mm) { -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - hyper_flush_cache_user(); - hyper_flush_whole_icache(); -#ifndef __SMP__ - } -#endif -} - -/* Boy was my older implementation inefficient... */ + register int ctr asm("g5"); + unsigned long tmp1; + + FLUSH_BEGIN(mm) + ctr = 0; + __asm__ __volatile__(" + 1: ld [%%g6 + %2], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %0, 1, %0 + bne 1b + save %%sp, -64, %%sp + 2: subcc %0, 1, %0 + bne 2b + restore %%g0, %%g0, %%g0" + : "=&r" (ctr) + : "0" (ctr), "i" (UWINMASK_OFFSET) + : "g4"); + + __asm__ __volatile__(" + 1: subcc %0, %2, %0 ! hyper_flush_cache_user + bne 1b + sta %%g0, [%0] %3 + sta %%g0, [%%g0] %4 ! hyper_flush_whole_icache" + : "=&r" (tmp1) + : "0" (vac_cache_size), "r" (vac_line_size), "i" (ASI_M_FLUSH_USER), + "i" (ASI_M_FLUSH_IWHOLE)); + FLUSH_END +} + +/* The things we do for performance... */ static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - volatile unsigned long clear; - int octx; - -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - flush_user_windows(); - octx = srmmu_get_context(); - start &= PAGE_MASK; - srmmu_set_context(mm->context); - while(start < end) { - if(srmmu_hwprobe(start)) - hyper_flush_cache_page(start); - start += PAGE_SIZE; - } - clear = srmmu_get_fstatus(); - srmmu_set_context(octx); - hyper_flush_whole_icache(); -#ifndef __SMP__ - } -#endif + register int ctr asm("g5"); + unsigned long tmp1; + + FLUSH_BEGIN(mm) + ctr = 0; + __asm__ __volatile__(" + 1: ld [%%g6 + %2], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %0, 1, %0 + bne 1b + save %%sp, -64, %%sp + 2: subcc %0, 1, %0 + bne 2b + restore %%g0, %%g0, %%g0" + : "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4"); + tmp1 = vac_cache_size; start &= PAGE_MASK; end = PAGE_ALIGN(end); + if((end - start) >= (tmp1 << 2)) { + __asm__ __volatile__(" + 1: subcc %0, %2, %0 ! hyper_flush_cache_user + bne 1b + sta %%g0, [%0] %3 + sta %%g0, [%%g0] %4" + : "=&r" (tmp1) : "0" (tmp1), "r" (vac_line_size), + "i" (ASI_M_FLUSH_USER), "i" (ASI_M_FLUSH_IWHOLE)); + } else { + tmp1 = srmmu_get_context(); srmmu_set_context(mm->context); + __asm__ __volatile__(" + sub %0, %3, %0 + 1: or %0, 0x400, %%g4 + lda [%%g4] %4, %%g4 + orcc %%g4, 0x0, %%g0 + be 3f + sethi %%hi(0x1000), %%g5 + 2: subcc %%g5, %7, %%g5 ! hyper_flush_cache_page + bne 2b + sta %%g0, [%0 + %%g5] %5 + 3: cmp %0, %2 + bne 1b + sub %0, %3, %0 + sta %%g0, [%%g0] %6 ! hyper_flush_whole_icache" + : "=&r" (end) + : "0" (end), "r" (start), "r" (PAGE_SIZE), "i" (ASI_M_FLUSH_PROBE), + "i" (ASI_M_FLUSH_PAGE), "i" (ASI_M_FLUSH_IWHOLE), "r" (vac_line_size) + : "g4", "g5"); + (void) srmmu_get_fstatus(); srmmu_set_context(tmp1); + } + FLUSH_END } /* HyperSparc requires a valid mapping where we are about to flush @@ -1759,35 +1797,64 @@ static void hypersparc_flush_cache_range(struct mm_struct *mm, unsigned long sta static void hypersparc_flush_cache_page(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; - volatile unsigned long clear; - int octx; - -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - octx = srmmu_get_context(); - flush_user_windows(); - srmmu_set_context(mm->context); - hyper_flush_whole_icache(); - if(!srmmu_hwprobe(page)) - goto no_mapping; - hyper_flush_cache_page(page); - no_mapping: - clear = srmmu_get_fstatus(); - srmmu_set_context(octx); -#ifndef __SMP__ - } -#endif + register int ctr asm("g5"); + unsigned long tmp1; + + FLUSH_BEGIN(mm) + ctr = 0; + __asm__ __volatile__(" + 1: ld [%%g6 + %2], %%g4 ! flush user windows + orcc %%g0, %%g4, %%g0 + add %0, 1, %0 + bne 1b + save %%sp, -64, %%sp + 2: subcc %0, 1, %0 + bne 2b + restore %%g0, %%g0, %%g0" + : "=&r" (ctr) : "0" (ctr), "i" (UWINMASK_OFFSET) : "g4"); + __asm__ __volatile__(" + mov 0x200, %%g4 + lda [%%g4] %6, %1 + sta %0, [%%g4] %6 + or %3, 0x400, %0 + lda [%0] %9, %0 + orcc %0, 0x0, %%g0 + be 2f + sethi %%hi(0x1000), %0 + 1: subcc %0, %5, %0 ! hyper_flush_cache_page + bne 1b + sta %%g0, [%3 + %0] %7 + 2: andcc %4, 0x4, %%g0 + sta %1, [%%g4] %6 + bne,a 1f + sta %%g0, [%%g0] %8 +1:" : "=&r" (tmp1), "=&r" (ctr) + : "0" (mm->context), "r" (page & PAGE_MASK), "r" (vma->vm_flags), + "r" (vac_line_size), "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PAGE), + "i" (ASI_M_FLUSH_IWHOLE), "i" (ASI_M_FLUSH_PROBE) + : "g4"); + (void) srmmu_get_fstatus(); + FLUSH_END } /* HyperSparc is copy-back. */ static void hypersparc_flush_page_to_ram(unsigned long page) { - volatile unsigned long clear; - - if(srmmu_hwprobe(page)) - hyper_flush_cache_page(page); - clear = srmmu_get_fstatus(); + page &= PAGE_MASK; + __asm__ __volatile__(" + lda [%0] %2, %%g4 + orcc %%g4, 0x0, %%g0 + be 2f + sethi %%hi(%7), %%g5 +1: subcc %%g5, %6, %%g5 ! hyper_flush_cache_page + bne 1b + sta %%g0, [%1 + %%g5] %3 +2: lda [%4] %5, %%g0" + : /* no outputs */ + : "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE), + "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS), + "r" (vac_line_size), "i" (PAGE_SIZE) + : "g4", "g5"); } /* HyperSparc is IO cache coherent. */ @@ -1805,108 +1872,106 @@ static void hypersparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_ static void hypersparc_flush_cache_page_to_uncache(unsigned long page) { - volatile unsigned long clear; - - if(srmmu_hwprobe(page)) - hyper_flush_cache_page(page); - clear = srmmu_get_fstatus(); + page &= PAGE_MASK; + __asm__ __volatile__(" + lda [%0] %2, %%g4 + orcc %%g4, 0x0, %%g0 + be 2f + sethi %%hi(%7), %%g5 +1: subcc %%g5, %6, %%g5 ! hyper_flush_cache_page + bne 1b + sta %%g0, [%1 + %%g5] %3 +2: lda [%4] %5, %%g0" + : /* no outputs */ + : "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE), + "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS), + "r" (vac_line_size), "i" (PAGE_SIZE) + : "g4", "g5"); } static unsigned long hypersparc_getpage(void) { - volatile unsigned long clear; unsigned long page = get_free_page(GFP_KERNEL); - unsigned long flags; - save_and_cli(flags); - if(srmmu_hwprobe(page)) - hyper_flush_cache_page(page); - clear = srmmu_get_fstatus(); - restore_flags(flags); + __asm__ __volatile__(" + lda [%0] %2, %%g4 + orcc %%g4, 0x0, %%g0 + be 2f + sethi %%hi(%7), %%g5 +1: subcc %%g5, %6, %%g5 ! hyper_flush_cache_page + bne 1b + sta %%g0, [%1 + %%g5] %3 +2: lda [%4] %5, %%g0" + : /* no outputs */ + : "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE), + "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS), + "r" (vac_line_size), "i" (PAGE_SIZE) + : "g4", "g5"); return page; } static void hypersparc_flush_tlb_all(void) { - module_stats.invall++; srmmu_flush_whole_tlb(); + module_stats.invall++; } static void hypersparc_flush_tlb_mm(struct mm_struct *mm) { - int octx; - + FLUSH_BEGIN(mm) + __asm__ __volatile__(" + lda [%0] %3, %%g5 + sta %2, [%0] %3 + sta %%g0, [%1] %4 + sta %%g5, [%0] %3" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (0x300), "r" (mm->context), + "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE) + : "g5"); module_stats.invmm++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - srmmu_flush_tlb_ctx(); - srmmu_set_context(octx); - -#ifndef __SMP__ - } -#endif + FLUSH_END } static void hypersparc_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - int octx; + unsigned long size; + FLUSH_BEGIN(mm) + start &= SRMMU_PGDIR_MASK; + size = SRMMU_PGDIR_ALIGN(end) - start; + __asm__ __volatile__(" + lda [%0] %5, %%g5 + sta %1, [%0] %5 + 1: subcc %3, %4, %3 + bne 1b + sta %%g0, [%2 + %3] %6 + sta %%g5, [%0] %5" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (start | 0x200), + "r" (size), "r" (SRMMU_PGDIR_SIZE), "i" (ASI_M_MMUREGS), + "i" (ASI_M_FLUSH_PROBE) + : "g5"); module_stats.invrnge++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - if((end - start) < SRMMU_PMD_SIZE) { - start &= PAGE_MASK; - while(start < end) { - srmmu_flush_tlb_page(start); - start += PAGE_SIZE; - } - } else if((end - start) < SRMMU_PGDIR_SIZE) { - start &= SRMMU_PMD_MASK; - while(start < end) { - srmmu_flush_tlb_segment(start); - start += SRMMU_PMD_SIZE; - } - } else { - start &= SRMMU_PGDIR_MASK; - while(start < end) { - srmmu_flush_tlb_region(start); - start += SRMMU_PGDIR_SIZE; - } - } - srmmu_set_context(octx); - -#ifndef __SMP__ - } -#endif + FLUSH_END } static void hypersparc_flush_tlb_page(struct vm_area_struct *vma, unsigned long page) { struct mm_struct *mm = vma->vm_mm; - int octx; + FLUSH_BEGIN(mm) + __asm__ __volatile__(" + lda [%0] %3, %%g5 + sta %1, [%0] %3 + sta %%g0, [%2] %4 + sta %%g5, [%0] %3" + : /* no outputs */ + : "r" (SRMMU_CTX_REG), "r" (mm->context), "r" (page & PAGE_MASK), + "i" (ASI_M_MMUREGS), "i" (ASI_M_FLUSH_PROBE) + : "g5"); module_stats.invpg++; -#ifndef __SMP__ - if(mm->context != NO_CONTEXT) { -#endif - - octx = srmmu_get_context(); - srmmu_set_context(mm->context); - srmmu_flush_tlb_page(page); - srmmu_set_context(octx); - -#ifndef __SMP__ - } -#endif + FLUSH_END } static void hypersparc_flush_tlb_page_for_cbit(unsigned long page) @@ -1922,21 +1987,27 @@ static void hypersparc_ctxd_set(ctxd_t *ctxp, pgd_t *pgdp) static void hypersparc_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { - volatile unsigned long clear; unsigned long page = ((unsigned long) pgdp) & PAGE_MASK; - unsigned long flags; - /* Do PGD flush. */ - save_and_cli(flags); - if(srmmu_hwprobe(page)) - hyper_flush_cache_page(page); - clear = srmmu_get_fstatus(); - restore_flags(flags); + __asm__ __volatile__(" + lda [%0] %2, %%g4 + orcc %%g4, 0x0, %%g0 + be 2f + sethi %%hi(%7), %%g5 +1: subcc %%g5, %6, %%g5 ! hyper_flush_cache_page + bne 1b + sta %%g0, [%1 + %%g5] %3 +2: lda [%4] %5, %%g0" + : /* no outputs */ + : "r" (page | 0x400), "r" (page), "i" (ASI_M_FLUSH_PROBE), + "i" (ASI_M_FLUSH_PAGE), "r" (SRMMU_FAULT_STATUS), "i" (ASI_M_MMUREGS), + "r" (vac_line_size), "i" (PAGE_SIZE) + : "g4", "g5"); if(tsk->mm->context != NO_CONTEXT) { - flush_cache_mm(current->mm); + flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], pgdp); - flush_tlb_mm(current->mm); + flush_tlb_mm(tsk->mm); } } @@ -2851,17 +2922,14 @@ static void srmmu_destroy_context(struct mm_struct *mm) static void srmmu_vac_update_mmu_cache(struct vm_area_struct * vma, unsigned long address, pte_t pte) { - unsigned long offset, vaddr; - unsigned long start; - pgd_t *pgdp; - pmd_t *pmdp; - pte_t *ptep; - if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) { struct vm_area_struct *vmaring; struct inode *inode; - unsigned long flags; + unsigned long flags, offset, vaddr, start; int alias_found = 0; + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; save_and_cli(flags); @@ -3522,6 +3590,7 @@ __initfunc(void ld_mmu_srmmu(void)) mmu_getpage = srmmu_getpage; set_pte = srmmu_set_pte_cacheable; init_new_context = srmmu_init_new_context; + switch_to_context = srmmu_switch_to_context; pmd_align = srmmu_pmd_align; pgdir_align = srmmu_pgdir_align; vmalloc_start = srmmu_vmalloc_start; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 1030a53a31fe..49556fb637d1 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -25,11 +25,11 @@ #include #ifdef CONFIG_BLK_DEV_LOOP_DES -#include +# /*nodep*/ include #endif #ifdef CONFIG_BLK_DEV_LOOP_IDEA -#include +# /*nodep*/ include #endif #include /* must follow des.h */ diff --git a/drivers/char/Config.in b/drivers/char/Config.in index d44d3a55efd5..0905205f141b 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -37,19 +37,14 @@ if [ "$CONFIG_MODULES" = "y" ]; then fi tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE -if [ "$CONFIG_QIC02_TAPE" = "y" -o "$CONFIG_QIC02_TAPE" = "m" ]; then - if [ "$CONFIG_QIC02_TAPE" = "y" ]; then - bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF - if [ "$CONFIG_QIC02_DYNCONF" = "y" ]; then - comment 'Setting runtime QIC-02 configuration is done with qic02conf' - comment 'from the tpqic02-support package. It is available at' - comment 'ftp://titus.cfw.com/pub/Linux/util/' - else - comment 'Edit configuration parameters in ./include/linux/tpqic02.h!' - fi - fi - if [ "$CONFIG_QIC02_TAPE" = "m" ]; then - comment 'Edit configuration parameters in ./include/linux/tpqic02.h!' +if [ "$CONFIG_QIC02_TAPE" != "n" ]; then + bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF + if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then + comment ' Edit configuration parameters in ./include/linux/tpqic02.h!' + else + comment ' Setting runtime QIC-02 configuration is done with qic02conf' + comment ' from the tpqic02-support package. It is available at' + comment ' sunsite.unc.edu or ftp://titus.cfw.com/pub/Linux/util/' fi fi diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f26c4195fd98..c18ea79eb203 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -206,11 +206,11 @@ M = y L_OBJS += rtc.o endif -ifdef CONFIG_QIC02_TAPE +ifeq ($(CONFIG_QIC02_TAPE),y) +L_OBJS += tpqic02.o +else ifeq ($(CONFIG_QIC02_TAPE),m) - M_OBJS += tpqic02.o - else - L_OBJS += tpqic02.o + M_OBJS += tpqic02.o endif endif diff --git a/drivers/char/esp.c b/drivers/char/esp.c index 263fe27e0d2d..2cc76af203e3 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -29,13 +29,17 @@ * by Chris Faylor. * * Most recent changes: (Andrew J. Robinson) - * Remove tx_flowed_[on|off], since the ESP handles flow itself. - * Enabled reinterrupt pacing to ensure all requests are serviced. - * Decreased RX timeout to reduce latency. + * Remove all references to tty->hw_stopped. + * Request a single region for multiple ports if possible. + * Stop a DMA transfer on a port when it is closed. + * Rename esp_init() to espserial_init(). + * Improve validation of IRQ (only accept those allowed by the ESP card). + * Return if a signal is received while wait for a break to start. + * Split NEED_DMA logic into NEED_DMA_RX and NEED_DMA_TX. * * This module exports the following rs232 io functions: * - * int esp_init(void); + * int espserial_init(void); */ #include @@ -76,16 +80,22 @@ static unsigned int divisor[NR_PRIMARY] = {0,0,0,0,0,0,0,0}; static unsigned int dma = CONFIG_ESPSERIAL_DMA_CHANNEL; /* DMA channel */ static unsigned int trigger = CONFIG_ESPSERIAL_TRIGGER_LEVEL; /* FIFO trigger level */ + +MODULE_PARM(irq, "1-8i"); +MODULE_PARM(divisor, "1-8i"); +MODULE_PARM(dma, "i"); +MODULE_PARM(trigger, "i"); /* END */ static char *dma_buffer; +static int dma_bytes; #define DMA_BUFFER_SZ 1024 #define WAKEUP_CHARS 1024 static char *serial_name = "ESP serial driver"; -static char *serial_version = "1.2"; +static char *serial_version = "1.3"; DECLARE_TASK_QUEUE(tq_esp); @@ -122,7 +132,6 @@ static int serial_refcount; static struct esp_struct *IRQ_ports[16]; -static void autoconfig(struct esp_struct * info); static void change_speed(struct esp_struct *info); static void rs_wait_until_sent(struct tty_struct *, int); @@ -288,32 +297,24 @@ static _INLINE_ void rs_sched_event(struct esp_struct *info, mark_bh(ESP_BH); } -static _INLINE_ void receive_chars_dma(struct esp_struct *info, int *dma_bytes) +static void receive_chars_dma(struct esp_struct *info) { - unsigned int num_chars; - - if (*dma_bytes) { - info->stat_flags |= ESP_STAT_NEED_DMA; - return; - } - - info->stat_flags &= ~(ESP_STAT_RX_TIMEOUT | ESP_STAT_NEED_DMA); + info->stat_flags &= ~(ESP_STAT_RX_TIMEOUT | ESP_STAT_NEED_DMA_RX); serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND); serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL); - num_chars = serial_in(info, UART_ESI_STAT1) << 8; - num_chars |= serial_in(info, UART_ESI_STAT2); + dma_bytes = serial_in(info, UART_ESI_STAT1) << 8; + dma_bytes |= serial_in(info, UART_ESI_STAT2); - if (!num_chars) + if (!dma_bytes) return; - *dma_bytes = num_chars; info->stat_flags |= ESP_STAT_DMA_RX; disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_READ); set_dma_addr(dma, virt_to_bus(dma_buffer)); - set_dma_count(dma, num_chars); + set_dma_count(dma, dma_bytes); enable_dma(dma); serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX); } @@ -368,8 +369,8 @@ static void do_ttybuf(void *private_) restore_flags(flags); } -static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, - int *dma_bytes, int status) +static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, + int status) { struct tty_struct *tty = info->tty; int num_bytes, bytes_left, x_bytes; @@ -382,10 +383,8 @@ static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, clear_dma_ff(dma); info->stat_flags &= ~ESP_STAT_DMA_RX; - num_bytes = *dma_bytes - get_dma_residue(dma); - + bytes_left = num_bytes = dma_bytes - get_dma_residue(dma); buffer = &(tty->flip); - bytes_left = num_bytes; if (info->tty_buf->count && (tty->flip.count < TTY_FLIPBUF_SIZE)) do_ttybuf(info); @@ -415,7 +414,6 @@ static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, if (num_bytes > 0) { buffer->flag_buf_ptr--; - status >>= 8; status &= (0x1c & info->read_status_mask); if (status & info->ignore_status_mask) { @@ -439,26 +437,18 @@ static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, queue_task_irq_off(&tty->flip.tqueue, &tq_timer); } - if (*dma_bytes != num_bytes) { - *dma_bytes = 0; - receive_chars_dma(info, dma_bytes); + if (dma_bytes != num_bytes) { + dma_bytes = 0; + receive_chars_dma(info); } else - *dma_bytes = 0; + dma_bytes = 0; } -static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int *dma_bytes) +static void transmit_chars_dma(struct esp_struct *info) { - int count; - - if (*dma_bytes) { - info->stat_flags |= ESP_STAT_NEED_DMA; - return; - } - - info->stat_flags &= ~ESP_STAT_NEED_DMA; + info->stat_flags &= ~ESP_STAT_NEED_DMA_TX; - if ((info->xmit_cnt <= 0) || info->tty->stopped || - info->tty->hw_stopped) { + if ((info->xmit_cnt <= 0) || info->tty->stopped) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); @@ -467,53 +457,53 @@ static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int *dma_bytes) serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND); serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL); - count = serial_in(info, UART_ESI_STAT1) << 8; - count |= serial_in(info, UART_ESI_STAT2); + dma_bytes = serial_in(info, UART_ESI_STAT1) << 8; + dma_bytes |= serial_in(info, UART_ESI_STAT2); - if (!count) + if (!dma_bytes) return; - count = ((count < info->xmit_cnt) ? count : info->xmit_cnt); + if (dma_bytes > info->xmit_cnt) + dma_bytes = info->xmit_cnt; - if (info->xmit_tail + count <= ESP_XMIT_SIZE) { + if (info->xmit_tail + dma_bytes <= ESP_XMIT_SIZE) { memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]), - count); + dma_bytes); } else { int i = ESP_XMIT_SIZE - info->xmit_tail; memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]), i); - memcpy(&(dma_buffer[i]), info->xmit_buf, count - i); + memcpy(&(dma_buffer[i]), info->xmit_buf, dma_bytes - i); } - info->xmit_cnt -= count; - info->xmit_tail = (info->xmit_tail + count) & (ESP_XMIT_SIZE - 1); + info->xmit_cnt -= dma_bytes; + info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1); - if (info->xmit_cnt < WAKEUP_CHARS) + if (info->xmit_cnt < WAKEUP_CHARS) { rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP); #ifdef SERIAL_DEBUG_INTR printk("THRE..."); #endif - if (info->xmit_cnt <= 0) { - info->IER &= ~UART_IER_THRI; - serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); - serial_out(info, UART_ESI_CMD2, info->IER); + if (info->xmit_cnt <= 0) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); + serial_out(info, UART_ESI_CMD2, info->IER); + } } - *dma_bytes = count; info->stat_flags |= ESP_STAT_DMA_TX; disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_WRITE); set_dma_addr(dma, virt_to_bus(dma_buffer)); - set_dma_count(dma, count); + set_dma_count(dma, dma_bytes); enable_dma(dma); serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); } -static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info, - int *dma_bytes) +static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info) { int num_bytes; @@ -523,21 +513,20 @@ static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info, disable_dma(dma); clear_dma_ff(dma); - num_bytes = *dma_bytes - get_dma_residue(dma); + num_bytes = dma_bytes - get_dma_residue(dma); - if (*dma_bytes != num_bytes) - { - *dma_bytes -= num_bytes; - memmove(dma_buffer, dma_buffer + num_bytes, *dma_bytes); + if (dma_bytes != num_bytes) { + dma_bytes -= num_bytes; + memmove(dma_buffer, dma_buffer + num_bytes, dma_bytes); disable_dma(dma); clear_dma_ff(dma); set_dma_mode(dma, DMA_MODE_WRITE); set_dma_addr(dma, virt_to_bus(dma_buffer)); - set_dma_count(dma, *dma_bytes); + set_dma_count(dma, dma_bytes); enable_dma(dma); serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX); } else { - *dma_bytes = 0; + dma_bytes = 0; info->stat_flags &= ~ESP_STAT_DMA_TX; } } @@ -585,12 +574,10 @@ static _INLINE_ void check_modem_status(struct esp_struct *info) */ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) { - struct esp_struct * info, *stop_port; + struct esp_struct * info, *stop_port = 0; unsigned err_status; unsigned int scratch; int pre_bytes; - int check_dma_only = 0; - static int dma_bytes; #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq); @@ -599,14 +586,13 @@ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) /* This routine will currently check ALL ports when an interrupt */ /* is received from ANY port */ - stop_port = info = IRQ_ports[irq]; + info = IRQ_ports[irq]; if (!info) return; do { - if (!info->tty || (check_dma_only && - !(info->stat_flags & ESP_STAT_NEED_DMA))) { + if (!info->tty) { info = info->next_port; continue; } @@ -616,33 +602,38 @@ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) scratch = serial_in(info, UART_ESI_SID); if (scratch & 0x04) { /* error - check for rx timeout */ serial_out(info, UART_ESI_CMD1, ESI_GET_ERR_STAT); - err_status = serial_in(info, UART_ESI_STAT1) << 8; - err_status |= serial_in(info, UART_ESI_STAT2); + err_status = serial_in(info, UART_ESI_STAT1); + serial_in(info, UART_ESI_STAT2); - if (err_status & 0x0100) + if (err_status & 0x01) info->stat_flags |= ESP_STAT_RX_TIMEOUT; - if (err_status & 0x2000) /* UART status */ + if (err_status & 0x20) /* UART status */ check_modem_status(info); - if (err_status & 0x8000) /* Start break */ + if (err_status & 0x80) /* Start break */ wake_up_interruptible(&info->break_wait); } if ((scratch & 0x88) || /* DMA completed or timed out */ - (err_status & 0x1c00) /* receive error */) { - receive_chars_dma_done(info, &dma_bytes, - err_status); - transmit_chars_dma_done(info, &dma_bytes); + (err_status & 0x1c) /* receive error */) { + receive_chars_dma_done(info, err_status); + transmit_chars_dma_done(info); } if (((scratch & 0x01) || (info->stat_flags & ESP_STAT_RX_TIMEOUT)) && (info->IER & UART_IER_RDI)) - receive_chars_dma(info, &dma_bytes); + if (dma_bytes) + info->stat_flags |= ESP_STAT_NEED_DMA_RX; + else + receive_chars_dma(info); if ((scratch & 0x02) && (info->IER & UART_IER_THRI)) - transmit_chars_dma(info, &dma_bytes); + if (dma_bytes) + info->stat_flags |= ESP_STAT_NEED_DMA_TX; + else + transmit_chars_dma(info); info->last_active = jiffies; @@ -650,13 +641,21 @@ static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs) stop_port = info; info = info->next_port; - - if ((info->irq != irq) || (info == IRQ_ports[irq])) - check_dma_only = 1; - - if (check_dma_only && dma_bytes) - info = stop_port; - } while (info != stop_port); + } while ((info->irq == irq) && (info != IRQ_ports[irq])); + + if (stop_port) { + while ((info != stop_port) && (!dma_bytes)) { + if (info->tty) { + if (info->stat_flags & ESP_STAT_NEED_DMA_RX) + receive_chars_dma(info); + if ((info->stat_flags & ESP_STAT_NEED_DMA_TX) + && !dma_bytes) + transmit_chars_dma(info); + } + + info = info->next_port; + } + } #ifdef SERIAL_DEBUG_INTR printk("end.\n"); @@ -730,7 +729,7 @@ static void do_serial_hangup(void *private_) * --------------------------------------------------------------- */ -static void esp_basic_init(struct esp_struct * info) +static _INLINE_ void esp_basic_init(struct esp_struct * info) { /* put ESPC in enhanced mode */ serial_out(info, UART_ESI_CMD1, ESI_SET_MODE); @@ -791,13 +790,6 @@ static int startup(struct esp_struct * info) return 0; } - if (!info->port) { - if (info->tty) - set_bit(TTY_IO_ERROR, &info->tty->flags); - restore_flags(flags); - return 0; - } - if (!info->xmit_buf) { info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL); if (!info->xmit_buf) { @@ -857,7 +849,7 @@ static int startup(struct esp_struct * info) */ if (!IRQ_ports[info->irq]) { retval = request_irq(info->irq, rs_interrupt_single, - SA_INTERRUPT, "esp", NULL); + SA_INTERRUPT, "esp serial", NULL); if (!retval) { int i = 1; @@ -872,7 +864,7 @@ static int startup(struct esp_struct * info) if (!dma_buffer) retval = -ENOMEM; else - retval = request_dma(dma, "esp"); + retval = request_dma(dma, "esp serial"); if (retval) free_irq(info->irq, NULL); @@ -892,10 +884,6 @@ static int startup(struct esp_struct * info) } info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; -#if defined(__alpha__) && !defined(CONFIG_PCI) - info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2; -#endif - serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART); serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, info->MCR); @@ -979,6 +967,29 @@ static void shutdown(struct esp_struct * info) */ wake_up_interruptible(&info->delta_msr_wait); wake_up_interruptible(&info->break_wait); + + /* stop a DMA transfer on the port being closed, and start the next + one */ + + if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) { + struct esp_struct *curr = info->next_port; + + disable_dma(dma); + clear_dma_ff(dma); + dma_bytes = 0; + + while ((curr != info) && (!dma_bytes)) { + if (curr->tty) { + if (curr->stat_flags & ESP_STAT_NEED_DMA_RX) + receive_chars_dma(curr); + if ((curr->stat_flags & ESP_STAT_NEED_DMA_TX) + && !dma_bytes) + transmit_chars_dma(curr); + } + + curr = curr->next_port; + } + } /* * First unlink the serial port from the IRQ chain... @@ -1025,7 +1036,21 @@ static void shutdown(struct esp_struct * info) info->xmit_buf = 0; } - if (info->tty_buf && !info->tty_buf->tqueue.sync) { + if (info->tty_buf) { + /* code borrowed from tty_io.c */ + if (info->tty_buf->tqueue.sync) { + struct tq_struct *tq, *prev; + + for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { + if (tq == &info->tty_buf->tqueue) { + if (prev) + prev->next = tq->next; + else + tq_timer = tq->next; + break; + } + } + } kfree(info->tty_buf); info->tty_buf = 0; } @@ -1065,8 +1090,7 @@ static void change_speed(struct esp_struct *info) if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; - if (!(port = info->port)) - return; + port = info->port; i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; @@ -1251,8 +1275,7 @@ static void rs_flush_chars(struct tty_struct *tty) if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return; - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) + if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf) return; save_flags(flags); cli(); @@ -1303,10 +1326,9 @@ static int rs_write(struct tty_struct * tty, int from_user, } if (from_user) up(&tmp_buf_sem); - if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(info->IER & UART_IER_THRI)) { + if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; - serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); /* set mask */ + serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); } restore_flags(flags); @@ -1371,8 +1393,8 @@ static void rs_throttle(struct tty_struct * tty) if (serial_paranoia_check(info, tty->device, "rs_throttle")) return; - info->IER &= ~UART_IER_RDI; cli(); + info->IER &= ~UART_IER_RDI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); @@ -1393,8 +1415,8 @@ static void rs_unthrottle(struct tty_struct * tty) if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) return; - info->IER |= UART_IER_RDI; cli(); + info->IER |= UART_IER_RDI; serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); serial_out(info, UART_ESI_CMD2, info->IER); serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT); @@ -1438,7 +1460,6 @@ static int set_serial_info(struct esp_struct * info, unsigned int change_irq; int i, retval = 0; struct esp_struct *current_async; - unsigned long flags = 0; if (!new_info) return -EFAULT; @@ -1450,7 +1471,10 @@ static int set_serial_info(struct esp_struct * info, (info->port != new_serial.port) || (new_serial.baud_base != BASE_BAUD) || (new_serial.irq > 15) || - (new_serial.irq < 1)) + (new_serial.irq < 2) || + (new_serial.irq == 6) || + (new_serial.irq == 8) || + (new_serial.irq == 13)) return -EINVAL; change_irq = new_serial.irq != info->irq; @@ -1474,7 +1498,6 @@ static int set_serial_info(struct esp_struct * info, new_serial.irq = 9; if (change_irq) { - save_flags(flags); cli(); i = 1; while ((i < 16) && !IRQ_ports[i]) @@ -1487,14 +1510,10 @@ static int set_serial_info(struct esp_struct * info, if ((current_async->line >= info->line) && (current_async->line < (info->line + 8))) { if (current_async == info) { - if (current_async->count > 1) { - restore_flags(flags); + if (current_async->count > 1) return -EBUSY; - } - } else { - restore_flags(flags); + } else return -EBUSY; - } } current_async = current_async->next_port; @@ -1534,13 +1553,9 @@ static int set_serial_info(struct esp_struct * info, serial_out(info, UART_ESI_CMD2, 0x02); else serial_out(info, UART_ESI_CMD2, info->irq); - - restore_flags(flags); } check_and_exit: - if (!info->port) - return 0; if (info->flags & ASYNC_INITIALIZED) { if (((old_info.flags & ASYNC_SPD_MASK) != (info->flags & ASYNC_SPD_MASK)) || @@ -1639,14 +1654,19 @@ static int set_modem_info(struct esp_struct * info, unsigned int cmd, */ static void send_break( struct esp_struct * info, int duration) { - if (!info->port) - return; cli(); serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); serial_out(info, UART_ESI_CMD2, 0x01); interruptible_sleep_on(&info->break_wait); + if (current->signal & ~current->blocked) { + serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK); + serial_out(info, UART_ESI_CMD2, 0x00); + sti(); + return; + } + current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + duration; schedule(); @@ -1832,8 +1852,7 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { info->MCR |= UART_MCR_DTR; - if (!tty->hw_stopped || - !(tty->termios->c_cflag & CRTSCTS)) + if (!(tty->termios->c_cflag & CRTSCTS)) info->MCR |= UART_MCR_RTS; cli(); serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART); @@ -1845,7 +1864,6 @@ static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios) /* Handle turning of CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; rs_start(tty); } @@ -1964,17 +1982,6 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->event = 0; info->tty = 0; - if (info->tty_buf) { - while (info->tty_buf->tqueue.sync) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ / 10; - schedule(); - } - - kfree(info->tty_buf); - info->tty_buf = 0; - } - if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; @@ -2299,9 +2306,9 @@ static int esp_open(struct tty_struct *tty, struct file * filp) /* * --------------------------------------------------------------------- - * esp_init() and friends + * espserial_init() and friends * - * esp_init() is called at boot-time to initialize the serial driver. + * espserial_init() is called at boot-time to initialize the serial driver. * --------------------------------------------------------------------- */ @@ -2317,45 +2324,47 @@ static void show_serial_version(void) } /* - * This routine is called by esp_init() to initialize a specific serial - * port. It determines what type of UART chip this serial port is - * using: 8250, 16450, 16550, 16550A. The important question is - * whether or not this UART is a 16550A or not, since this will - * determine whether or not we can use its FIFO features or not. + * This routine is called by espserial_init() to initialize a specific serial + * port. */ -static void autoconfig(struct esp_struct * info) +static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start) { - unsigned char status1, status2, scratch; - unsigned port = info->port; + int port_detected = 0; unsigned long flags; - if (!port) - return; - save_flags(flags); cli(); /* * Check for ESP card */ - scratch = serial_in(info, UART_ESI_BASE); - if (scratch == 0xf3) { + + if (!check_region(info->port, 8) && + serial_in(info, UART_ESI_BASE) == 0xf3) { serial_out(info, UART_ESI_CMD1, 0x00); serial_out(info, UART_ESI_CMD1, 0x01); - status1 = serial_in(info, UART_ESI_STAT2); - status2 = status1 & 0x70; - if (status2 != 0x20) { - printk(" Old ESP found at %x\n",info->port); - info->port = 0; - } else { - serial_out(info, UART_ESI_CMD1, 0x02); - status1 = serial_in(info, UART_ESI_STAT1) & 0x03; + + if ((serial_in(info, UART_ESI_STAT2) & 0x70) == 0x20) { + port_detected = 1; + if (!(info->irq)) { - if ((status1 == 0x00) || (status1 == 0x02)) - info->irq = 4; - else + serial_out(info, UART_ESI_CMD1, 0x02); + + if (serial_in(info, UART_ESI_STAT1) & 0x01) info->irq = 3; + else + info->irq = 4; } - request_region(port,8,"esp"); + + if (IRQ_ports[0] && + (IRQ_ports[0]->port == (info->port - 8))) { + release_region(*region_start, + info->port - *region_start); + } else + *region_start = info->port; + + request_region(*region_start, + info->port - *region_start + 8, + "esp serial"); /* put card in enhanced mode */ /* this prevents access through */ @@ -2367,19 +2376,19 @@ static void autoconfig(struct esp_struct * info) serial_out(info, UART_ESI_CMD2, UART_MCR); serial_out(info, UART_ESI_CMD2, 0x00); } - } else { - info->port = 0; } restore_flags(flags); + return (port_detected); } /* * The serial driver boot-time initialization code! */ -int esp_init(void) +int espserial_init(void) { int i, offset; + int region_start; struct esp_struct * info; int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380}; @@ -2389,6 +2398,14 @@ int esp_init(void) IRQ_ports[i] = 0; } + for (i = 0; i < NR_PRIMARY; i++) + if (irq[i] != 0) + if ((irq[i] < 2) || (irq[i] > 15) || (irq[i] == 6) || + (irq[i] == 8) || (irq[i] == 13)) + irq[i] = 0; + else if (irq[i] == 2) + irq[i] = 9; + if ((dma != 1) && (dma != 3)) dma = 1; @@ -2458,19 +2475,12 @@ int esp_init(void) do { info->port = esp[i] + offset; + info->irq = irq[i]; + info->line = (i * 8) + (offset / 8); - /* check if i/o region is already in use */ - if (check_region(info->port, 8)) { - /* if it is a primary port, skip secondary ports */ - if (!offset) - i++; - else if (offset == 56) { - i++; - offset = 0; - } - else - offset += 8; - + if (!autoconfig(info, ®ion_start)) { + i++; + offset = 0; continue; } @@ -2478,11 +2488,7 @@ int esp_init(void) info->flags = STD_COM_FLAGS; if (info->custom_divisor) info->flags |= ASYNC_SPD_CUST; - info->irq = irq[i]; - info->magic = ESP_MAGIC; - info->line = (i * 8) + (offset / 8); - info->tty = 0; info->close_delay = 5*HZ/10; info->closing_wait = 30*HZ; info->tqueue.routine = do_softint; @@ -2492,15 +2498,6 @@ int esp_init(void) info->callout_termios = esp_callout_driver.init_termios; info->normal_termios = esp_driver.init_termios; - if (info->irq == 2) - info->irq = 9; - - autoconfig(info); - if (!info->port) { - i++; - offset = 0; - continue; - } if (IRQ_ports[0]) IRQ_ports[0]->prev_port = info; info->next_port = IRQ_ports[0]; @@ -2539,13 +2536,14 @@ int esp_init(void) int init_module(void) { - return esp_init(); + return espserial_init(); } void cleanup_module(void) { unsigned long flags; int e1, e2; + unsigned int region_start, region_end; struct esp_struct *current_async, *temp_async; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ @@ -2561,8 +2559,31 @@ void cleanup_module(void) current_async = IRQ_ports[0]; while (current_async != 0) { - release_region(current_async->port, 8); - current_async = current_async->next_port; + if (current_async->port != 0) { + region_start = region_end = current_async->port; + temp_async = current_async; + + while (temp_async != 0) { + if ((region_start - temp_async->port) == 8) { + region_start = temp_async->port; + temp_async->port = 0; + temp_async = current_async; + } else if ((temp_async->port - region_end) + == 8) { + region_end = temp_async->port; + temp_async->port = 0; + temp_async = current_async; + } else + temp_async = temp_async->next_port; + } + + release_region(region_start, + region_end - region_start + 8); + } + + temp_async = current_async->next_port; + kfree(current_async); + current_async = temp_async; } if (dma_buffer) @@ -2571,13 +2592,5 @@ void cleanup_module(void) if (tmp_buf) free_page((unsigned long)tmp_buf); - - /* free the port information */ - current_async = IRQ_ports[0]; - while (current_async != 0) { - temp_async = current_async->next_port; - kfree(current_async); - current_async = temp_async; - } } #endif /* MODULE */ diff --git a/drivers/char/esp.h b/drivers/char/esp.h index 96c3b9662204..b20f1895ee83 100644 --- a/drivers/char/esp.h +++ b/drivers/char/esp.h @@ -46,9 +46,10 @@ #define ESI_NO_COMMAND 0xff #define ESP_STAT_RX_TIMEOUT 0x01 -#define ESP_STAT_NEED_DMA 0x02 -#define ESP_STAT_DMA_RX 0x04 -#define ESP_STAT_DMA_TX 0x08 +#define ESP_STAT_NEED_DMA_RX 0x02 +#define ESP_STAT_NEED_DMA_TX 0x04 +#define ESP_STAT_DMA_RX 0x08 +#define ESP_STAT_DMA_TX 0x10 #define ESP_EVENT_WRITE_WAKEUP 0 #define ESP_MAGIC 0x53ee diff --git a/drivers/char/tpqic02.c b/drivers/char/tpqic02.c index 5a1f4983653d..c67cdc7056e8 100644 --- a/drivers/char/tpqic02.c +++ b/drivers/char/tpqic02.c @@ -1,12 +1,9 @@ -/* $Id: tpqic02.c,v 0.4.1.5 1994/10/29 02:46:13 root Exp root $ +/* $Id: tpqic02.c,v 0.7.1.5 1996/12/14 22:58:52 root Exp root $ * - * Driver for tape drive support for Linux-i386 1.1.58 + * Driver for tape drive support for Linux-i386 * - * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved. - * Current e-mail address: hennus@sky.ow.org [This is a UUCP link.] - * [If you are unable to reach me directly, try the TAPE mailing list - * channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as - * the first line in your message.] + * Copyright (c) 1992--1996 by H. H. Bergman. All rights reserved. + * Current e-mail address: hennus@cybercomm.nl * * Distribution of this program in executable form is only allowed if * all of the corresponding source files are made available through the same @@ -33,154 +30,28 @@ * * You are not allowed to change this line nor the text above. * - * $Log: tpqic02.c,v $ - * Revision 0.4.1.5 1994/10/29 02:46:13 root - * Minor cleanups. + * 1996/10/10 Emerald changes * - * Revision 0.4.1.4 1994/07/21 02:15:45 root - * ifdef'd DDI. Exception masks. + * 1996/05/21 Misc changes+merges+cleanups + I/O reservations * - * Revision 0.4.1.3 1994/05/03 01:49:09 root - * Initial attempt at Mountain support for the Mountain 7150. - * Based on patches provided by Erik Jacobson. + * 1996/05/20 Module support patches submitted by Brian McCauley. * - * Revision 0.4.1.2 1994/03/18 21:16:50 root - * Many driver messages can now be turned off (runtime selectable). + * 1994/05/03 Initial attempt at Mountain support for the Mountain 7150. + * Based on patches provided by Erik Jacobson. Still incomplete, I suppose. * - * Revision 0.4.1.1 1994/02/16 19:47:22 root - * First stab at runtime debug-variable. + * 1994/02/07 Archive changes & some cleanups by Eddy Olk. * - * Revision 0.4 1994/02/15 01:53:16 root - * DYNCONF mark II. - * Minor cleanups. + * 1994/01/19 Speed measuring stuff moved from aperf.h to delay.h. + * BogoMips (tm) introduced by Linus. * - * Revision 0.3 1994/02/07 01:23:16 root - * More improved DYNCONF. - * Archive changes & some cleanups by Eddy Olk. - * Removed status_open, more cleanups, misc other. - * - * Revision 0.2.1.25 1994/01/24 02:01:33 root - * Changed tape_qic02 to QIC02_TAPE. - * Changes to prepare for DYNCONF. - * - * Revision 0.2.1.24 1994/01/23 07:27:18 root - * Attempt to remove compilation warnings, G++ bug, - * Linus changed TAPE_QIC02 to QIC02_TAPE. - * - * Revision 0.2.1.23 1994/01/20 23:49:28 root - * Changed some exception decoding stuff. - * TP_HAVE_SEEK, TP_HAVE_DENS. byte_swap_w() on arg, not global. - * Attempt to fix cartridge-changed-problem for 2150L. - * Release irq and dma reservations if initial reset fails. - * - * Revision 0.2.1.22 1994/01/19 20:56:55 root - * Speed measuring stuff moved from aperf.h to delay.h. - * BogoMips (tm) introduced by Linus. - * - * Revision 0.2.1.21 1993/06/18 19:04:33 root - * minor fixes for 0.99.10. - * - * Revision 0.2.1.20 1993/06/11 21:38:51 root - * Added exception code for status 0x8000 (Cypher weirdness). - * - * Revision 0.2.1.19 1993/04/19 23:13:59 root - * Cleanups. Changed to 0.99.8. - * - * Revision 0.2.1.18 1993/03/22 17:39:47 root - * Moved to 0.99.7. Added Archive MTSEEK and MTTELL support. - * - * Revision 0.2.1.17 1993/03/08 18:51:59 root - * Tried to `fix' write-once bug in previous release. - * - * Revision 0.2.1.16 1993/03/01 00:06:16 root - * Use register_chrdev() for 0.99.6. - * - * Revision 0.2.1.15 1993/02/25 00:14:25 root - * minor cleanups. - * - * Revision 0.2.1.14 1993/01/25 00:06:14 root - * Kernel udelay. Eof fixups. - * Removed report_ read/write dummies; have strace(1) now. - * - * Revision 0.2.1.13 1993/01/10 02:24:43 root - * Rewrote wait_for_ready() to use newer schedule() features. - * This improves performance for rewinds etc. - * - * Revision 0.2.1.12 1993/01/05 18:44:09 root - * Changes for 0.99.1. Fixed `restartable reads'. - * - * Revision 0.2.1.11 1992/11/28 01:19:10 root - * Changes to exception handling (significant). - * Changed returned error codes. Hopefully they're correct now. - * Changed declarations to please gcc-2.3.1. - * Patch to deal with bogus interrupts for Archive cards. - * - * Revision 0.2.1.10 1992/10/28 00:50:44 root - * underrun/error counter needed byte swapping. - * - * Revision 0.2.1.9 1992/10/15 17:06:01 root - * Removed online() stuff. Changed EOF handling. - * - * Revision 0.2.1.8 1992/10/02 22:25:48 root - * Removed `no_sleep' parameters (got usleep() now), - * cleaned up some comments. - * - * Revision 0.2.1.7 1992/09/27 01:41:55 root - * Changed write() to do entire user buffer in one go, rather than just - * a kernel-buffer sized portion each time. - * - * Revision 0.2.1.6 1992/09/21 02:15:30 root - * Introduced udelay() function for microsecond-delays. - * Trying to use get_dma_residue rather than TC flags. - * Patch to fill entire user buffer on reads before - * returning. - * - * Revision 0.2.1.5 1992/09/19 02:31:28 root - * Some changes based on patches by Eddy Olk to - * support Archive SC402/SC499R controller cards. - * - * Revision 0.2.1.4 1992/09/07 01:37:37 root - * Minor changes - * - * Revision 0.2.1.3 1992/08/13 00:11:02 root - * Added some support for Archive SC402 and SC499 cards. - * (Untested.) - * - * Revision 0.2.1.2 1992/08/10 02:02:36 root - * Changed from linux/system.h macros to asm/dma.h inline functions. - * - * Revision 0.2.1.1 1992/08/08 01:12:39 root - * cleaned up a bit. added stuff for selftesting. - * preparing for asm/dma.h instead of linux/system.h - * - * Revision 0.2 1992/08/03 20:11:30 root - * Changed to use new IRQ allocation. Padding now done at runtime, pads to - * 512 bytes. Because of this the page regs must be re-programmed every - * block! Added hooks for selftest commands. - * Moved to linux-0.97. - * - * Revision 0.1.0.5 1992/06/22 22:20:30 root - * moved to Linux 0.96b - * - * Revision 0.1.0.4 1992/06/18 02:00:04 root - * Use minor bit-7 to enable/disable printing of extra debugging info - * when do tape access. - * Added semop stuff for DMA/IRQ allocation checking. Don't think this - * is the right way to do it though. - * - * Revision 0.1.0.3 1992/06/01 01:57:34 root - * changed DRQ to DMA. added TDEBUG ifdefs to reduce output. - * - * Revision 0.1.0.2 1992/05/31 14:02:38 root - * changed SET_DMA_PAGE handling slightly. - * - * Revision 0.1.0.1 1992/05/27 12:12:03 root - * Can now use multiple files on tape (sort of). - * First release. + * 1993/01/25 Kernel udelay. Eof fixups. + * + * 1992/09/19 Some changes based on patches by Eddy Olk to support + * Archive SC402/SC499R controller cards. * - * Revision 0.1 1992/05/26 01:16:31 root - * Initial version. Copyright H. H. Bergman 1992 + * 1992/05/27 First release. * + * 1992/05/26 Initial version. Copyright H. H. Bergman 1992 */ /* After the legalese, now the important bits: @@ -200,14 +71,10 @@ #define REALLY_SLOW_IO /* it sure is ... */ +#include + #include -#ifdef MODULE - #ifdef CONFIG_QIC02_DYNCONF - #error dynamic configuration as module not implemented! - #endif -#endif - #include #include #include @@ -218,6 +85,7 @@ #include #include #include +#include #include #include #include @@ -227,14 +95,9 @@ #include #include -/* We really shouldn't be using this define.. */ -#define IOCCMD_MASK 0x0000ffff - /* check existence of required configuration parameters */ -#if !defined(QIC02_CMD_PORT) || \ - !defined(QIC02_TAPE_IRQ) || \ - !defined(QIC02_TAPE_DMA) -#error qic02_tape configuration error +#if !defined(QIC02_CMD_PORT) || !defined(QIC02_TAPE_IRQ) || !defined(QIC02_TAPE_DMA) +# error qic02_tape configuration error #endif @@ -248,17 +111,19 @@ /* This holds the dynamic configuration info for the interface * card+drive info if runtime configuration has been selected. */ -struct mtconfiginfo qic02_tape_dynconf = { 0, }; /* user settable */ -struct qic02_ccb qic02_tape_ccb = { 0, }; /* private stuff */ + +static struct mtconfiginfo qic02_tape_dynconf = /* user settable */ + { 0, 0, BOGUS_IRQ, 0, 0, TPQD_DEFAULT_FLAGS, }; +static struct qic02_ccb qic02_tape_ccb = { 0, }; /* private stuff */ #else -unsigned long qic02_tape_debug; +unsigned long qic02_tape_debug = TPQD_DEFAULT_FLAGS; # if ((QIC02_TAPE_IFC!=WANGTEK) && (QIC02_TAPE_IFC!=ARCHIVE) && (QIC02_TAPE_IFC!=MOUNTAIN)) # error No valid interface card specified # endif -#endif +#endif /* CONFIG_QIC02_DYNCONF */ static volatile int ctlbits = 0; /* control reg bits for tape interface */ @@ -268,8 +133,8 @@ static volatile struct mtget ioctl_status; /* current generic status */ static volatile struct tpstatus tperror; /* last drive status */ -static char rcs_revision[] = "$Revision: 0.4.1.5 $"; -static char rcs_date[] = "$Date: 1994/10/29 02:46:13 $"; +static char rcs_revision[] = "$Revision: 0.7.1.5 $"; +static char rcs_date[] = "$Date: 1996/12/14 22:58:52 $"; /* Flag bits for status and outstanding requests. * (Could all be put in one bit-field-struct.) @@ -330,27 +195,17 @@ static char seek_addr_buf[AR_SEEK_BUF_SIZE]; */ static int mode_access; /* access mode: READ or WRITE */ +static int qic02_get_resources(void); +static void qic02_release_resources(void); -/* This is the actual kernel buffer where the interrupt routines read - * from/write to. It is needed because the DMA channels 1 and 3 cannot +/* This is a pointer to the actual kernel buffer where the interrupt routines + * read from/write to. It is needed because the DMA channels 1 and 3 cannot * always access the user buffers. [The kernel buffer must reside in the - * lower 16MBytes of system memory because of the DMA controller.] - * The user must ensure that a large enough buffer is passed to the - * kernel, in order to reduce tape repositioning. - * - * The buffer is 512 bytes larger than expected, because I want to align it - * at 512 bytes, to prevent problems with 64k boundaries. + * lower 16MBytes of system memory because of the DMA controller.] The user + * must ensure that a large enough buffer is passed to the kernel, in order + * to reduce tape repositioning wear and tear. */ - -#ifdef MODULE -static char *qic02_tape_buf = NULL; -#else -static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE]; -/* A really good compiler would be able to align this at 512 bytes... :-( */ -#endif /* MODULE */ - -static unsigned long buffaddr; /* aligned physical address of buffer */ - +static unsigned long buffaddr = 0; /* physical address of buffer */ /* This translates minor numbers to the corresponding recording format: */ static const char *format_names[] = { @@ -436,7 +291,15 @@ static struct exception_list_type { }; #define NR_OF_EXC (sizeof(exception_list)/sizeof(struct exception_list_type)) - +/* Compare expected struct size and actual struct size. This + * is useful to catch programs compiled with old #includes. + */ +#define CHECK_IOC_SIZE(structure) \ + if (_IOC_SIZE(iocmd) != sizeof(struct structure)) { \ + tpqputs(TPQD_ALWAYS, "sizeof(struct " #structure \ + ") does not match!"); \ + return -EFAULT; \ + } \ static void tpqputs(unsigned long flags, const char *s) { @@ -445,8 +308,6 @@ static void tpqputs(unsigned long flags, const char *s) } /* tpqputs */ - - /* Perform byte order swapping for a 16-bit word. * * [FIXME] This should probably be in include/asm/ @@ -468,27 +329,36 @@ static inline void byte_swap_w(volatile unsigned short * w) */ static void ifc_init(void) { - if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ { - ctlbits = WT_CTL_ONLINE; /* online */ - outb_p(ctlbits, QIC02_CTL_PORT); - - } else if (QIC02_TAPE_IFC == ARCHIVE) { - ctlbits = 0; /* no interrupts yet */ - outb_p(ctlbits, QIC02_CTL_PORT); - outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */ - - } else /* MOUNTAIN */ { - ctlbits = MTN_CTL_ONLINE; /* online, and logic enabled */ - outb_p(ctlbits, QIC02_CTL_PORT); - } + if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ + { + ctlbits = WT_CTL_ONLINE; /* online */ + outb_p(ctlbits, QIC02_CTL_PORT); + } + else if (QIC02_TAPE_IFC == ARCHIVE) + { + ctlbits = 0; /* no interrupts yet */ + outb_p(ctlbits, QIC02_CTL_PORT); + outb_p(0, AR_RESET_DMA_PORT); /* dummy write to reset DMA */ + } + else /* MOUNTAIN */ + { + ctlbits = MTN_CTL_ONLINE; /* online, and logic enabled */ + outb_p(ctlbits, QIC02_CTL_PORT); + } } /* ifc_init */ static void report_qic_exception(unsigned n) { - if (n >= NR_OF_EXC) { tpqputs(TPQD_ALWAYS, "Oops -- report_qic_exception"); n = 0; } - if (TPQDBG(SENSE_TEXT) || n==0) - printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg); + if (n >= NR_OF_EXC) + { + tpqputs(TPQD_ALWAYS, "Oops -- report_qic_exception"); + n = 0; + } + if (TPQDBG(SENSE_TEXT) || n==0) + { + printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg); + } } /* report_qic_exception */ @@ -499,126 +369,63 @@ static void report_qic_exception(unsigned n) */ static int decode_qic_exception_nr(unsigned s) { - int i; + int i; - for (i=1; i= 0) - sensemsg(n); -} /* report_error */ -#endif - /* Perform appropriate action for certain exceptions. * should return a value to indicate stop/continue (in case of bad blocks) */ static void handle_qic_exception(int exnr, int exbits) { - if (exnr==EXC_NCART) { - /* Cartridge was changed. Redo sense(). - * EXC_NCART should be handled in open(). - * It is not permitted to remove the tape while - * the tape driver has open files. - */ - need_rewind = YES; - status_eof_detected = NO; - status_eom_detected = NO; - } - else if (exnr==EXC_XFILLER) - tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]"); - else if (exnr==EXC_XBAD) - tpqputs(TPQD_ALWAYS, "[CRC failed!]"); - else if (exnr==EXC_MARGINAL) { - /* A marginal block behaves much like a FM. - * User may continue reading, if desired. - */ - tpqputs(TPQD_ALWAYS, "[Marginal block]"); - doing_read = NO; - } else if (exnr==EXC_FM) - doing_read = NO; + if (exnr==EXC_NCART) + { + /* Cartridge was changed. Redo sense(). + * EXC_NCART should be handled in open(). + * It is not permitted to remove the tape while + * the tape driver has open files. + */ + need_rewind = YES; + status_eof_detected = NO; + status_eom_detected = NO; + } + else if (exnr==EXC_XFILLER) + { + tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]"); + } + else if (exnr==EXC_XBAD) + { + tpqputs(TPQD_ALWAYS, "[CRC failed!]"); + } + else if (exnr==EXC_MARGINAL) + { + /* A marginal block behaves much like a FM. + * User may continue reading, if desired. + */ + tpqputs(TPQD_ALWAYS, "[Marginal block]"); + doing_read = NO; + } + else if (exnr==EXC_FM) + { + doing_read = NO; + } } /* handle_qic_exception */ static inline int is_exception(void) { - return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0; + return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0; } /* is_exception */ @@ -628,41 +435,53 @@ static inline int is_exception(void) */ static int tape_reset(int verbose) { - ifc_init(); /* reset interface card */ - - /* assert reset */ - if (QIC02_TAPE_IFC == MOUNTAIN) - outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); - else /* WANGTEK, ARCHIVE */ - outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT); - - /* Next, we need to wait >=25 usec. */ - udelay(30); - - /* after reset, we will be at BOT (modulo an automatic rewind) */ - status_eof_detected = NO; - status_eom_detected = NO; - status_cmd_pending = 0; - need_rewind = YES; - doing_read = doing_write = NO; - ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0; - - /* de-assert reset */ - if (QIC02_TAPE_IFC == MOUNTAIN) - outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); - else - outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT); - - /* KLUDGE FOR G++ BUG */ - { int stat = inb_p(QIC02_STAT_PORT); - status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); } - /* if successful, inb(STAT) returned RESETVAL */ - if (status_dead == YES) - printk(TPQIC02_NAME ": reset failed!\n"); - else if (verbose) - printk(TPQIC02_NAME ": reset successful\n"); - - return (status_dead == YES)? TE_DEAD : TE_OK; + ifc_init(); /* reset interface card */ + + /* assert reset */ + if (QIC02_TAPE_IFC == MOUNTAIN) + { + outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); + } + else /* WANGTEK, ARCHIVE */ + { + outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT); + } + + /* Next, we need to wait >=25 usec. */ + udelay(30); + + /* after reset, we will be at BOT (modulo an automatic rewind) */ + status_eof_detected = NO; + status_eom_detected = NO; + status_cmd_pending = 0; + need_rewind = YES; + doing_read = doing_write = NO; + ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0; + + /* de-assert reset */ + if (QIC02_TAPE_IFC == MOUNTAIN) + { + outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT); + } + else + { + outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT); + } + + /* KLUDGE FOR G++ BUG */ + { int stat = inb_p(QIC02_STAT_PORT); + status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); } + /* if successful, inb(STAT) returned RESETVAL */ + if (status_dead == YES) + { + printk(TPQIC02_NAME ": reset failed!\n"); + } + else if (verbose) + { + printk(TPQIC02_NAME ": reset successful\n"); + } + + return (status_dead == YES)? TE_DEAD : TE_OK; } /* tape_reset */ @@ -1852,9 +1671,10 @@ static void qic02_tape_interrupt(int irq, void *dev_id, struct pt_regs *regs) * if dma channel hasn't finished last byte yet. */ r = 0; -/* Skip next ready check for Archive controller because - * it may be busy reading ahead. Weird. --hhb - */ + + /* Skip next ready check for Archive controller because + * it may be busy reading ahead. Weird. --hhb + */ if (QIC02_TAPE_IFC == WANGTEK) /* I think this is a drive-dependency, not IFC -- hhb */ if (stat & QIC02_STAT_READY) { /* not ready */ tpqputs(TPQD_ALWAYS, "isr: ? Tape controller not ready"); @@ -1892,7 +1712,7 @@ static void qic02_tape_interrupt(int irq, void *dev_id, struct pt_regs *regs) static long long qic02_tape_lseek(struct inode * inode, struct file * file, - long long offset, int origin) + long long offset, int origin) { return -EINVAL; /* not supported */ } /* qic02_tape_lseek */ @@ -1931,145 +1751,187 @@ static long long qic02_tape_lseek(struct inode * inode, struct file * file, */ static long qic02_tape_read(struct inode * inode, struct file * filp, - char * buf, unsigned long count) + char * buf, unsigned long count) { - kdev_t dev = inode->i_rdev; - unsigned short flags = filp->f_flags; - unsigned long bytes_todo, bytes_done, total_bytes_done = 0; - int stat; - - if (status_zombie==YES) { - tpqputs(TPQD_ALWAYS, "configs not set"); - return -ENXIO; - } - - if (TP_DIAGS(current_tape_dev)) - /* can't print a ``long long'' (for filp->f_pos), so chop it */ - printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n", - MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags); - - if (count % TAPE_BLKSIZE) { /* Only allow mod 512 bytes at a time. */ - tpqputs(TPQD_BLKSZ, "Wrong block size"); - return -EINVAL; + int err; + kdev_t dev = inode->i_rdev; + unsigned short flags = filp->f_flags; + unsigned long bytes_todo, bytes_done, total_bytes_done = 0; + int stat; + + if (status_zombie==YES) + { + tpqputs(TPQD_ALWAYS, "configs not set"); + return -ENXIO; + } + + if (TP_DIAGS(current_tape_dev)) + /* can't print a ``long long'' (for filp->f_pos), so chop it */ + printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n", + MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags); + + if (count % TAPE_BLKSIZE) /* Only allow mod 512 bytes at a time. */ + { + tpqputs(TPQD_BLKSZ, "Wrong block size"); + return -EINVAL; + } + + /* Just assume everything is ok. Controller will scream if not. */ + + if (status_bytes_wr) /* Once written, no more reads, 'till after WFM. */ + { + return -EACCES; + } + + /* This is rather ugly because it has to implement a finite state + * machine in order to handle the EOF situations properly. + */ + while ((signed)count>=0) + { + bytes_done = 0; + /* see how much fits in the kernel buffer */ + bytes_todo = TPQBUF_SIZE; + if (bytes_todo>count) + { + bytes_todo = count; } - - /* Just assume everything is ok. Controller will scream if not. */ - - if (status_bytes_wr) /* Once written, no more reads, 'till after WFM. */ - return -EACCES; - - /* This is rather ugly because it has to implement a finite state - * machine in order to handle the EOF situations properly. - */ - while ((signed)count>=0) { - bytes_done = 0; - /* see how much fits in the kernel buffer */ - bytes_todo = TPQBUF_SIZE; - if (bytes_todo>count) - bytes_todo = count; - - /* Must ensure that user program sees exactly one EOF token (==0) */ - if (return_read_eof==YES) { - if (TPQDBG(DEBUG)) - printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\n", return_read_eof, reported_read_eof, total_bytes_done); - - if (reported_read_eof==NO) { - /* have not yet returned EOF to user program */ - if (total_bytes_done>0) { - return total_bytes_done; /* next time return EOF */ - } else { - reported_read_eof = YES; /* move on next time */ - return 0; /* return EOF */ - } - } else { - /* Application program has already received EOF - * (above), now continue with next file on tape, - * if possible. - * When the FM is reached, EXCEPTION is set, - * causing a sense(). Subsequent read/writes will - * continue after the FM. - */ -/*********** ?????????? this should check for (EOD|NDT), not EOM, 'cause we can read past EW: ************/ - if (status_eom_detected) - /* If EOM, nothing left to read, so keep returning EOFs. - *** should probably set some flag to avoid clearing - *** status_eom_detected through ioctls or something - */ - return 0; - else { - /* just eof, there may be more files ahead... */ - return_read_eof = NO; - reported_read_eof = NO; - status_eof_detected = NO; /* reset this too */ - /*fall through*/ - } - } + + /* Must ensure that user program sees exactly one EOF token (==0) */ + if (return_read_eof==YES) + { + if (TPQDBG(DEBUG)) + { + printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\n", return_read_eof, reported_read_eof, total_bytes_done); + } + + if (reported_read_eof==NO) + { + /* have not yet returned EOF to user program */ + if (total_bytes_done>0) + { + return total_bytes_done; /* next time return EOF */ } - -/*****************************/ - if (bytes_todo==0) - return total_bytes_done; - - if (bytes_todo>0) { - /* start reading data */ - if (is_exception()) /****************************************/ - tpqputs(TPQD_DMAX, "is_exception() before start_dma()!"); + else + { + reported_read_eof = YES; /* move on next time */ + return 0; /* return EOF */ + } + } + else + { + /* Application program has already received EOF + * (above), now continue with next file on tape, + * if possible. + * When the FM is reached, EXCEPTION is set, + * causing a sense(). Subsequent read/writes will + * continue after the FM. + */ + /*********** ?????????? this should check for (EOD|NDT), not EOM, 'cause we can read past EW: ************/ + if (status_eom_detected) + { + /* If EOM, nothing left to read, so keep returning EOFs. + *** should probably set some flag to avoid clearing + *** status_eom_detected through ioctls or something + */ + return 0; + } + else + { + /* just eof, there may be more files ahead... */ + return_read_eof = NO; + reported_read_eof = NO; + status_eof_detected = NO; /* reset this too */ + /*fall through*/ + } + } + } + + /*****************************/ + if (bytes_todo==0) + { + return total_bytes_done; + } + + if (bytes_todo>0) + { + /* start reading data */ + if (is_exception()) /****************************************/ + { + tpqputs(TPQD_DMAX, "is_exception() before start_dma()!"); + } + /****************************************************************** ***** if start_dma() fails because the head is positioned 0 bytes ***** before the FM, (causing EXCEPTION to be set) return_read_eof should ***** be set to YES, and we should return total_bytes_done, rather than -ENXIO. ***** The app should recognize this as an EOF condition. ***************************************************************************/ - stat = start_dma(READ, bytes_todo); - if (stat == TE_OK) { - /* Wait for transfer to complete, interrupt should wake us */ - while (dma_mode != 0) { - sleep_on(&qic02_tape_transfer); - } - if (status_error) - return_read_eof = YES; - } else if (stat != TE_END) { - /* should do sense() on error here */ + stat = start_dma(READ, bytes_todo); + if (stat == TE_OK) + { + /* Wait for transfer to complete, interrupt should wake us */ + while (dma_mode != 0) + { + sleep_on(&qic02_tape_transfer); + } + if (status_error) + { + return_read_eof = YES; + } + + } + else if (stat != TE_END) + { + /* should do sense() on error here */ #if 0 - return -ENXIO; + return -ENXIO; #else - printk("Trouble: stat==%02x\n", stat); - return_read_eof = YES; - /*************** check EOF/EOT handling!!!!!! **/ + printk("Trouble: stat==%02x\n", stat); + return_read_eof = YES; + /*************** check EOF/EOT handling!!!!!! **/ #endif - } - end_dma(&bytes_done); - if (bytes_done>bytes_todo) { - tpqputs(TPQD_ALWAYS, "read: Oops, read more bytes than requested"); - return -EIO; - } - /* copy buffer to user-space in one go */ - if (bytes_done>0) - if (copy_to_user( (void *) buf, (void *) bus_to_virt(buffaddr), - bytes_done)) - return -EFAULT; - + } + end_dma(&bytes_done); + if (bytes_done>bytes_todo) + { + tpqputs(TPQD_ALWAYS, "read: Oops, read more bytes than requested"); + return -EIO; + } + /* copy buffer to user-space in one go */ + if (bytes_done>0) + { + err = copy_to_user( (void *) buf, (void *) bus_to_virt(buffaddr), bytes_done); + if (err) + { + return -EFAULT; + } + } #if 1 - /* Checks Ton's patch below */ - if ((return_read_eof == NO) && (status_eof_detected == YES)) { - printk(TPQIC02_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof); - } + /* Checks Ton's patch below */ + if ((return_read_eof == NO) && (status_eof_detected == YES)) + { + printk(TPQIC02_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof); + } #endif - if ((bytes_todo != bytes_done) || (status_eof_detected == YES)) - /* EOF or EOM detected. return EOF next time. */ - return_read_eof = YES; - } /* else: ignore read request for 0 bytes */ - - if (bytes_done>0) { - status_bytes_rd = YES; - buf += bytes_done; - filp->f_pos += bytes_done; - total_bytes_done += bytes_done; - count -= bytes_done; - } - } - tpqputs(TPQD_ALWAYS, "read request for <0 bytes"); - return -EINVAL; + if ((bytes_todo != bytes_done) || (status_eof_detected == YES)) + { + /* EOF or EOM detected. return EOF next time. */ + return_read_eof = YES; + } + + } /* else: ignore read request for 0 bytes */ + + if (bytes_done>0) + { + status_bytes_rd = YES; + buf += bytes_done; + filp->f_pos += bytes_done; + total_bytes_done += bytes_done; + count -= bytes_done; + } + } + tpqputs(TPQD_ALWAYS, "read request for <0 bytes"); + return -EINVAL; } /* qic02_tape_read */ @@ -2102,77 +1964,98 @@ static long qic02_tape_read(struct inode * inode, struct file * filp, * tape device again. The driver will detect an exception status in (No Cartridge) * and force a rewind. After that tar may continue writing. */ -static long qic02_tape_write(struct inode * inode, struct file * filp, - const char * buf, unsigned long count) +static long qic02_tape_write(struct inode * inode, struct file * filp, + const char * buf, unsigned long count) { - kdev_t dev = inode->i_rdev; - unsigned short flags = filp->f_flags; - unsigned long bytes_todo, bytes_done, total_bytes_done = 0; - - if (status_zombie==YES) { - tpqputs(TPQD_ALWAYS, "configs not set"); - return -ENXIO; - } - - if (TP_DIAGS(current_tape_dev)) - /* can't print a ``long long'' (for filp->f_pos), so chop it */ - printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n", - MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags); - - if (count % TAPE_BLKSIZE) { /* only allow mod 512 bytes at a time */ - tpqputs(TPQD_BLKSZ, "Wrong block size"); - return -EINVAL; + int err; + kdev_t dev = inode->i_rdev; + unsigned short flags = filp->f_flags; + unsigned long bytes_todo, bytes_done, total_bytes_done = 0; + + if (status_zombie==YES) + { + tpqputs(TPQD_ALWAYS, "configs not set"); + return -ENXIO; + } + + if (TP_DIAGS(current_tape_dev)) + { + /* can't print a ``long long'' (for filp->f_pos), so chop it */ + printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%lx, pos=%lx, flags=%x\n", + MINOR(dev), buf, count, (unsigned long) filp->f_pos, flags); + } + + if (count % TAPE_BLKSIZE) /* only allow mod 512 bytes at a time */ + { + tpqputs(TPQD_BLKSZ, "Wrong block size"); + return -EINVAL; + } + + if (mode_access==READ) + { + tpqputs(TPQD_ALWAYS, "Not in write mode"); + return -EACCES; + } + + /* open() does a sense() and we can assume the tape isn't changed + * between open() and release(), so the tperror.exs bits will still + * be valid. + */ + if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) + { + tpqputs(TPQD_ALWAYS, "Cartridge is write-protected."); + return -EACCES; /* don't even try when write protected */ + } + + if (doing_read == YES) + { + terminate_read(0); + } + + while ((signed)count>=0) + { + /* see how much fits in the kernel buffer */ + bytes_done = 0; + bytes_todo = TPQBUF_SIZE; + if (bytes_todo>count) + { + bytes_todo = count; } - - if (mode_access==READ) { - tpqputs(TPQD_ALWAYS, "Not in write mode"); - return -EACCES; + + if (return_write_eof == YES) + { + /* return_write_eof should be reset on reverse tape movements. */ + + if (reported_write_eof==NO) + { + if (bytes_todo>0) + { + tpqputs(TPQD_ALWAYS, "partial write"); + /* partial write signals EOF to user program */ + } + reported_write_eof = YES; + return total_bytes_done; + } + else + { + return -ENOSPC; /* return error */ + } } - - /* open() does a sense() and we can assume the tape isn't changed - * between open() and release(), so the tperror.exs bits will still - * be valid. - */ - if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) { - tpqputs(TPQD_ALWAYS, "Cartridge is write-protected."); - return -EACCES; /* don't even try when write protected */ + + /* Quit when done. */ + if (bytes_todo==0) + { + return total_bytes_done; } - - if (doing_read == YES) - terminate_read(0); - - while ((signed)count>=0) { - /* see how much fits in the kernel buffer */ - bytes_done = 0; - bytes_todo = TPQBUF_SIZE; - if (bytes_todo>count) - bytes_todo = count; - if (return_write_eof == YES) { - /* return_write_eof should be reset on reverse tape movements. */ - - if (reported_write_eof==NO) { - if (bytes_todo>0) { - tpqputs(TPQD_ALWAYS, "partial write"); - /* partial write signals EOF to user program */ - } - reported_write_eof = YES; - return total_bytes_done; - } else { - return -ENOSPC; /* return error */ - } - } - - /* Quit when done. */ - if (bytes_todo==0) - return total_bytes_done; - - - /* copy from user to DMA buffer and initiate transfer. */ - if (bytes_todo>0) { - if (copy_from_user( (void *) bus_to_virt(buffaddr), - (const void *) buf, bytes_todo)) - return -EFAULT; + /* copy from user to DMA buffer and initiate transfer. */ + if (bytes_todo>0) + { + err = copy_from_user( (void *) bus_to_virt(buffaddr), (const void *) buf, bytes_todo); + if (err) + { + return -EFAULT; + } /****************** similar problem with read() at FM could happen here at EOT. ******************/ @@ -2180,62 +2063,75 @@ static long qic02_tape_write(struct inode * inode, struct file * filp, /***** if at EOT, 0 bytes can be written. start_dma() will ***** fail and write() will return ENXIO error *****/ - if (start_dma(WRITE, bytes_todo) != TE_OK) { - tpqputs(TPQD_ALWAYS, "write: start_dma() failed"); - /* should do sense() on error here */ - return -ENXIO; /*********** FIXTHIS **************/ - } - - /* Wait for write to complete, interrupt should wake us. */ - while ((status_error == 0) && (dma_mode != 0)) { - sleep_on(&qic02_tape_transfer); - } - - end_dma(&bytes_done); - if (bytes_done>bytes_todo) { - tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested"); - return -EIO; - } - /* If the dma-transfer was aborted because of an exception, - * status_error will have been set in the interrupt handler. - * Then end_dma() will do a sense(). - * If the exception was EXC_EOM, the EW-hole was encountered - * and two more blocks could be written. For the time being we'll - * just consider this to be the EOT. - * Otherwise, something Bad happened, such as the maximum number - * of block-rewrites was exceeded. [e.g. A very bad spot on tape was - * encountered. Normally short dropouts are compensated for by - * rewriting the block in error, up to 16 times. I'm not sure - * QIC-24 drives can do this.] - */ - if (status_error) { - if (status_eom_detected == YES) { - tpqputs(TPQD_ALWAYS, "write: EW detected"); - return_write_eof = YES; - } else { - /* probably EXC_RWA */ - tpqputs(TPQD_ALWAYS, "write: dma: error in writing"); - return -EIO; - } - } - if (bytes_todo != bytes_done) - /* EOF or EOM detected. return EOT next time. */ - return_write_eof = YES; + if (start_dma(WRITE, bytes_todo) != TE_OK) + { + tpqputs(TPQD_ALWAYS, "write: start_dma() failed"); + /* should do sense() on error here */ + return -ENXIO; /*********** FIXTHIS **************/ + } + + /* Wait for write to complete, interrupt should wake us. */ + while ((status_error == 0) && (dma_mode != 0)) + { + sleep_on(&qic02_tape_transfer); + } + + end_dma(&bytes_done); + if (bytes_done>bytes_todo) + { + tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested"); + return -EIO; + } + /* If the dma-transfer was aborted because of an exception, + * status_error will have been set in the interrupt handler. + * Then end_dma() will do a sense(). + * If the exception was EXC_EOM, the EW-hole was encountered + * and two more blocks could be written. For the time being we'll + * just consider this to be the EOT. + * Otherwise, something Bad happened, such as the maximum number + * of block-rewrites was exceeded. [e.g. A very bad spot on tape was + * encountered. Normally short dropouts are compensated for by + * rewriting the block in error, up to 16 times. I'm not sure + * QIC-24 drives can do this.] + */ + if (status_error) + { + if (status_eom_detected == YES) + { + tpqputs(TPQD_ALWAYS, "write: EW detected"); + return_write_eof = YES; } - /* else: ignore write request for 0 bytes. */ - - if (bytes_done>0) { - status_bytes_wr = YES; - buf += bytes_done; - filp->f_pos += bytes_done; - total_bytes_done += bytes_done; - count -= bytes_done; + else + { + /* probably EXC_RWA */ + tpqputs(TPQD_ALWAYS, "write: dma: error in writing"); + return -EIO; } - } - tpqputs(TPQD_ALWAYS, "write request for <0 bytes"); - if (TPQDBG(DEBUG)) - printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %lx\n", status_bytes_wr, buf, total_bytes_done, count); - return -EINVAL; + } + if (bytes_todo != bytes_done) + { + /* EOF or EOM detected. return EOT next time. */ + return_write_eof = YES; + } + } + /* else: ignore write request for 0 bytes. */ + + if (bytes_done>0) + { + status_bytes_wr = YES; + buf += bytes_done; + filp->f_pos += bytes_done; + total_bytes_done += bytes_done; + count -= bytes_done; + } + } + + tpqputs(TPQD_ALWAYS, "write request for <0 bytes"); + if (TPQDBG(DEBUG)) + { + printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %lx\n", status_bytes_wr, buf, total_bytes_done, count); + } + return -EINVAL; } /* qic02_tape_write */ @@ -2251,344 +2147,388 @@ static long qic02_tape_write(struct inode * inode, struct file * filp, * remembered values, rewind the tape and set the required density. * Don't rewind if the minor bits specify density 0. */ + static int qic02_tape_open(struct inode * inode, struct file * filp) { - kdev_t dev = inode->i_rdev; - unsigned short flags = filp->f_flags; - unsigned short dens = 0; - int s; - - - if (TP_DIAGS(dev)) { - printk("qic02_tape_open: dev=%s, flags=%x ", - kdevname(dev), flags); - } + static int qic02_tape_open_no_use_count(struct inode *, struct file *); + int open_error; + + open_error = qic02_tape_open_no_use_count(inode, filp); + if (!open_error) + { + MOD_INC_USE_COUNT; + } + return open_error; +} -#ifdef MODULE - MOD_INC_USE_COUNT; -#endif +static int qic02_tape_open_no_use_count(struct inode * inode, struct file * filp) +{ + kdev_t dev = inode->i_rdev; + unsigned short flags = filp->f_flags; + unsigned short dens = 0; + int s; - if (MINOR(dev)==255) { /* special case for resetting */ -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - if (suser()) - return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO; - else - return -EPERM; - } - if (status_dead==YES) - /* Allow `mt reset' ioctl() even when already open()ed. */ - return 0; + if (TP_DIAGS(dev)) + { + printk("qic02_tape_open: dev=%s, flags=%x ", + kdevname(dev), flags); + } - /* Only one at a time from here on... */ - if (filp->f_count>1) { /* filp->f_count==1 for the first open() */ -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return -EBUSY; + if (MINOR(dev)==255) /* special case for resetting */ + { + if (suser()) + { + return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO; } - - if (status_zombie==YES) - /* no irq/dma/port stuff allocated yet, no reset done - * yet, so return until MTSETCONFIG has been done. - */ - return 0; - - status_bytes_rd = NO; - status_bytes_wr = NO; - - return_read_eof = NO; /********????????????????*****/ - return_write_eof = (status_eot_detected)? YES : NO; - - /* Clear this in case user app close()d before reading EOF token */ - status_eof_detected = NO; - - reported_read_eof = NO; - reported_write_eof = NO; - - - switch (flags & O_ACCMODE) { - case O_RDONLY: - mode_access = READ; - break; - case O_WRONLY: /* Fallthru... Strictly speaking this is not correct... */ - case O_RDWR: /* Reads are allowed as long as nothing is written */ - mode_access = WRITE; - break; + else + { + return -EPERM; } - - /* This is to avoid tape-changed problems (TP_CNI exception). - * - * Since removing the cartridge will not raise an exception, - * we always do a tp_sense() to make sure we have the proper - * CNI status, the 2150L may need an additional sense.... - Eddy + } + + if (status_dead==YES) + { + /* Allow `mt reset' ioctl() even when already open()ed. */ + return 0; + } + + /* Only one at a time from here on... */ + if (filp->f_count>1) /* filp->f_count==1 for the first open() */ + { + return -EBUSY; + } + + if (status_zombie==YES) + { + /* no irq/dma/port stuff allocated yet, no reset done + * yet, so return until MTSETCONFIG has been done. */ - s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); - - if (s == TE_OK) - /* Try to clear cartridge-changed status for Archive-2150L */ - if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) - s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); - - if (s != TE_OK) { - tpqputs(TPQD_ALWAYS, "open: sense() failed"); -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return -EIO; - } - - /* exception bits should be up-to-date now, so check for - * tape presence and exit if absent. - * Even `mt stat' will fail without a tape. + return 0; + } + + status_bytes_rd = NO; + status_bytes_wr = NO; + + return_read_eof = NO; /********????????????????*****/ + return_write_eof = (status_eot_detected)? YES : NO; + + /* Clear this in case user app close()d before reading EOF token */ + status_eof_detected = NO; + + reported_read_eof = NO; + reported_write_eof = NO; + + + switch (flags & O_ACCMODE) + { + case O_RDONLY: + mode_access = READ; + break; + case O_WRONLY: /* Fallthru... Strictly speaking this is not correct... */ + case O_RDWR: /* Reads are allowed as long as nothing is written */ + mode_access = WRITE; + break; + } + + /* This is to avoid tape-changed problems (TP_CNI exception). + * + * Since removing the cartridge will not raise an exception, + * we always do a tp_sense() to make sure we have the proper + * CNI status, the 2150L may need an additional sense.... - Eddy + */ + s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); + + if (s == TE_OK) + { + /* Try to clear cartridge-changed status for Archive-2150L */ + if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) + { + s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR); + } + } + + if (s != TE_OK) + { + tpqputs(TPQD_ALWAYS, "open: sense() failed"); + return -EIO; + } + + /* exception bits should be up-to-date now, so check for + * tape presence and exit if absent. + * Even `mt stat' will fail without a tape. + */ + if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) + { + tpqputs(TPQD_ALWAYS, "No tape present."); + return -EIO; + } + + /* At this point we can assume that a tape is present and + * that it will remain present until release() is called. + */ + + /* not allowed to do QCMD_DENS_* unless tape is rewound */ + if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) + { + /* force rewind if minor bits have changed, + * i.e. user wants to use tape in different format. + * [assuming single drive operation] */ - if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) { - tpqputs(TPQD_ALWAYS, "No tape present."); -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return -EIO; - } - - /* At this point we can assume that a tape is present and - * that it will remain present until release() is called. + if (TP_HAVE_DENS) + { + tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind."); + need_rewind = YES; + } + } + else + { + /* density bits still the same, but TP_DIAGS bit + * may have changed. */ - - /* not allowed to do QCMD_DENS_* unless tape is rewound */ - if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) { - /* force rewind if minor bits have changed, - * i.e. user wants to use tape in different format. - * [assuming single drive operation] - */ - if (TP_HAVE_DENS) { - tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind."); - need_rewind = YES; - } - } else { - /* density bits still the same, but TP_DIAGS bit - * may have changed. - */ - current_tape_dev = dev; - } - - if (need_rewind == YES) { /***************** CHECK THIS!!!!!!!! **********/ - s = do_qic_cmd(QCMD_REWIND, TIM_R); - if (s != 0) { - tpqputs(TPQD_ALWAYS, "open: rewind failed"); -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return -EIO; - } + current_tape_dev = dev; + } + + if (need_rewind == YES) /***************** CHECK THIS!!!!!!!! **********/ + { + s = do_qic_cmd(QCMD_REWIND, TIM_R); + if (s != 0) + { + tpqputs(TPQD_ALWAYS, "open: rewind failed"); + return -EIO; } + } /* Note: After a reset command, the controller will rewind the tape * just before performing any tape movement operation! ************ SO SET need_rewind flag!!!!! */ - if (status_dead==YES) { - tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset"); - if (tape_reset(1)!=TE_OK) { -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return -ENXIO; - } else { - status_dead = NO; - if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) { - tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n"); - status_dead = YES; /* try reset next time */ -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return -EIO; - } - } + if (status_dead==YES) + { + tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset"); + if (tape_reset(1)!=TE_OK) + { + return -ENXIO; } - - /* things should be ok, once we get here */ - - - /* set density: only allowed when TP_BOM status bit is set, - * so we must have done a rewind by now. If not, just skip over. - * Only give set density command when minor bits have changed. - */ - if (TP_DENS(current_tape_dev) == TP_DENS(dev) ) - return 0; - - current_tape_dev = dev; - need_rewind = NO; - if (TP_HAVE_DENS) - dens = TP_DENS(dev); - - if (dens < sizeof(format_names)/sizeof(char *)) - printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]); else - tpqputs(TPQD_REWIND, "Wait for retensioning..."); - - switch (TP_DENS(dev)) { - case 0: /* Minor 0 is for drives without set-density support */ - s = 0; - break; - case 1: - s = do_qic_cmd(QCMD_DENS_11, TIM_S); - break; - case 2: - s = do_qic_cmd(QCMD_DENS_24, TIM_S); - break; - case 3: - s = do_qic_cmd(QCMD_DENS_120, TIM_S); - break; - case 4: - s = do_qic_cmd(QCMD_DENS_150, TIM_S); - break; - case 5: - s = do_qic_cmd(QCMD_DENS_300, TIM_S); - break; - case 6: - s = do_qic_cmd(QCMD_DENS_600, TIM_S); - break; - default: /* otherwise do a retension before anything else */ - s = do_qic_cmd(QCMD_RETEN, TIM_R); - } - if (s != 0) { - status_dead = YES; /* force reset */ - current_tape_dev = 0; /* earlier 0xff80 */ -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif + { + status_dead = NO; + if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) + { + tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n"); + status_dead = YES; /* try reset next time */ return -EIO; - } - + } + } + } + + /* things should be ok, once we get here */ + + + /* set density: only allowed when TP_BOM status bit is set, + * so we must have done a rewind by now. If not, just skip over. + * Only give set density command when minor bits have changed. + */ + if (TP_DENS(current_tape_dev) == TP_DENS(dev) ) + { return 0; + } + + current_tape_dev = dev; + need_rewind = NO; + if (TP_HAVE_DENS) + { + dens = TP_DENS(dev); + } + + if (dens < sizeof(format_names)/sizeof(char *)) + { + printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]); + } + else + { + tpqputs(TPQD_REWIND, "Wait for retensioning..."); + } + + switch (TP_DENS(dev)) + { + case 0: /* Minor 0 is for drives without set-density support */ + s = 0; + break; + case 1: + s = do_qic_cmd(QCMD_DENS_11, TIM_S); + break; + case 2: + s = do_qic_cmd(QCMD_DENS_24, TIM_S); + break; + case 3: + s = do_qic_cmd(QCMD_DENS_120, TIM_S); + break; + case 4: + s = do_qic_cmd(QCMD_DENS_150, TIM_S); + break; + case 5: + s = do_qic_cmd(QCMD_DENS_300, TIM_S); + break; + case 6: + s = do_qic_cmd(QCMD_DENS_600, TIM_S); + break; + default: /* otherwise do a retension before anything else */ + s = do_qic_cmd(QCMD_RETEN, TIM_R); + } + if (s != 0) + { + status_dead = YES; /* force reset */ + current_tape_dev = 0; /* earlier 0xff80 */ + return -EIO; + } + + return 0; } /* qic02_tape_open */ static void qic02_tape_release(struct inode * inode, struct file * filp) { - kdev_t dev = inode->i_rdev; - - if (TP_DIAGS(dev)) - printk("qic02_tape_release: dev=%s\n", - kdevname(dev)); - - if (status_zombie==YES) /* don't rewind in zombie mode */ -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return; - + kdev_t dev = inode->i_rdev; + + if (TP_DIAGS(dev)) + { + printk("qic02_tape_release: dev=%s\n", kdevname(dev)); + } + + if (status_zombie==NO) /* don't rewind in zombie mode */ + { /* Terminate any pending write cycle. Terminating the read-cycle * is delayed until it is required to do so for a new command. */ terminate_write(-1); - + if (status_dead==YES) - tpqputs(TPQD_ALWAYS, "release: device dead!?"); - - /* Rewind only if minor number requires it AND + { + tpqputs(TPQD_ALWAYS, "release: device dead!?"); + } + + /* Rewind only if minor number requires it AND * read/writes have been done. ************* IS THIS CORRECT?????????? */ - if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) { - tpqputs(TPQD_REWIND, "release: Doing rewind..."); - (void) do_qic_cmd(QCMD_REWIND, TIM_R); + if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) + { + tpqputs(TPQD_REWIND, "release: Doing rewind..."); + (void) do_qic_cmd(QCMD_REWIND, TIM_R); } + } #ifdef MODULE - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; #endif - return; + return; } /* qic02_tape_release */ #ifdef CONFIG_QIC02_DYNCONF /* Set masks etc. based on the interface card type. */ -int update_ifc_masks(int ifc) +static int update_ifc_masks(int ifc) { - QIC02_TAPE_IFC = ifc; - - if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) { - QIC02_STAT_PORT = QIC02_TAPE_PORT; - QIC02_CTL_PORT = QIC02_TAPE_PORT; - QIC02_CMD_PORT = QIC02_TAPE_PORT+1; - QIC02_DATA_PORT = QIC02_TAPE_PORT+1; - QIC02_STAT_READY = WT_QIC02_STAT_READY; - QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION; - QIC02_STAT_MASK = WT_QIC02_STAT_MASK; - - QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK; - QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL; - - QIC02_CTL_RESET = WT_QIC02_CTL_RESET; - QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST; - - if (QIC02_TAPE_DMA == 3) - WT_CTL_DMA = WT_CTL_DMA3; - else if (QIC02_TAPE_DMA == 1) - WT_CTL_DMA = WT_CTL_DMA1; - else { - tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); - return -EIO; - } - - if (QIC02_TAPE_IFC == EVEREX) { - /* Everex is a special case for Wangtek (actually - * it's the other way 'round, but I saw Wangtek first) - */ - if (QIC02_TAPE_DMA==3) - WT_CTL_DMA = WT_CTL_DMA1; - /* Fixup the kernel copy of the IFC type to that - * we don't have to distinguish between Wangtek and - * and Everex at runtime. - */ - QIC02_TAPE_IFC = WANGTEK; - } - } else if (QIC02_TAPE_IFC == ARCHIVE) { - QIC02_STAT_PORT = QIC02_TAPE_PORT+1; - QIC02_CTL_PORT = QIC02_TAPE_PORT+1; - QIC02_CMD_PORT = QIC02_TAPE_PORT; - QIC02_DATA_PORT = QIC02_TAPE_PORT; - QIC02_STAT_READY = AR_QIC02_STAT_READY; - QIC02_STAT_EXCEPTION = AR_QIC02_STAT_EXCEPTION; - QIC02_STAT_MASK = AR_QIC02_STAT_MASK; - - QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK; - QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL; - - QIC02_CTL_RESET = AR_QIC02_CTL_RESET; - QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST; - - if (QIC02_TAPE_DMA > 3) { - tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); - return -EIO; - } - } else if (QIC02_TAPE_IFC == MOUNTAIN) { - QIC02_STAT_PORT = QIC02_TAPE_PORT+1; - QIC02_CTL_PORT = QIC02_TAPE_PORT+1; - QIC02_CMD_PORT = QIC02_TAPE_PORT; - QIC02_DATA_PORT = QIC02_TAPE_PORT; - - QIC02_STAT_READY = MTN_QIC02_STAT_READY; - QIC02_STAT_EXCEPTION = MTN_QIC02_STAT_EXCEPTION; - QIC02_STAT_MASK = MTN_QIC02_STAT_MASK; - - QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK; - QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL; - - QIC02_CTL_RESET = MTN_QIC02_CTL_RESET; - QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST; + QIC02_TAPE_IFC = ifc; + + if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) + { + QIC02_STAT_PORT = QIC02_TAPE_PORT; + QIC02_CTL_PORT = QIC02_TAPE_PORT; + QIC02_CMD_PORT = QIC02_TAPE_PORT+1; + QIC02_DATA_PORT = QIC02_TAPE_PORT+1; + QIC02_STAT_READY = WT_QIC02_STAT_READY; + QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION; + QIC02_STAT_MASK = WT_QIC02_STAT_MASK; + + QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK; + QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL; + + QIC02_CTL_RESET = WT_QIC02_CTL_RESET; + QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST; - if (QIC02_TAPE_DMA > 3) { - tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); - return -EIO; - } - } else { - tpqputs(TPQD_ALWAYS, "Invalid interface type"); - return -ENXIO; + if (QIC02_TAPE_DMA == 3) + { + WT_CTL_DMA = WT_CTL_DMA3; } - return 0; -} /* update_ifc-masks */ + else if (QIC02_TAPE_DMA == 1) + { + WT_CTL_DMA = WT_CTL_DMA1; + } + else + { + tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); + return -EIO; + } + + if (QIC02_TAPE_IFC == EVEREX) + { + /* Everex is a special case for Wangtek (actually + * it's the other way 'round, but I saw Wangtek first) + */ + if (QIC02_TAPE_DMA==3) + { + WT_CTL_DMA = WT_CTL_DMA1; + } + + /* Fixup the kernel copy of the IFC type to that + * we don't have to distinguish between Wangtek and + * and Everex at runtime. + */ + QIC02_TAPE_IFC = WANGTEK; + } + } + else if (QIC02_TAPE_IFC == ARCHIVE) + { + QIC02_STAT_PORT = QIC02_TAPE_PORT+1; + QIC02_CTL_PORT = QIC02_TAPE_PORT+1; + QIC02_CMD_PORT = QIC02_TAPE_PORT; + QIC02_DATA_PORT = QIC02_TAPE_PORT; + QIC02_STAT_READY = AR_QIC02_STAT_READY; + QIC02_STAT_EXCEPTION = AR_QIC02_STAT_EXCEPTION; + QIC02_STAT_MASK = AR_QIC02_STAT_MASK; + + QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK; + QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL; + + QIC02_CTL_RESET = AR_QIC02_CTL_RESET; + QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST; + + if (QIC02_TAPE_DMA > 3) + { + tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); + return -EIO; + } + } + else if (QIC02_TAPE_IFC == MOUNTAIN) + { + QIC02_STAT_PORT = QIC02_TAPE_PORT+1; + QIC02_CTL_PORT = QIC02_TAPE_PORT+1; + QIC02_CMD_PORT = QIC02_TAPE_PORT; + QIC02_DATA_PORT = QIC02_TAPE_PORT; + + QIC02_STAT_READY = MTN_QIC02_STAT_READY; + QIC02_STAT_EXCEPTION = MTN_QIC02_STAT_EXCEPTION; + QIC02_STAT_MASK = MTN_QIC02_STAT_MASK; + + QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK; + QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL; + + QIC02_CTL_RESET = MTN_QIC02_CTL_RESET; + QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST; + + if (QIC02_TAPE_DMA > 3) + { + tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel"); + return -EIO; + } + } + else + { + tpqputs(TPQD_ALWAYS, "Invalid interface type"); + return -ENXIO; + } + return qic02_get_resources(); +} /* update_ifc_masks */ #endif @@ -2596,209 +2536,207 @@ int update_ifc_masks(int ifc) static int qic02_tape_ioctl(struct inode * inode, struct file * filp, unsigned int iocmd, unsigned long ioarg) { - int error; - int dev_maj = MAJOR(inode->i_rdev); - int c; - struct mtop operation; - char *stp, *argp; - unsigned char blk_addr[6]; - struct mtpos ioctl_tell; - + int error; + int dev_maj = MAJOR(inode->i_rdev); + int c; + struct mtop operation; + unsigned char blk_addr[6]; + struct mtpos ioctl_tell; + + + if (TP_DIAGS(current_tape_dev)) + { + printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg); + } + + if (!inode || !ioarg) + { + return -EINVAL; + } + + /* check iocmd first */ - if (TP_DIAGS(current_tape_dev)) - printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg); + if (dev_maj != QIC02_TAPE_MAJOR) + { + printk(TPQIC02_NAME ": Oops! Wrong device?\n"); + /* A panic() would be appropriate here */ + return -ENODEV; + } - if (!inode || !ioarg) - return -EINVAL; + c = _IOC_NR(iocmd); - /* check iocmd first */ +#ifdef CONFIG_QIC02_DYNCONF + if (c == _IOC_NR(MTIOCGETCONFIG)) + { + CHECK_IOC_SIZE(mtconfiginfo); - if (dev_maj != QIC02_TAPE_MAJOR) { - printk(TPQIC02_NAME ": Oops! Wrong device?\n"); - /* A panic() would be appropriate here */ - return -ENODEV; + if (copy_to_user((char *) ioarg, (char *) &qic02_tape_dynconf, sizeof(qic02_tape_dynconf))) + { + return -EFAULT; } + return 0; - c = iocmd & IOCCMD_MASK; - -#ifdef DDIOCSDBG - /* Check for DDI Debug Control, contributed by FvK, edited by HHB. */ - if (c == DDIOCSDBG) { - if (!suser()) - return -EPERM; - error = get_user(c, (int *) ioarg); - if (error) - return error; - if (c==0) { - QIC02_TAPE_DEBUG = 0; - return 0; - } - if ((c>=1) && (c<=32)) { - QIC02_TAPE_DEBUG |= (1 << (c-1)); - return 0; - } - if (c >= 128) { - QIC02_TAPE_DEBUG &= ~(1 << (c - 128)); - return 0; - } - return -EINVAL; + } + else if (c == _IOC_NR(MTIOCSETCONFIG)) + { + /* One should always do a MTIOCGETCONFIG first, then update + * user-settings, then write back with MTIOCSETCONFIG. + * The qic02conf program should re-open() the device before actual + * use, to make sure everything is initialized. + */ + + CHECK_IOC_SIZE(mtconfiginfo); + + if (!suser()) + { + return -EPERM; } + + if ((doing_read!=NO) || (doing_write!=NO)) + { + return -EBUSY; + } + + if (status_zombie==NO) + { + qic02_release_resources(); /* and go zombie */ + } + + /* copy struct from user space to kernel space */ + if (copy_from_user((char *) &qic02_tape_dynconf, (char *) ioarg, sizeof(qic02_tape_dynconf))) + { + return -EFAULT; + } + return update_ifc_masks(qic02_tape_dynconf.ifc_type); + } + if (status_zombie==YES) + { + tpqputs(TPQD_ALWAYS, "Configs not set"); + return -ENXIO; + } #endif + if (c == _IOC_NR(MTIOCTOP)) + { + CHECK_IOC_SIZE(mtop); -#ifdef CONFIG_QIC02_DYNCONF - if (c == (MTIOCGETCONFIG & IOCCMD_MASK)) { - if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) { - tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!"); - return -EFAULT; - } - - /* check for valid user address and copy current settings to user space */ - stp = (char *) &qic02_tape_dynconf; - argp = (char *) ioarg; - if (copy_to_user(stp, argp, sizeof(qic02_tape_dynconf))) - return -EFAULT; - return 0; - - } else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) { - static int qic02_get_resources(void), qic02_release_resources(void); + /* copy mtop struct from user space to kernel space */ + if (copy_from_user((char *) &operation, (char *) ioarg, sizeof(operation))) + { + return -EFAULT; + } - /* One should always do a MTIOCGETCONFIG first, then update - * user-settings, then write back with MTIOCSETCONFIG. - * Re-open() the device before actual use to make sure - * everything is initialized. - */ - if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) { - tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!"); - return -EFAULT; - } - if (!suser()) - return -EPERM; - if ((doing_read!=NO) || (doing_write!=NO)) - return -EBUSY; - - /* copy struct from user space to kernel space */ - stp = (char *) &qic02_tape_dynconf; - argp = (char *) ioarg; - if (copy_from_user(stp, argp, sizeof(qic02_tape_dynconf))) - return -EFAULT; - if (status_zombie==NO) - qic02_release_resources(); /* and go zombie */ - if (update_ifc_masks(qic02_tape_dynconf.ifc_type)) - return -EIO; - if (qic02_get_resources()) - return -ENXIO; - return 0; + /* ---note: mt_count is signed, negative seeks must be + * --- translated to seeks in opposite direction! + * (only needed for Sun-programs, I think.) + */ + /* ---note: MTFSF with count 0 should position the + * --- tape at the beginning of the current file. + */ + if (TP_DIAGS(current_tape_dev)) + { + printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count); } - if (status_zombie==YES) { - tpqputs(TPQD_ALWAYS, "Configs not set"); - return -ENXIO; + + if (operation.mt_count < 0) + { + tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored"); } -#endif - if (c == (MTIOCTOP & IOCCMD_MASK)) { - - /* Compare expected struct size and actual struct size. This - * is useful to catch programs compiled with old #includes. - */ - if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtop)) { - tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!"); - return -EFAULT; + + ioctl_status.mt_resid = operation.mt_count; + if (operation.mt_op == MTSEEK) + { + if (!TP_HAVE_SEEK) + { + return -ENOTTY; + } + + seek_addr_buf[0] = (operation.mt_count>>16)&0xff; + seek_addr_buf[1] = (operation.mt_count>>8)&0xff; + seek_addr_buf[2] = (operation.mt_count)&0xff; + if (operation.mt_count>>24) + { + return -EINVAL; + } + if ((error = do_ioctl_cmd(operation.mt_op)) != 0) + { + return error; + } + + ioctl_status.mt_resid = 0; + } + else + { + while (operation.mt_count > 0) + { + operation.mt_count--; + if ((error = do_ioctl_cmd(operation.mt_op)) != 0) + { + return error; } - - /* copy mtop struct from user space to kernel space */ - stp = (char *) &operation; - argp = (char *) ioarg; - if (copy_from_user(stp, argp, sizeof(operation))) - return -EFAULT; - - /* ---note: mt_count is signed, negative seeks must be - * --- translated to seeks in opposite direction! - * (only needed for Sun-programs, I think.) - */ - /* ---note: MTFSF with count 0 should position the - * --- tape at the beginning of the current file. - */ - - if (TP_DIAGS(current_tape_dev)) - printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count); - - if (operation.mt_count < 0) - tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored"); - + ioctl_status.mt_resid = operation.mt_count; - if (operation.mt_op == MTSEEK) { - if (!TP_HAVE_SEEK) - return -ENOTTY; - seek_addr_buf[0] = (operation.mt_count>>16)&0xff; - seek_addr_buf[1] = (operation.mt_count>>8)&0xff; - seek_addr_buf[2] = (operation.mt_count)&0xff; - if (operation.mt_count>>24) - return -EINVAL; - if ((error = do_ioctl_cmd(operation.mt_op)) != 0) - return error; - ioctl_status.mt_resid = 0; - } else { - while (operation.mt_count > 0) { - operation.mt_count--; - if ((error = do_ioctl_cmd(operation.mt_op)) != 0) - return error; - ioctl_status.mt_resid = operation.mt_count; - } - } - return 0; - - } else if (c == (MTIOCGET & IOCCMD_MASK)) { - if (TP_DIAGS(current_tape_dev)) - printk("GET "); - - /* compare expected struct size and actual struct size */ - if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) { - tpqputs(TPQD_ALWAYS, "sizeof(struct mtget) does not match!"); - return -EFAULT; - } - - /* It appears (gmt(1)) that it is normal behaviour to - * first set the status with MTNOP, and then to read - * it out with MTIOCGET - */ - - /* copy results to user space */ - stp = (char *) &ioctl_status; - argp = (char *) ioarg; - if (copy_to_user(stp, argp, sizeof(ioctl_status))) - return -EFAULT; - return 0; - - - } else if (TP_HAVE_TELL && (c == (MTIOCPOS & IOCCMD_MASK))) { - if (TP_DIAGS(current_tape_dev)) - printk("POS "); - - /* compare expected struct size and actual struct size */ - if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) { - tpqputs(TPQD_ALWAYS, "sizeof(struct mtpos) does not match!"); - return -EFAULT; - } - - tpqputs(TPQD_IOCTLS, "MTTELL reading block address"); - if ((doing_read==YES) || (doing_write==YES)) - finish_rw(AR_QCMDV_TELL_BLK); + } + } + return 0; + + } + else if (c == _IOC_NR(MTIOCGET)) + { + if (TP_DIAGS(current_tape_dev)) + { + printk("GET "); + } + + CHECK_IOC_SIZE(mtget); - c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK); - if (c!=TE_OK) - return -EIO; + /* It appears (gmt(1)) that it is normal behaviour to + * first set the status with MTNOP, and then to read + * it out with MTIOCGET + */ - ioctl_tell.mt_blkno = (blk_addr[3] << 16) | (blk_addr[4] << 8) | blk_addr[5]; + /* copy results to user space */ + if (copy_to_user((char *) &ioctl_status, (char *) ioarg, sizeof(ioctl_status))) + { + return -EFAULT; + } + return 0; + } + else if (TP_HAVE_TELL && (c == _IOC_NR(MTIOCPOS))) + { + if (TP_DIAGS(current_tape_dev)) + { + printk("POS "); + } + + CHECK_IOC_SIZE(mtpos); + + tpqputs(TPQD_IOCTLS, "MTTELL reading block address"); + if ((doing_read==YES) || (doing_write==YES)) + { + finish_rw(AR_QCMDV_TELL_BLK); + } + + c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK); + if (c!=TE_OK) + { + return -EIO; + } + + ioctl_tell.mt_blkno = (blk_addr[3] << 16) | (blk_addr[4] << 8) | blk_addr[5]; - /* copy results to user space */ - stp = (char *) &ioctl_tell; - argp = (char *) ioarg; - if (copy_to_user(stp, argp, sizeof(ioctl_status))) - return -EFAULT; - return 0; + /* copy results to user space */ + if (copy_to_user((char *) ioarg, (char *) &ioctl_tell, sizeof(ioctl_tell))) + { + return -EFAULT; + } + return 0; - } else - return -ENOTTY; /* Other cmds not supported. */ + } + else + { + return -ENOTTY; /* Other cmds not supported. */ + } } /* qic02_tape_ioctl */ @@ -2820,215 +2758,229 @@ static struct file_operations qic02_tape_fops = { NULL /* revalidate */ }; -/* align `a' at `size' bytes. `size' must be a power of 2 */ -static inline unsigned long const align_buffer(unsigned long a, unsigned size) + +/* Why is this not is one place? */ +/* Pure 2^n version of get_order */ +static inline int __get_order(unsigned long size) { - if (a & (size-1)) /* if not aligned */ - return (a | (size-1)) + 1; - else /* else is aligned */ - return a; + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do + { + size >>= 1; + order++; + } while (size); + return order; } - static void qic02_release_resources(void) { - free_irq(QIC02_TAPE_IRQ, NULL); - free_dma(QIC02_TAPE_DMA); - status_zombie = YES; + free_irq(QIC02_TAPE_IRQ, NULL); + free_dma(QIC02_TAPE_DMA); + release_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE); + if (buffaddr) + { + free_pages(buffaddr, __get_order(TPQBUF_SIZE)); + } + buffaddr = 0; /* Better to cause a panic than overwite someone else */ + status_zombie = YES; } /* qic02_release_resources */ - - static int qic02_get_resources(void) { - /* First perform some checks. If one of them fails, - * the tape driver will not be registered to the system. - */ - if (QIC02_TAPE_IRQ>16) { - tpqputs(TPQD_ALWAYS, "Bogus interrupt number."); - return -1; - } - - /* for DYNCONF, allocating DMA & IRQ should not be done until - * the config parameters have been set using MTSETCONFIG. - */ - - /* get IRQ */ - if (request_irq(QIC02_TAPE_IRQ, qic02_tape_interrupt, SA_INTERRUPT, "QIC-02", NULL)) { - printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n", - QIC02_TAPE_IRQ); - status_zombie = YES; - return -1; - } - - /* After IRQ, allocate DMA channel */ - if (request_dma(QIC02_TAPE_DMA,"QIC-02")) { - printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n", - QIC02_TAPE_DMA); - free_irq(QIC02_TAPE_IRQ, NULL); - status_zombie = YES; - return -1; - } - - printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n", - QIC02_TAPE_IRQ, QIC02_TAPE_DMA, - ((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))? - QIC02_CMD_PORT : QIC02_STAT_PORT, - (QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" : - ((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek")); - - if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) { - /* No drive detected, so vanish */ - tpqputs(TPQD_ALWAYS, "No drive detected -- releasing irq and dma."); - status_dead = YES; - qic02_release_resources(); - return -1; - } + /* First perform some checks. If one of them fails, + * the tape driver will not be registered to the system. + */ + if (QIC02_TAPE_IRQ>16) + { + tpqputs(TPQD_ALWAYS, "Bogus interrupt number."); + return -ENXIO; + } + + /* for DYNCONF, allocating IO, DMA and IRQ should not be done until + * the config parameters have been set using MTSETCONFIG. + */ + + if (check_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE)) + { + printk(TPQIC02_NAME ": IO space at 0x%x [%d ports] already reserved\n", + QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE); + return -ENXIO; + } + + /* get IRQ */ + if (request_irq(QIC02_TAPE_IRQ, qic02_tape_interrupt, SA_INTERRUPT, "QIC-02", NULL)) + { + printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n", + QIC02_TAPE_IRQ); + return -EBUSY; + } + + /* After IRQ, allocate DMA channel */ + if (request_dma(QIC02_TAPE_DMA,"QIC-02")) + { + printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n", + QIC02_TAPE_DMA); + free_irq(QIC02_TAPE_IRQ, NULL); + return -EBUSY; + } + + /* Grab the IO region. We already made sure it's available. */ + request_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE, TPQIC02_NAME); + + /* Setup the page-address for the dma transfer. */ + + /*** TODO: does _get_dma_pages() really return the physical address?? ****/ + buffaddr = __get_dma_pages(GFP_KERNEL,__get_order(TPQBUF_SIZE)); + + if (!buffaddr) + { + qic02_release_resources(); + return -EBUSY; /* Not ideal, EAGAIN perhaps? */ + } + + memset( (void*) buffaddr, 0, TPQBUF_SIZE ); + + printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n", + QIC02_TAPE_IRQ, QIC02_TAPE_DMA, + ((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))? + QIC02_CMD_PORT : QIC02_STAT_PORT, + (QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" : + ((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek")); + + if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) + { + /* No drive detected, so vanish */ + tpqputs(TPQD_ALWAYS, "No drive detected -- releasing IO/IRQ/DMA."); + status_dead = YES; + qic02_release_resources(); + return -EIO; + } - /* All should be ok now */ - status_zombie = NO; - return 0; + /* All should be ok now */ + status_zombie = NO; + return 0; } /* qic02_get_resources */ - +#ifdef MODULE +static +#endif int qic02_tape_init(void) - /* Shouldn't this be a caddr_t ? */ { - - if (TPSTATSIZE != 6) { - printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n"); - return -ENODEV; - } - if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) { - printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n"); - return -ENODEV; - } - - QIC02_TAPE_DEBUG = TPQD_DEFAULT_FLAGS; - - current_tape_dev = MKDEV(QIC02_TAPE_MAJOR, 0); + if (TPSTATSIZE != 6) + { + printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n"); + return -ENODEV; + } + if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) + { + printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n"); + return -ENODEV; + } + + current_tape_dev = MKDEV(QIC02_TAPE_MAJOR, 0); #ifndef CONFIG_QIC02_DYNCONF - printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n", - QIC02_TAPE_IRQ, QIC02_TAPE_DMA, + printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n", + QIC02_TAPE_IRQ, QIC02_TAPE_DMA, # if QIC02_TAPE_IFC == WANGTEK - QIC02_STAT_PORT, "Wangtek", + QIC02_STAT_PORT, "Wangtek", # elif QIC02_TAPE_IFC == ARCHIVE - QIC02_CMD_PORT, "Archive", + QIC02_CMD_PORT, "Archive", # elif QIC02_TAPE_IFC == MOUNTAIN - QIC02_CMD_PORT, "Mountain", + QIC02_CMD_PORT, "Mountain", # else # error # endif - rcs_revision, rcs_date); - if (qic02_get_resources()) - return -ENODEV; + rcs_revision, rcs_date); + if (qic02_get_resources()) + { + return -ENODEV; + } #else - printk(TPQIC02_NAME ": Runtime config, %s, %s\n", - rcs_revision, rcs_date); - - QIC02_TAPE_IRQ = BOGUS_IRQ; /* invalid value */ -#endif - - printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF); - - /* - * Setup the page-address for the dma transfer. - */ -#ifdef MODULE - qic02_tape_buf = kmalloc(TPQBUF_SIZE+TAPE_BLKSIZE, GFP_DMA); - if (qic02_tape_buf == NULL) { -#ifndef CONFIG_QIC02_DYNCONF - /* - * irq and dma were requested by qic_get_resources, so - * relase them only when _not_ using DYNCONF - */ - qic02_release_resources(); -#endif - return -ENODEV; - } - buffaddr = align_buffer(virt_to_bus((unsigned long *)qic02_tape_buf), - TAPE_BLKSIZE); - printk(", at address 0x%lx (0x%lx)\n", buffaddr, - virt_to_bus((unsigned long *)qic02_tape_buf)); -#else /* no MODULE */ - buffaddr = align_buffer(virt_to_bus(qic02_tape_buf), TAPE_BLKSIZE); - printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf); -#endif /* MODULE */ - - -#ifndef CONFIG_MAX_16M - if (buffaddr+TPQBUF_SIZE>=0x1000000) { - printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n"); -#ifdef MODULE - qic02_release_resources(); - kfree(qic02_tape_buf); -#endif - - return -ENODEV; - } + printk(TPQIC02_NAME ": Runtime config, %s, %s\n", + rcs_revision, rcs_date); #endif - - /* If we got this far, install driver functions */ - if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) { - printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR); + printk(TPQIC02_NAME ": DMA buffers: %u blocks\n", NR_BLK_BUF); + /* If we got this far, install driver functions */ + if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) + { + printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR); #ifndef CONFIG_QIC02_DYNCONF - qic02_release_resources(); + qic02_release_resources(); #endif - return -ENODEV; - } - - /* prepare timer */ - TIMEROFF; - timer_table[QIC02_TAPE_TIMER].expires = 0; - timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out; - + return -ENODEV; + } + + /* prepare timer */ + TIMEROFF; + timer_table[QIC02_TAPE_TIMER].expires = 0; + timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out; + #ifndef CONFIG_QIC02_DYNCONF - if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) { - /* No drive detected, so vanish */ - tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation..."); - unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); - qic02_release_resources(); - return -ENODEV; - } else { - if (is_exception()) { - tpqputs(TPQD_ALWAYS, "exception detected\n"); - (void) tp_sense(TP_WRP|TP_POR|TP_CNI); - } + if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) + { + /* No drive detected, so vanish */ + tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation..."); + qic02_release_resources(); + status_dead = YES; + return -ENODEV; + } + else + { + if (is_exception()) + { + tpqputs(TPQD_ALWAYS, "exception detected\n"); + (void) tp_sense(TP_WRP|TP_POR|TP_CNI); } + } #endif - /* initialize generic status for ioctl requests */ - - ioctl_status.mt_type = QIC02_TAPE_DRIVE; /* MT_IS* id nr */ - - ioctl_status.mt_resid = 0; /* ---residual count */ - ioctl_status.mt_gstat = 0; /* ---generic status */ - ioctl_status.mt_erreg = 0; /* not used */ - ioctl_status.mt_fileno = 0; /* number of current file on tape */ - ioctl_status.mt_blkno = 0; /* number of current (logical) block */ - - return 0; + /* initialize generic status for ioctl requests */ + + ioctl_status.mt_type = QIC02_TAPE_DRIVE; /* MT_IS* id nr */ + + ioctl_status.mt_resid = 0; /* ---residual count */ + ioctl_status.mt_gstat = 0; /* ---generic status */ + ioctl_status.mt_erreg = 0; /* not used */ + ioctl_status.mt_fileno = 0; /* number of current file on tape */ + ioctl_status.mt_blkno = 0; /* number of current (logical) block */ + + return 0; } /* qic02_tape_init */ #ifdef MODULE -int init_module(void) { - return qic02_tape_init(); -} - -void cleanup_module(void) { - unsigned long flags; - - save_flags(flags); - cli(); - unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); +void cleanup_module(void) +{ + if (status_zombie == NO) + { qic02_release_resources(); - if (qic02_tape_buf) kfree(qic02_tape_buf); - sti(); - restore_flags(flags); + } + unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); } -#endif /* MODULE */ +int init_module(void) +{ + int retval; + retval=qic02_tape_init(); +# ifdef CONFIG_QIC02_DYNCONF + /* This allows the dynamic config program to setup the card + * by presetting qic02_tape_dynconf via insmod + */ + if (!retval && qic02_tape_dynconf.ifc_type) + { + retval=update_ifc_masks(qic02_tape_dynconf.ifc_type); + if (retval) + { + cleanup_module(); + } + } +# endif + return retval; +} +#endif diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index fc0ab0d578e3..ca2fdd8962b7 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1924,7 +1924,7 @@ int tty_init(void) kbd_init(); #ifdef CONFIG_ESPSERIAL /* init ESP before rs, so rs doesn't see the port */ - esp_init(); + espserial_init(); #endif #ifdef CONFIG_SERIAL rs_init(); diff --git a/drivers/char/wdt.c b/drivers/char/wdt.c index 0512203286ff..216bb1f73cfc 100644 --- a/drivers/char/wdt.c +++ b/drivers/char/wdt.c @@ -22,6 +22,7 @@ * Alan Cox : Added the watchdog ioctl() stuff * Alan Cox : Fixed the reboot problem (as noted by * Matt Crocker). + * Alan Cox : Added wdt= boot option */ #include @@ -46,6 +47,7 @@ static int wdt_is_open=0; /* * You must set these - there is no sane way to probe for this board. + * You can use wdt=x,y to set these now. */ int io=0x240; @@ -53,6 +55,20 @@ int irq=14; #define WD_TIMO (100*60) /* 1 minute */ +/* + * Setup options + */ + +void wdt_setup(char *str, int *ints) +{ + if(ints[0]>0) + { + io=ints[1]; + if(ints[0]>1) + irq=ints[2]; + } +} + /* * Programming support */ diff --git a/drivers/net/Config.in b/drivers/net/Config.in index 52cb6d30e563..70ef038fa116 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -140,6 +140,10 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then tristate 'WIC Radio IP bridge' CONFIG_WIC fi +if [ "$CONFIG_X25" != "n" ]; then + tristate 'LAPB over Ethernet driver' CONFIG_LAPBETHER +fi + tristate 'SLIP (serial line) support' CONFIG_SLIP if [ "$CONFIG_SLIP" != "n" ]; then bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 72013e46e8e5..4411ba789d02 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -524,6 +524,14 @@ else endif endif +ifeq ($(CONFIG_LAPBETHER),y) +L_OBJS += lapbether.o +else + ifeq ($(CONFIG_LAPBETHER),m) + M_OBJS += lapbether.o + endif +endif + # If anything built-in uses slhc, then build it into the kernel also. # If not, but a module uses it, build as a module. ifdef CONFIG_SLHC_BUILTIN diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 1a5c41d49c88..c6222f45afd9 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -254,25 +254,6 @@ ethif_probe(struct device *dev) # define NEXT_DEV (&sdla0_dev) #endif -#ifdef CONFIG_AX25 -#ifdef CONFIG_NETROM - extern int nr_init(struct device *); - static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, }; - static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, }; - static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, }; - static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, }; -# undef NEXT_DEV -# define NEXT_DEV (&nr0_dev) -#endif -#ifdef CONFIG_ROSE - extern int rose_init(struct device *); - static struct device rose1_dev = { "rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, rose_init, }; - static struct device rose0_dev = { "rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &rose1_dev, rose_init, }; -# undef NEXT_DEV -# define NEXT_DEV (&rose0_dev) -#endif -#endif - /* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */ #ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */ static struct device atp_dev = { diff --git a/drivers/net/arcnet.c b/drivers/net/arcnet.c index 40a1c96f2990..db1e95e3d478 100644 --- a/drivers/net/arcnet.c +++ b/drivers/net/arcnet.c @@ -1308,11 +1308,10 @@ int arcnet_reset(struct device *dev,int reset_delay) * * Intelligent defaults?! Nah. */ + void arcnet_setup(struct device *dev) { - int i; - for (i=0; ibuffs[i]); + dev_init_buffers(dev); dev->broadcast[0] = 0x00; /* for us, broadcasts are address 0 */ dev->addr_len = 1; diff --git a/drivers/net/bpqether.c b/drivers/net/bpqether.c index a7a94e99dc0b..7ebdfea5e7df 100644 --- a/drivers/net/bpqether.c +++ b/drivers/net/bpqether.c @@ -543,8 +543,7 @@ static int bpq_new_device(struct device *dev) return -EIO; } - for (k=0; k < DEV_NUMBUFFS; k++) - skb_queue_head_init(&dev->buffs[k]); + dev_init_buffers(dev); dev->hard_start_xmit = bpq_xmit; dev->open = bpq_open; diff --git a/drivers/net/bsd_comp.c b/drivers/net/bsd_comp.c index 1e1c194f09fb..55dd955536ad 100644 --- a/drivers/net/bsd_comp.c +++ b/drivers/net/bsd_comp.c @@ -90,7 +90,7 @@ #include #ifdef NEW_SKBUFF -#include +# /*nodep*/ include #endif #include diff --git a/drivers/net/de4x5.c b/drivers/net/de4x5.c index 7e8f51ce7175..62ca67dc1b0e 100644 --- a/drivers/net/de4x5.c +++ b/drivers/net/de4x5.c @@ -46,20 +46,14 @@ to the differences in the EISA and PCI CSR address offsets from the base address. - The ability to load this driver as a loadable module has been included - and used extensively during the driver development (to save those long - reboot sequences). Loadable module support under PCI and EISA has been + The ability to load this driver as a loadable module has been included + and used extensively during the driver development (to save those long + reboot sequences). Loadable module support under PCI and EISA has been achieved by letting the driver autoprobe as if it were compiled into the kernel, except that there is no autoprobing of the IRQ lines. This is of - no great consequence except do make sure you're not sharing interrupts - with anything that cannot accommodate interrupt sharing! The existing - register_netdevice() code will only allow one device to be registered at - a time. - - ************************************************************************ - For now, please only use the 'io=??' assignment (see 2. below, ?? != 0) - when loading a module. - ************************************************************************ + no great consequence except do make sure you're not sharing interrupts + with anything that cannot accommodate interrupt sharing! By default, + the driver will autoprobe for the next available card. Essentially, the I/O address and IRQ information are ignored and filled in later by the PCI BIOS during the PCI probe. Note that the board @@ -71,13 +65,20 @@ 0) have a copy of the loadable modules code installed on your system. 1) copy de4x5.c from the /linux/drivers/net directory to your favourite temporary directory. - 2) edit the source code near line 3779 to reflect the I/O address you're + 2) edit the source code near line 4146 to reflect the I/O address you're using (only if you want to manually load the module), or assign these when loading by: insmod de4x5.o io=0xghh where g = bus number hh = device number + NB: autoprobing for modules is now supported by default. You may just + use: + + insmod de4x5.o + + to load the next available board. For a specific board, still use + the 'io=?' above. 3) compile de4x5.c, but include -DMODULE in the command line to ensure that the correct bits are compiled (see end of source code). 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a @@ -215,11 +216,13 @@ by 0.45 8-Dec-96 Include endian functions for PPC use, from work by . + 0.451 28-Dec-96 Added fix to allow autoprobe for modules after + suggestion from ========================================================================= */ -static const char *version = "de4x5.c:v0.45 96/12/8 davies@maniac.ultranet.com\n"; +static const char *version = "de4x5.c:v0.451 96/12/28 davies@maniac.ultranet.com\n"; #include @@ -1666,7 +1669,9 @@ eisa_probe(struct device *dev, u_long ioaddr) maxSlots = i + 1; } - for (status = -ENODEV; (irebuild_header = NULL; /* set in fddi_setup() */ tmp_dev->set_multicast_list = &dfx_ctl_set_multicast_list; tmp_dev->set_mac_address = &dfx_ctl_set_mac_address; - tmp_dev->do_ioctl = NULL; /* not supported for now &&& */ - tmp_dev->set_config = NULL; /* not supported for now &&& */ - tmp_dev->header_cache_bind = NULL; /* not supported */ - tmp_dev->header_cache_update = NULL; /* not supported */ - tmp_dev->change_mtu = NULL; /* set in fddi_setup() */ + tmp_dev->do_ioctl = NULL; /* not supported for now &&& */ + tmp_dev->set_config = NULL; /* not supported for now &&& */ + tmp_dev->hard_header_cache = NULL; /* not supported */ + tmp_dev->header_cache_update = NULL; /* not supported */ + tmp_dev->change_mtu = NULL; /* set in fddi_setup() */ /* Initialize remaining device structure information */ @@ -3134,13 +3134,13 @@ int dfx_xmt_queue_pkt( */ if (!IN_RANGE(skb->len, FDDI_K_LLC_ZLEN, FDDI_K_LLC_LEN)) - { - printk("%s: Invalid packet length - %lu bytes\n", dev->name, skb->len); - bp->xmt_length_errors++; /* bump error counter */ + { + printk("%s: Invalid packet length - %u bytes\n", + dev->name, skb->len); + bp->xmt_length_errors++; /* bump error counter */ dev_tint(dev); /* dequeue packets from xmt queue and send them */ - return(0); /* return "success" */ - } - + return(0); /* return "success" */ + } /* * See if adapter link is available, if not, free buffer * diff --git a/drivers/net/dlci.c b/drivers/net/dlci.c index 8eb2d0416f48..191d82d0af69 100644 --- a/drivers/net/dlci.c +++ b/drivers/net/dlci.c @@ -619,9 +619,8 @@ int dlci_init(struct device *dev) dev->pa_brdaddr = 0; dev->pa_mask = 0; - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - + dev_init_buffers(dev); + return(0); } diff --git a/drivers/net/eql.c b/drivers/net/eql.c index 85f9297faba9..0ff21a9f0664 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -249,13 +249,11 @@ int eql_init(struct device *dev) /* * Fill in the fields of the device structure with - * eql-generic values. This should be in a common - * file instead of per-driver. + * eql-generic values. */ - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - + dev_init_buffers(dev); + /* * Now we undo some of the things that eth_setup does * that we don't like diff --git a/drivers/net/hdlcdrv.c b/drivers/net/hdlcdrv.c index 2cb5b6e9a809..77d7b9229aa7 100644 --- a/drivers/net/hdlcdrv.c +++ b/drivers/net/hdlcdrv.c @@ -790,8 +790,8 @@ static int hdlcdrv_probe(struct device *dev) dev->get_stats = hdlcdrv_get_stats; /* Fill in the fields of the device structure */ - for (i=0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + + dev_init_buffers(dev); skb_queue_head_init(&s->send_queue); diff --git a/drivers/net/lapbether.c b/drivers/net/lapbether.c new file mode 100644 index 000000000000..37eb26e65323 --- /dev/null +++ b/drivers/net/lapbether.c @@ -0,0 +1,629 @@ +/* + * "LAPB via ethernet" driver release 001 + * + * This is ALPHA test software. This code may break your machine, randomly + * fail to work with new releases, misbehave and/or generally screw up. + * It might even work. + * + * This code REQUIRES 2.1.15 or higher/ NET3.038 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This is a "pseudo" network driver to allow LAPB over Ethernet. + * + * This driver can use any ethernet destination address, and can be + * limited to accept frames from one dedicated ethernet card only. + * + * History + * LAPBETH 001 Jonathan Naylor Cloned from bpqether.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + +static char lapbeth_eth_addr[6]; + +static int lapbeth_rcv(struct sk_buff *, struct device *, struct packet_type *); +static int lapbeth_device_event(struct notifier_block *, unsigned long, void *); + +static struct packet_type lapbeth_packet_type = { + 0, /* ntohs(ETH_P_DEC),*/ + 0, /* copy */ + lapbeth_rcv, + NULL, + NULL, +}; + +static struct notifier_block lapbeth_dev_notifier = { + lapbeth_device_event, + 0 +}; + + +#define MAXLAPBDEV 100 + +static struct lapbethdev { + struct lapbethdev *next; + char ethname[14]; /* ether device name */ + struct device *ethdev; /* link to ethernet device */ + struct device axdev; /* lapbeth device (lapb#) */ + struct enet_statistics stats; /* some statistics */ + char dest_addr[6]; /* ether destination address */ + char acpt_addr[6]; /* accept ether frames from this address only */ +} *lapbeth_devices = NULL; + + +/* ------------------------------------------------------------------------ */ + + +/* + * Get the ethernet device for a LAPB device + */ +static __inline__ struct device *lapbeth_get_ether_dev(struct device *dev) +{ + struct lapbethdev *lapbeth; + + lapbeth = (struct lapbethdev *)dev->priv; + + return (lapbeth != NULL) ? lapbeth->ethdev : NULL; +} + +/* + * Get the LAPB device for the ethernet device + */ +static __inline__ struct device *lapbeth_get_x25_dev(struct device *dev) +{ + struct lapbethdev *lapbeth; + + for (lapbeth = lapbeth_devices; lapbeth != NULL; lapbeth = lapbeth->next) + if (lapbeth->ethdev == dev) + return &lapbeth->axdev; + + return NULL; +} + +static __inline__ int dev_is_ethdev(struct device *dev) +{ + return ( + dev->type == ARPHRD_ETHER + && strncmp(dev->name, "dummy", 5) +#ifdef CONFIG_NET_ALIAS + && !net_alias_is(dev) +#endif + ); +} + +/* + * Sanity check: remove all devices that ceased to exists and + * return '1' if the given LAPB device was affected. + */ +static int lapbeth_check_devices(struct device *dev) +{ + struct lapbethdev *lapbeth, *lapbeth_prev; + int result = 0; + unsigned long flags; + + save_flags(flags); + cli(); + + lapbeth_prev = NULL; + + for (lapbeth = lapbeth_devices; lapbeth != NULL; lapbeth = lapbeth->next) { + if (!dev_get(lapbeth->ethname)) { + if (lapbeth_prev) + lapbeth_prev->next = lapbeth->next; + else + lapbeth_devices = lapbeth->next; + + if (&lapbeth->axdev == dev) + result = 1; + + unregister_netdev(&lapbeth->axdev); + kfree(lapbeth); + } + + lapbeth_prev = lapbeth; + } + + restore_flags(flags); + + return result; +} + + +/* ------------------------------------------------------------------------ */ + + +/* + * Receive a LAPB frame via an ethernet interface. + */ +static int lapbeth_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) +{ + int len, err; + struct ethhdr *eth = (struct ethhdr *)skb->mac.raw; + struct lapbethdev *lapbeth; + + skb->sk = NULL; /* Initially we don't know who it's for */ + + dev = lapbeth_get_x25_dev(dev); + + if (dev == NULL || dev->start == 0) { + kfree_skb(skb, FREE_READ); + return 0; + } + + /* + * if we want to accept frames from just one ethernet device + * we check the source address of the sender. + */ + + lapbeth = (struct lapbethdev *)dev->priv; + + if (!(lapbeth->acpt_addr[0] & 0x01) && memcmp(eth->h_source, lapbeth->acpt_addr, ETH_ALEN)) { + printk(KERN_DEBUG "lapbeth: wrong dest address\n"); + kfree_skb(skb, FREE_READ); + return 0; + } + + ((struct lapbethdev *)dev->priv)->stats.rx_packets++; + + len = skb->data[0] + skb->data[1] * 256; + + skb_pull(skb, 2); /* Remove the length bytes */ + skb_trim(skb, len); /* Set the length of the data */ + + if ((err = lapb_data_received(lapbeth, skb)) != LAPB_OK) { + kfree_skb(skb, FREE_READ); + printk(KERN_DEBUG "lapbether: lapb_data_received err - %d\n", err); + } + + return 0; +} + +static void lapbeth_data_indication(void *token, struct sk_buff *skb) +{ + struct lapbethdev *lapbeth = (struct lapbethdev *)token; + unsigned char *ptr; + + ptr = skb_push(skb, 1); + *ptr = 0x00; + + skb->dev = &lapbeth->axdev; + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); +} + +/* + * Send a LAPB frame via an ethernet interface + */ +static int lapbeth_xmit(struct sk_buff *skb, struct device *dev) +{ + struct lapbethdev *lapbeth; + int err; + + lapbeth = (struct lapbethdev *)dev->priv; + + /* + * Just to be *really* sure not to send anything if the interface + * is down, the ethernet device may have gone. + */ + if (!dev->start) { + lapbeth_check_devices(dev); + dev_kfree_skb(skb, FREE_WRITE); + return -ENODEV; + } + + switch (skb->data[0]) { + case 0x00: + break; + case 0x01: + if ((err = lapb_connect_request(lapbeth)) != LAPB_OK) + printk(KERN_ERR "lapbeth: lapb_connect_request error - %d\n", err); + kfree_skb(skb, FREE_WRITE); + return 0; + case 0x02: + if ((err = lapb_disconnect_request(lapbeth)) != LAPB_OK) + printk(KERN_ERR "lapbeth: lapb_disconnect_request err - %d\n", err); + kfree_skb(skb, FREE_WRITE); + return 0; + default: + kfree_skb(skb, FREE_WRITE); + return 0; + } + + skb_pull(skb, 1); + + if ((err = lapb_data_request(lapbeth, skb)) != LAPB_OK) { + printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err); + kfree_skb(skb, FREE_WRITE); + return -ENOMEM; + } + + return 0; +} + +static void lapbeth_data_transmit(void *token, struct sk_buff *skb) +{ + struct lapbethdev *lapbeth = (struct lapbethdev *)token; + unsigned char *ptr; + struct device *dev; + int size; + + skb->protocol = htons(ETH_P_X25); + + size = skb->len; + + ptr = skb_push(skb, 2); + + *ptr++ = size % 256; + *ptr++ = size / 256; + + lapbeth->stats.tx_packets++; + + skb->dev = dev = lapbeth->ethdev; + + dev->hard_header(skb, dev, ETH_P_DEC, lapbeth->dest_addr, NULL, 0); + + dev->hard_start_xmit(skb, dev); +} + +static void lapbeth_connected(void *token, int reason) +{ + struct lapbethdev *lapbeth = (struct lapbethdev *)token; + struct sk_buff *skb; + unsigned char *ptr; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "lapbeth: out of memory\n"); + return; + } + + ptr = skb_put(skb, 1); + *ptr = 0x01; + + skb->dev = &lapbeth->axdev; + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); +} + +static void lapbeth_disconnected(void *token, int reason) +{ + struct lapbethdev *lapbeth = (struct lapbethdev *)token; + struct sk_buff *skb; + unsigned char *ptr; + + if ((skb = dev_alloc_skb(1)) == NULL) { + printk(KERN_ERR "lapbeth: out of memory\n"); + return; + } + + ptr = skb_put(skb, 1); + *ptr = 0x02; + + skb->dev = &lapbeth->axdev; + skb->protocol = htons(ETH_P_X25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); +} + +/* + * Statistics + */ +static struct enet_statistics *lapbeth_get_stats(struct device *dev) +{ + struct lapbethdev *lapbeth; + + lapbeth = (struct lapbethdev *)dev->priv; + + return &lapbeth->stats; +} + +/* + * Set AX.25 callsign + */ +static int lapbeth_set_mac_address(struct device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *)addr; + + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + + return 0; +} + +/* Ioctl commands + * + * SIOCSLAPBETHADDR set the destination and accepted + * source ethernet address (broadcast + * or multicast: accept all) + */ +static int lapbeth_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +{ + int err; + struct lapbeth_ethaddr *ethaddr = (struct lapbeth_ethaddr *)ifr->ifr_data; + struct lapbethdev *lapbeth = dev->priv; + + if (!suser()) + return -EPERM; + + if (lapbeth == NULL) /* woops! */ + return -ENODEV; + + switch (cmd) { + case SIOCSLAPBETHADDR: + if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct lapbeth_ethaddr))) != 0) + return err; + copy_from_user(lapbeth->dest_addr, ethaddr->destination, ETH_ALEN); + copy_from_user(lapbeth->acpt_addr, ethaddr->accept, ETH_ALEN); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * open/close a device + */ +static int lapbeth_open(struct device *dev) +{ + struct lapb_register_struct lapbeth_callbacks; + struct lapbethdev *lapbeth; + int err; + + if (lapbeth_check_devices(dev)) + return -ENODEV; /* oops, it's gone */ + + dev->tbusy = 0; + dev->start = 1; + + lapbeth = (struct lapbethdev *)dev->priv; + + lapbeth_callbacks.connect_confirmation = lapbeth_connected; + lapbeth_callbacks.connect_indication = lapbeth_connected; + lapbeth_callbacks.disconnect_confirmation = lapbeth_disconnected; + lapbeth_callbacks.disconnect_indication = lapbeth_disconnected; + lapbeth_callbacks.data_indication = lapbeth_data_indication; + lapbeth_callbacks.data_transmit = lapbeth_data_transmit; + + if ((err = lapb_register(lapbeth, &lapbeth_callbacks)) != LAPB_OK) { + printk(KERN_ERR "lapbeth: lapb_register error - %d\n", err); + dev->tbusy = 1; + dev->start = 0; + return -ENODEV; + } + + MOD_INC_USE_COUNT; + + return 0; +} + +static int lapbeth_close(struct device *dev) +{ + struct lapbethdev *lapbeth; + int err; + + dev->tbusy = 1; + dev->start = 0; + + lapbeth = (struct lapbethdev *)dev->priv; + + if ((err = lapb_unregister(lapbeth)) != LAPB_OK) + printk(KERN_ERR "lapbeth: lapb_unregister error - %d\n", err); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static int lapbeth_dev_init(struct device *dev) +{ + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* + * Setup a new device. + */ +static int lapbeth_new_device(struct device *dev) +{ + int k; + unsigned char *buf; + struct lapbethdev *lapbeth, *lapbeth2; + + if ((lapbeth = (struct lapbethdev *)kmalloc(sizeof(struct lapbethdev), GFP_KERNEL)) == NULL) + return -ENOMEM; + + memset(lapbeth, 0, sizeof(struct lapbethdev)); + + lapbeth->ethdev = dev; + + lapbeth->ethname[sizeof(lapbeth->ethname)-1] = '\0'; + strncpy(lapbeth->ethname, dev->name, sizeof(lapbeth->ethname)-1); + + memcpy(lapbeth->dest_addr, bcast_addr, sizeof(lapbeth_eth_addr)); + memcpy(lapbeth->acpt_addr, bcast_addr, sizeof(lapbeth_eth_addr)); + + dev = &lapbeth->axdev; + buf = (unsigned char *)kmalloc(14, GFP_KERNEL); + + for (k = 0; k < MAXLAPBDEV; k++) { + struct device *odev; + + sprintf(buf, "lapb%d", k); + + if ((odev = dev_get(buf)) == NULL || lapbeth_check_devices(odev)) + break; + } + + if (k == MAXLAPBDEV) { + kfree(lapbeth); + return -ENODEV; + } + + dev->priv = (void *)lapbeth; /* pointer back */ + dev->name = buf; + dev->init = lapbeth_dev_init; + + if (register_netdev(dev) != 0) { + kfree(lapbeth); + return -EIO; + } + + dev_init_buffers(dev); + + dev->hard_start_xmit = lapbeth_xmit; + dev->open = lapbeth_open; + dev->stop = lapbeth_close; + dev->set_mac_address = lapbeth_set_mac_address; + dev->get_stats = lapbeth_get_stats; + dev->do_ioctl = lapbeth_ioctl; + + /* preset with reasonable values */ + +#if CONFIG_INET + dev->flags = 0; + dev->family = AF_INET; + dev->pa_addr = 0; + dev->pa_brdaddr = 0; + dev->pa_mask = 0; + dev->pa_alen = 4; +#endif + + dev->type = ARPHRD_X25; + dev->hard_header_len = 3; + dev->mtu = 1000; + dev->addr_len = 0; + + cli(); + + if (lapbeth_devices == NULL) { + lapbeth_devices = lapbeth; + } else { + for (lapbeth2 = lapbeth_devices; lapbeth2->next != NULL; lapbeth2 = lapbeth2->next); + lapbeth2->next = lapbeth; + } + + sti(); + + return 0; +} + + +/* + * Handle device status changes. + */ +static int lapbeth_device_event(struct notifier_block *this,unsigned long event, void *ptr) +{ + struct device *dev = (struct device *)ptr; + + if (!dev_is_ethdev(dev)) + return NOTIFY_DONE; + + lapbeth_check_devices(NULL); + + switch (event) { + case NETDEV_UP: /* new ethernet device -> new LAPB interface */ + if (lapbeth_get_x25_dev(dev) == NULL) + lapbeth_new_device(dev); + break; + + case NETDEV_DOWN: /* ethernet device closed -> close LAPB interface */ + if ((dev = lapbeth_get_x25_dev(dev)) != NULL) + dev_close(dev); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + + +/* ------------------------------------------------------------------------ */ + +/* + * Initialize driver. To be called from af_ax25 if not compiled as a + * module + */ +int lapbeth_init(void) +{ + struct device *dev; + + lapbeth_packet_type.type = htons(ETH_P_DEC); + dev_add_pack(&lapbeth_packet_type); + + register_netdevice_notifier(&lapbeth_dev_notifier); + + printk(KERN_INFO "LAPB Ethernet driver version 0.01\n"); + + for (dev = dev_base; dev != NULL; dev = dev->next) { + if (dev_is_ethdev(dev)) + lapbeth_new_device(dev); + } + + return 0; +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + return lapbeth_init(); +} + +void cleanup_module(void) +{ + struct lapbethdev *lapbeth; + + dev_remove_pack(&lapbeth_packet_type); + + unregister_netdevice_notifier(&lapbeth_dev_notifier); + + for (lapbeth = lapbeth_devices; lapbeth != NULL; lapbeth = lapbeth->next) + unregister_netdev(&lapbeth->axdev); +} +#endif diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 66424c1c0e5a..0f6c8c0f63aa 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -110,8 +110,6 @@ static int loopback_open(struct device *dev) /* Initialize the rest of the LOOPBACK device. */ int loopback_init(struct device *dev) { - int i; - dev->mtu = LOOPBACK_MTU; dev->tbusy = 0; dev->hard_start_xmit = loopback_xmit; @@ -142,8 +140,7 @@ int loopback_init(struct device *dev) * Fill in the generic fields of the device structure. */ - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + dev_init_buffers(dev); return(0); }; diff --git a/drivers/net/mkiss.c b/drivers/net/mkiss.c index 400d04d3d285..dfe8695fdc5b 100644 --- a/drivers/net/mkiss.c +++ b/drivers/net/mkiss.c @@ -862,11 +862,11 @@ int mkiss_init_ctrl_dev(void) } -/* Initialize the driver. Called by DDI. */ +/* Initialize the driver. Called by network startup. */ + static int ax25_init(struct device *dev) { struct ax_disp *ax = (struct ax_disp*)dev->priv; - int i; static char ax25_bcast[AX25_ADDR_LEN] = {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1}; @@ -903,8 +903,7 @@ static int ax25_init(struct device *dev) dev->rebuild_header = ax_rebuild_header; #endif - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + dev_init_buffers(dev); /* New-style flags. */ dev->flags = 0; diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c index c6a7743e7ce6..3a8d48b10fdb 100644 --- a/drivers/net/net_init.c +++ b/drivers/net/net_init.c @@ -174,9 +174,9 @@ void ether_setup(struct device *dev) int i; /* Fill in the fields of the device structure with ethernet-generic values. This should be in a common file instead of per-driver. */ - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + dev_init_buffers(dev); + /* register boot-defined "eth" devices */ if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) { i = simple_strtoul(dev->name + 3, NULL, 0); @@ -217,16 +217,14 @@ void ether_setup(struct device *dev) #ifdef CONFIG_FDDI void fddi_setup(struct device *dev) - { - int i; - +{ /* * Fill in the fields of the device structure with FDDI-generic values. * This should be in a common file instead of per-driver. */ - for (i=0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - + + dev_init_buffers(dev); + dev->change_mtu = fddi_change_mtu; dev->hard_header = fddi_header; dev->rebuild_header = fddi_rebuild_header; @@ -247,7 +245,7 @@ void fddi_setup(struct device *dev) dev->pa_mask = 0; dev->pa_alen = 4; return; - } +} #endif @@ -398,8 +396,7 @@ void unregister_netdev(struct device *dev) #define MAX_TR_CARDS 16 /* same as the number of irq's in irq2dev[] */ static struct device *trdev_index[MAX_TR_CARDS]; -struct device * -init_trdev(struct device *dev, int sizeof_priv) +struct device *init_trdev(struct device *dev, int sizeof_priv) { int new_device = 0; int i; @@ -436,7 +433,7 @@ init_trdev(struct device *dev, int sizeof_priv) new_device = 1; } - trfound: /* From the double loop above. */ +trfound: /* From the double loop above. */ for (i = 0; i < MAX_TR_CARDS; ++i) if (trdev_index[i] == NULL) { @@ -511,11 +508,9 @@ void tr_freedev(struct device *dev) int register_trdev(struct device *dev) { unsigned long flags; - int i; - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - + dev_init_buffers(dev); + save_flags(flags); if (dev && dev->init) { diff --git a/drivers/net/pi2.c b/drivers/net/pi2.c index f172bfe0c071..498ff4b6ce75 100644 --- a/drivers/net/pi2.c +++ b/drivers/net/pi2.c @@ -1400,9 +1400,9 @@ static int pi_probe(struct device *dev, int card_type) dev->get_stats = pi_get_stats; /* Fill in the fields of the device structure */ - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + dev_init_buffers(dev): + #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) dev->hard_header = ax25_encapsulate; dev->rebuild_header = ax25_rebuild_header; diff --git a/drivers/net/ppp.c b/drivers/net/ppp.c index fe2c21ebda46..af51ac20cc99 100644 --- a/drivers/net/ppp.c +++ b/drivers/net/ppp.c @@ -402,9 +402,8 @@ ppp_init_dev (struct device *dev) dev->tx_queue_len = 10; dev->type = ARPHRD_PPP; - for (indx = 0; indx < DEV_NUMBUFFS; indx++) - skb_queue_head_init (&dev->buffs[indx]); - + dev_init_buffers(dev); + /* New-style flags */ dev->flags = IFF_POINTOPOINT; dev->family = AF_INET; diff --git a/drivers/net/pt.c b/drivers/net/pt.c index bc9620adbdd7..8a8d8456ed3e 100644 --- a/drivers/net/pt.c +++ b/drivers/net/pt.c @@ -733,7 +733,6 @@ static int pt_probe(struct device *dev) { short ioaddr; struct pt_local *lp; - int i; unsigned long flags; unsigned long mem_ptr; @@ -851,8 +850,7 @@ static int pt_probe(struct device *dev) dev->get_stats = pt_get_stats; /* Fill in the fields of the device structure */ - for (i=0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + dev_init_buffers(dev); #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) dev->hard_header = ax25_encapsulate; diff --git a/drivers/net/scc.c b/drivers/net/scc.c index 28ac41ac9502..8e1a465bbc4b 100644 --- a/drivers/net/scc.c +++ b/drivers/net/scc.c @@ -229,8 +229,7 @@ static io_port Vector_Latch = 0; /* These provide interrupt save 2-step access to the Z8530 registers */ -static __inline__ unsigned char -InReg(io_port port, unsigned char reg) +extern __inline__ unsigned char InReg(io_port port, unsigned char reg) { unsigned long flags; unsigned char r; @@ -250,8 +249,7 @@ InReg(io_port port, unsigned char reg) return r; } -static __inline__ void -OutReg(io_port port, unsigned char reg, unsigned char val) +extern __inline__ void OutReg(io_port port, unsigned char reg, unsigned char val) { unsigned long flags; @@ -267,28 +265,26 @@ OutReg(io_port port, unsigned char reg, unsigned char val) restore_flags(flags); } -static __inline__ void -wr(struct scc_channel *scc, unsigned char reg, unsigned char val) +extern __inline__ void wr(struct scc_channel *scc, unsigned char reg, + unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); } -static __inline__ void -or(struct scc_channel *scc, unsigned char reg, unsigned char val) +extern __inline__ void or(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); } -static __inline__ void -cl(struct scc_channel *scc, unsigned char reg, unsigned char val) +extern __inline__ void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); } #ifdef DISABLE_ALL_INTS -static __inline__ void scc_cli(int irq) +extern __inline__ void scc_cli(int irq) { cli(); } -static __inline__ void scc_sti(int irq) +extern __inline__ void scc_sti(int irq) { sti(); } #else static __inline__ void scc_cli(int irq) @@ -302,20 +298,17 @@ static __inline__ void scc_sti(int irq) /* ******************************************************************** */ -static __inline__ void -scc_lock_dev(struct scc_channel *scc) +extern __inline__ void scc_lock_dev(struct scc_channel *scc) { scc->dev->tbusy = 1; } -static __inline__ void -scc_unlock_dev(struct scc_channel *scc) +extern __inline__ void scc_unlock_dev(struct scc_channel *scc) { scc->dev->tbusy = 0; } -static __inline__ void -scc_discard_buffers(struct scc_channel *scc) +extern __inline__ void scc_discard_buffers(struct scc_channel *scc) { unsigned long flags; @@ -343,8 +336,7 @@ scc_discard_buffers(struct scc_channel *scc) /* ----> subroutines for the interrupt handlers <---- */ -static __inline__ void -scc_notify(struct scc_channel *scc, int event) +extern __inline__ void scc_notify(struct scc_channel *scc, int event) { struct sk_buff *skb; char *bp; @@ -364,8 +356,7 @@ scc_notify(struct scc_channel *scc, int event) scc->stat.nospace++; } -static __inline__ void -flush_rx_FIFO(struct scc_channel *scc) +extern __inline__ void flush_rx_FIFO(struct scc_channel *scc) { int k; @@ -385,8 +376,7 @@ flush_rx_FIFO(struct scc_channel *scc) /* DCD/CTS and Rx/Tx errors */ /* Transmitter interrupt handler */ -static __inline__ void -scc_txint(struct scc_channel *scc) +extern __inline__ void scc_txint(struct scc_channel *scc) { struct sk_buff *skb; @@ -450,8 +440,7 @@ scc_txint(struct scc_channel *scc) /* External/Status interrupt handler */ -static __inline__ void -scc_exint(struct scc_channel *scc) +extern __inline__ void scc_exint(struct scc_channel *scc) { unsigned char status,changes,chg_and_stat; @@ -532,8 +521,7 @@ scc_exint(struct scc_channel *scc) /* Receiver interrupt handler */ -static __inline__ void -scc_rxint(struct scc_channel *scc) +extern __inline__ void scc_rxint(struct scc_channel *scc) { struct sk_buff *skb; @@ -583,8 +571,7 @@ scc_rxint(struct scc_channel *scc) /* Receive Special Condition interrupt handler */ -static __inline__ void -scc_spint(struct scc_channel *scc) +extern __inline__ void scc_spint(struct scc_channel *scc) { unsigned char status; struct sk_buff *skb; @@ -630,8 +617,7 @@ scc_spint(struct scc_channel *scc) /* ----> interrupt service routine for the Z8530 <---- */ -static void -scc_isr_dispatch(struct scc_channel *scc, int vector) +static void scc_isr_dispatch(struct scc_channel *scc, int vector) { switch (vector & VECTOR_MASK) { @@ -649,8 +635,7 @@ scc_isr_dispatch(struct scc_channel *scc, int vector) #define SCC_IRQTIMEOUT 30000 -static void -scc_isr(int irq, void *dev_id, struct pt_regs *regs) +static void scc_isr(int irq, void *dev_id, struct pt_regs *regs) { unsigned char vector; struct scc_channel *scc; @@ -742,8 +727,7 @@ scc_isr(int irq, void *dev_id, struct pt_regs *regs) /* ----> set SCC channel speed <---- */ -static __inline__ void -set_brg(struct scc_channel *scc, unsigned int tc) +extern __inline__ void set_brg(struct scc_channel *scc, unsigned int tc) { cl(scc,R14,BRENABL); /* disable baudrate generator */ wr(scc,R12,tc & 255); /* brg rate LOW */ @@ -751,8 +735,7 @@ set_brg(struct scc_channel *scc, unsigned int tc) or(scc,R14,BRENABL); /* enable baudrate generator */ } -static __inline__ void -set_speed(struct scc_channel *scc) +extern __inline__ void set_speed(struct scc_channel *scc) { disable_irq(scc->irq); @@ -765,8 +748,7 @@ set_speed(struct scc_channel *scc) /* ----> initialize a SCC channel <---- */ -static __inline__ void -init_brg(struct scc_channel *scc) +extern __inline__ void init_brg(struct scc_channel *scc) { wr(scc, R14, BRSRC); /* BRG source = PCLK */ OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ @@ -818,8 +800,7 @@ init_brg(struct scc_channel *scc) * */ -static void -init_channel(struct scc_channel *scc) +static void init_channel(struct scc_channel *scc) { disable_irq(scc->irq); @@ -929,8 +910,7 @@ init_channel(struct scc_channel *scc) /* ----> scc_key_trx sets the time constant for the baudrate generator and keys the transmitter <---- */ -static void -scc_key_trx(struct scc_channel *scc, char tx) +static void scc_key_trx(struct scc_channel *scc, char tx) { unsigned int time_const; @@ -1001,8 +981,7 @@ scc_key_trx(struct scc_channel *scc, char tx) /* ----> SCC timer interrupt handler and friends. <---- */ -static void -scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) +static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) { unsigned long flags; @@ -1027,8 +1006,7 @@ scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsi restore_flags(flags); } -static void -scc_start_defer(struct scc_channel *scc) +static void scc_start_defer(struct scc_channel *scc) { unsigned long flags; @@ -1048,8 +1026,7 @@ scc_start_defer(struct scc_channel *scc) restore_flags(flags); } -static void -scc_start_maxkeyup(struct scc_channel *scc) +static void scc_start_maxkeyup(struct scc_channel *scc) { unsigned long flags; @@ -1075,8 +1052,7 @@ scc_start_maxkeyup(struct scc_channel *scc) * Not exactly a timer function, but it is a close friend of the family... */ -static void -scc_tx_done(struct scc_channel *scc) +static void scc_tx_done(struct scc_channel *scc) { /* * trx remains keyed in fulldup mode 2 until t_idle expires. @@ -1103,8 +1079,7 @@ scc_tx_done(struct scc_channel *scc) static unsigned char Rand = 17; -static __inline__ int -is_grouped(struct scc_channel *scc) +extern __inline__ int is_grouped(struct scc_channel *scc) { int k; struct scc_channel *scc2; @@ -1140,8 +1115,7 @@ is_grouped(struct scc_channel *scc) * fulldup == 2: mintime expired, reset status or key trx and start txdelay */ -static void -t_dwait(unsigned long channel) +static void t_dwait(unsigned long channel) { struct scc_channel *scc = (struct scc_channel *) channel; @@ -1184,8 +1158,7 @@ t_dwait(unsigned long channel) * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. */ -static void -t_txdelay(unsigned long channel) +static void t_txdelay(unsigned long channel) { struct scc_channel *scc = (struct scc_channel *) channel; @@ -1206,8 +1179,7 @@ t_txdelay(unsigned long channel) * transmission after 'mintime' seconds */ -static void -t_tail(unsigned long channel) +static void t_tail(unsigned long channel) { struct scc_channel *scc = (struct scc_channel *) channel; unsigned long flags; @@ -1243,8 +1215,7 @@ t_tail(unsigned long channel) * throw away send buffers if DCD remains active too long. */ -static void -t_busy(unsigned long channel) +static void t_busy(unsigned long channel) { struct scc_channel *scc = (struct scc_channel *) channel; unsigned long flags; @@ -1272,8 +1243,7 @@ t_busy(unsigned long channel) * this is our watchdog. */ -static void -t_maxkeyup(unsigned long channel) +static void t_maxkeyup(unsigned long channel) { struct scc_channel *scc = (struct scc_channel *) channel; unsigned long flags; @@ -1310,8 +1280,7 @@ t_maxkeyup(unsigned long channel) * expires. */ -static void -t_idle(unsigned long channel) +static void t_idle(unsigned long channel) { struct scc_channel *scc = (struct scc_channel *) channel; unsigned long flags; @@ -1331,8 +1300,7 @@ t_idle(unsigned long channel) scc->stat.tx_state = TXS_WAIT; } -static void -scc_init_timer(struct scc_channel *scc) +static void scc_init_timer(struct scc_channel *scc) { unsigned long flags; @@ -1356,8 +1324,7 @@ scc_init_timer(struct scc_channel *scc) #define CAST(x) (unsigned long)(x) -static unsigned int -scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) +static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) { int dcd; @@ -1423,8 +1390,7 @@ scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) -static unsigned long -scc_get_param(struct scc_channel *scc, unsigned int cmd) +static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) { switch (cmd) { @@ -1460,8 +1426,7 @@ scc_get_param(struct scc_channel *scc, unsigned int cmd) * Reset the Z8530s and setup special hardware */ -static void -z8530_init(void) +static void z8530_init(void) { struct scc_channel *scc; int chip, k; @@ -1518,8 +1483,7 @@ z8530_init(void) * Allocate device structure, err, instance, and register driver */ -static int -scc_net_setup(struct scc_channel *scc, unsigned char *name) +static int scc_net_setup(struct scc_channel *scc, unsigned char *name) { unsigned char *buf; struct device *dev; @@ -1565,13 +1529,10 @@ static unsigned char ax25_nocall[AX25_ADDR_LEN] = /* ----> Initialize device <----- */ -static int -scc_net_init(struct device *dev) +static int scc_net_init(struct device *dev) { - int k; - - for (k=0; k < DEV_NUMBUFFS; k++) - skb_queue_head_init(&dev->buffs[k]); + dev_init_buffers(dev); + dev->tx_queue_len = 16; /* should be enough... */ dev->open = scc_net_open; @@ -1604,8 +1565,7 @@ scc_net_init(struct device *dev) /* ----> open network device <---- */ -static int -scc_net_open(struct device *dev) +static int scc_net_open(struct device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; @@ -1630,8 +1590,7 @@ scc_net_open(struct device *dev) /* ----> close network device <---- */ -static int -scc_net_close(struct device *dev) +static int scc_net_close(struct device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; unsigned long flags; @@ -1663,8 +1622,7 @@ scc_net_close(struct device *dev) /* ----> receive frame, called from scc_rxint() <---- */ -static void -scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) +static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) { if (skb->len == 0) { @@ -1684,8 +1642,7 @@ scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) /* ----> transmit frame <---- */ -static int -scc_net_tx(struct sk_buff *skb, struct device *dev) +static int scc_net_tx(struct sk_buff *skb, struct device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; unsigned long flags; @@ -1783,8 +1740,7 @@ scc_net_tx(struct sk_buff *skb, struct device *dev) * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg */ -static int -scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd) +static int scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd) { struct scc_kiss_cmd kiss_cmd; struct scc_mem_config memcfg; @@ -2006,8 +1962,7 @@ scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd) /* ----> set interface callsign <---- */ -static int -scc_net_set_mac_address(struct device *dev, void *addr) +static int scc_net_set_mac_address(struct device *dev, void *addr) { struct sockaddr *sa = (struct sockaddr *) addr; memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); @@ -2016,25 +1971,22 @@ scc_net_set_mac_address(struct device *dev, void *addr) /* ----> rebuild header <---- */ -static int -scc_net_rebuild_header(struct sk_buff *skb) +static int scc_net_rebuild_header(struct sk_buff *skb) { return ax25_rebuild_header(skb); } /* ----> "hard" header <---- */ -static int -scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, - void *daddr, void *saddr, unsigned len) +static int scc_net_header(struct sk_buff *skb, struct device *dev, + unsigned short type, void *daddr, void *saddr, unsigned len) { return ax25_encapsulate(skb, dev, type, daddr, saddr, len); } /* ----> get statistics <---- */ -static struct enet_statistics * -scc_net_get_stats(struct device *dev) +static struct enet_statistics *scc_net_get_stats(struct device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; @@ -2054,8 +2006,7 @@ scc_net_get_stats(struct device *dev) /* ******************************************************************** */ -static int -scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy) +static int scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy) { struct scc_channel *scc; struct scc_kiss *kiss; @@ -2161,10 +2112,11 @@ done: } #ifdef CONFIG_PROC_FS + struct proc_dir_entry scc_proc_dir_entry = { - PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, - &proc_net_inode_operations, scc_net_get_info + PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, + &proc_net_inode_operations, scc_net_get_info }; #define scc_net_procfs_init() proc_net_register(&scc_proc_dir_entry); diff --git a/drivers/net/sdla.c b/drivers/net/sdla.c index f5e4b16a6314..68a6525f5b5b 100644 --- a/drivers/net/sdla.c +++ b/drivers/net/sdla.c @@ -1636,7 +1636,6 @@ static struct enet_statistics *sdla_stats(struct device *dev) int sdla_init(struct device *dev) { struct frad_local *flp; - int i; /* allocate the private data structure */ flp = kmalloc(sizeof(struct frad_local), GFP_KERNEL); @@ -1666,9 +1665,8 @@ int sdla_init(struct device *dev) dev->addr_len = 0; dev->mtu = SDLA_MAX_MTU; - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - + dev_init_buffers(dev); + flp->activate = sdla_activate; flp->deactivate = sdla_deactivate; flp->assoc = sdla_assoc; diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c index 73cff70e3b7e..dcea3f33f457 100644 --- a/drivers/net/shaper.c +++ b/drivers/net/shaper.c @@ -547,8 +547,6 @@ static struct shaper *shaper_alloc(struct device *dev) int shaper_probe(struct device *dev) { - int i; - /* * Set up the shaper. */ @@ -567,8 +565,7 @@ int shaper_probe(struct device *dev) * Intialise the packet queues */ - for(i=0;ibuffs[i]); + dev_init_buffers(dev); /* * Handlers for when we attach to a device. diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 30c40104c440..4a1f7deb3f8f 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -1143,12 +1143,11 @@ int slip_init_ctrl_dev(struct device *dummy) } -/* Initialize the SLIP driver. Called by DDI. */ -int -slip_init(struct device *dev) +/* Initialise the SLIP driver. Called by the device init code */ + +int slip_init(struct device *dev) { struct slip *sl = (struct slip*)(dev->priv); - int i; if (sl == NULL) /* Allocation failed ?? */ return -ENODEV; @@ -1159,7 +1158,10 @@ slip_init(struct device *dev) sl->magic = SLIP_MAGIC; sl->dev = dev; - /* Finish setting up the DEVICE info. */ + /* + * Finish setting up the DEVICE info. + */ + dev->mtu = SL_MTU; dev->hard_start_xmit = sl_xmit; dev->open = sl_open_dev; @@ -1170,10 +1172,8 @@ slip_init(struct device *dev) dev->type = ARPHRD_SLIP + SL_MODE_DEFAULT; dev->tx_queue_len = 10; - for (i = 0; i < DEV_NUMBUFFS; i++) { - skb_queue_head_init(&dev->buffs[i]); - } - + dev_init_buffers(dev); + /* New-style flags. */ dev->flags = IFF_NOARP|IFF_MULTICAST; dev->family = AF_INET; diff --git a/drivers/net/strip.c b/drivers/net/strip.c index a09e0b9ae2e1..3f420d1734a3 100644 --- a/drivers/net/strip.c +++ b/drivers/net/strip.c @@ -2414,8 +2414,6 @@ static int strip_close_low(struct device *dev) static int strip_dev_init(struct device *dev) { - int i; - /* * Finish setting up the DEVICE info. */ @@ -2446,8 +2444,7 @@ static int strip_dev_init(struct device *dev) * Pointer to the interface buffers. */ - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + dev_init_buffers(dev); /* * Pointers to interface service routines. diff --git a/drivers/net/tunnel.c b/drivers/net/tunnel.c index 43487fc96c9d..55c14db31c4d 100644 --- a/drivers/net/tunnel.c +++ b/drivers/net/tunnel.c @@ -215,8 +215,6 @@ static struct enet_statistics *tunnel_get_stats(struct device *dev) int tunnel_init(struct device *dev) { - int i; - /* Oh, just say we're here, in case anyone cares */ static int tun_msg=0; if(!tun_msg) @@ -236,8 +234,8 @@ int tunnel_init(struct device *dev) memset(dev->priv, 0, sizeof(struct enet_statistics)); /* Initialize the tunnel device structure */ - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); + + dev_init_buffers(dev); dev->hard_header = NULL; dev->rebuild_header = NULL; diff --git a/drivers/net/wic.c b/drivers/net/wic.c index 16b21dabc6ee..894128ade859 100644 --- a/drivers/net/wic.c +++ b/drivers/net/wic.c @@ -77,8 +77,7 @@ void wic_bh(struct device *dev); void wic_interrupt(int irq, void *dev_ptr, struct pt_regs *regs); /* Functions for DEV methods */ -int wic_rebuild_header(void *buff, struct device *dev, - unsigned long raddr, struct sk_buff *skb); +int wic_rebuild_header(struct sk_buff *skb); int wic_tx_packet(struct sk_buff *skb, struct device *dev); int wic_open(struct device *dev); int wic_close(struct device *dev); @@ -155,14 +154,13 @@ struct net_local { enum wic_connection_state connection; unsigned short timeout_count; char is_deferred; - int (*orig_rebuild_header)(void *eth, struct device *dev, - unsigned long raddr, struct sk_buff *skb); + int (*orig_rebuild_header)(struct sk_buff *skb); }; /* Entry point of WIC driver. Probe the hardware, and register/initialize the driver. */ -int -wic_init(struct device *dev) + +int wic_init(struct device *dev) { struct net_local *nl; struct wicconf wc; @@ -266,8 +264,7 @@ wic_init(struct device *dev) /* Bottom half handler for the delayed request. This routine is kicked by do_timer(). Request `wic_bh' to be invoked. */ -void -wic_kick_bh(struct device *dev) +void wic_kick_bh(struct device *dev) { struct net_local *nl = (struct net_local *)dev->priv; @@ -309,8 +306,7 @@ wic_func connection_state_table[] = wic_error }; -void -wic_set_multicast_list(struct device *dev) +void wic_set_multicast_list(struct device *dev) { struct wicconf wc; struct wic_net *wn; @@ -358,8 +354,7 @@ wic_set_multicast_list(struct device *dev) } /* Bottom half handler of WIC. */ -void -wic_bh(struct device *dev) +void wic_bh(struct device *dev) { struct net_local *nl = (struct net_local *)dev->priv; struct wic_local *snd = &nl->snd_data; @@ -376,8 +371,7 @@ wic_bh(struct device *dev) } } -int -wic_bh_timeout_error(struct device *dev, struct net_local *nl, +int wic_bh_timeout_error(struct device *dev, struct net_local *nl, struct wic_local *snd, struct wic_local *rcv, int error) { @@ -423,13 +417,11 @@ wic_bh_timeout_error(struct device *dev, struct net_local *nl, } rcv->state = WIC_PK_DONE; if (rcv->skb) { - rcv->skb->free = 1; kfree_skb(rcv->skb, FREE_READ); rcv->skb = NULL; } snd->state = WIC_PK_DONE; if (snd->skb) { - snd->skb->free = 1; dev_kfree_skb(snd->skb, FREE_WRITE); snd->skb = NULL; } @@ -446,8 +438,7 @@ wic_bh_timeout_error(struct device *dev, struct net_local *nl, return TIMEOUT; } -int -wic_none(struct device *dev, struct net_local *nl, +int wic_none(struct device *dev, struct net_local *nl, struct wic_local *snd, struct wic_local *rcv) { return OK; @@ -455,11 +446,10 @@ wic_none(struct device *dev, struct net_local *nl, /* WIC_RECEIVE --- receive a byte(two nibbles) Returns OK on success, TIMEOUT on timeout */ -inline int -wic_receive(unsigned short nibble_timeout, unsigned short status_addr, - enum wic_nibble_state *ns_p, unsigned char *data_p) +extern inline int wic_receive(unsigned short nibble_timeout, + unsigned short status_addr, enum wic_nibble_state *ns_p, unsigned char *data_p) { -unsigned int cx; + unsigned int cx; cx = LOOPCNT; while ((inb(status_addr) & 0x08) != ((tog<<3) & 0x08)) { @@ -474,8 +464,8 @@ unsigned int cx; } /* WIC_RECEIVE_PACKET --- receive a packet */ -int -wic_receive_packet(struct device *dev, struct net_local *nl, + +int wic_receive_packet(struct device *dev, struct net_local *nl, struct wic_local *snd, struct wic_local *rcv) { unsigned short status_addr = PAR_STATUS(dev); @@ -637,11 +627,11 @@ wic_receive_packet(struct device *dev, struct net_local *nl, /* WIC_SEND --- send a byte (two nibbles) Returns OK on success, TIMEOUT when timeout */ -inline int -wic_send(unsigned short nibble_timeout, unsigned short data_addr, - enum wic_nibble_state *ns_p, unsigned char data) +extern inline int wic_send(unsigned short nibble_timeout, + unsigned short data_addr, enum wic_nibble_state *ns_p, + unsigned char data) { -unsigned int cx; + unsigned int cx; cx = LOOPCNT; while ((inb(data_addr+1) & 0x80) == ((tog<<7) & 0x80)) { @@ -656,8 +646,7 @@ unsigned int cx; } /* WIC_SEND_PACKET --- send a packet */ -int -wic_send_packet(struct device *dev, struct net_local *nl, +int wic_send_packet(struct device *dev, struct net_local *nl, struct wic_local *snd, struct wic_local *rcv) { unsigned short data_addr = PAR_DATA(dev); @@ -819,11 +808,10 @@ wic_send_packet(struct device *dev, struct net_local *nl, return OK; } -int -wic_connection_close(struct device *dev, struct net_local *nl, +int wic_connection_close(struct device *dev, struct net_local *nl, struct wic_local *snd, struct wic_local *rcv) { -unsigned long flags; + unsigned long flags; save_flags(flags); cli(); @@ -837,8 +825,7 @@ unsigned long flags; } /* WIC_ERROR --- wait till other end settled */ -int -wic_error(struct device *dev, struct net_local *nl, +int wic_error(struct device *dev, struct net_local *nl, struct wic_local *snd, struct wic_local *rcv) { unsigned char status; @@ -863,8 +850,7 @@ wic_error(struct device *dev, struct net_local *nl, } /* Handle the parallel port interrupts. */ -void -wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs) +void wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs) { struct device *dev = (struct device *) irq2dev_map[irq]; struct net_local *nl = (struct net_local *)dev->priv; @@ -916,16 +902,15 @@ wic_interrupt(int irq, void *dev_ptr, struct pt_regs * regs) } } -int -wic_rebuild_header(void *buff, struct device *dev, unsigned long dst, - struct sk_buff *skb) +int wic_rebuild_header(struct sk_buff *skb) { + struct device *dev = skb->dev; struct net_local *nl = (struct net_local *)dev->priv; - struct ethhdr *eth = (struct ethhdr *)buff; + struct ethhdr *eth = (struct ethhdr *)skb->data; int i; if ((dev->flags & IFF_NOARP)==0) - return nl->orig_rebuild_header(buff, dev, dst, skb); + return nl->orig_rebuild_header(skb); if (eth->h_proto != htons(ETH_P_IP)) { printk("wic_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto); @@ -939,8 +924,7 @@ wic_rebuild_header(void *buff, struct device *dev, unsigned long dst, return 0; } -int -wic_tx_packet(struct sk_buff *skb, struct device *dev) +int wic_tx_packet(struct sk_buff *skb, struct device *dev) { struct net_local *nl = (struct net_local *)dev->priv; struct wic_local *snd = &nl->snd_data; @@ -994,8 +978,8 @@ wic_tx_packet(struct sk_buff *skb, struct device *dev) This routine gets exclusive access to the parallel port by allocating its IRQ line. */ -int -wic_open(struct device *dev) + +int wic_open(struct device *dev) { struct net_local *nl = (struct net_local *)dev->priv; unsigned long flags; @@ -1031,8 +1015,7 @@ wic_open(struct device *dev) } /* The inverse routine to wic_open (). */ -int -wic_close(struct device *dev) +int wic_close(struct device *dev) { struct net_local *nl = (struct net_local *)dev->priv; struct wic_local *snd = &nl->snd_data; @@ -1050,13 +1033,11 @@ wic_close(struct device *dev) snd->state = WIC_PK_DONE; if (snd->skb) { - snd->skb->free = 1; dev_kfree_skb(snd->skb, FREE_WRITE); snd->skb = NULL; } rcv->state = WIC_PK_DONE; if (rcv->skb) { - rcv->skb->free = 1; kfree_skb(rcv->skb, FREE_READ); rcv->skb = NULL; } diff --git a/drivers/sbus/char/sunserial.c b/drivers/sbus/char/sunserial.c index eb33a0b4ed9e..a02cbe8416da 100644 --- a/drivers/sbus/char/sunserial.c +++ b/drivers/sbus/char/sunserial.c @@ -1,8 +1,9 @@ -/* $Id: sunserial.c,v 1.24 1996/11/21 16:57:56 jj Exp $ +/* $Id: sunserial.c,v 1.25 1996/12/30 07:50:26 davem Exp $ * serial.c: Serial port driver for the Sparc. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) + * Fixes by Pete A. Zaitcev . */ #include @@ -489,6 +490,8 @@ clear_and_exit: static _INLINE_ void transmit_chars(struct sun_serial *info) { + struct tty_struct *tty = info->tty; + if (info->x_char) { /* Send next char */ zs_put_char(info->zs_channel, info->x_char); @@ -496,7 +499,7 @@ static _INLINE_ void transmit_chars(struct sun_serial *info) goto clear_and_return; } - if((info->xmit_cnt <= 0) || info->tty->stopped) { + if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) { /* That's peculiar... */ info->zs_channel->control = RES_Tx_P; udelay(5); @@ -666,6 +669,10 @@ static void do_serial_hangup(void *private_) tty = info->tty; if (!tty) return; +#ifdef SERIAL_DEBUG_OPEN + printk("do_serial_hangup<%p: tty-%d\n", + __builtin_return_address(0), info->line); +#endif tty_hangup(tty); } @@ -700,7 +707,7 @@ static int startup(struct sun_serial * info) save_flags(flags); cli(); #ifdef SERIAL_DEBUG_OPEN - printk("starting up ttys%d (irq %d)...", info->line, info->irq); + printk("Starting up tty-%d (irq %d)...\n", info->line, info->irq); #endif /* @@ -820,10 +827,7 @@ static void change_speed(struct sun_serial *info) */ i = B9600; } - if (i == 0) { - /* XXX B0, hangup the line. */ - do_serial_hangup(info); - } else if (baud_table[i]) { + if (baud_table[i]) { info->zs_baud = baud_table[i]; info->clk_divisor = 16; @@ -833,6 +837,8 @@ static void change_speed(struct sun_serial *info) info->curregs[12] = (brg & 255); info->curregs[13] = ((brg >> 8) & 255); info->curregs[14] = BRSRC | BRENAB; + /* } else { */ + /* XXX */ } /* byte size and parity */ @@ -1405,7 +1411,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp) } #ifdef SERIAL_DEBUG_OPEN - printk("rs_close ttys%d, count = %d\n", info->line, info->count); + printk("rs_close tty-%d, count = %d\n", info->line, info->count); #endif if ((tty->count == 1) && (info->count != 1)) { /* @@ -1484,6 +1490,9 @@ static void rs_close(struct tty_struct *tty, struct file * filp) info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE| ZILOG_CLOSING); wake_up_interruptible(&info->close_wait); +#ifdef SERIAL_DEBUG_OPEN + printk("rs_close tty-%d exiting, count = %d\n", info->line, info->count); +#endif restore_flags(flags); } @@ -1497,6 +1506,11 @@ void rs_hangup(struct tty_struct *tty) if (serial_paranoia_check(info, tty->device, "rs_hangup")) return; +#ifdef SERIAL_DEBUG_OPEN + printk("rs_hangup<%p: tty-%d, count = %d bye\n", + __builtin_return_address(0), info->line, info->count); +#endif + rs_flush_buffer(tty); shutdown(info); info->event = 0; @@ -1517,6 +1531,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, struct wait_queue wait = { current, NULL }; int retval; int do_clocal = 0; + unsigned char r0; /* * If the device is in the middle of being closed, then block @@ -1599,6 +1614,10 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, current->state = TASK_INTERRUPTIBLE; if (tty_hung_up_p(filp) || !(info->flags & ZILOG_INITIALIZED)) { +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready hup-ed: ttys%d, count = %d\n", + info->line, info->count); +#endif #ifdef SERIAL_DO_RESTART if (info->flags & ZILOG_HUP_NOTIFY) retval = -EAGAIN; @@ -1610,9 +1629,12 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, break; } + cli(); + r0 = read_zsreg(info->zs_channel, R0); + sti(); if (!(info->flags & ZILOG_CALLOUT_ACTIVE) && !(info->flags & ZILOG_CLOSING) && - (do_clocal || (DCD & read_zsreg(info->zs_channel, R0)))) + (do_clocal || (DCD & r0))) break; if (current->signal & ~current->blocked) { retval = -ERESTARTSYS; @@ -1668,6 +1690,11 @@ int rs_open(struct tty_struct *tty, struct file * filp) printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->count); #endif + if (info->tty != 0 && info->tty != tty) { + /* Never happen? */ + printk("rs_open %s%d, tty overwrite.\n", tty->driver.name, info->line); + return -EBUSY; + } info->count++; tty->driver_data = info; info->tty = tty; @@ -1709,7 +1736,7 @@ int rs_open(struct tty_struct *tty, struct file * filp) static void show_serial_version(void) { - char *revision = "$Revision: 1.24 $"; + char *revision = "$Revision: 1.25 $"; char *version, *p; version = strchr(revision, ' '); diff --git a/drivers/scsi/ChangeLog.ncr53c8xx b/drivers/scsi/ChangeLog.ncr53c8xx index 76cc5d1b4cdc..1cf40cc17410 100644 --- a/drivers/scsi/ChangeLog.ncr53c8xx +++ b/drivers/scsi/ChangeLog.ncr53c8xx @@ -1,3 +1,55 @@ +Thu Dec 26 22:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16b + - Remove useless code. + - Try to improve error recovery in case of abort and reset. + - Remove DEBUG_NEGO by default. + - Add boot setup command support. + Now, all experimental config options can be removed. + - Update README file. + + +Mon Dec 23 23:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h - revision 1.16a + New display for speed ##.# MB/s (From Stefan) + - I add "WIDE" qualifier after ULTRA and FAST + - I get "FAST WIDE SCSI-2 20 MB/s" with my Atlas. That's nice. + + Richard Waltham reports SYMBIOS set the 875 to 20 MB/s for 13 ns + period factor. I decide to trust SYMBIOS. 20 MB/s output speed + instead of 19.2 MB/s should not cause problem. The ncr is only able + to use 16.67 MB/s when 20 MB/s is not possible. + + Fix from Markus Kossman: "Ultra SCSI enabled" wrongly printed + when not enabled. + + Set DEBUG_NEGO by default in order to get reports about sync nego. + Will remove it in the next patch. + +Thu Dec 19 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h README.ncr53c8xx - revision 1.16 + Incorporate new definitions in ncr53c8xx.h (From Stefan). + Check changes against Stefan's current version of the driver. + All seems ok. + +Sat Nov 30 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c ncr53c8xx.h + Make changes in order to support: + - Clock doubler and so 80 Mhz scsi clock for 875 chips. + - Sync transfers below 7.5 MB/sec. + Use Clock/2 between 5 and 10 Mega-transfers/s and Clock/4 below 5. + - Ultra SCSI data transfers. + - Offset 16. + + Works with my configuration. However I cannot test Ultra transfers, + since my disks are only fast scsi-2. + +Tue Nov 28 21:00 1996 Gerard Roudier (groudier@club-internet.fr) + * ncr53c8xx.c + I received yesterday my Promise SCSI Ultra board. + NCR53C875 rev. 3 with clock doubler. + Add the code to support some bus features, the large 536 dma fifo and + burst 128. Works. + Mon Nov 4 21:00 1996 Gerard Roudier (groudier@club-internet.fr) * ncr53c8xx.c ncr53c8xx.h - revision 1.14c Severall control command improvements: diff --git a/drivers/scsi/Config.in b/drivers/scsi/Config.in index 300e830ca523..f7b92c2c4b5a 100644 --- a/drivers/scsi/Config.in +++ b/drivers/scsi/Config.in @@ -61,11 +61,6 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_SCSI_NCR53C7xx" != "y" ]; then bool ' not allow targets to disconnect' CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT fi fi - if [ "$CONFIG_SCSI_NCR53C8XX" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then - bool ' disable master parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK - bool ' disable scsi parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK - bool ' force synchronous negotiation' CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO - fi fi if [ "$CONFIG_MCA" = "y" ]; then bool 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA diff --git a/drivers/scsi/README.ncr53c8xx b/drivers/scsi/README.ncr53c8xx index 7726190b666a..de5a95c25226 100644 --- a/drivers/scsi/README.ncr53c8xx +++ b/drivers/scsi/README.ncr53c8xx @@ -4,7 +4,7 @@ Written by Gerard Roudier 21 Rue Carnot 95170 DEUIL LA BARRE - FRANCE -05 November 1996 +29 December 1996 =============================================================================== 1. Introduction @@ -23,13 +23,18 @@ Written by Gerard Roudier 8.6 Clear profile counters 8.7 Set flag (no_sync) 9. Configuration parameters -10. Some constants and flags of the ncr53c8xx.h header files -11. Provided files -12. Installation procedure +10. Boot setup commands + 10.1 Syntax + 10.2 Available arguments + 10.3 Advised boot setup commands +11. Some constants and flags of the ncr53c8xx.h header file +12. Installation + 12.1 Provided files + 12.2 Installation procedure 13. Control commands under linux-1.2.13 14. Known problems 14.1 Tagged commands with Iomega Jaz device -15. SCSI problems solving +15. SCSI problem troubleshooting =============================================================================== @@ -81,13 +86,13 @@ Chip SDMS BIOS Wide Ultra SCSI the driver the driver 810A N N N Y Y 815 Y N N Y Y 825 Y Y N Y Y -825A Y Y N Y Not yet +825A Y Y N Y Y 860 N N Y(1) Y Y 875 Y Y Y(1) Y Y -(1) Ultra SCSI extensions will be supported in a future release of the - driver. - +(1) Ultra (fast20 / fast40) SCSI data transfers are "in theory" supported + by the driver, but not fully tested, since the maintainer does not have + for the moment Ultra SCSI hard disks. 3. Summary of other supported features. @@ -98,6 +103,7 @@ Chip SDMS BIOS Wide Ultra SCSI the driver the driver Debugging information: written to syslog (expert only) Scatter / gather Shared interrupt + Boot setup commands 4. Memory mapped I/O versus normal I/O @@ -259,21 +265,8 @@ Due to the 1/100 second tick of the system clock, "ms_post" time may be wrong. In the example above, we got 18038 interrupts "on the fly" and only -1673 script breaks probably due to disconnections inside a segment of -the scatter list. This is an excellent result due to the fact that -the driver tries to use small data segments (512) for the scatter -list. The CPU load of this rescatter process is acceptable. Unlike -other SCSI processors, NCR53C8XX controllers do not need large data -chunks in order to get better performance, and it seems that it is -just the opposite. The scatter/gather algorithm of the middle SCSI -driver is not optimal for NCR SCSI processors and should be tunable -according to host type. - -You can tune the "wished" segment size for the scatterlist by changing -the following "define" in the file ncr53c8xx.h. Use only power of 2 -greater than 512 (1024, 2048 or 4096). - -SCSI_NCR_SEGMENT_SIZE (default: 512) +1673 script breaks generally due to disconnections inside a segment +of the scatter list. 8. Control commands @@ -407,19 +400,138 @@ CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT (default and only reasonnable answer: n) even while performing long SCSI operations. -10. Some constants and flags of the ncr53c8xx.h header files +10. Boot setup commands + +10.1 Syntax + +Setup commands can be passed to the driver at boot time. +A boot setup command for the ncr53c8xx driver begins with the driver name +"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of +integers separated with comma followed by an optionnal list of comma- +separated strings. Example of boot setup command under lilo prompt: + +lilo: linux root=/dev/hda2 ncr53c8xx=tags:4,sync:10,debug:0x200 + +- enable tagged commands, up to 4 tagged commands queued. +- set synchronous negotiation speed to 10 Mega-transfers / second. +- set DEBUG_NEGO flag. + +For the moment, the integer list of arguments is disgarded by the driver. +It will be used in the future in order to allow a per controller setup. + +Each string argument must be specified as "keyword:value". Only lower-case +characters and digits are allowed. + +10.2 Available arguments + +Master parity checking + mpar:y enabled + mpar:n disabled + +Scsi parity checking + spar:y enabled + spar:n disabled + +Scsi disconnections + disc:y enabled + disc:n disabled + +Special features + Only apply to 810A, 825A, 860 and 875 controllers. + Have no effect with normal 810 and 825. + specf:y enabled + specf:n disabled + +Ultra SCSI support + Only apply to 860 and 875 controllers. + Have no effect with other ones. + ultra:y enabled + ultra:n disabled + +Number of tagged commands + tags:0 (or tags:1 ) tagged command queuing disabled + tags:#tags (#tags > 1) tagged command queuing enabled + #tags will be truncated to the max queued commands configuration parameter. + If the driver is configured with a maximum of 4 queued commands, tags:4 is + the right argument to specify. + +Default synchronous negotiation frequency + sync:0 disabled (asynchronous transfer mode) + sync:#MHz enabled up to #MHz. + #MHz > 10 Ultra SCSI + #MHz > 5 Fast SCSI + In all cases, the driver will use the minimum transfer period supported by + controllers according to NCR53C8XX chip type. + +Negotiate synchronous with all devices + (force sync nego) + fsn:y enabled + fsn:n disabled + +Verbosity level + verb:0 minimal + verb:1 normal + verb:2 too much + +Debug mode + debug:0 clear debug flags + debug:#x set debug flags + #x is an integer value combining the following power-of-2 values: + DEBUG_ALLOC 0x1 + DEBUG_PHASE 0x2 + DEBUG_POLL 0x4 + DEBUG_QUEUE 0x8 + DEBUG_RESULT 0x10 + DEBUG_SCATTER 0x20 + DEBUG_SCRIPT 0x40 + DEBUG_TINY 0x80 + DEBUG_TIMING 0x100 + DEBUG_NEGO 0x200 + DEBUG_TAGS 0x400 + DEBUG_FREEZE 0x800 + DEBUG_RESTART 0x1000 + +You can play safely with DEBUG_NEGO. However, some of these flags may +generate bunches of syslog messages. + +10.3 Advised boot setup commands + +If the driver has been configured with default options, the equivalent +boot setup is: + ncr53c8xx=mpar:y,spar:y,disc:y,fsn:n,specf:y,ultra:y,sync:5,tags:0,verb:1 + +For an installation diskette or a safe but not fast system, boot setup is: + ncr53c8xx=mpar:y,spar:y,disc:n,fsn:n,specf:n,ultra:n,sync:0,tags:0 + +My personnal system works flawlessly with the following setup: + ncr53c8xx=mpar:y,spar:y,disc:y,fsn:n,specf:y,ultra:y,sync:20,tags:8 + +The driver prints its actual setup when verbosity level is 2. You can try +"ncr53c8xx=verb:2" to get the "static" setup of the driver, or add "verb:2" +to your boot setup command in order to check the actual setup the drive use. + + +11. Some constants and flags of the ncr53c8xx.h header file Some of these are defined from the configuration parameters. To change other "defines", you must edit the header file. Do that only if you know what you are doing. -SCSI_NCR_TRUST_BIOS_SETTING (default: not defined) - If defined, the driver will preserve features bits from - dmode/dcntl/ctest3/ctest4 io registers. +SCSI_NCR_SETUP_ULTRA_SUPPORT (default: defined) + Ultra SCSI support. + Can be changed by the following boot setup command: + ncr53c8xx=ultra:n -SCSI_NCR_SPECIAL_FEATURES (default: not defined) +SCSI_NCR_SETUP_SPECIAL_FEATURES (default: defined) If defined, the driver will enable some special features according to chip and revision id. + For 810A, 860, 825A and 875 scsi chips, this option enables support + of features that reduce load of PCI bus and memory accesses during + scsi transfer processing: burst op-code fetch, read multiple, + read line, prefetch, cache line line, write and invalidate, + burst 128 (875 only), large dma fifo (875 only), offset 16 (875 only). + Can be changed by the following boot setup command: + ncr53c8xx=specf:n SCSI_NCR_IOMAPPED (default: not defined) If defined, normal I/O is forced. @@ -431,12 +543,12 @@ SCSI_NCR_MAX_TAGS (default: 4) Maximum number of simultaneous tagged commands to a device. Can be changed by "settags " -SCSI_NCR_DEFAULT_SYNC (default: 5) +SCSI_NCR_SETUP_DEFAULT_SYNC (default: 5) Frequency in KHz the driver will use at boot time for synchronous negotiation. 0 means asynchronous. Can be changed by "setsync " -SCSI_NCR_DEFAULT_TAGS (default: 4) +SCSI_NCR_SETUP_DEFAULT_TAGS (default: 4) Default number of simultaneous tagged commands to a device. < 1 means tagged command queuing disabled at start-up. @@ -444,18 +556,18 @@ SCSI_NCR_ALWAYS_SIMPLE_TAG (default: defined) Use SIMPLE TAG for read and write commands. Can be changed by "setorder " -SCSI_NCR_NO_DISCONNECT (default: not defined) - If defined, targets are not allowed to disconnect. +SCSI_NCR_SETUP_DISCONNECTION (default: defined) + If defined, targets are allowed to disconnect. -SCSI_NCR_FORCE_SYNC_NEGO (default: not defined) +SCSI_NCR_SETUP_FORCE_SYNC_NEGO (default: not defined) If defined, synchronous negotiation is tried for all SCSI-2 devices. Can be changed by "setsync " -SCSI_NCR_DISABLE_MPARITY_CHECK (default: not defined) - If defined, master parity checking is disabled. +SCSI_NCR_SETUP_MASTER_PARITY (default: defined) + If defined, master parity checking is enabled. -SCSI_NCR_DISABLE_PARITY_CHECK (default: not defined) - If defined, SCSI parity checking is disabled. +SCSI_NCR_SETUP_MASTER_PARITY (default: defined) + If defined, SCSI parity checking is enabled. SCSI_NCR_PROFILE (default: defined) If defined, profiling information is gathered. @@ -463,11 +575,6 @@ SCSI_NCR_PROFILE (default: defined) SCSI_NCR_MAX_SCATTER (default: 128) Scatter list size of the driver ccb. -SCSI_NCR_SEGMENT_SIZE (default: 512) - If defined, the driver will try to use scatter segments of this size. - If not defined, the Linux scatter list is used as is. - Not defined by default. - SCSI_NCR_MAX_TARGET (default: 16) Max number of targets per host. @@ -494,8 +601,9 @@ SCSI_NCR_SG_TABLESIZE (default: SCSI_NCR_MAX_SCATTER-1) SCSI_NCR_MAX_LUN (default: 8) Max number of LUNs per target. +12. Installation -11. Provided files +12.1 Provided files Driver and common files: @@ -509,20 +617,19 @@ Driver and common files: Install.ncr53c8xx : installation script - Patch-1.2.13.ncr53c8xx : patch for linux-1.2.13 - Patch-2.0.22-25.ncr53c8xx : patch for linux-2.0.22 to linux-2.0.25 + Patch-1.2.13.ncr53c8xx : patch for linux-1.2.13 + Patch-2.0.27.ncr53c8xx : patch for linux-2.0.27 Prior to installing the driver, you must untar the distribution, as follow: mkdir ncrB2L cd ncrB2L - tar zxvf ncrBsd2Linux-1.14c-src.tar.gz + tar zxvf ncrBsd2Linux-1.16b-src.tar.gz -12. Installation procedure +12.2 Installation procedure -This install script has been tested with linux-1.2.13 and linux-2.0.22 -to 2.0.25. It will probably work with linux 2.0.X (X>25). +This install script has been tested with linux-1.2.13 and 2.0.27. This procedure copies the new driver files to the kernel tree and applies a patch to some files of the kernel tree. diff --git a/drivers/scsi/README.st b/drivers/scsi/README.st index 864836cc141e..fc3ff8e3906e 100644 --- a/drivers/scsi/README.st +++ b/drivers/scsi/README.st @@ -2,7 +2,7 @@ This file contains brief information about the SCSI tape driver. The driver is currently maintained by Kai M{kisara (email Kai.Makisara@metla.fi) -Last modified: Sun Jun 30 15:47:14 1996 by root@kai.makisara.fi +Last modified: Wed Jan 1 15:44:49 1997 by makisara@kai.makisara.fi BASICS @@ -78,6 +78,17 @@ returning zero bytes for two consecutive reads. The compile options are defined in the file linux/drivers/scsi/st_options.h. +BSD AND SYS V SEMANTICS + +The user can choose between these two behaviours of the tape driver by +defining the value of the symbol ST_SYSV. The semantics differ when a +file being read is closed. The BSD semantics leaves the tape where it +currently is whereas the SYS V semantics moves the tape past the next +filemark unless the filemark has just been crossed. + +The default is BSD semantics. + + BUFFERING The driver uses tape buffers allocated either at system initialization @@ -186,7 +197,11 @@ MTSETBLK Set the drive block size. Setting to zero sets the drive into MTSETDENSITY Sets the drive density code to arg. See drive documentation for available codes. MTLOCK and MTUNLOCK Explicitly lock/unlock the tape drive door. -MTLOAD and MTUNLOAD Explicitly load and unload the tape. +MTLOAD and MTUNLOAD Explicitly load and unload the tape. If the + command argument x is between MT_ST_HPLOADER_OFFSET + 1 and + MT_ST_HPLOADER_OFFSET + 6, the number x is used sent to the + drive with the command and it selects the tape slot to use of + HP C1553A changer. MTCOMPRESSION Sets compressing or uncompressing drive mode using the SCSI mode page 15. Note that some drives other methods for control of compression. Some drives (like the Exabytes) use diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index c7efd81857dd..d23518438738 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -40,7 +40,7 @@ */ /* -** 13 October 1996, version 1.14a +** 26 December 1996, version 1.16b ** ** Supported SCSI-II features: ** Synchronous negotiation @@ -55,8 +55,8 @@ ** 53C815 (~53C810 with on board rom BIOS) ** 53C820 (Wide, NCR BIOS in flash bios required) ** 53C825 (Wide, ~53C820 with on board rom BIOS) -** 53C860 (not fully ested) -** 53C875 (not fully tested) +** 53C860 (Narrow fast 20, BIOS required) +** 53C875 (Wide fast 40 with on board rom BIOS) ** ** Other features: ** Memory mapped IO (linux-1.3.X and above only) @@ -67,7 +67,7 @@ #define SCSI_NCR_DEBUG #define SCSI_NCR_DEBUG_FLAGS (0) -#define NCR_DATE "pl23 95/09/07" +#define NCR_DATE "pl24 96/12/14" #define NCR_VERSION (2) @@ -105,7 +105,7 @@ #include #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) -#include "linux/blk.h" +#include #else #include "../block/blk.h" #endif @@ -159,10 +159,6 @@ typedef u32 u_int32; #define SCSI_NCR_MAX_SYNC (10000) #endif -#ifndef SCSI_NCR_DEFAULT_SYNC -#define SCSI_NCR_DEFAULT_SYNC SCSI_NCR_MAX_SYNC -#endif - /* ** The maximal bus with (in log2 byte) ** (0=8 bit, 1=16 bit) @@ -427,6 +423,24 @@ static struct { unsigned char and_map[MAX_TARGET]; } target_capabilities[SCSI_NCR_MAX_HOST] = { NCR53C8XX_TARGET_CAPABILITIES }; +/* +** Driver setup. +** +** This structure is initialized from linux config options. +** It can be overridden at boot-up by the boot command line. +*/ +static struct { + unsigned master_parity : 1; + unsigned scsi_parity : 1; + unsigned disconnection : 1; + unsigned special_features : 1; + unsigned ultra_scsi : 1; + unsigned force_sync_nego: 1; + unsigned verbose : 8; + unsigned default_tags; + unsigned default_sync; + unsigned debug; +} driver_setup = SCSI_NCR_DRIVER_SETUP; /* ** Other Linux definitions @@ -446,7 +460,7 @@ static void ncr53c8xx_intr(int irq, struct pt_regs * regs); static void ncr53c8xx_timeout(unsigned long np); -#define bootverbose 1 +#define bootverbose (driver_setup.verbose) /*========================================================== ** @@ -1269,7 +1283,7 @@ struct ncb { ** between ncr chips. ** sv_xxx are some io register bit value at start-up and ** so assumed to have been set by the sdms bios. - ** uf_xxx are the bit fields of io register that will keep + ** rv_xxx are the bit fields of io register that will keep ** the features used by the driver. **----------------------------------------------- */ @@ -1284,10 +1298,12 @@ struct ncb { u_char sv_ctest3; u_char sv_ctest4; - u_char uf_dmode; - u_char uf_dcntl; - u_char uf_ctest3; - u_char uf_ctest4; + u_char rv_dmode; + u_char rv_dcntl; + u_char rv_ctest3; + u_char rv_ctest4; + u_char rv_ctest5; + u_char uf_doubler; /*----------------------------------------------- ** Scripts .. @@ -1337,10 +1353,14 @@ struct ncb { /* ** timing parameters */ - u_char ns_async; u_char ns_sync; u_char rv_scntl3; + /* + ** controller chip dependent maximal offset. + */ + u_char maxoffs; + /*----------------------------------------------- ** Link to the generic SCSI driver **----------------------------------------------- @@ -1566,7 +1586,7 @@ static void ncr_script_copy_and_bind static void ncr_script_fill (struct script * scr); static int ncr_scatter (ccb_p cp, Scsi_Cmnd *cmd); static void ncr_setmaxtags (ncb_p np, tcb_p tp, u_long usrtags); -static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer); +static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer); static void ncr_settags (tcb_p tp, lcb_p lp); static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide); static int ncr_show_msg (u_char * msg); @@ -3437,7 +3457,7 @@ struct host_data { */ #define PRINT_LUN(np, target, lun) \ -printf("%s-: ", ncr_name(np), (int) (target), (int) (lun)) +printf("%s-<%d,%d>: ", ncr_name(np), (int) (target), (int) (lun)) static inline void PRINT_ADDR(Scsi_Cmnd *cmd) { @@ -3517,7 +3537,8 @@ printf("ncr53c8xx: unit=%d chip=%d rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n", #endif } else - printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr); + if (bootverbose) + printf("%s: using memory mapped IO at virtual address 0x%lx\n", ncr_name(np), (u_long) np->vaddr); /* ** Make the controller's registers available. @@ -3549,7 +3570,7 @@ printf("ncr53c8xx: unit=%d chip=%d rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n", np->maxwide = 0; np->rv_scntl3 = 0x13; /* default: 40MHz clock */ np->ns_sync = 25; - np->ns_async = 50; + np->maxoffs = 8; /* ** Get the frequency of the chip's clock. @@ -3561,14 +3582,24 @@ printf("ncr53c8xx: unit=%d chip=%d rev=0x%x base=0x%x, io_port=0x%x, irq=%d\n", np->maxwide = 1; break; case PCI_DEVICE_ID_NCR_53C860: - np->rv_scntl3 = 0x35; /* always assume 80MHz clock for 860 */ + if (driver_setup.ultra_scsi) { + np->rv_scntl3 = 0x15; + np->ns_sync = 12; + } + else + np->rv_scntl3 = 0x35; /* always assume 80MHz clock for 860 */ break; case PCI_DEVICE_ID_NCR_53C875: np->maxwide = 1; + if (driver_setup.special_features) + np->maxoffs = 16; ncr_getclock(np); break; } + if (bootverbose && np->ns_sync < 25) + printf ("%s: Ultra SCSI support enabled\n", ncr_name(np)); + /* ** Fill Linux host instance structure */ @@ -3623,14 +3654,6 @@ retry_chip_init: DELAY (1000); OUTB (nc_istat, 0 ); - /* - ** Reset chip, once again. - */ - - OUTB (nc_istat, SRST); - DELAY (1000); - OUTB (nc_istat, 0 ); - /* ** Now check the cache handling of the pci chipset. */ @@ -3653,8 +3676,9 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n", */ #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70) # ifdef SCSI_NCR_SHARE_IRQ - printf("%s: requesting shared irq %d (dev_id=0x%lx)\n", - ncr_name(np), irq, (u_long) np); + if (bootverbose) + printf("%s: requesting shared irq %d (dev_id=0x%lx)\n", + ncr_name(np), irq, (u_long) np); if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT|SA_SHIRQ, "53c8xx", np)) { # else if (request_irq(irq, ncr53c8xx_intr, SA_INTERRUPT, "53c8xx", NULL)) { @@ -3671,13 +3695,11 @@ printf("%s: cache misconfigured, retrying with IO mapped at 0x%lx\n", ** Not allow disconnections for all targets if asked by config */ -#ifdef SCSI_NCR_NO_DISCONNECT - { + if (!driver_setup.disconnection) { int i; for (i = 0 ; i < MAX_TARGET ; i++) np->target[i].usrflag |= UF_NODISC; } -#endif /* ** After SCSI devices have been opened, we cannot @@ -3885,12 +3907,12 @@ int ncr_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) ** **---------------------------------------------------- */ -#if (SCSI_NCR_DEFAULT_TAGS < SCSI_NCR_MAX_TAGS) - if (cmd->device && cmd->device->tagged_queue && - (lp = tp->lp[cmd->lun]) && (!lp->usetags)) { - ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS); + if (driver_setup.default_tags < SCSI_NCR_MAX_TAGS) { + if (cmd->device && cmd->device->tagged_queue && + (lp = tp->lp[cmd->lun]) && (!lp->usetags)) { + ncr_setmaxtags (np, tp, SCSI_NCR_MAX_TAGS); + } } -#endif /*--------------------------------------------------- ** @@ -4346,6 +4368,9 @@ static int ncr_abort_command (Scsi_Cmnd *cmd) /* ** Disable reselect. ** Remove it from startqueue. + ** Set cp->tlimit to 0. The ncr_timeout() handler will use + ** this condition in order to complete the canceled command + ** after the script skipped the ccb, if necessary. */ cp->jump_ccb.l_cmd = (SCR_JUMP); if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) { @@ -4353,35 +4378,15 @@ static int ncr_abort_command (Scsi_Cmnd *cmd) cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip); } - switch (cp->host_status) { - case HS_BUSY: - case HS_NEGOTIATE: - /* - ** still in start queue ? - */ - if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, skip)) { - retv = SCSI_ABORT_BUSY; - break; - } - /* fall through */ - case HS_DISCONNECT: - cp->host_status=HS_ABORTED; - cp->tag = 0; - /* - ** wakeup this ccb. - */ - ncr_complete (np, cp); - retv = SCSI_ABORT_SUCCESS; - break; - default: - cp->tag = 0; - /* - ** wakeup this ccb. - */ - ncr_complete (np, cp); - retv = SCSI_ABORT_SUCCESS; - break; - } + cp->tlimit = 0; + retv = SCSI_ABORT_PENDING; + + /* + ** If there are no requests, the script + ** processor will sleep on SEL_WAIT_RESEL. + ** Let's wake it up, since it may have to work. + */ + OUTB (nc_istat, SIGP); restore_flags(flags); @@ -4453,7 +4458,7 @@ static int ncr_detach(ncb_p np, int irq) /* ** Reset NCR chip - ** Preserve bios setting for automatic clock detection. + ** Restore bios setting for automatic clock detection. */ printf("%s: resetting chip\n", ncr_name(np)); @@ -4461,12 +4466,22 @@ static int ncr_detach(ncb_p np, int irq) DELAY (1000); OUTB (nc_istat, 0 ); - OUTB(nc_scntl3, np->sv_scntl3); OUTB(nc_dmode, np->sv_dmode); OUTB(nc_dcntl, np->sv_dcntl); OUTB(nc_ctest3, np->sv_ctest3); OUTB(nc_ctest4, np->sv_ctest4); + if (np->uf_doubler) { + OUTB(nc_stest1, DBLEN); /* Enable clock doubler */ + DELAY(10); + OUTB(nc_stest3, 0x20); /* Halt the scsi clock */ + OUTB(nc_scntl3, np->sv_scntl3); + OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock doubler */ + OUTB(nc_stest3, 0x00); /* Restart scsi clock */ + } + else + OUTB(nc_scntl3, np->sv_scntl3); + /* ** Release Memory mapped IO region and IO mapped region */ @@ -4641,11 +4656,11 @@ void ncr_complete (ncb_p np, ccb_p cp) */ if (cmd->lun == 0 && cmd->cmnd[0] == 0x12) { if (np->unit < SCSI_NCR_MAX_HOST) { -#ifdef SCSI_NCR_FORCE_SYNC_NEGO - ((char *) cmd->request_buffer)[7] |= INQ7_SYNC; -#endif - ((char *) cmd->request_buffer)[7] &= - (target_capabilities[np->unit].and_map[cmd->target]); + if (driver_setup.force_sync_nego) + ((char *) cmd->request_buffer)[7] |= INQ7_SYNC; + else + ((char *) cmd->request_buffer)[7] &= + (target_capabilities[np->unit].and_map[cmd->target]); } bcopy ( cmd->request_buffer, &tp->inqdata, @@ -4654,7 +4669,7 @@ void ncr_complete (ncb_p np, ccb_p cp) /* ** set number of tags */ - ncr_setmaxtags (np, tp, SCSI_NCR_DEFAULT_TAGS); + ncr_setmaxtags (np, tp, driver_setup.default_tags); /* ** prepare negotiation of synch and wide. */ @@ -4915,70 +4930,71 @@ void ncr_init (ncb_p np, char * msg, u_long code) */ ncr_wakeup (np, code); - /* - ** Remove Reset, abort ... - */ - OUTB (nc_istat, 0 ); - /* ** Init chip. */ #if defined SCSI_NCR_TRUST_BIOS_SETTING - np->uf_dmode = np->sv_dmode; - np->uf_dcntl = np->sv_dcntl; - np->uf_ctest3 = np->sv_ctest3; - np->uf_ctest4 = np->sv_ctest4; + np->rv_dmode = np->sv_dmode; + np->rv_dcntl = np->sv_dcntl; + np->rv_ctest3 = np->sv_ctest3; + np->rv_ctest4 = np->sv_ctest4; #else - np->uf_dmode = 0; - np->uf_dcntl = 0; - np->uf_ctest3 = 0; - np->uf_ctest4 = 0; + np->rv_dmode = 0; + np->rv_dcntl = 0; + np->rv_ctest3 = 0; + np->rv_ctest4 = 0; /** NCR53C810 **/ if (ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion == 0) { - np->uf_dmode = 0x80; /* burst length 8 */ + np->rv_dmode = 0x80; /* burst length 8 */ } else /** NCR53C815 **/ if (ChipDevice == PCI_DEVICE_ID_NCR_53C815) { - np->uf_dmode = 0x80; /* burst length 8 */ + np->rv_dmode = 0x80; /* burst length 8 */ } else /** NCR53C825 **/ if (ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion == 0) { - np->uf_dmode = 0x8a; /* burst length 8, burst opcode fetch */ + np->rv_dmode = 0x8a; /* burst length 8, burst opcode fetch */ } else /** NCR53C810A or NCR53C860 **/ if ((ChipDevice == PCI_DEVICE_ID_NCR_53C810 && ChipVersion >= 0x10) || ChipDevice == PCI_DEVICE_ID_NCR_53C860) { -#ifndef SCSI_NCR_SPECIAL_FEATURES - np->uf_dmode = 0xc0; /* burst length 16 */ -#else - np->uf_dmode = 0xce; /* burst op-code fetch, read multiple */ - /* read line, burst length 16 */ - np->uf_dcntl = 0xa0; /* prefetch, cache line size */ - np->uf_ctest3 = 0x1; /* write and invalidate */ - np->uf_ctest4 = 0x0; /* burst not disabled */ -#endif + if (!driver_setup.special_features) + np->rv_dmode = 0xc0; /* burst length 16 */ + else { + np->rv_dmode = 0xc0 | BOF | ERMP | ERL; + /* burst op-code fetch, read multiple */ + /* read line, burst 16 */ + np->rv_dcntl = PFEN | CLSE; + /* prefetch, cache line size */ + np->rv_ctest3 = WRIE; /* write and invalidate */ + np->rv_ctest4 = 0x0; /* burst not disabled */ + } } else /** NCR53C825A or NCR53C875 **/ if ((ChipDevice == PCI_DEVICE_ID_NCR_53C825 && ChipVersion >= 0x10) || ChipDevice == PCI_DEVICE_ID_NCR_53C875) { -#ifndef SCSI_NCR_SPECIAL_FEATURES - np->uf_dmode = 0xc0; /* burst length 16 */ -#else - np->uf_dmode = 0xce; /* burst op-code fetch, read multiple */ - /* read line, burst length 16 */ - np->uf_dcntl = 0xa0; /* prefetch, cache line size */ - np->uf_ctest3 = 0x1; /* write and invalidate */ - np->uf_ctest4 = 0x0; /* burst not disabled */ -#endif + if (!driver_setup.special_features) + np->rv_dmode = 0xc0; /* burst length 16 */ + else { + np->rv_dmode = 0xc0 | BOF | ERMP | ERL; + /* burst op-code fetch, read multiple */ + /* read line, burst 128 (ctest5&4) */ + np->rv_dcntl = PFEN | CLSE; + /* prefetch, cache line size */ + np->rv_ctest3 = WRIE; /* write and invalidate */ + np->rv_ctest4 = 0x0; /* burst not disabled */ + np->rv_ctest5 = 0x24; /* burst 128 (0x04) */ + /* dma fifo 536 (0x20) */ + } } /** OTHERS **/ else { - np->uf_dmode = 0xc0; /* burst length 16 */ + np->rv_dmode = 0xc0; /* burst length 16 */ } #endif /* SCSI_NCR_TRUST_BIOS_SETTING */ @@ -4986,28 +5002,44 @@ void ncr_init (ncb_p np, char * msg, u_long code) printf("%s: bios: dmode=0x%02x, dcntl=0x%02x, ctest3=0x%02x, ctest4=0x%02x\n", ncr_name(np), np->sv_dmode, np->sv_dcntl, np->sv_ctest3, np->sv_ctest4); printf("%s: used: dmode=0x%02x, dcntl=0x%02x, ctest3=0x%02x, ctest4=0x%02x\n", - ncr_name(np), np->uf_dmode, np->uf_dcntl, np->uf_ctest3, np->uf_ctest4); + ncr_name(np), np->rv_dmode, np->rv_dcntl, np->rv_ctest3, np->rv_ctest4); #endif -#ifdef SCSI_NCR_DISABLE_PARITY_CHECK - OUTB (nc_scntl0, 0xc0 ); /* full arb., (no parity) */ -#else - OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */ -#endif + OUTB (nc_istat, 0x00 ); /* Remove Reset, abort ... */ + if (driver_setup.scsi_parity) + OUTB (nc_scntl0, 0xca ); /* full arb., ena parity, par->ATN */ + else + OUTB (nc_scntl0, 0xc0 ); /* full arb., (no parity) */ + OUTB (nc_scntl1, 0x00 ); /* odd parity, and remove CRST!! */ + if (np->uf_doubler) { + if (bootverbose >= 2) + printf ("%s: enabling clock doubler\n", ncr_name(np)); + OUTB(nc_stest1, DBLEN); /* Enable clock doubler */ + DELAY(10); + OUTB(nc_stest3, 0x20); /* Halt the scsi clock */ + OUTB(nc_scntl3, 0x05); /* Safe timing for now */ + OUTB(nc_stest1, (DBLEN|DBLSEL));/* Select clock doubler */ + OUTB(nc_stest3, 0x00); /* Restart scsi clock */ + } + OUTB (nc_scntl3, np->rv_scntl3);/* timing prescaler */ OUTB (nc_scid , RRE|np->myaddr);/* host adapter SCSI address */ OUTW (nc_respid, 1ul<myaddr);/* id to respond to */ OUTB (nc_istat , SIGP ); /* Signal Process */ - OUTB (nc_dmode , np->uf_dmode); /* Burst length = 2 .. 16 transfers */ - OUTB (nc_dcntl , NOCOM|np->uf_dcntl);/* no single step mode, protect SFBR*/ + OUTB (nc_dmode , np->rv_dmode); /* Burst length = 2 .. 16 transfers */ -#ifdef SCSI_NCR_DISABLE_MPARITY_CHECK - OUTB (nc_ctest4, 0x00|np->uf_ctest4); /* disable master parity checking */ -#else - OUTB (nc_ctest4, 0x08|np->uf_ctest4); /* enable master parity checking */ -#endif + if (driver_setup.special_features && np->rv_ctest5) + OUTB (nc_ctest5, np->rv_ctest5); /* large fifo + large burst */ + + OUTB (nc_dcntl , NOCOM|np->rv_dcntl);/* no single step mode, protect SFBR*/ + OUTB (nc_ctest3, np->rv_ctest3); /* write and invalidate */ + + if (driver_setup.master_parity) + OUTB (nc_ctest4, MPEE|np->rv_ctest4); /* enable master parity checking */ + else + OUTB (nc_ctest4, 0x00|np->rv_ctest4); /* disable master parity checking */ OUTB (nc_stest2, EXT ); /* Extended Sreq/Sack filtering */ OUTB (nc_stest3, TE ); /* TolerANT enable */ @@ -5021,18 +5053,16 @@ void ncr_init (ncb_p np, char * msg, u_long code) usrsync = 255; -#if defined(SCSI_NCR_DEFAULT_SYNC) && SCSI_NCR_DEFAULT_SYNC != 0 - if (SCSI_NCR_MAX_SYNC) { + if (driver_setup.default_sync && SCSI_NCR_MAX_SYNC) { u_long period; - period =1000000/SCSI_NCR_DEFAULT_SYNC; /* ns = 10e6 / kHz */ - if (period <= 11 * np->ns_sync) { + period =1000000/driver_setup.default_sync; /* ns = 10e6 / kHz */ + if (period <= 11 * 50) { if (period < 4 * np->ns_sync) usrsync = np->ns_sync; else usrsync = period / 4; }; }; -#endif /* ** Reinitialize usrwide. @@ -5094,7 +5124,12 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp) u_long minsync = tp->usrsync; - if (minsync < 25) minsync=25; + if (driver_setup.ultra_scsi) { + if (minsync < 12) minsync=12; + } + else { + if (minsync < 25) minsync=25; + } /* ** if not scsi 2 @@ -5115,11 +5150,11 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp) ** divider limit */ - if (minsync > (np->ns_sync * 11) / 4) + if (minsync > (11*50)/4) minsync = 255; tp->minsync = minsync; - tp->maxoffs = (minsync<255 ? 8 : 0); + tp->maxoffs = (minsync<255 ? np->maxoffs : 0); /* ** period=0: has to negotiate sync transfer @@ -5140,11 +5175,12 @@ static void ncr_negotiate (struct ncb* np, struct tcb* tp) **========================================================== */ -static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer) +static void ncr_setsync (ncb_p np, ccb_p cp, u_char scntl3, u_char sxfer) { Scsi_Cmnd *cmd; tcb_p tp; u_char target = INB (nc_ctest0) & 0x0f; + u_char p2; assert (cp); if (!cp) return; @@ -5155,26 +5191,47 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer) assert (target == (cmd->target & 0xf)); tp = &np->target[target]; - tp->period= sxfer&0xf ? ((sxfer>>5)+4) * np->ns_sync : 0xffff; - if (tp->sval == sxfer) return; + if (!scntl3 || !(sxfer & 0x1f)) + scntl3 = np->rv_scntl3; + scntl3 = (scntl3 & 0xf0) | (tp->wval & EWS) | (np->rv_scntl3 & 0x07); + + /* + ** Deduce the value of controller sync period from scntl3. + ** p2 is twice this value since we do integer calculations. + * (12.5 ns would give inaccurate results) + */ + p2 = (scntl3 & 0x07) == 0x05 ? 25 : 50; + switch(scntl3 & 0x70) { + case 0x50: p2 *= 4; break; + case 0x30: p2 *= 2; break; + default: break; + } + + tp->period= sxfer&0x1f ? (((sxfer>>5)+4) * p2)/2 : 0xffff; + + if (tp->sval == sxfer && tp->wval == scntl3) return; tp->sval = sxfer; + tp->wval = scntl3; /* ** Bells and whistles ;-) */ PRINT_ADDR(cmd); - if (sxfer & 0x0f) { + if (sxfer & 0x01f) { + unsigned f10 = 10000 << (tp->widedone ? tp->widedone -1 : 0); + unsigned mb10 = (f10 + tp->period/2) / tp->period; /* ** Disable extended Sreq/Sack filtering */ if (tp->period <= 200) OUTB (nc_stest2, 0); - - printf ("%s%dns (%d Mb/sec) offset %d.\n", - tp->period<200 ? "FAST SCSI-2 ":"", - tp->period, - (((tp->wval & EWS)? 2:1)*1000+tp->period/2)/tp->period, - sxfer & 0x0f); + printf ("%s%d.%d MB/s (%d ns, offset %d)\n", + tp->widedone > 1 ? + (tp->period < 100 ? "ULTRA WIDE SCSI " : + (tp->period < 200 ? "FAST WIDE SCSI-2 ":"WIDE ")) : + (tp->period < 100 ? "ULTRA SCSI " : + (tp->period < 200 ? "FAST SCSI-2 ":"")), + mb10 / 10, mb10 % 10, tp->period, sxfer & 0x1f); } else printf ("asynchronous.\n"); /* @@ -5182,6 +5239,8 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer) */ OUTB (nc_sxfer, sxfer); np->sync_st = sxfer; + OUTB (nc_scntl3, scntl3); + np->wide_st = scntl3; /* ** patch ALL ccbs of this target. @@ -5190,6 +5249,7 @@ static void ncr_setsync (ncb_p np, ccb_p cp, u_char sxfer) if (!cp->cmd) continue; if (cp->cmd->target != target) continue; cp->sync_status = sxfer; + cp->wide_status = scntl3; }; } @@ -5205,7 +5265,7 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide) Scsi_Cmnd *cmd; u_short target = INB (nc_ctest0) & 0x0f; tcb_p tp; - u_char scntl3 = np->rv_scntl3 | (wide ? EWS : 0); + u_char scntl3; assert (cp); if (!cp) return; @@ -5217,17 +5277,20 @@ static void ncr_setwide (ncb_p np, ccb_p cp, u_char wide) tp = &np->target[target]; tp->widedone = wide+1; + scntl3 = (tp->wval & (~EWS)) | (wide ? EWS : 0); if (tp->wval == scntl3) return; tp->wval = scntl3; /* ** Bells and whistles ;-) */ - PRINT_ADDR(cmd); - if (scntl3 & EWS) - printf ("WIDE SCSI (16 bit) enabled.\n"); - else - printf ("WIDE SCSI disabled.\n"); + if (bootverbose >= 2) { + PRINT_ADDR(cmd); + if (scntl3 & EWS) + printf ("WIDE SCSI (16 bit) enabled.\n"); + else + printf ("WIDE SCSI disabled.\n"); + } /* ** set actual value and sync_status @@ -5421,7 +5484,7 @@ static void ncr_timeout (ncb_p np) ** If release process in progress, let's go ** Set the release stage from 1 to 2 to synchronize ** with the release process. - **/ + */ if (np->release_stage) { if (np->release_stage == 1) np->release_stage = 2; @@ -5436,7 +5499,12 @@ static void ncr_timeout (ncb_p np) add_timer(&np->timer); - if (np->lasttime + HZ < thistime) { + /* + ** Since the generic scsi driver only allows us 0.5 second + ** to perform abort of a command, we must look at ccbs about + ** every 0.25 second. + */ + if (np->lasttime + (HZ>>2) <= thistime) { /* ** block ncr interrupts */ @@ -5472,33 +5540,14 @@ static void ncr_timeout (ncb_p np) ** But we have to check whether it died. ** Let's wake it up. */ + OUTB (nc_istat, SIGP); } -#ifdef undef - if (np->latetime>10) { - /* - ** Although we tried to wake it up, - ** the script processor didn't respond. - ** - ** May be a target is hanging, - ** or another initator lets a tape device - ** rewind with disconnect disabled :-( - ** - ** We won't accept that. - */ - if (INB (nc_sbcl) & CBSY) - OUTB (nc_scntl1, CRST); - DELAY (1000); - ncr_init (np, "ncr dead ?", HS_TIMEOUT); - np->disc = 1; - np->heartbeat = thistime; - } -#endif /* undef */ /*---------------------------------------------------- ** - ** should handle ccb timeouts - ** Let the middle scsi driver manage timeouts. + ** handle ccb timeouts + ** **---------------------------------------------------- */ @@ -5511,7 +5560,7 @@ static void ncr_timeout (ncb_p np) /* ** Have to force ordered tag to avoid timeouts */ - if (cp->cmd && cp->tlimit <= + if (cp->cmd && cp->tlimit && cp->tlimit <= thistime + NCR_TIMEOUT_INCREASE + SCSI_NCR_TIMEOUT_ALERT) { lcb_p lp; lp = np->target[cp->cmd->target].lp[cp->cmd->lun]; @@ -5519,24 +5568,14 @@ static void ncr_timeout (ncb_p np) lp->force_ordered_tag = 1; } } -/* -** Let the middle scsi driver manage timeouts -*/ -#if 0 - if (cp->tlimit > thistime) continue; - /* - ** Disable reselect. - ** Remove it from startqueue. + ** ncr_abort_command() cannot complete canceled + ** commands immediately. It sets tlimit to zero + ** and ask the script to skip the scsi process if + ** necessary. We have to complete this work here. */ - cp->jump_ccb.l_cmd = (SCR_JUMP); - if (cp->phys.header.launch.l_paddr == - NCB_SCRIPT_PHYS (np, select)) { - printf ("%s: timeout ccb=%p (skip)\n", - ncr_name (np), cp); - cp->phys.header.launch.l_paddr - = NCB_SCRIPT_PHYS (np, skip); - }; + + if (cp->tlimit) continue; switch (cp->host_status) { @@ -5551,7 +5590,7 @@ static void ncr_timeout (ncb_p np) /* fall through */ case HS_DISCONNECT: - cp->host_status=HS_TIMEOUT; + cp->host_status=HS_ABORTED; }; cp->tag = 0; @@ -5559,7 +5598,8 @@ static void ncr_timeout (ncb_p np) ** wakeup this ccb. */ ncr_complete (np, cp); -#endif + + OUTB (nc_istat, SIGP); } restore_flags(flags); } @@ -5615,8 +5655,8 @@ void ncr_exception (ncb_p np) ** Never test for an error condition you don't know how to handle. */ - dstat = INB (nc_dstat); - sist = INW (nc_sist) ; + sist = (istat & SIP) ? INW (nc_sist) : 0; + dstat = (istat & DIP) ? INB (nc_dstat) : 0; np->profile.num_int++; if (DEBUG_FLAGS & DEBUG_TINY) @@ -5756,11 +5796,11 @@ void ncr_exception (ncb_p np) if (((script_ofs & 3) == 0) && (unsigned)script_ofs < sizeof(struct script)) { - printf ("\tscript cmd = %08x\n", + printf ("%s: script cmd = %08x\n", ncr_name(np), (int) *(ncrcmd *)((char*)np->script +script_ofs)); } - printf ("\treg:\t"); + printf ("%s: reg:", ncr_name(np)); for (i=0; i<16;i++) printf (" %02x", (unsigned)INB_OFF(i)); printf (".\n"); @@ -5995,7 +6035,8 @@ static void ncr_int_ma (ncb_p np) u_int32 oadr, olen; u_int32 *tblp; ncrcmd *newcmd; - u_char cmd, sbcl, delta, ss0, ss2; + u_char cmd, sbcl, ss0, ss2, ctest5; + u_short delta; ccb_p cp; dsp = INL (nc_dsp); @@ -6005,9 +6046,18 @@ static void ncr_int_ma (ncb_p np) ss2 = INB (nc_sstat2); sbcl= INB (nc_sbcl); + if (driver_setup.special_features) + ctest5 = INB (nc_ctest5); + else + ctest5 = 0; + cmd = dbc >> 24; rest= dbc & 0xffffff; - delta=(INB (nc_dfifo) - rest) & 0x7f; + + if (ctest5 & 0x20) + delta=(((ctest5 << 8) | (INB (nc_dfifo) & 0xff)) - rest) & 0x3ff; + else + delta=(INB (nc_dfifo) - rest) & 0x7f; /* ** The data in the dma fifo has not been transfered to @@ -6183,6 +6233,7 @@ static int ncr_show_msg (u_char * msg) void ncr_int_sir (ncb_p np) { + u_char scntl3; u_char chg, ofs, per, fak, wide; u_char num = INB (nc_dsps); ccb_p cp=0; @@ -6384,7 +6435,7 @@ void ncr_int_sir (ncb_p np) switch (cp->nego_status) { case NS_SYNC: - ncr_setsync (np, cp, 0xe0); + ncr_setsync (np, cp, 0, 0xe0); break; case NS_WIDE: @@ -6440,24 +6491,60 @@ void ncr_int_sir (ncb_p np) /* ** Check against controller limits. + ** -------------------------------- + ** per <= 13 special case, allow fast 20 MHz transfer. + ** per < 25 fast 20 + ** per < 50 fast + ** per < 100 slow + ** Use a value p2 twice the controller limit in order to + ** not do wrong integer calculation for 80 MHz clock. + ** (12.5x2 = 25ns). + ** Compute scntl3&0xf0 sync clock divisor for 50 ns period. + ** Ajust it according to actual controller sync period. + ** - 0x40 divides it by 4 -> 50/4 = 12.5ns + ** - 0x20 divides it by 2 -> 50/2 = 25 ns + ** Similar stuff is used in ncr_getclock(). */ + fak = 7; + scntl3 = 0; if (ofs != 0) { - fak = (4ul * per - 1) / np->ns_sync - 3; - if (fak>7) { - chg = 1; - ofs = 0; + u_char p2; + + p2 = 100; + scntl3 = (np->rv_scntl3 & 0x07) << 4; + + if (per <= 13) { + fak = 0; + scntl3 = (scntl3 - 0x40) | 0x80; + } + else { + if (per < 25) { + p2 = 25; + scntl3 = (scntl3 - 0x40) | 0x80; + } + else if (per < 50) { + p2 = 50; + scntl3 = scntl3 - 0x20; + } + + fak = (8 * per - 1) / p2 - 3; + if (fak > 7) { + chg = 1; + ofs = 0; + } } } if (ofs == 0) { - fak = 7; - per = 0; + fak = 7; + per = 0; + scntl3 = 0; tp->minsync = 0; } if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->cmd); - printf ("sync: per=%d ofs=%d fak=%d chg=%d.\n", - per, ofs, fak, chg); + printf ("sync: per=%d scntl3=0x%x ofs=%d fak=%d chg=%d.\n", + per, scntl3, ofs, fak, chg); } if (INB (HS_PRT) == HS_NEGOTIATE) { @@ -6472,13 +6559,13 @@ void ncr_int_sir (ncb_p np) /* ** Answer wasn't acceptable. */ - ncr_setsync (np, cp, 0xe0); + ncr_setsync (np, cp, 0, 0xe0); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, msg_bad)); } else { /* ** Answer is ok. */ - ncr_setsync (np, cp, (fak<<5)|ofs); + ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, clrack)); }; return; @@ -6508,7 +6595,7 @@ void ncr_int_sir (ncb_p np) ** prepare an answer message */ - ncr_setsync (np, cp, (fak<<5)|ofs); + ncr_setsync (np, cp, scntl3, (fak<<5)|ofs); np->msgout[0] = M_EXTENDED; np->msgout[1] = 3; @@ -6521,7 +6608,7 @@ void ncr_int_sir (ncb_p np) if (DEBUG_FLAGS & DEBUG_NEGO) { PRINT_ADDR(cp->cmd); printf ("sync msgout: "); - (void) ncr_show_msg (np->msgin); + (void) ncr_show_msg (np->msgout); printf (".\n"); } @@ -6595,7 +6682,7 @@ void ncr_int_sir (ncb_p np) return; case NS_SYNC: - ncr_setsync (np, cp, 0xe0); + ncr_setsync (np, cp, 0, 0xe0); break; }; }; @@ -6688,8 +6775,8 @@ void ncr_int_sir (ncb_p np) */ PRINT_ADDR(cp->cmd); - printf ("M_DISCONNECT received, but datapointer not saved:\n" - "\tdata=%x save=%x goal=%x.\n", + printf ("M_DISCONNECT received, but datapointer not saved: " + "data=%x save=%x goal=%x.\n", (unsigned) INL (nc_temp), (unsigned) np->header.savep, (unsigned) np->header.goalp); @@ -6974,7 +7061,7 @@ static void ncr_alloc_ccb (ncb_p np, u_long target, u_long lun) tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb); tp->lp[lun] = lp; - ncr_setmaxtags (np, tp, SCSI_NCR_DEFAULT_TAGS); + ncr_setmaxtags (np, tp, driver_setup.default_tags); } /* @@ -7112,8 +7199,7 @@ static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd) **---------------------------------------------------------- */ -/* FreeBSD driver important comments -** --------------------------------- +/* ** We try to reduce the number of interrupts caused ** by unexpected phase changes due to disconnects. ** A typical harddisk may disconnect before ANY block. @@ -7121,130 +7207,11 @@ static void ncr_opennings (ncb_p np, lcb_p lp, Scsi_Cmnd * cmd) ** we had to use a break point every 512 bytes. ** Of course the number of scatter/gather blocks is ** limited. +** Under Linux, the scatter/gatter blocks are provided by +** the generic driver. We just have to copy addresses and +** sizes to the data segment array. */ -/* -** The scatterlist passed by the linux middle-level scsi drivers -** may contain blocks of any size (Generaly < 1024 bytes blocks, -** can be 4096 with a 4K fs). -*/ - -#if defined(SCSI_NCR_SEGMENT_SIZE) -static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd) -{ - struct scatterlist *scatter; - struct dsb *phys; - register u_short segment = 0; - register u_short o_segment = 0; - u_short chunk, chunk_min; - u_long segaddr; - int segsize; - int datalen; - - phys = &cp->phys; - cp->data_len = 0; - - /* - ** Compute a good value for chunk size - ** If SCSI_NCR_SEGMENT_SIZE is OK, we will try to use it. - */ - - if (!cmd->use_sg) - cp->data_len = cmd->request_bufflen; - else { - scatter = (struct scatterlist *)cmd->buffer; - for (segment = 0 ; segment < cmd->use_sg ; segment++) - cp->data_len += scatter[segment].length; - } - - - if (!cp->data_len) { - bzero (&phys->data, sizeof (phys->data)); - return 0; - } - - chunk_min = cp->data_len / MAX_SCATTER; - for (chunk = SCSI_NCR_SEGMENT_SIZE ; chunk < chunk_min ; chunk += chunk); - - /* - ** If the linux scsi command is not a scatterlist, - ** the computed chunk size is OK. - */ - - if (!cmd->use_sg) { - bzero (&phys->data, sizeof (phys->data)); - datalen = cmd->request_bufflen; - segaddr = vtophys(cmd->request_buffer); - segsize = chunk; - o_segment = 0; - -if (DEBUG_FLAGS & DEBUG_SCATTER) - printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n", - (unsigned) segaddr, (int) datalen, (int) chunk); - - while (datalen && (o_segment < MAX_SCATTER)) { - if (segsize > datalen) segsize = datalen; - phys->data[o_segment].addr = segaddr; - phys->data[o_segment].size = segsize; - - datalen -= segsize; - -if(DEBUG_FLAGS & DEBUG_SCATTER) - printf ("ncr53c8xx: seg #%d addr=%lx size=%d (rest=%d).\n", - o_segment, segaddr, (int) segsize, (int) datalen); - - segaddr += segsize; - o_segment++; - } - - return datalen ? -1 : o_segment; - } - - /* - ** Else, the computed chunk size is not so good - ** and we have to iterate. - ** Rescatter the Linux scatterlist into the data block descriptor. - ** Loop if necessary, beginning with the not so good chunk size and - ** doubling it if the scatter process fails. - */ - - scatter = (struct scatterlist *)cmd->buffer; - for (segment = 0; segment < cmd->use_sg; chunk += chunk) { - o_segment = 0; - bzero (&phys->data, sizeof (phys->data)); - for (segment = 0 ; segment < cmd->use_sg ; segment++) { - datalen = scatter[segment].length; - segaddr = vtophys(scatter[segment].address); - segsize = chunk; - -if (DEBUG_FLAGS & DEBUG_SCATTER) - printf("ncr53c8xx: re-scattering physical=0x%x size=%d chunk=%d.\n", - (unsigned) segaddr, (int) datalen, (int) chunk); - - while (datalen && (o_segment < MAX_SCATTER)) { - if (segsize > datalen) segsize = datalen; - phys->data[o_segment].addr = segaddr; - phys->data[o_segment].size = segsize; - - datalen -= segsize; - -if(DEBUG_FLAGS & DEBUG_SCATTER) - printf ("ncr53c8xx: seg #%d addr=%lx size=%d (rest=%d).\n", - o_segment, segaddr, (int) segsize, (int) datalen); - - segaddr += segsize; - o_segment++; - } - - if (datalen) break; - } - } - - return segment < cmd->use_sg ? -1 : o_segment; -} - -#else /* !defined SCSI_NCR_SEGMENT_SIZE */ - static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd) { struct scr_tblmove *data; @@ -7280,8 +7247,6 @@ static int ncr_scatter(ccb_p cp, Scsi_Cmnd *cmd) return segment; } -#endif /* SCSI_NCR_SEGMENT_SIZE */ - /*========================================================== ** ** @@ -7377,6 +7342,9 @@ static int ncr_snooptest (struct ncb* np) */ if (pc != NCB_SCRIPT_PHYS (np, snoopend)+8) { printf ("CACHE TEST FAILED: script execution failed.\n"); + printf ("start=%08lx, pc=%08lx, end=%08lx\n", + (u_long) NCB_SCRIPT_PHYS (np, snooptest), pc, + (u_long) NCB_SCRIPT_PHYS (np, snoopend) +8); return (0x40); }; /* @@ -7606,7 +7574,7 @@ ncrgetfreq (ncb_p np, int gen) */ OUTB (nc_scntl3, 0); - if (bootverbose) + if (bootverbose >= 2) printf ("%s: Delay (GEN=%d): %u msec\n", ncr_name(np), gen, ms); /* * adjust for prescaler, and convert into KHz @@ -7624,11 +7592,19 @@ static void ncr_getclock (ncb_p np) ** If true, disable clock doubler and assume 40 MHz clock. */ if ((stest1 & (DBLEN+DBLSEL)) == DBLEN+DBLSEL) { - if (bootverbose) - printf ("%s: disabling clock doubler\n", ncr_name(np)); - OUTB(nc_stest1, 0); - np->sv_scntl3 = 3; /* Fix scntl3 for next insmod */ - scntl3 = 3; + if (driver_setup.ultra_scsi) { + if (bootverbose >= 2) + printf ("%s: clock doubler found\n", ncr_name(np)); + np->uf_doubler = 1; + scntl3 = 5; + } + else { + if (bootverbose >= 2) + printf ("%s: disabling clock doubler\n", ncr_name(np)); + OUTB(nc_stest1, 0); + np->sv_scntl3 = 3; /* Fix scntl3 for next insmod */ + scntl3 = 3; + } } else { if ((scntl3 & 7) == 0) { unsigned f1, f2; @@ -7655,9 +7631,14 @@ static void ncr_getclock (ncb_p np) scntl3 = 3; } - np->ns_sync = 25; - np->ns_async = 50; - np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7); + if (driver_setup.ultra_scsi && ((scntl3 & 0x7) == 0x5)) { + np->ns_sync = 12; + np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x40 + (scntl3 & 0x7); + } + else { + np->ns_sync = 25; + np->rv_scntl3 = ((scntl3 & 0x7) << 4) -0x20 + (scntl3 & 0x7); + } if (bootverbose) { printf ("%s: initial value of SCNTL3 = %02x, final = %02x\n", @@ -7705,6 +7686,76 @@ static void ncr_save_bios_setting(ncb_p np) #define ulong unsigned long #endif +/* --------------------------------------------------------------------- +** +** Driver setup from the boot command line +** +** --------------------------------------------------------------------- +*/ + +void ncr53c8xx_setup(char *str, int *ints) +{ + char *cur = str; + char *pv; + int val; + int base; + int c; + + while (cur != NULL && (pv = strchr(cur, ':')) != NULL) { + val = 0; + c = *++pv; + if (c == 'n') + val = 0; + else if (c == 'y') + val = 1; + else { + base = 0; +#if 0 + if (c == '0') { + c = *pv++; + base = 8; + } + if (c == 'x') { + ++pv; + base = 16; + } + else if (c >= '0' && c <= '9') + base = 10; + else + break; +#endif + val = (int) simple_strtoul(pv, NULL, base); + } + + if (!strncmp(cur, "mpar:", 5)) + driver_setup.master_parity = val; + else if (!strncmp(cur, "spar:", 5)) + driver_setup.scsi_parity = val; + else if (!strncmp(cur, "disc:", 5)) + driver_setup.disconnection = val; + else if (!strncmp(cur, "specf:", 6)) + driver_setup.special_features = val; + else if (!strncmp(cur, "ultra:", 6)) + driver_setup.ultra_scsi = val; + else if (!strncmp(cur, "fsn:", 4)) + driver_setup.force_sync_nego = val; + else if (!strncmp(cur, "tags:", 5)) { + if (val > SCSI_NCR_MAX_TAGS) + val = SCSI_NCR_MAX_TAGS; + driver_setup.default_tags = val; + } + else if (!strncmp(cur, "sync:", 5)) + driver_setup.default_sync = val * 1000; + else if (!strncmp(cur, "verb:", 5)) + driver_setup.verbose = val; + else if (!strncmp(cur, "debug:", 6)) + driver_setup.debug = val; + + if ((cur = strchr(cur, ',')) != NULL) + ++cur; + } +} + static int ncr53c8xx_pci_init(Scsi_Host_Template *tpnt, int unit, int board, int chip, uchar bus, uchar device_fn, int options); @@ -7751,6 +7802,27 @@ int ncr53c8xx_detect(Scsi_Host_Template *tpnt) uchar pci_bus, pci_device_fn; short pci_index; /* Device index to PCI BIOS calls */ +#define YesNo(y) y ? 'y' : 'n' + if (bootverbose >= 2) { + printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d\n", + YesNo(driver_setup.disconnection), + YesNo(driver_setup.special_features), + YesNo(driver_setup.ultra_scsi), + driver_setup.default_tags, + driver_setup.default_sync/1000); + printk("ncr53c8xx: setup=mpar:%c,spar:%c,fsn=%c,verb:%d,debug:0x%x\n", + YesNo(driver_setup.master_parity), + YesNo(driver_setup.scsi_parity), + YesNo(driver_setup.force_sync_nego), + driver_setup.verbose, + driver_setup.debug); + } +#undef YesNo + +#ifdef SCSI_NCR_DEBUG + ncr_debug = driver_setup.debug; +#endif + #if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) tpnt->proc_dir = &proc_scsi_ncr53c8xx; # ifdef SCSI_NCR_PROC_INFO_SUPPORT @@ -8376,6 +8448,8 @@ printf("ncr_user_command: data=%ld\n", uc->data); SKIP_SPACES(1); if ((arg_len = is_keyword(ptr, len, "alloc"))) uc->data |= DEBUG_ALLOC; + else if ((arg_len = is_keyword(ptr, len, "phase"))) + uc->data |= DEBUG_PHASE; else if ((arg_len = is_keyword(ptr, len, "poll"))) uc->data |= DEBUG_POLL; else if ((arg_len = is_keyword(ptr, len, "queue"))) @@ -8388,6 +8462,8 @@ printf("ncr_user_command: data=%ld\n", uc->data); uc->data |= DEBUG_SCRIPT; else if ((arg_len = is_keyword(ptr, len, "tiny"))) uc->data |= DEBUG_TINY; + else if ((arg_len = is_keyword(ptr, len, "timing"))) + uc->data |= DEBUG_TIMING; else if ((arg_len = is_keyword(ptr, len, "nego"))) uc->data |= DEBUG_NEGO; else if ((arg_len = is_keyword(ptr, len, "tags"))) diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h index e81b68c7ab20..06dc2241ace9 100644 --- a/drivers/scsi/ncr53c8xx.h +++ b/drivers/scsi/ncr53c8xx.h @@ -45,10 +45,10 @@ /* ** Name and revision of the driver */ -#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 1.14c" +#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 1.16b" /* -** If SCSI_NCR_SPECIAL_FEATURES is defined, +** If SCSI_NCR_SETUP_SPECIAL_FEATURES is defined, ** the driver enables or not the following features according to chip id ** revision id: ** DMODE 0xce @@ -63,6 +63,9 @@ ** 0x01 set write and invalidate ** CTEST4 0x80 ** 0x80 burst disabled +** CTEST5 0x24 (825a and 875 only) +** 0x04 burst 128 +** 0x80 dma fifo 536 ** ** If SCSI_NCR_TRUST_BIOS_SETTING is defined, the driver will use the ** initial value of corresponding bit fields, assuming they have been @@ -71,14 +74,6 @@ ** the driver will not be able to guess it. */ -#if 0 -#define SCSI_NCR_TRUST_BIOS_SETTING -#endif - -#if 0 -#define SCSI_NCR_SPECIAL_FEATURES -#endif - /*********** LINUX SPECIFIC SECTION ******************/ /* @@ -144,10 +139,29 @@ # define SCSI_NCR_SHARE_IRQ #endif -/* -** Avoid to change these constants, unless you know what you are doing. +/* --------------------------------------------------------------------- +** Take into account kernel configured parameters. +** Most of these options can be overridden at startup by a command line. +** --------------------------------------------------------------------- */ +/* + * For Ultra SCSI support option, use special features and allow 20Mhz + * synchronous data transfers. + */ +#if 1 /* CONFIG_SCSI_NCR53C8XX_ULTRA_SUPPORT */ +#define SCSI_NCR_SETUP_SPECIAL_FEATURES (1) +#define SCSI_NCR_SETUP_ULTRA_SCSI (1) +#define SCSI_NCR_MAX_SYNC (20000) +#else +#define SCSI_NCR_SETUP_SPECIAL_FEATURES (0) +#define SCSI_NCR_SETUP_ULTRA_SCSI (0) +#define SCSI_NCR_MAX_SYNC (10000) +#endif + +/* + * Allow tags from 2 to 12, default 4 + */ #ifdef CONFIG_SCSI_NCR53C8XX_MAX_TAGS #if CONFIG_SCSI_NCR53C8XX_MAX_TAGS < 2 #define SCSI_NCR_MAX_TAGS (2) @@ -160,57 +174,91 @@ #define SCSI_NCR_MAX_TAGS (4) #endif -#define SCSI_NCR_ALWAYS_SIMPLE_TAG - -#ifdef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE -#define SCSI_NCR_DEFAULT_TAGS SCSI_NCR_MAX_TAGS +/* + * Allow tagged command queuing support if configured with default number + * of tags set to max (see above). + */ +#ifdef CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE +#define SCSI_NCR_SETUP_DEFAULT_TAGS SCSI_NCR_MAX_TAGS #else -#define SCSI_NCR_DEFAULT_TAGS (0) +#define SCSI_NCR_SETUP_DEFAULT_TAGS (0) #endif -#ifdef CONFIG_SCSI_NCR53C8XX_IOMAPPED +/* + * Use normal IO if configured. Forced for alpha. + */ +#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__) #define SCSI_NCR_IOMAPPED #endif +/* + * Sync transfer frequency at startup. + * Allow from 5Mhz to 20Mhz default 10 Mhz. + */ #ifdef CONFIG_SCSI_NCR53C8XX_SYNC #if CONFIG_SCSI_NCR53C8XX_SYNC == 0 -#define SCSI_NCR_DEFAULT_SYNC (0) -#elif CONFIG_SCSI_NCR53C8XX_SYNC < 5 -#define SCSI_NCR_DEFAULT_SYNC (5000) -#elif CONFIG_SCSI_NCR53C8XX_SYNC > 10 -#define SCSI_NCR_DEFAULT_SYNC (10000) +#define SCSI_NCR_SETUP_DEFAULT_SYNC (0) +#elif CONFIG_SCSI_NCR53C8XX_SYNC*1000 < 5000 +#define SCSI_NCR_SETUP_DEFAULT_SYNC (5000) +#elif CONFIG_SCSI_NCR53C8XX_SYNC*1000 > SCSI_NCR_MAX_SYNC +#define SCSI_NCR_SETUP_DEFAULT_SYNC SCSI_NCR_MAX_SYNC #else -#define SCSI_NCR_DEFAULT_SYNC (CONFIG_SCSI_NCR53C8XX_SYNC * 1000) +#define SCSI_NCR_SETUP_DEFAULT_SYNC (CONFIG_SCSI_NCR53C8XX_SYNC * 1000) #endif #else -#define SCSI_NCR_DEFAULT_SYNC (10000) +#define SCSI_NCR_SETUP_DEFAULT_SYNC (10000) #endif +/* + * Default sync to 0 will force asynchronous at startup + */ #ifdef CONFIG_SCSI_FORCE_ASYNCHRONOUS -#undef SCSI_NCR_DEFAULT_SYNC -#define SCSI_NCR_DEFAULT_SYNC (0) +#undef SCSI_NCR_SETUP_DEFAULT_SYNC +#define SCSI_NCR_SETUP_DEFAULT_SYNC (0) #endif +/* + * Disallow disconnections at boot-up + */ #ifdef CONFIG_SCSI_NCR53C8XX_NO_DISCONNECT -#define SCSI_NCR_NO_DISCONNECT +#define SCSI_NCR_SETUP_DISCONNECTION (0) +#else +#define SCSI_NCR_SETUP_DISCONNECTION (1) #endif +/* + * Force synchronous negotiation for all targets + */ #ifdef CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO -#define SCSI_NCR_FORCE_SYNC_NEGO +#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (1) +#else +#define SCSI_NCR_SETUP_FORCE_SYNC_NEGO (0) #endif +/* + * Disable master parity checking (flawed hardwares need that) + */ #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK -#define SCSI_NCR_DISABLE_MPARITY_CHECK +#define SCSI_NCR_SETUP_MASTER_PARITY (0) +#else +#define SCSI_NCR_SETUP_MASTER_PARITY (1) #endif +/* + * Disable scsi parity checking (flawed devices may need that) + */ #ifdef CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK -#define SCSI_NCR_DISABLE_PARITY_CHECK +#define SCSI_NCR_SETUP_SCSI_PARITY (0) +#else +#define SCSI_NCR_SETUP_SCSI_PARITY (1) #endif -#if 0 -#define SCSI_NCR_SEGMENT_SIZE (512) -#endif +/* +** Other parameters not configurable with "make config" +** Avoid to change these constants, unless you know what you are doing. +*/ +#define SCSI_NCR_ALWAYS_SIMPLE_TAG #define SCSI_NCR_MAX_SCATTER (128) #define SCSI_NCR_MAX_TARGET (16) #define SCSI_NCR_MAX_HOST (2) @@ -221,13 +269,32 @@ #define SCSI_NCR_CMD_PER_LUN (SCSI_NCR_MAX_TAGS) #define SCSI_NCR_SG_TABLESIZE (SCSI_NCR_MAX_SCATTER-1) +#define SCSI_NCR_TIMER_INTERVAL ((HZ+5-1)/5) + #if 1 /* defined CONFIG_SCSI_MULTI_LUN */ #define SCSI_NCR_MAX_LUN (8) #else #define SCSI_NCR_MAX_LUN (1) #endif -#define SCSI_NCR_TIMER_INTERVAL ((HZ+5-1)/5) +/* +** Initial setup. +** Can be overriden at startup by a command line. +*/ +#define SCSI_NCR_DRIVER_SETUP \ +{ \ + SCSI_NCR_SETUP_MASTER_PARITY, \ + SCSI_NCR_SETUP_SCSI_PARITY, \ + SCSI_NCR_SETUP_DISCONNECTION, \ + SCSI_NCR_SETUP_SPECIAL_FEATURES, \ + SCSI_NCR_SETUP_ULTRA_SCSI, \ + SCSI_NCR_SETUP_FORCE_SYNC_NEGO, \ + 1, \ + SCSI_NCR_SETUP_DEFAULT_TAGS, \ + SCSI_NCR_SETUP_DEFAULT_SYNC, \ + 0x00 \ +} + /* ** Define Scsi_Host_Template parameters @@ -237,7 +304,11 @@ #if defined(HOSTS_C) || defined(MODULE) +#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98) #include +#else +#include +#endif int ncr53c8xx_abort(Scsi_Cmnd *); int ncr53c8xx_detect(Scsi_Host_Template *tpnt); @@ -516,12 +587,18 @@ struct ncr_reg { #define CSIGP 0x40 /*1b*/ u_char nc_ctest3; - #define CLF 0x04 /* clear scsi fifo */ + #define FLF 0x08 /* cmd: flush dma fifo */ + #define CLF 0x04 /* cmd: clear dma fifo */ + #define FM 0x02 /* mod: fetch pin mode */ + #define WRIE 0x01 /* mod: write and invalidate enable */ /*1c*/ u_int32 nc_temp; /* ### Temporary stack */ /*20*/ u_char nc_dfifo; /*21*/ u_char nc_ctest4; + #define BDIS 0x80 /* mod: burst disable */ + #define MPEE 0x08 /* mod: master parity error enable */ + /*22*/ u_char nc_ctest5; /*23*/ u_char nc_ctest6; @@ -532,13 +609,25 @@ struct ncr_reg { /*34*/ u_int32 nc_scratcha; /* ??? Temporary register a */ /*38*/ u_char nc_dmode; + #define BL_2 0x80 /* mod: burst length shift value +2 */ + #define BL_1 0x40 /* mod: burst length shift value +1 */ + #define ERL 0x08 /* mod: enable read line */ + #define ERMP 0x04 /* mod: enable read multiple */ + #define BOF 0x02 /* mod: burst op code fetch */ + /*39*/ u_char nc_dien; /*3a*/ u_char nc_dwt; /*3b*/ u_char nc_dcntl; /* --> Script execution control */ - #define SSM 0x10 /* mod: single step mode */ - #define STD 0x04 /* cmd: start dma mode */ - #define NOCOM 0x01 /* cmd: protect sfbr while reselect */ + + #define CLSE 0x80 /* mod: cache line size enable */ + #define PFF 0x40 /* cmd: pre-fetch flush */ + #define PFEN 0x20 /* mod: pre-fetch enable */ + #define SSM 0x10 /* mod: single step mode */ + #define IRQM 0x08 /* mod: irq mode (1 = totem pole !) */ + #define STD 0x04 /* cmd: start dma mode */ + #define IRQD 0x02 /* mod: irq disable */ + #define NOCOM 0x01 /* cmd: protect sfbr while reselect */ /*3c*/ u_int32 nc_adder; diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 321bf3ff5eae..f4cf17abfa2e 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -6,12 +6,12 @@ Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara. Contribution and ideas from several people including (in alphabetical order) Klaus Ehrenfried, Wolfgang Denk, Steve Hirsch, Andreas Koppenh"ofer, - Eyal Lebedinsky, J"org Weule, and Eric Youngdale. + Michael Leodolter, Eyal Lebedinsky, J"org Weule, and Eric Youngdale. - Copyright 1992 - 1996 Kai Makisara + Copyright 1992 - 1997 Kai Makisara email Kai.Makisara@metla.fi - Last modified: Tue Oct 22 20:59:52 1996 by root@kai.makisara.fi + Last modified: Wed Jan 1 15:26:54 1997 by makisara@kai.makisara.fi Some small formal changes - aeb, 950809 */ @@ -88,7 +88,7 @@ static int st_buffer_size = ST_BUFFER_SIZE; static int st_write_threshold = ST_WRITE_THRESHOLD; static int st_max_buffers = ST_MAX_BUFFERS; -Scsi_Tape * scsi_tapes = NULL; +static Scsi_Tape * scsi_tapes = NULL; static int modes_defined = FALSE; @@ -130,6 +130,9 @@ st_chk_result(Scsi_Cmnd * SCpnt) if (!result /* && SCpnt->sense_buffer[0] == 0 */ ) return 0; + + scode = sense[2] & 0x0f; + #if DEBUG if (debugging) { printk(ST_DEB_MSG "st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", @@ -142,10 +145,8 @@ st_chk_result(Scsi_Cmnd * SCpnt) else printk("\n"); } + else #endif - scode = sense[2] & 0x0f; - -#if !DEBUG if (!(driver_byte(result) & DRIVER_SENSE) || ((sense[0] & 0x70) == 0x70 && scode != NO_SENSE && @@ -162,7 +163,6 @@ st_chk_result(Scsi_Cmnd * SCpnt) else printk(KERN_WARNING "st%d: Error %x.\n", dev, result); } -#endif if ((sense[0] & 0x70) == 0x70 && scode == RECOVERED_ERROR @@ -273,6 +273,7 @@ st_do_scsi(Scsi_Cmnd *SCpnt, Scsi_Tape *STp, unsigned char *cmd, int bytes, write_behind_check(Scsi_Tape *STp) { ST_buffer * STbuffer; + ST_partstat * STps; STbuffer = STp->buffer; @@ -290,11 +291,12 @@ write_behind_check(Scsi_Tape *STp) STbuffer->b_data + STbuffer->writing, STbuffer->buffer_bytes - STbuffer->writing); STbuffer->buffer_bytes -= STbuffer->writing; - if (STp->drv_block >= 0) { + STps = &(STp->ps[STp->partition]); + if (STps->drv_block >= 0) { if (STp->block_size == 0) - STp->drv_block++; + STps->drv_block++; else - STp->drv_block += STbuffer->writing / STp->block_size; + STps->drv_block += STbuffer->writing / STp->block_size; } STbuffer->writing = 0; @@ -302,30 +304,37 @@ write_behind_check(Scsi_Tape *STp) } -/* Back over EOF if it has been inadvertently crossed (ioctl not used because +/* Step over EOF if it has been inadvertently crossed (ioctl not used because it messes up the block number). */ static int -back_over_eof(Scsi_Tape *STp) +cross_eof(Scsi_Tape *STp, int forward) { Scsi_Cmnd *SCpnt; unsigned char cmd[10]; cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ - cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */ + if (forward) { + cmd[2] = cmd[3] = 0; + cmd[4] = 1; + } + else + cmd[2] = cmd[3] = cmd[4] = 0xff; /* -1 filemarks */ cmd[5] = 0; +#if DEBUG + if (debugging) + printk(ST_DEB_MSG "st%d: Stepping over filemark %s.\n", + TAPE_NR(STp->devt), forward ? "forward" : "backward"); +#endif SCpnt = st_do_scsi(NULL, STp, cmd, 0, ST_TIMEOUT, MAX_RETRIES); if (!SCpnt) return (-EBUSY); SCpnt->request.rq_status = RQ_INACTIVE; - if ((STp->buffer)->last_result != 0) { - printk(KERN_ERR "st%d: Backing over filemark failed.\n", TAPE_NR(STp->devt)); - if ((STp->mt_status)->mt_fileno >= 0) - (STp->mt_status)->mt_fileno += 1; - (STp->mt_status)->mt_blkno = 0; - } + if ((STp->buffer)->last_result != 0) + printk(KERN_ERR "st%d: Stepping over filemark %s failed.\n", + TAPE_NR(STp->devt), forward ? "forward" : "backward"); return (STp->buffer)->last_result_fatal; } @@ -339,6 +348,7 @@ flush_write_buffer(Scsi_Tape *STp) int result; unsigned char cmd[10]; Scsi_Cmnd *SCpnt; + ST_partstat * STps; if ((STp->buffer)->writing) { write_behind_check(STp); @@ -381,6 +391,7 @@ flush_write_buffer(Scsi_Tape *STp) if (!SCpnt) return (-EBUSY); + STps = &(STp->ps[STp->partition]); if ((STp->buffer)->last_result_fatal != 0) { if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x40) && @@ -393,11 +404,11 @@ flush_write_buffer(Scsi_Tape *STp) printk(KERN_ERR "st%d: Error on flush.\n", TAPE_NR(STp->devt)); result = (-EIO); } - STp->drv_block = (-1); + STps->drv_block = (-1); } else { - if (STp->drv_block >= 0) - STp->drv_block += blks; + if (STps->drv_block >= 0) + STps->drv_block += blks; STp->dirty = 0; (STp->buffer)->buffer_bytes = 0; } @@ -415,6 +426,7 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next) int backspace, result; Scsi_Tape * STp; ST_buffer * STbuffer; + ST_partstat * STps; int dev = TAPE_NR(inode->i_rdev); STp = &(scsi_tapes[dev]); @@ -430,14 +442,12 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next) if (STp->ready != ST_READY) return 0; - if (STp->ps[STp->partition].rw == ST_WRITING) /* Writing */ + STps = &(STp->ps[STp->partition]); + if (STps->rw == ST_WRITING) /* Writing */ return flush_write_buffer(STp); - if (STp->block_size == 0) { - STp->eof = ST_NOEOF; - STp->eof_hit = 0; + if (STp->block_size == 0) return 0; - } backspace = ((STp->buffer)->buffer_bytes + (STp->buffer)->read_pointer) / STp->block_size - @@ -447,20 +457,24 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next) (STp->buffer)->read_pointer = 0; result = 0; if (!seek_next) { - if ((STp->eof == ST_FM) && !STp->eof_hit) { - result = back_over_eof(STp); /* Back over the EOF hit */ - if (!result) { - STp->eof = ST_NOEOF; - STp->eof_hit = 0; + if (STps->eof == ST_FM_HIT) { + result = cross_eof(STp, FALSE); /* Back over the EOF hit */ + if (!result) + STps->eof = ST_NOEOF; + else { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; } } if (!result && backspace > 0) result = st_int_ioctl(inode, MTBSR, backspace); } - else if ((STp->eof == ST_FM) && !STp->eof_hit) { - if ((STp->mt_status)->mt_fileno >= 0) - (STp->mt_status)->mt_fileno++; - STp->drv_block = 0; + else if (STps->eof == ST_FM_HIT) { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_NOEOF; } return result; @@ -514,6 +528,7 @@ scsi_tape_open(struct inode * inode, struct file * filp) Scsi_Cmnd * SCpnt; Scsi_Tape * STp; ST_mode * STm; + ST_partstat * STps; int dev = TAPE_NR(inode->i_rdev); int mode = TAPE_MODE(inode->i_rdev); @@ -561,12 +576,11 @@ scsi_tape_open(struct inode * inode, struct file * filp) STp->write_prot = ((flags & O_ACCMODE) == O_RDONLY); STp->dirty = 0; - for (i=0; i < ST_NBR_PARTITIONS; i++) - STp->ps[i].rw = ST_IDLE; + for (i=0; i < ST_NBR_PARTITIONS; i++) { + STps = &(STp->ps[i]); + STps->rw = ST_IDLE; + } STp->ready = ST_READY; - if (STp->eof != ST_EOD) /* Save EOD across opens */ - STp->eof = ST_NOEOF; - STp->eof_hit = 0; STp->recover_count = 0; #if DEBUG STp->nbr_waits = STp->nbr_finished = 0; @@ -589,23 +603,23 @@ scsi_tape_open(struct inode * inode, struct file * filp) if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */ - (STp->mt_status)->mt_fileno = 0 ; memset ((void *) &cmd[0], 0, 10); cmd[0] = TEST_UNIT_READY; SCpnt = st_do_scsi(SCpnt, STp, cmd, 0, ST_LONG_TIMEOUT, MAX_READY_RETRIES); - (STp->mt_status)->mt_fileno = STp->drv_block = 0; - STp->eof = ST_NOEOF; (STp->device)->was_reset = 0; STp->partition = STp->new_partition = 0; if (STp->can_partitions) STp->nbr_partitions = 1; /* This guess will be updated later if necessary */ for (i=0; i < ST_NBR_PARTITIONS; i++) { - STp->ps[i].rw = ST_IDLE; - STp->ps[i].moves_after_eof = 1; - STp->ps[i].at_sm = 0; - STp->ps[i].last_block_valid = FALSE; + STps = &(STp->ps[i]); + STps->rw = ST_IDLE; + STps->eof = ST_NOEOF; + STps->at_sm = 0; + STps->last_block_valid = FALSE; + STps->drv_block = 0; + STps->drv_file = 0 ; } new_session = TRUE; } @@ -613,19 +627,15 @@ scsi_tape_open(struct inode * inode, struct file * filp) if ((STp->buffer)->last_result_fatal != 0) { if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 && (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) { - (STp->mt_status)->mt_fileno = STp->drv_block = 0 ; printk(KERN_NOTICE "st%d: No tape.\n", dev); STp->ready = ST_NO_TAPE; - } else { - (STp->mt_status)->mt_fileno = STp->drv_block = (-1); + } else STp->ready = ST_NOT_READY; - } SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ STp->density = 0; /* Clear the erroneous "residue" */ STp->write_prot = 0; STp->block_size = 0; - STp->eof = ST_NOEOF; - (STp->mt_status)->mt_fileno = STp->drv_block = 0; + STp->ps[0].drv_file = STp->ps[0].drv_block = 0; STp->partition = STp->new_partition = 0; STp->door_locked = ST_UNLOCKED; STp->in_use = 1; @@ -796,11 +806,16 @@ scsi_tape_close(struct inode * inode, struct file * filp) static unsigned char cmd[10]; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; + ST_mode * STm; + ST_partstat * STps; + kdev_t devt = inode->i_rdev; int dev; dev = TAPE_NR(devt); STp = &(scsi_tapes[dev]); + STm = &(STp->modes[STp->current_mode]); + STps = &(STp->ps[STp->partition]); if (STp->can_partitions && update_partition(inode) < 0) { @@ -811,7 +826,7 @@ scsi_tape_close(struct inode * inode, struct file * filp) goto out; } - if ( STp->ps[STp->partition].rw == ST_WRITING && !(STp->device)->was_reset) { + if ( STps->rw == ST_WRITING && !(STp->device)->was_reset) { result = flush_write_buffer(STp); @@ -845,11 +860,12 @@ scsi_tape_close(struct inode * inode, struct file * filp) SCpnt->sense_buffer[6]) == 0))) /* Filter out successful write at EOM */ printk(KERN_ERR "st%d: Error on write filemark.\n", dev); else { - if ((STp->mt_status)->mt_fileno >= 0) - (STp->mt_status)->mt_fileno++ ; - STp->drv_block = 0; + if (STps->drv_file >= 0) + STps->drv_file++ ; + STps->drv_block = 0; if (STp->two_fm) - back_over_eof(STp); + cross_eof(STp, FALSE); + STps->eof = ST_FM; } } @@ -860,10 +876,28 @@ scsi_tape_close(struct inode * inode, struct file * filp) #endif } else if (!STp->rew_at_close) { - if (STp->can_bsr) - flush_buffer(inode, filp, 0); - else if ((STp->eof == ST_FM) && !STp->eof_hit) - back_over_eof(STp); + STps = &(STp->ps[STp->partition]); + if (!STm->sysv || STps->rw != ST_READING) { + if (STp->can_bsr) + flush_buffer(inode, filp, 0); + else if (STps->eof == ST_FM_HIT) { + if (cross_eof(STp, FALSE)) { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_FM; + } + else + STps->eof = ST_NOEOF; + } + } + else if ((STps->eof == ST_NOEOF && !cross_eof(STp, TRUE)) || + STps->eof == ST_FM_HIT) { + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_FM; + } } out: @@ -893,7 +927,7 @@ st_write(struct inode * inode, struct file * filp, const char * buf, unsigned long count) { long total; - int do_count, blks, retval, transfer; + int i, do_count, blks, retval, transfer; int write_threshold; int doing_write = 0; static unsigned char cmd[10]; @@ -910,6 +944,8 @@ st_write(struct inode * inode, struct file * filp, const char * buf, STm = &(STp->modes[STp->current_mode]); if (!STm->defined) return (-ENXIO); + if (count == 0) + return 0; /* * If there was a bus reset, block further access @@ -949,7 +985,7 @@ st_write(struct inode * inode, struct file * filp, const char * buf, STps->rw = ST_WRITING; } else if (STps->rw != ST_WRITING && - (STp->mt_status)->mt_fileno == 0 && STp->drv_block == 0) { + STps->drv_file == 0 && STps->drv_block == 0) { if ((retval = set_mode_densblk(inode, STp, STm)) < 0) return retval; if (STm->default_compression != ST_DONT_TOUCH && @@ -963,9 +999,6 @@ st_write(struct inode * inode, struct file * filp, const char * buf, } } - if (STps->moves_after_eof < 255) - STps->moves_after_eof++; - if ((STp->buffer)->writing) { write_behind_check(STp); if ((STp->buffer)->last_result_fatal) { @@ -974,20 +1007,24 @@ st_write(struct inode * inode, struct file * filp, const char * buf, printk(ST_DEB_MSG "st%d: Async write error (write) %x.\n", dev, (STp->buffer)->last_result); #endif - if ((STp->buffer)->last_result == INT_MAX) { - retval = (-ENOSPC); /* All has been written */ - STp->eof = ST_EOM_OK; - } + if ((STp->buffer)->last_result == INT_MAX) + STps->eof = ST_EOM_OK; else - retval = (-EIO); - return retval; + STps->eof = ST_EOM_ERROR; } } - if (STp->eof == ST_EOM_OK) + if (STps->eof == ST_EOM_OK) return (-ENOSPC); - else if (STp->eof == ST_EOM_ERROR) + else if (STps->eof == ST_EOM_ERROR) return (-EIO); + /* Check the buffer readability in cases where copy_user might catch + the problems after some tape movement. */ + if (STp->block_size != 0 && + (copy_from_user(&i, buf, 1) != 0 || + copy_from_user(&i, buf + count - 1, 1) != 0)) + return (-EFAULT); + if (!STm->do_buffer_writes) { if (STp->block_size != 0 && (count % STp->block_size) != 0) return (-EIO); /* Write must be integral number of blocks */ @@ -1020,8 +1057,14 @@ st_write(struct inode * inode, struct file * filp, const char * buf, if (do_count > count) do_count = count; } - copy_from_user((STp->buffer)->b_data + - (STp->buffer)->buffer_bytes, b_point, do_count); + + i = copy_from_user((STp->buffer)->b_data + + (STp->buffer)->buffer_bytes, b_point, do_count); + if (i) { + if (SCpnt != NULL) + SCpnt->request.rq_status = RQ_INACTIVE; + return (-EFAULT); + } if (STp->block_size == 0) blks = transfer = do_count; @@ -1059,13 +1102,13 @@ st_write(struct inode * inode, struct file * filp, const char * buf, if (transfer <= do_count) { filp->f_pos += do_count - transfer; count -= do_count - transfer; - if (STp->drv_block >= 0) { + if (STps->drv_block >= 0) { if (STp->block_size == 0 && transfer < do_count) - STp->drv_block++; + STps->drv_block++; else if (STp->block_size != 0) - STp->drv_block += (do_count - transfer) / STp->block_size; + STps->drv_block += (do_count - transfer) / STp->block_size; } - STp->eof = ST_EOM_OK; + STps->eof = ST_EOM_OK; retval = (-ENOSPC); /* EOM within current request */ #if DEBUG if (debugging) @@ -1074,8 +1117,8 @@ st_write(struct inode * inode, struct file * filp, const char * buf, #endif } else { - STp->eof = ST_EOM_ERROR; - STp->drv_block = (-1); /* Too cautious? */ + STps->eof = ST_EOM_ERROR; + STps->drv_block = (-1); /* Too cautious? */ retval = (-EIO); /* EOM for old data */ #if DEBUG if (debugging) @@ -1084,7 +1127,7 @@ st_write(struct inode * inode, struct file * filp, const char * buf, } } else { - STp->drv_block = (-1); /* Too cautious? */ + STps->drv_block = (-1); /* Too cautious? */ retval = (-EIO); } @@ -1099,19 +1142,24 @@ st_write(struct inode * inode, struct file * filp, const char * buf, filp->f_pos += do_count; b_point += do_count; count -= do_count; - if (STp->drv_block >= 0) { + if (STps->drv_block >= 0) { if (STp->block_size == 0) - STp->drv_block++; + STps->drv_block++; else - STp->drv_block += blks; + STps->drv_block += blks; } (STp->buffer)->buffer_bytes = 0; STp->dirty = 0; } if (count != 0) { STp->dirty = 1; - copy_from_user((STp->buffer)->b_data + - (STp->buffer)->buffer_bytes,b_point,count); + i = copy_from_user((STp->buffer)->b_data + + (STp->buffer)->buffer_bytes, b_point, count); + if (i) { + if (SCpnt != NULL) + SCpnt->request.rq_status = RQ_INACTIVE; + return (-EFAULT); + } filp->f_pos += count; (STp->buffer)->buffer_bytes += count; count = 0; @@ -1163,8 +1211,199 @@ st_write(struct inode * inode, struct file * filp, const char * buf, SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ STps->at_sm &= (total == 0); + if (total > 0) + STps->eof = ST_NOEOF; + return( total); } + +/* Read data from the tape. Returns zero in the normal case, one if the + eof status has changed, and the negative error code in case of a + fatal error. Otherwise updates the buffer and the eof state. */ + static long +read_tape(struct inode *inode, long count, Scsi_Cmnd **aSCpnt) +{ + int transfer, blks, bytes; + static unsigned char cmd[10]; + Scsi_Cmnd *SCpnt; + Scsi_Tape *STp; + ST_mode * STm; + ST_partstat * STps; + int dev = TAPE_NR(inode->i_rdev); + int retval = 0; + + if (count == 0) + return 0; + + STp = &(scsi_tapes[dev]); + STm = &(STp->modes[STp->current_mode]); + STps = &(STp->ps[STp->partition]); + if (STps->eof == ST_FM_HIT) + return 1; + + memset(cmd, 0, 10); + cmd[0] = READ_6; + cmd[1] = (STp->block_size != 0); + if (STp->block_size == 0) + blks = bytes = count; + else { + if (STm->do_read_ahead) { + blks = (STp->buffer)->buffer_blocks; + bytes = blks * STp->block_size; + } + else { + bytes = count; + if (bytes > (STp->buffer)->buffer_size) + bytes = (STp->buffer)->buffer_size; + blks = bytes / STp->block_size; + bytes = blks * STp->block_size; + } + } + cmd[2] = blks >> 16; + cmd[3] = blks >> 8; + cmd[4] = blks; + + SCpnt = *aSCpnt; + SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, ST_TIMEOUT, MAX_RETRIES); + *aSCpnt = SCpnt; + if (!SCpnt) + return (-EBUSY); + + (STp->buffer)->read_pointer = 0; + STps->at_sm = 0; + + + /* Something to check */ + if ((STp->buffer)->last_result_fatal) { + retval = 1; +#if DEBUG + if (debugging) + printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", + dev, + SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], + SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], + SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], + SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]); +#endif + if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ + + if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) + SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */ + + if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */ + /* Compute the residual count */ + if ((SCpnt->sense_buffer[0] & 0x80) != 0) + transfer = (SCpnt->sense_buffer[3] << 24) | + (SCpnt->sense_buffer[4] << 16) | + (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; + else + transfer = 0; + if (STp->block_size == 0 && + (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR) + transfer = bytes; + + if (SCpnt->sense_buffer[2] & 0x20) { /* ILI */ + if (STp->block_size == 0) { + if (transfer <= 0) + transfer = 0; + (STp->buffer)->buffer_bytes = bytes - transfer; + } + else { + SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + SCpnt = NULL; + if (transfer == blks) { /* We did not get anything, error */ + printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev); + if (STps->drv_block >= 0) + STps->drv_block += blks - transfer + 1; + st_int_ioctl(inode, MTBSR, 1); + return (-EIO); + } + /* We have some data, deliver it */ + (STp->buffer)->buffer_bytes = (blks - transfer) * + STp->block_size; +#if DEBUG + if (debugging) + printk(ST_DEB_MSG + "st%d: ILI but enough data received %ld %d.\n", + dev, count, (STp->buffer)->buffer_bytes); +#endif + if (STps->drv_block >= 0) + STps->drv_block += 1; + if (st_int_ioctl(inode, MTBSR, 1)) + return (-EIO); + } + } + else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */ + if (STps->eof != ST_FM_HIT) + STps->eof = ST_FM_HIT; + else + STps->eof = ST_EOD_2; + if (STp->block_size == 0) + (STp->buffer)->buffer_bytes = 0; + else + (STp->buffer)->buffer_bytes = + bytes - transfer * STp->block_size; +#if DEBUG + if (debugging) + printk(ST_DEB_MSG + "st%d: EOF detected (%d bytes read).\n", + dev, (STp->buffer)->buffer_bytes); +#endif + } + else if (SCpnt->sense_buffer[2] & 0x40) { + if (STps->eof == ST_FM) + STps->eof = ST_EOD_1; + else + STps->eof = ST_EOM_OK; + if (STp->block_size == 0) + (STp->buffer)->buffer_bytes = bytes - transfer; + else + (STp->buffer)->buffer_bytes = + bytes - transfer * STp->block_size; +#if DEBUG + if (debugging) + printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n", + dev, (STp->buffer)->buffer_bytes); +#endif + } + } /* end of EOF, EOM, ILI test */ + else { /* nonzero sense key */ +#if DEBUG + if (debugging) + printk(ST_DEB_MSG "st%d: Tape error while reading.\n", dev); +#endif + STps->drv_block = (-1); + if (STps->eof == ST_FM && + (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) { +#if DEBUG + if (debugging) + printk(ST_DEB_MSG + "st%d: Zero returned for first BLANK CHECK after EOF.\n", + dev); +#endif + STps->eof = ST_EOD_2; /* First BLANK_CHECK after FM */ + } + else /* Some other extended sense code */ + retval = (-EIO); + } + } /* End of extended sense test */ + else { /* Non-extended sense */ + retval = (STp->buffer)->last_result_fatal; + } + + } /* End of error handling */ + else /* Read successful */ + (STp->buffer)->buffer_bytes = bytes; + + if (STps->drv_block >= 0) { + if (STp->block_size == 0) + STps->drv_block++; + else + STps->drv_block += (STp->buffer)->buffer_bytes / STp->block_size; + } + + return retval; +} /* Read command */ @@ -1172,8 +1411,8 @@ st_write(struct inode * inode, struct file * filp, const char * buf, st_read(struct inode * inode, struct file * filp, char * buf, unsigned long count) { long total; - int transfer, blks, bytes; - static unsigned char cmd[10]; + int i, transfer; + int special; Scsi_Cmnd * SCpnt = NULL; Scsi_Tape * STp; ST_mode * STm; @@ -1196,7 +1435,6 @@ st_read(struct inode * inode, struct file * filp, char * buf, unsigned long coun if (STp->can_partitions && (total = update_partition(inode)) < 0) return total; - STps = &(STp->ps[STp->partition]); if (STp->block_size == 0 && count > (STp->buffer)->buffer_size && @@ -1211,232 +1449,103 @@ st_read(struct inode * inode, struct file * filp, char * buf, unsigned long coun !st_int_ioctl(inode, MTLOCK, 0)) STp->door_locked = ST_LOCKED_AUTO; + STps = &(STp->ps[STp->partition]); if (STps->rw == ST_WRITING) { transfer = flush_buffer(inode, filp, 0); if (transfer) return transfer; STps->rw = ST_READING; } - if (STps->moves_after_eof < 255) - STps->moves_after_eof++; #if DEBUG - if (debugging && STp->eof != ST_NOEOF) - printk(ST_DEB_MSG "st%d: EOF flag up. Bytes %d\n", dev, - (STp->buffer)->buffer_bytes); + if (debugging && STps->eof != ST_NOEOF) + printk(ST_DEB_MSG "st%d: EOF/EOM flag up (%d). Bytes %d\n", dev, + STps->eof, (STp->buffer)->buffer_bytes); #endif - if (((STp->buffer)->buffer_bytes == 0) && - (STp->eof == ST_EOM_OK || STp->eof == ST_EOD)) - return (-EIO); /* EOM or Blank Check */ - - STps->rw = ST_READING; - - for (total = 0; total < count; ) { - - if ((STp->buffer)->buffer_bytes == 0 && - STp->eof == ST_NOEOF) { - - memset(cmd, 0, 10); - cmd[0] = READ_6; - cmd[1] = (STp->block_size != 0); - if (STp->block_size == 0) - blks = bytes = count; - else { - if (STm->do_read_ahead) { - blks = (STp->buffer)->buffer_blocks; - bytes = blks * STp->block_size; - } - else { - bytes = count - total; - if (bytes > (STp->buffer)->buffer_size) - bytes = (STp->buffer)->buffer_size; - blks = bytes / STp->block_size; - bytes = blks * STp->block_size; - } + if ((STp->buffer)->buffer_bytes == 0 && + STps->eof >= ST_EOD_1) { + if (STps->eof < ST_EOD) { + STps->eof += 1; + return 0; } - cmd[2] = blks >> 16; - cmd[3] = blks >> 8; - cmd[4] = blks; - - SCpnt = st_do_scsi(SCpnt, STp, cmd, bytes, ST_TIMEOUT, MAX_RETRIES); - if (!SCpnt) - return (-EBUSY); + return (-EIO); /* EOM or Blank Check */ + } - (STp->buffer)->read_pointer = 0; - STp->eof_hit = 0; - STps->at_sm = 0; + /* Check the buffer writability before any tape movement. Don't alter + buffer data. */ + if (copy_from_user(&i, buf, 1) != 0 || + copy_to_user(buf, &i, 1) != 0 || + copy_from_user(&i, buf + count - 1, 1) != 0 || + copy_to_user(buf + count - 1, &i, 1) != 0) + return (-EFAULT); - if ((STp->buffer)->last_result_fatal) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", - dev, - SCpnt->sense_buffer[0], SCpnt->sense_buffer[1], - SCpnt->sense_buffer[2], SCpnt->sense_buffer[3], - SCpnt->sense_buffer[4], SCpnt->sense_buffer[5], - SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]); -#endif - if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */ + STps->rw = ST_READING; - if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) - SCpnt->sense_buffer[2] &= 0xcf; /* No need for EOM in this case */ - if ((SCpnt->sense_buffer[2] & 0xe0) != 0) { /* EOF, EOM, or ILI */ + /* Loop until enough data in buffer or a special condition found */ + for (total = 0, special = 0; total < count && !special; ) { - if ((SCpnt->sense_buffer[0] & 0x80) != 0) - transfer = (SCpnt->sense_buffer[3] << 24) | - (SCpnt->sense_buffer[4] << 16) | - (SCpnt->sense_buffer[5] << 8) | SCpnt->sense_buffer[6]; - else - transfer = 0; - if (STp->block_size == 0 && - (SCpnt->sense_buffer[2] & 0x0f) == MEDIUM_ERROR) - transfer = bytes; - - if (SCpnt->sense_buffer[2] & 0x20) { - if (STp->block_size == 0) { - if (transfer <= 0) - transfer = 0; - (STp->buffer)->buffer_bytes = bytes - transfer; - } - else { + /* Get new data if the buffer is empty */ + if ((STp->buffer)->buffer_bytes == 0) { + special = read_tape(inode, count - total, &SCpnt); + if (special < 0) { /* No need to continue read */ + if (SCpnt != NULL) SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - if (transfer == blks) { /* We did not get anything, signal error */ - printk(KERN_NOTICE "st%d: Incorrect block size.\n", dev); - if (STp->drv_block >= 0) - STp->drv_block += blks - transfer + 1; - st_int_ioctl(inode, MTBSR, 1); - return (-EIO); - } - /* We have some data, deliver it */ - (STp->buffer)->buffer_bytes = (blks - transfer) * STp->block_size; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: ILI but enough data received %ld %d.\n", - dev, count - total, (STp->buffer)->buffer_bytes); -#endif - if (count - total > (STp->buffer)->buffer_bytes) - count = total + (STp->buffer)->buffer_bytes; - if (STp->drv_block >= 0) - STp->drv_block += 1; - if (st_int_ioctl(inode, MTBSR, 1)) - return (-EIO); - SCpnt = NULL; - } - } - else if (SCpnt->sense_buffer[2] & 0x80) { /* FM overrides EOM */ - STp->eof = ST_FM; - if (STp->block_size == 0) - (STp->buffer)->buffer_bytes = 0; - else - (STp->buffer)->buffer_bytes = - bytes - transfer * STp->block_size; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG - "st%d: EOF detected (%d bytes read, transferred %ld bytes).\n", - dev, (STp->buffer)->buffer_bytes, total); -#endif - } - else if (SCpnt->sense_buffer[2] & 0x40) { - STp->eof = ST_EOM_OK; - if (STp->block_size == 0) - (STp->buffer)->buffer_bytes = bytes - transfer; - else - (STp->buffer)->buffer_bytes = - bytes - transfer * STp->block_size; -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: EOM detected (%d bytes read).\n", dev, - (STp->buffer)->buffer_bytes); -#endif - } - } /* end of EOF, EOM, ILI test */ - else { /* nonzero sense key */ -#if DEBUG - if (debugging) - printk(ST_DEB_MSG "st%d: Tape error while reading.\n", dev); -#endif - SCpnt->request.rq_status = RQ_INACTIVE; - STp->drv_block = (-1); - if (total) - return total; - else if (STps->moves_after_eof == 1 && - (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) { -#if DEBUG - if (debugging) - printk(ST_DEB_MSG - "st%d: Zero returned for first BLANK CHECK after EOF.\n", - dev); -#endif - STp->eof = ST_EOD; - return 0; /* First BLANK_CHECK after EOF */ - } - else - return -EIO; - } - } /* End of extended sense test */ - else { - transfer = (STp->buffer)->last_result_fatal; - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - return transfer; + return special; } - } /* End of error handling */ - else /* Read successful */ - (STp->buffer)->buffer_bytes = bytes; - - if (STp->drv_block >= 0) { - if (STp->block_size == 0) - STp->drv_block++; - else - STp->drv_block += (STp->buffer)->buffer_bytes / STp->block_size; - } - - } /* if ((STp->buffer)->buffer_bytes == 0 && - STp->eof == ST_NOEOF) */ + } + /* Move the data from driver buffer to user buffer */ if ((STp->buffer)->buffer_bytes > 0) { #if DEBUG - if (debugging && STp->eof != ST_NOEOF) - printk(ST_DEB_MSG "st%d: EOF up. Left %d, needed %d.\n", dev, - (STp->buffer)->buffer_bytes, count - total); + if (debugging && STps->eof != ST_NOEOF) + printk(ST_DEB_MSG "st%d: EOF up (%d). Left %d, needed %ld.\n", dev, + STps->eof, (STp->buffer)->buffer_bytes, count - total); #endif transfer = (STp->buffer)->buffer_bytes < count - total ? (STp->buffer)->buffer_bytes : count - total; - copy_to_user(buf, (STp->buffer)->b_data + - (STp->buffer)->read_pointer,transfer); + i = copy_to_user(buf, (STp->buffer)->b_data + + (STp->buffer)->read_pointer, transfer); + if (i) { + if (SCpnt != NULL) + SCpnt->request.rq_status = RQ_INACTIVE; + return (-EFAULT); + } filp->f_pos += transfer; buf += transfer; total += transfer; (STp->buffer)->buffer_bytes -= transfer; (STp->buffer)->read_pointer += transfer; } - else if (STp->eof != ST_NOEOF) { - STp->eof_hit = 1; - if (SCpnt != NULL) - SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - if (total == 0 && STp->eof == ST_FM) { - STp->eof = ST_NOEOF; - STp->eof_hit = 0; - STp->drv_block = 0; - if (STps->moves_after_eof > 1) - STps->moves_after_eof = 0; - if ((STp->mt_status)->mt_fileno >= 0) - (STp->mt_status)->mt_fileno++; - } - if (total == 0 && STp->eof == ST_EOM_OK) - return (-ENOSPC); /* ST_EOM_ERROR not used in read */ - return total; - } if (STp->block_size == 0) - count = total; /* Read only one variable length block */ + break; /* Read only one variable length block */ - } /* for (total = 0; total < count; ) */ + } /* for (total = 0, special = 0; total < count && !special; ) */ if (SCpnt != NULL) SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ + /* Change the eof state if no data from tape or buffer */ + if (total == 0) { + if (STps->eof == ST_FM_HIT) { + STps->eof = ST_FM; + STps->drv_block = 0; + if (STps->drv_file >= 0) + STps->drv_file++; + } + else if (STps->eof == ST_EOD_1) { + STps->eof = ST_EOD_2; + STps->drv_block = 0; + if (STps->drv_file >= 0) + STps->drv_file++; + } + else if (STps->eof == ST_EOD_2) + STps->eof = ST_EOD; + } + else if (STps->eof == ST_FM) + STps->eof = ST_NOEOF; + return total; } @@ -1705,6 +1814,7 @@ st_int_ioctl(struct inode * inode, int timeout = ST_LONG_TIMEOUT; long ltmp; int i, ioctl_result; + int chg_eof = TRUE; unsigned char cmd[10]; Scsi_Cmnd * SCpnt; Scsi_Tape * STp; @@ -1716,15 +1826,16 @@ st_int_ioctl(struct inode * inode, if (STp->ready != ST_READY && cmd_in != MTLOAD) return (-EIO); STps = &(STp->ps[STp->partition]); - fileno = (STp->mt_status)->mt_fileno ; - blkno = STp->drv_block; + fileno = STps->drv_file; + blkno = STps->drv_block; at_sm = STps->at_sm; memset(cmd, 0, 10); datalen = 0; switch (cmd_in) { - case MTFSF: case MTFSFM: + chg_eof = FALSE; /* Changed from the FSF after this */ + case MTFSF: cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ cmd[2] = (arg >> 16); @@ -1740,8 +1851,9 @@ st_int_ioctl(struct inode * inode, blkno = 0; at_sm &= (arg == 0); break; - case MTBSF: case MTBSFM: + chg_eof = FALSE; /* Changed from the FSF after this */ + case MTBSF: cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ ltmp = (-arg); @@ -1877,6 +1989,21 @@ st_int_ioctl(struct inode * inode, cmd[0] = START_STOP; if (cmd_in == MTLOAD) cmd[4] |= 1; + /* + * If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A + */ + if (cmd_in != MTOFFL && + arg >= 1 + MT_ST_HPLOADER_OFFSET + && arg <= 6 + MT_ST_HPLOADER_OFFSET) { +#if DEBUG + if (debugging) { + printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n", + dev, (cmd[4]) ? "" : "un", + arg - MT_ST_HPLOADER_OFFSET); + } +#endif + cmd[3] = arg; /* MediaID field of C1553A */ + } #if ST_NOWAIT cmd[1] = 1; /* Don't wait for completion */ timeout = ST_TIMEOUT; @@ -1917,8 +2044,8 @@ st_int_ioctl(struct inode * inode, if (!STp->fast_mteom) { /* space to the end of tape */ ioctl_result = st_int_ioctl(inode, MTFSF, 0x3fff); - fileno = (STp->mt_status)->mt_fileno ; - if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK) + fileno = STps->drv_file; + if (STps->eof >= ST_EOD_1) return 0; /* The next lines would hide the number of spaced FileMarks That's why I inserted the previous lines. I had no luck @@ -1954,6 +2081,7 @@ st_int_ioctl(struct inode * inode, fileno = blkno = at_sm = 0 ; break; case MTLOCK: + chg_eof = FALSE; cmd[0] = ALLOW_MEDIUM_REMOVAL; cmd[4] = SCSI_REMOVAL_PREVENT; #if DEBUG @@ -1962,6 +2090,7 @@ st_int_ioctl(struct inode * inode, #endif; break; case MTUNLOCK: + chg_eof = FALSE; cmd[0] = ALLOW_MEDIUM_REMOVAL; cmd[4] = SCSI_REMOVAL_ALLOW; #if DEBUG @@ -1973,6 +2102,7 @@ st_int_ioctl(struct inode * inode, case MTSETDENSITY: /* Set tape density */ case MTSETDRVBUFFER: /* Set drive buffering */ case SET_DENS_AND_BLK: /* Set density and block size */ + chg_eof = FALSE; if (STp->dirty || (STp->buffer)->buffer_bytes != 0) return (-EIO); /* Not allowed if data in buffer */ if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) && @@ -2040,64 +2170,63 @@ st_int_ioctl(struct inode * inode, SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ - if (cmd_in == MTFSF) - STps->moves_after_eof = 0; - else if (cmd_in != MTLOAD && cmd_in != MTLOCK && cmd_in != MTUNLOCK && - cmd_in != MTSETBLK && cmd_in != MTSETDENSITY && - cmd_in != MTSETDRVBUFFER) - STps->moves_after_eof = 1; if (!ioctl_result) { /* SCSI command successful */ - STp->drv_block = blkno; - (STp->mt_status)->mt_fileno = fileno; + STps->drv_block = blkno; + STps->drv_file = fileno; STps->at_sm = at_sm; + if (cmd_in == MTLOCK) STp->door_locked = ST_LOCKED_EXPLICIT; else if (cmd_in == MTUNLOCK) STp->door_locked = ST_UNLOCKED; + if (cmd_in == MTBSFM) ioctl_result = st_int_ioctl(inode, MTFSF, 1); else if (cmd_in == MTFSFM) ioctl_result = st_int_ioctl(inode, MTBSF, 1); - else if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) { + + if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) { STp->block_size = arg & MT_ST_BLKSIZE_MASK; if (STp->block_size != 0) (STp->buffer)->buffer_blocks = (STp->buffer)->buffer_size / STp->block_size; (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0; + if (cmd_in == SET_DENS_AND_BLK) + STp->density = arg >> MT_ST_DENSITY_SHIFT; } else if (cmd_in == MTSETDRVBUFFER) STp->drv_buffer = (arg & 7); else if (cmd_in == MTSETDENSITY) STp->density = arg; - else if (cmd_in == MTEOM) { - STp->eof = ST_EOD; - STp->eof_hit = 0; - } - else if (cmd_in != MTSETBLK && cmd_in != MTNOP) { - STp->eof = ST_NOEOF; - STp->eof_hit = 0; - } - if (cmd_in == SET_DENS_AND_BLK) - STp->density = arg >> MT_ST_DENSITY_SHIFT; + + if (cmd_in == MTEOM) + STps->eof = ST_EOD; + else if (cmd_in == MTFSF) + STps->eof = ST_FM; + else if (chg_eof) + STps->eof = ST_NOEOF; + + if (cmd_in == MTOFFL || cmd_in == MTUNLOAD) STp->rew_at_close = 0; else if (cmd_in == MTLOAD) { STp->rew_at_close = (MINOR(inode->i_rdev) & 0x80) == 0; for (i=0; i < ST_NBR_PARTITIONS; i++) { STp->ps[i].rw = ST_IDLE; - STp->ps[i].moves_after_eof = 1; STp->ps[i].last_block_valid = FALSE; } STp->partition = 0; } + } else { /* SCSI command was not completely successful */ + if (SCpnt->sense_buffer[2] & 0x40) { if (cmd_in != MTBSF && cmd_in != MTBSFM && cmd_in != MTBSR && cmd_in != MTBSS) - STp->eof = ST_EOM_OK; - STp->eof_hit = 0; - STp->drv_block = 0; + STps->eof = ST_EOM_OK; + STps->drv_block = 0; } + undone = ( (SCpnt->sense_buffer[3] << 24) + (SCpnt->sense_buffer[4] << 16) + @@ -2110,50 +2239,64 @@ st_int_ioctl(struct inode * inode, ioctl_result = 0; /* EOF written succesfully at EOM */ if (fileno >= 0) fileno++; - (STp->mt_status)->mt_fileno = fileno; + STps->drv_file = fileno; + STps->eof = ST_NOEOF; } else if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) { if (fileno >= 0) - (STp->mt_status)->mt_fileno = fileno - undone ; + STps->drv_file = fileno - undone ; else - (STp->mt_status)->mt_fileno = fileno; - STp->drv_block = 0; + STps->drv_file = fileno; + STps->drv_block = 0; + STps->eof = ST_NOEOF; } else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) { - (STp->mt_status)->mt_fileno = fileno + undone ; - STp->drv_block = 0; + if (fileno >= 0) + STps->drv_file = fileno + undone ; + else + STps->drv_file = fileno; + STps->drv_block = 0; + STps->eof = ST_NOEOF; } else if (cmd_in == MTFSR) { if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ - (STp->mt_status)->mt_fileno++; - STp->drv_block = 0; + if (STps->drv_file >= 0) + STps->drv_file++; + STps->drv_block = 0; + STps->eof = ST_FM; } else { if (blkno >= undone) - STp->drv_block = blkno - undone; + STps->drv_block = blkno - undone; else - STp->drv_block = (-1); + STps->drv_block = (-1); + STps->eof = ST_NOEOF; } } else if (cmd_in == MTBSR) { if (SCpnt->sense_buffer[2] & 0x80) { /* Hit filemark */ - (STp->mt_status)->mt_fileno--; - STp->drv_block = (-1); + STps->drv_file--; + STps->drv_block = (-1); } else { if (blkno >= 0) - STp->drv_block = blkno + undone; + STps->drv_block = blkno + undone; else - STp->drv_block = (-1); + STps->drv_block = (-1); } + STps->eof = ST_NOEOF; } else if (cmd_in == MTEOM) { - (STp->mt_status)->mt_fileno = (-1); - STp->drv_block = (-1); + STps->drv_file = (-1); + STps->drv_block = (-1); + STps->eof = ST_EOD; } - if (STp->eof == ST_NOEOF && - (SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) - STp->eof = ST_EOD; + else if (chg_eof) + STps->eof = ST_NOEOF; + + if ((SCpnt->sense_buffer[2] & 0x0f) == BLANK_CHECK) + STps->eof = ST_EOD; + if (cmd_in == MTLOCK) STp->door_locked = ST_LOCK_FAILS; } @@ -2219,7 +2362,7 @@ get_location(struct inode * inode, unsigned int *block, int *partition, *partition = (STp->buffer)->b_data[1]; if (((STp->buffer)->b_data[0] & 0x80) && (STp->buffer)->b_data[1] == 0) /* BOP of partition 0 */ - STp->drv_block = (STp->mt_status)->mt_fileno = 0; + STp->ps[0].drv_block = STp->ps[0].drv_file = 0; } #if DEBUG if (debugging) @@ -2315,8 +2458,8 @@ set_location(struct inode * inode, unsigned int block, int partition, if (!SCpnt) return (-EBUSY); - STp->drv_block = (STp->mt_status)->mt_fileno = (-1); - STp->eof = ST_NOEOF; + STps->drv_block = STps->drv_file = (-1); + STps->eof = ST_NOEOF; if ((STp->buffer)->last_result_fatal != 0) { result = (-EIO); if (STp->can_partitions && @@ -2330,7 +2473,6 @@ set_location(struct inode * inode, unsigned int block, int partition, STps = &(STp->ps[partition]); if (!STps->last_block_valid || STps->last_block_visited != block) { - STps->moves_after_eof = 1; STps->at_sm = 0; STps->rw = ST_IDLE; } @@ -2338,7 +2480,7 @@ set_location(struct inode * inode, unsigned int block, int partition, else STps->at_sm = 0; if (block == 0) - STp->drv_block = (STp->mt_status)->mt_fileno = 0; + STps->drv_block = STps->drv_file = 0; result = 0; } SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */ @@ -2528,11 +2670,9 @@ st_ioctl(struct inode * inode,struct file * file, if (_IOC_SIZE(cmd_in) != sizeof(mtc)) return (-EINVAL); - i = verify_area(VERIFY_READ, (void *)arg, sizeof(mtc)); + i = copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop)); if (i) - return i; - - copy_from_user((char *) &mtc, (char *)arg, sizeof(struct mtop)); + return (-EFAULT); if (mtc.mt_op == MTSETDRVBUFFER && !suser()) { printk(KERN_WARNING "st%d: MTSETDRVBUFFER only allowed for root.\n", dev); @@ -2544,16 +2684,16 @@ st_ioctl(struct inode * inode,struct file * file, if (!(STp->device)->was_reset) { - if (STp->eof_hit) { + if (STps->eof == ST_FM_HIT) { if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM|| mtc.mt_op == MTEOM) { mtc.mt_count -= 1; - if ((STp->mt_status)->mt_fileno >= 0) - (STp->mt_status)->mt_fileno += 1; + if (STps->drv_file >= 0) + STps->drv_file += 1; } else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) { mtc.mt_count += 1; - if ((STp->mt_status)->mt_fileno >= 0) - (STp->mt_status)->mt_fileno += 1; + if (STps->drv_file >= 0) + STps->drv_file += 1; } } @@ -2591,9 +2731,8 @@ st_ioctl(struct inode * inode,struct file * file, if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK && mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM && - mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSEEK && - mtc.mt_op != MTSETPART) - STps->rw = ST_IDLE; /* Prevent automatic WEOF */ + mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART) + STps->rw = ST_IDLE; /* Prevent automatic WEOF and fsf */ if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED) st_int_ioctl(inode, MTUNLOCK, 0); /* Ignore result! */ @@ -2621,13 +2760,12 @@ st_ioctl(struct inode * inode,struct file * file, return i; for (i=0; i < ST_NBR_PARTITIONS; i++) { STp->ps[i].rw = ST_IDLE; - STp->ps[i].moves_after_eof = 1; STp->ps[i].at_sm = 0; STp->ps[i].last_block_valid = FALSE; } STp->partition = STp->new_partition = 0; STp->nbr_partitions = 1; /* Bad guess ?-) */ - STp->drv_block = (STp->mt_status)->mt_fileno = 0; + STps->drv_block = STps->drv_file = 0; return 0; } if (mtc.mt_op == MTSEEK) { @@ -2658,14 +2796,12 @@ st_ioctl(struct inode * inode,struct file * file, if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) return (-EINVAL); - i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct mtget)); - if (i) - return i; (STp->mt_status)->mt_dsreg = ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) | ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK); - (STp->mt_status)->mt_blkno = STp->drv_block; + (STp->mt_status)->mt_blkno = STps->drv_block; + (STp->mt_status)->mt_fileno = STps->drv_file; if (STp->block_size != 0) { if (STps->rw == ST_WRITING) (STp->mt_status)->mt_blkno += @@ -2685,9 +2821,9 @@ st_ioctl(struct inode * inode,struct file * file, (STp->mt_status)->mt_gstat |= GMT_EOF(0xffffffff); } (STp->mt_status)->mt_resid = STp->partition; - if (STp->eof == ST_EOM_OK || STp->eof == ST_EOM_ERROR) + if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR) (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff); - else if (STp->eof == ST_EOD) + else if (STps->eof >= ST_EOM_OK) (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff); if (STp->density == 1) (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff); @@ -2705,8 +2841,10 @@ st_ioctl(struct inode * inode,struct file * file, STp->drv_buffer != 0) (STp->mt_status)->mt_gstat |= GMT_IM_REP_EN(0xffffffff); - copy_to_user((char *)arg, (char *)(STp->mt_status), - sizeof(struct mtget)); + i = copy_to_user((char *)arg, (char *)(STp->mt_status), + sizeof(struct mtget)); + if (i) + return (-EFAULT); (STp->mt_status)->mt_erreg = 0; /* Clear after read */ return 0; @@ -2717,11 +2855,10 @@ st_ioctl(struct inode * inode,struct file * file, return (-EINVAL); if ((i = get_location(inode, &blk, &bt, 0)) < 0) return i; - i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(mt_pos)); - if (i) - return i; mt_pos.mt_blkno = blk; - copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos)); + i = copy_to_user((char *)arg, (char *) (&mt_pos), sizeof(struct mtpos)); + if (i) + return (-EFAULT); return 0; } @@ -2890,7 +3027,6 @@ static int st_attach(Scsi_Device * SDp){ tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i); tpnt->dirty = 0; - tpnt->eof = ST_NOEOF; tpnt->waiting = NULL; tpnt->in_use = 0; tpnt->drv_buffer = 1; /* Try buffering if no mode sense */ @@ -2907,12 +3043,11 @@ static int st_attach(Scsi_Device * SDp){ tpnt->partition = 0; tpnt->new_partition = 0; tpnt->nbr_partitions = 0; - tpnt->drv_block = (-1); - (tpnt->mt_status)->mt_fileno = (tpnt->mt_status)->mt_blkno = (-1); for (i=0; i < ST_NBR_MODES; i++) { STm = &(tpnt->modes[i]); STm->defined = FALSE; + STm->sysv = ST_SYSV; STm->defaults_for_writes = 0; STm->do_async_writes = ST_ASYNC_WRITES; STm->do_buffer_writes = ST_BUFFER_WRITES; @@ -2925,9 +3060,11 @@ static int st_attach(Scsi_Device * SDp){ for (i=0; i < ST_NBR_PARTITIONS; i++) { STps = &(tpnt->ps[i]); STps->rw = ST_IDLE; - STps->moves_after_eof = 1; + STps->eof = ST_NOEOF; STps->at_sm = 0; STps->last_block_valid = FALSE; + STps->drv_block = 0; + STps->drv_file = 0; } tpnt->current_mode = 0; diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index dd1158a1ebe0..c29455fa6deb 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -29,6 +29,7 @@ typedef struct { /* The tape mode definition */ typedef struct { unsigned char defined; + unsigned char sysv; /* SYS V semantics? */ unsigned char do_async_writes; unsigned char do_buffer_writes; unsigned char do_read_ahead; @@ -46,10 +47,12 @@ typedef struct { /* The status related to each partition */ typedef struct { unsigned char rw; - unsigned char moves_after_eof; + unsigned char eof; unsigned char at_sm; unsigned char last_block_valid; u32 last_block_visited; + int drv_block; /* The block where the drive head is */ + int drv_file; } ST_partstat; #define ST_NBR_PARTITIONS 4 @@ -87,11 +90,9 @@ typedef struct { ST_partstat ps[ST_NBR_PARTITIONS]; unsigned char dirty; unsigned char ready; - unsigned char eof; unsigned char write_prot; unsigned char drv_write_prot; unsigned char in_use; - unsigned char eof_hit; unsigned char blksize_changed; unsigned char density_changed; unsigned char compression_changed; @@ -103,13 +104,14 @@ typedef struct { int min_block; int max_block; int recover_count; - int drv_block; /* The block where the drive head is */ struct mtget * mt_status; #if DEBUG unsigned char write_pending; int nbr_finished; int nbr_waits; + unsigned char last_cmnd[6]; + unsigned char last_sense[16]; #endif } Scsi_Tape; @@ -117,10 +119,15 @@ extern Scsi_Tape * scsi_tapes; /* Values of eof */ #define ST_NOEOF 0 -#define ST_FM 1 -#define ST_EOM_OK 2 -#define ST_EOM_ERROR 3 -#define ST_EOD 4 +#define ST_FM_HIT 1 +#define ST_FM 2 +#define ST_EOM_OK 3 +#define ST_EOM_ERROR 4 +#define ST_EOD_1 5 +#define ST_EOD_2 6 +#define ST_EOD 7 +/* EOD hit while reading => ST_EOD_1 => return zero => ST_EOD_2 => + return zero => ST_EOD, return ENOSPC */ /* Values of rw */ #define ST_IDLE 0 diff --git a/fs/buffer.c b/fs/buffer.c index 67c0345b5a40..55c06171a9c8 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1546,7 +1546,7 @@ asmlinkage int sync_old_buffers(void) asmlinkage int sys_bdflush(int func, long data) { - int i, error; + int i; if (!suser()) return -EPERM; @@ -1560,11 +1560,7 @@ asmlinkage int sys_bdflush(int func, long data) if (i < 0 || i >= N_PARAM) return -EINVAL; if((func & 1) == 0) { - error = verify_area(VERIFY_WRITE, (void *) data, sizeof(int)); - if (error) - return error; - put_user(bdf_prm.data[i], (int*)data); - return 0; + return put_user(bdf_prm.data[i], (int*)data); }; if (data < bdflush_min[i] || data > bdflush_max[i]) return -EINVAL; diff --git a/fs/dquot.c b/fs/dquot.c index 2c8778995155..aa3c5e517a20 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -19,6 +19,7 @@ * * Fixes: Dmitry Gorodchanin , 11 Feb 96 * removed race conditions in dqput(), dqget() and iput(). + * Andi Kleen removed all verify_area() calls, 31 Dec 96 * * (C) Copyright 1994, 1995 Marco van Wieringen * @@ -591,9 +592,8 @@ static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dq return(-EFAULT); if (flags & QUOTA_SYSCALL) { - if ((error = verify_area(VERIFY_READ, dqblk, sizeof(struct dqblk))) != 0) - return(error); - copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)); + if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk))) + return -EFAULT; } else { memcpy(&dq_dqblk, dqblk, sizeof(struct dqblk)); } @@ -649,13 +649,10 @@ static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk) if (dqblk == (struct dqblk *)NULL) return(-EFAULT); - if ((error = verify_area(VERIFY_WRITE, dqblk, sizeof(struct dqblk))) != 0) - return(error); - if ((dquot = dqget(dev, id, type)) != NODQUOT) { - copy_to_user(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); + error = copy_to_user(dqblk, (char *)&dquot->dq_dqb, sizeof(struct dqblk)); dqput(dquot); - return(0); + return error ? -EFAULT : 0; } } return(-ESRCH); @@ -665,13 +662,10 @@ static int get_stats(caddr_t addr) { int error; - if ((error = verify_area(VERIFY_WRITE, addr, sizeof(struct dqstats))) != 0) - return(error); - dqstats.allocated_dquots = nr_dquots; dqstats.free_dquots = nr_free_dquots; - copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats)); - return(0); + return copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats)) + ? -EFAULT : 0; } /* diff --git a/fs/exec.c b/fs/exec.c index eda6449eec8c..e8b8d176343c 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -386,8 +386,10 @@ static void exec_mmap(void) new_page_tables(current); return; } + flush_cache_mm(current->mm); exit_mmap(current->mm); clear_page_tables(current); + flush_tlb_mm(current->mm); } /* diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c index 8235a63016b3..0892ce79f076 100644 --- a/fs/ext2/ioctl.c +++ b/fs/ext2/ioctl.c @@ -19,23 +19,16 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { - int err; unsigned long flags; ext2_debug ("cmd = %u, arg = %lu\n", cmd, arg); switch (cmd) { case EXT2_IOC_GETFLAGS: - err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int)); - if (err) - return err; - put_user(inode->u.ext2_i.i_flags, (int *) arg); - return 0; + return put_user(inode->u.ext2_i.i_flags, (int *) arg); case EXT2_IOC_SETFLAGS: - err = verify_area(VERIFY_READ, (int *) arg, sizeof(int)); - if (err) - return err; - get_user(flags, (int *) arg); + if (get_user(flags, (int *) arg)) + return -EFAULT; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the super user when the security level is zero. @@ -64,20 +57,14 @@ int ext2_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, inode->i_dirt = 1; return 0; case EXT2_IOC_GETVERSION: - err = verify_area(VERIFY_WRITE, (int *) arg, sizeof(int)); - if (err) - return err; - put_user(inode->u.ext2_i.i_version, (int *) arg); - return 0; + return put_user(inode->u.ext2_i.i_version, (int *) arg); case EXT2_IOC_SETVERSION: if ((current->fsuid != inode->i_uid) && !fsuser()) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; - err = verify_area(VERIFY_READ, (int *) arg, sizeof(int)); - if (err) - return err; - get_user(inode->u.ext2_i.i_version, (int *) arg); + if (get_user(inode->u.ext2_i.i_version, (int *) arg)) + return -EFAULT; inode->i_ctime = CURRENT_TIME; inode->i_dirt = 1; return 0; diff --git a/fs/ext2/symlink.c b/fs/ext2/symlink.c index ead6e0bc1059..fca0f7923c00 100644 --- a/fs/ext2/symlink.c +++ b/fs/ext2/symlink.c @@ -105,7 +105,6 @@ static int ext2_readlink (struct inode * inode, char * buffer, int buflen) struct buffer_head * bh = NULL; char * link; int i, err; - char c; if (!S_ISLNK(inode->i_mode)) { iput (inode); @@ -123,12 +122,14 @@ static int ext2_readlink (struct inode * inode, char * buffer, int buflen) } else link = (char *) inode->u.ext2_i.i_data; - i = 0; - while (i < buflen && (c = link[i])) { - i++; - put_user (c, buffer++); - } - if (DO_UPDATE_ATIME(inode)) { + + /* XXX I hope link is always '\0'-terminated. */ + i = strlen(link)+1; + if (i > buflen) + i = buflen; + if (copy_to_user(buffer, link, i)) + i = -EFAULT; + if (DO_UPDATE_ATIME(inode)) { inode->i_atime = CURRENT_TIME; inode->i_dirt = 1; } diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 5d4857ed1625..8db9bbb39e31 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -193,6 +193,11 @@ static unsigned int isofs_get_last_session(kdev_t dev) vol_desc_start=0; if (get_blkfops(MAJOR(dev))->ioctl!=NULL) { + /* Whoops. We must save the old FS, since otherwise + * we would destroy the kernels idea about FS on root + * mount in read_super... [chexum] + */ + unsigned long old_fs=get_fs(); inode_fake.i_rdev=dev; ms_info.addr_format=CDROM_LBA; set_fs(KERNEL_DS); @@ -200,7 +205,7 @@ static unsigned int isofs_get_last_session(kdev_t dev) NULL, CDROMMULTISESSION, (unsigned long) &ms_info); - set_fs(USER_DS); + set_fs(old_fs); #if 0 printk("isofs.inode: CDROMMULTISESSION: rc=%d\n",i); if (i==0) diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 9f267e69dced..76c3168b2870 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -287,7 +287,7 @@ int parse_rock_ridge_inode(struct iso_directory_record * de, CHECK_CE; break; case SIG('E','R'): - printk("ISO9660 Extensions: "); + printk(KERN_DEBUG"ISO9660 Extensions: "); { int p; for(p=0;pu.ER.len_id;p++) printk("%c",rr->u.ER.data[p]); }; diff --git a/fs/isofs/symlink.c b/fs/isofs/symlink.c index 59489dc4b67b..87e54432400e 100644 --- a/fs/isofs/symlink.c +++ b/fs/isofs/symlink.c @@ -83,7 +83,6 @@ static int isofs_readlink(struct inode * inode, char * buffer, int buflen) { char * pnt; int i; - char c; if (!S_ISLNK(inode->i_mode)) { iput(inode); @@ -97,12 +96,12 @@ static int isofs_readlink(struct inode * inode, char * buffer, int buflen) iput(inode); if (!pnt) return 0; - i = 0; - while (i buflen) + i = buflen; + if (copy_to_user(buffer, pnt, i)) + i = -EFAULT; kfree(pnt); return i; } diff --git a/fs/locks.c b/fs/locks.c index c4be4a63e458..6fe7da42a074 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -258,18 +258,15 @@ asmlinkage int sys_flock(unsigned int fd, unsigned int cmd) */ int fcntl_getlk(unsigned int fd, struct flock *l) { - int error; struct flock flock; struct file *filp; struct file_lock *fl,file_lock; if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) return (-EBADF); - error = verify_area(VERIFY_WRITE, l, sizeof(*l)); - if (error) - return (error); + if (copy_from_user(&flock, l, sizeof(flock))) + return -EFAULT; - copy_from_user(&flock, l, sizeof(flock)); if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) return (-EINVAL); @@ -286,14 +283,12 @@ int fcntl_getlk(unsigned int fd, struct flock *l) fl->fl_end - fl->fl_start + 1; flock.l_whence = 0; flock.l_type = fl->fl_type; - copy_to_user(l, &flock, sizeof(flock)); - return (0); + return copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0; } } flock.l_type = F_UNLCK; /* no conflict found */ - copy_to_user(l, &flock, sizeof(flock)); - return (0); + return copy_to_user(l, &flock, sizeof(flock)) ? -EFAULT : 0; } /* Apply the lock described by l to an open file descriptor. @@ -301,7 +296,6 @@ int fcntl_getlk(unsigned int fd, struct flock *l) */ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) { - int error; struct file *filp; struct file_lock file_lock; struct flock flock; @@ -313,10 +307,6 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) return (-EBADF); - error = verify_area(VERIFY_READ, l, sizeof(*l)); - if (error) - return (error); - if (!(inode = filp->f_inode)) return (-EINVAL); @@ -334,7 +324,8 @@ int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) } while (vma != inode->i_mmap); } - copy_from_user(&flock, l, sizeof(flock)); + if (copy_from_user(&flock, l, sizeof(flock))) + return -EFAULT; if (!posix_make_lock(filp, &file_lock, &flock)) return (-EINVAL); diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index e628e0182a5a..3d6219238994 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -108,10 +108,12 @@ static int nfs_readlink(struct inode *inode, char *buffer, int buflen) error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem, &res, &len, buflen); iput(inode); - if (! error) { - copy_to_user(buffer, res, len); - put_user('\0', buffer + len); - error = len; + if (!error) { + error = copy_to_user(buffer, res, len); + if (!error) + error = put_user('\0', buffer + len); + if (!error) + error = len; } kfree(mem); return error; diff --git a/fs/open.c b/fs/open.c index 8ab0614a70df..7924c7cb173b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -176,13 +176,13 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times) /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { - error = verify_area(VERIFY_READ, times, sizeof(*times)); + error = get_user(newattrs.ia_atime, ×->actime); + if (!error) + error = get_user(newattrs.ia_mtime, ×->modtime); if (error) { iput(inode); return error; } - get_user(newattrs.ia_atime, ×->actime); - get_user(newattrs.ia_mtime, ×->modtime); newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; } else { if (current->fsuid != inode->i_uid && @@ -219,12 +219,10 @@ asmlinkage int sys_utimes(char * filename, struct timeval * utimes) newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (utimes) { struct timeval times[2]; - error = verify_area(VERIFY_READ, utimes, sizeof(times)); - if (error) { + if (copy_from_user(×, utimes, sizeof(times))) { iput(inode); - return error; - } - copy_from_user(×, utimes, sizeof(times)); + return -EFAULT; + } newattrs.ia_atime = times[0].tv_sec; newattrs.ia_mtime = times[1].tv_sec; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; diff --git a/fs/pipe.c b/fs/pipe.c index 2f3c30916dbb..d43e604cdf48 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -18,7 +18,11 @@ * Define this if you want SunOS compatibility wrt braindead * select behaviour on FIFO's. */ +#ifdef __sparc__ +#define FIFO_SUNOS_BRAINDAMAGE +#else #undef FIFO_SUNOS_BRAINDAMAGE +#endif /* We don't use the head/tail construction any more. Now we use the start/len*/ /* construction providing full use of PIPE_BUF (multiple of PAGE_SIZE) */ @@ -147,14 +151,9 @@ static long bad_pipe_w(struct inode * inode, struct file * filp, static int pipe_ioctl(struct inode *pino, struct file * filp, unsigned int cmd, unsigned long arg) { - int error; - switch (cmd) { case FIONREAD: - error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); - if (!error) - put_user(PIPE_SIZE(*pino),(int *) arg); - return error; + return put_user(PIPE_SIZE(*pino),(int *) arg); default: return -EINVAL; } diff --git a/fs/stat.c b/fs/stat.c index 0dc66d5383ca..612d4be40b57 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -20,7 +20,7 @@ * For backward compatibility? Maybe this should be moved * into arch/i386 instead? */ -static void cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf) +static int cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf) { struct __old_kernel_stat tmp; @@ -39,12 +39,12 @@ static void cp_old_stat(struct inode * inode, struct __old_kernel_stat * statbuf tmp.st_atime = inode->i_atime; tmp.st_mtime = inode->i_mtime; tmp.st_ctime = inode->i_ctime; - copy_to_user(statbuf,&tmp,sizeof(tmp)); + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } #endif -static void cp_new_stat(struct inode * inode, struct stat * statbuf) +static int cp_new_stat(struct inode * inode, struct stat * statbuf) { struct stat tmp; unsigned int blocks, indirect; @@ -99,7 +99,7 @@ static void cp_new_stat(struct inode * inode, struct stat * statbuf) tmp.st_blocks = inode->i_blocks; tmp.st_blksize = inode->i_blksize; } - copy_to_user(statbuf,&tmp,sizeof(tmp)); + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } #if !defined(__alpha__) && !defined(__sparc__) @@ -112,15 +112,12 @@ asmlinkage int sys_stat(char * filename, struct __old_kernel_stat * statbuf) struct inode * inode; int error; - error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); - if (error) - return error; error = namei(filename,&inode); if (error) return error; - cp_old_stat(inode,statbuf); + error = cp_old_stat(inode,statbuf); iput(inode); - return 0; + return error; } #endif @@ -129,15 +126,12 @@ asmlinkage int sys_newstat(char * filename, struct stat * statbuf) struct inode * inode; int error; - error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); - if (error) - return error; error = namei(filename,&inode); if (error) return error; - cp_new_stat(inode,statbuf); + error = cp_new_stat(inode,statbuf); iput(inode); - return 0; + return error; } #if !defined(__alpha__) && !defined(__sparc__) @@ -151,15 +145,12 @@ asmlinkage int sys_lstat(char * filename, struct __old_kernel_stat * statbuf) struct inode * inode; int error; - error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); - if (error) - return error; error = lnamei(filename,&inode); if (error) return error; - cp_old_stat(inode,statbuf); + error = cp_old_stat(inode,statbuf); iput(inode); - return 0; + return error; } #endif @@ -169,15 +160,12 @@ asmlinkage int sys_newlstat(char * filename, struct stat * statbuf) struct inode * inode; int error; - error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); - if (error) - return error; error = lnamei(filename,&inode); if (error) return error; - cp_new_stat(inode,statbuf); + error = cp_new_stat(inode,statbuf); iput(inode); - return 0; + return error; } #if !defined(__alpha__) && !defined(__sparc__) @@ -190,15 +178,10 @@ asmlinkage int sys_fstat(unsigned int fd, struct __old_kernel_stat * statbuf) { struct file * f; struct inode * inode; - int error; - error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); - if (error) - return error; if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode)) return -EBADF; - cp_old_stat(inode,statbuf); - return 0; + return cp_old_stat(inode,statbuf); } #endif @@ -207,15 +190,10 @@ asmlinkage int sys_newfstat(unsigned int fd, struct stat * statbuf) { struct file * f; struct inode * inode; - int error; - error = verify_area(VERIFY_WRITE,statbuf,sizeof (*statbuf)); - if (error) - return error; if (fd >= NR_OPEN || !(f=current->files->fd[fd]) || !(inode=f->f_inode)) return -EBADF; - cp_new_stat(inode,statbuf); - return 0; + return cp_new_stat(inode,statbuf); } asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz) diff --git a/fs/super.c b/fs/super.c index 8010ee272315..b5b66b6c650e 100644 --- a/fs/super.c +++ b/fs/super.c @@ -213,7 +213,7 @@ static int fs_index(const char * __name) static int fs_name(unsigned int index, char * buf) { struct file_system_type * tmp; - int err, len; + int len; tmp = file_systems; while (tmp && index > 0) { @@ -223,11 +223,7 @@ static int fs_name(unsigned int index, char * buf) if (!tmp) return -EINVAL; len = strlen(tmp->name) + 1; - err = verify_area(VERIFY_WRITE, buf, len); - if (err) - return err; - copy_to_user(buf, tmp->name, len); - return 0; + return copy_to_user(buf, tmp->name, len) ? -EFAULT : 0; } static int fs_maxindex(void) @@ -477,7 +473,6 @@ asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf) struct ustat tmp; struct statfs sbuf; unsigned long old_fs; - int error; s = get_super(to_kdev_t(dev)); if (s == NULL) @@ -486,10 +481,6 @@ asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf) if (!(s->s_op->statfs)) return -ENOSYS; - error = verify_area(VERIFY_WRITE,ubuf,sizeof(struct ustat)); - if (error) - return error; - old_fs = get_fs(); set_fs(get_ds()); s->s_op->statfs(s,&sbuf,sizeof(struct statfs)); @@ -499,8 +490,7 @@ asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf) tmp.f_tfree = sbuf.f_bfree; tmp.f_tinode = sbuf.f_ffree; - copy_to_user(ubuf,&tmp,sizeof(struct ustat)); - return 0; + return copy_to_user(ubuf,&tmp,sizeof(struct ustat)) ? -EFAULT : 0; } static struct super_block * read_super(kdev_t dev,const char *name,int flags, @@ -807,7 +797,10 @@ static int copy_mount_options (const void * data, unsigned long *where) if (!(page = __get_free_page(GFP_KERNEL))) { return -ENOMEM; } - copy_from_user((void *) page,data,i); + if (copy_from_user((void *) page,data,i)) { + free_page(page); + return -EFAULT; + } *where = page; return 0; } diff --git a/include/asm-i386/string.h b/include/asm-i386/string.h index b4a1e13928f1..9419161187ca 100644 --- a/include/asm-i386/string.h +++ b/include/asm-i386/string.h @@ -550,7 +550,9 @@ __asm__ __volatile__( "cmpl $-1,%2\n\t" "jne 1b\n" "3:\tsubl %1,%0" - :"=a" (__res):"c" (s),"d" (count)); + :"=a" (__res) + :"c" (s),"d" (count) + :"dx"); return __res; } /* end of additional stuff */ diff --git a/include/asm-i386/uaccess.h b/include/asm-i386/uaccess.h index 2c81e938ea83..ea9a1aad56e7 100644 --- a/include/asm-i386/uaccess.h +++ b/include/asm-i386/uaccess.h @@ -342,6 +342,33 @@ __constant_copy_from_user(void *to, const void *from, unsigned long n) return n; } +static inline unsigned long +__generic_copy_to_user_nocheck(void *to, const void *from, unsigned long n) +{ + __copy_user(to,from,n); + return n; +} + +static inline unsigned long +__constant_copy_to_user_nocheck(void *to, const void *from, unsigned long n) +{ + __constant_copy_user(to,from,n); + return n; +} + +static inline unsigned long +__generic_copy_from_user_nocheck(void *to, const void *from, unsigned long n) +{ + __copy_user(to,from,n); + return n; +} + +static inline unsigned long +__constant_copy_from_user_nocheck(void *to, const void *from, unsigned long n) +{ + __constant_copy_user(to,from,n); + return n; +} #define copy_to_user(to,from,n) \ (__builtin_constant_p(n) ? \ @@ -354,11 +381,22 @@ __constant_copy_from_user(void *to, const void *from, unsigned long n) __generic_copy_from_user((to),(from),(n))) +#define __copy_to_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_to_user_nocheck((to),(from),(n)) : \ + __generic_copy_to_user_nocheck((to),(from),(n))) + +#define __copy_from_user(to,from,n) \ + (__builtin_constant_p(n) ? \ + __constant_copy_from_user_nockeck((to),(from),(n)) : \ + __generic_copy_from_user_nocheck((to),(from),(n))) + + /* * Zero Userspace */ -#define __clear_user(addr,size) \ +#define __do_clear_user(addr,size) \ __asm__ __volatile__( \ "0: rep; stosl\n" \ " movl %1,%0\n" \ @@ -380,7 +418,14 @@ static inline unsigned long clear_user(void *to, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) - __clear_user(to, n); + __do_clear_user(to, n); + return n; +} + +static inline unsigned long +__clear_user(void *to, unsigned long n) +{ + __do_clear_user(to, n); return n; } @@ -389,7 +434,7 @@ clear_user(void *to, unsigned long n) * Copy a null terminated string from userspace. */ -#define __strncpy_from_user(dst,src,count,res) \ +#define __do_strncpy_from_user(dst,src,count,res) \ __asm__ __volatile__( \ " testl %1,%1\n" \ " jz 2f\n" \ @@ -412,12 +457,20 @@ clear_user(void *to, unsigned long n) : "i"(-EFAULT), "0"(count), "1"(count), "S"(src), "D"(dst) \ : "si", "di", "ax", "memory") +static inline long +__strncpy_from_user(char *dst, const char *src, long count) +{ + long res; + __do_strncpy_from_user(dst, src, count, res); + return res; +} + static inline long strncpy_from_user(char *dst, const char *src, long count) { long res = -EFAULT; if (access_ok(VERIFY_READ, src, 1)) - __strncpy_from_user(dst, src, count, res); + __do_strncpy_from_user(dst, src, count, res); return res; } diff --git a/include/linux/fddidevice.h b/include/linux/fddidevice.h index bb0b298ee2e9..a093ccf71d9a 100644 --- a/include/linux/fddidevice.h +++ b/include/linux/fddidevice.h @@ -25,18 +25,15 @@ #include #ifdef __KERNEL__ -extern int fddi_header(struct sk_buff *skb, - struct device *dev, - unsigned short type, - void *daddr, - void *saddr, - unsigned len); -extern int fddi_rebuild_header(void *buff, - struct device *dev, - unsigned long dest, - struct sk_buff *skb); +extern int fddi_header(struct sk_buff *skb, + struct device *dev, + unsigned short type, + void *daddr, + void *saddr, + unsigned len); +extern int fddi_rebuild_header(struct sk_buff *skb); extern unsigned short fddi_type_trans(struct sk_buff *skb, - struct device *dev); + struct device *dev); #endif #endif /* _LINUX_FDDIDEVICE_H */ diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index b8126a9f20fd..478c8503c83a 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -4,35 +4,6 @@ #include #include -/* - * IPv6 fixed header - */ - -struct ipv6hdr { -#if defined(__LITTLE_ENDIAN_BITFIELD) - __u8 priority:4, - version:4; -#elif defined(__BIG_ENDIAN_BITFIELD) - __u8 version:4, - priority:4; -#else -#error "Please fix " -#endif - __u8 flow_lbl[3]; - - __u16 payload_len; - __u8 nexthdr; - __u8 hop_limit; - - struct in6_addr saddr; - struct in6_addr daddr; -}; - -struct in6_ifreq { - struct in6_addr addr; - __u32 prefix_len; - char devname[8]; -}; /* * Advanced API @@ -46,6 +17,13 @@ struct in6_pktinfo { int ipi6_ifindex; }; + +struct in6_ifreq { + struct in6_addr ifr6_addr; + __u32 ifr6_prefixlen; + int ifr6_ifindex; +}; + #define IPV6_SRCRT_STRICT 0x01 /* this hop must be a neighbor */ #define IPV6_SRCRT_TYPE_0 0 /* IPv6 type 0 Routing Header */ @@ -78,6 +56,30 @@ struct rt0_hdr { #ifdef __KERNEL__ +/* + * IPv6 fixed header + */ + +struct ipv6hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + __u8 priority:4, + version:4; +#elif defined(__BIG_ENDIAN_BITFIELD) + __u8 version:4, + priority:4; +#else +#error "Please fix " +#endif + __u8 flow_lbl[3]; + + __u16 payload_len; + __u8 nexthdr; + __u8 hop_limit; + + struct in6_addr saddr; + struct in6_addr daddr; +}; + /* * The length of this struct cannot be greater than the length of * the proto_priv field in a sk_buff which is currently diff --git a/include/linux/lapb.h b/include/linux/lapb.h new file mode 100644 index 000000000000..0d1ff4e7977e --- /dev/null +++ b/include/linux/lapb.h @@ -0,0 +1,56 @@ +/* + * These are the public elements of the Linux LAPB module. + */ + +#ifndef LAPB_KERNEL_H +#define LAPB_KERNEL_H + +#define LAPB_OK 0 +#define LAPB_BADTOKEN 1 +#define LAPB_INVALUE 2 +#define LAPB_CONNECTED 3 +#define LAPB_NOTCONNECTED 4 +#define LAPB_REFUSED 5 +#define LAPB_TIMEDOUT 6 +#define LAPB_NOMEM 7 + +#define LAPB_STANDARD 0x00 +#define LAPB_EXTENDED 0x01 + +#define LAPB_SLP 0x00 +#define LAPB_MLP 0x02 + +#define LAPB_DTE 0x00 +#define LAPB_DCE 0x04 + +struct lapb_register_struct { + void (*connect_confirmation)(void *token, int reason); + void (*connect_indication)(void *token, int reason); + void (*disconnect_confirmation)(void *token, int reason); + void (*disconnect_indication)(void *token, int reason); + void (*data_indication)(void *token, struct sk_buff *skb); + void (*data_transmit)(void *token, struct sk_buff *skb); +}; + +struct lapb_parms_struct { + unsigned int t1; + unsigned int t1timer; + unsigned int t2; + unsigned int t2timer; + unsigned int n2; + unsigned int n2count; + unsigned int window; + unsigned int state; + unsigned int mode; +}; + +extern int lapb_register(void *token, struct lapb_register_struct *callbacks); +extern int lapb_unregister(void *token); +extern int lapb_getparms(void *token, struct lapb_parms_struct *parms); +extern int lapb_setparms(void *token, struct lapb_parms_struct *parms); +extern int lapb_connect_request(void *token); +extern int lapb_disconnect_request(void *token); +extern int lapb_data_request(void *token, struct sk_buff *skb); +extern int lapb_data_received(void *token, struct sk_buff *skb); + +#endif diff --git a/include/linux/lapbether.h b/include/linux/lapbether.h new file mode 100644 index 000000000000..b528c37d3982 --- /dev/null +++ b/include/linux/lapbether.h @@ -0,0 +1,19 @@ +#ifndef __LAPBETHER_H +#define __LAPBETHER_H + +/* + * Defines for the LAPBETHER pseudo device driver + */ + +#ifndef __LINUX_IF_ETHER_H +#include +#endif + +#define SIOCSLAPBETHADDR (SIOCDEVPRIVATE+1) + +struct lapbeth_ethaddr { + unsigned char destination[ETH_ALEN]; + unsigned char accept[ETH_ALEN]; +}; + +#endif diff --git a/include/linux/mm.h b/include/linux/mm.h index 07fa76120a27..aa13c7773e20 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -256,7 +256,7 @@ extern void clear_page_tables(struct task_struct * tsk); extern int new_page_tables(struct task_struct * tsk); extern int copy_page_tables(struct task_struct * to); -extern int zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size); +extern void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size); extern int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma); extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, pgprot_t prot); extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t prot); diff --git a/include/linux/mtio.h b/include/linux/mtio.h index 22cec7aaa5b1..2e20465a65df 100644 --- a/include/linux/mtio.h +++ b/include/linux/mtio.h @@ -245,4 +245,7 @@ struct mtconfiginfo { #define MT_ST_DEF_COMPRESSION (MT_ST_DEF_OPTIONS | 0x200000) #define MT_ST_DEF_DRVBUFFER (MT_ST_DEF_OPTIONS | 0x300000) +/* The offset for the arguments for the special HP changer load command. */ +#define MT_ST_HPLOADER_OFFSET 10000 + #endif /* _LINUX_MTIO_H */ diff --git a/include/linux/net_alias.h b/include/linux/net_alias.h index 67a9f9beaa2c..c31df3a36c32 100644 --- a/include/linux/net_alias.h +++ b/include/linux/net_alias.h @@ -17,6 +17,7 @@ #ifndef _NET_ALIAS_H #define _NET_ALIAS_H +#include #include #include #include @@ -33,20 +34,20 @@ struct net_alias_type; /* - * main alias structure - * note that *defines* dev & devname + * Main alias structure + * Note that *defines* dev & devname. */ struct net_alias { - struct device dev; /* alias device defn*/ - char name[IFNAMSIZ]; /* device name defn */ - unsigned hash; /* my hash value: for quick rehash */ - unsigned slot; /* slot number */ - void *data; /* private data */ - struct device *main_dev; /* pointer to main device */ - struct net_alias_type *nat; /* alias type object bound */ - struct net_alias *next; /* next alias (hashed linked list) */ + struct device dev; /* alias device defn*/ + char name[IFNAMSIZ]; /* device name defn */ + unsigned hash; /* my hash value: for quick rehash */ + unsigned slot; /* slot number */ + void *data; /* private data */ + struct device *main_dev; /* pointer to main device */ + struct net_alias_type *nat; /* alias type object bound */ + struct net_alias *next; /* next alias (hashed linked list) */ }; @@ -57,59 +58,102 @@ struct net_alias struct net_alias_info { - int n_aliases; /* num aliases */ - struct device *taildev; /* my last (alias) device */ - struct net_alias *hash_tab[16]; /* hashed alias table */ + int n_aliases; /* num aliases */ + struct device *taildev; /* my last (alias) device */ + struct net_alias *hash_tab[16]; /* hashed alias table */ }; /* - * net_alias_type class - * declares a generic (AF_ independent) structure that will - * manage generic to family-specific behavior. + * net_alias_type class + * Declares a generic (AF_ independent) structure that will + * manage generic to family-specific behavior. */ struct net_alias_type { - int type; /* aliasing type: address family */ - int n_attach; /* number of aliases attached */ - char name[16]; /* af_name */ - __u32 (*get_addr32) /* get __u32 addr 'representation'*/ - (struct net_alias_type *this, struct sockaddr*); - int (*dev_addr_chk) /* address checking func: */ - (struct net_alias_type *this, struct device *, struct sockaddr *); - struct device * (*dev_select) /* closest alias selector*/ - (struct net_alias_type *this, struct device *, struct sockaddr *sa); - int (*alias_init_1) /* called after alias creation: */ - (struct net_alias_type *this,struct net_alias *alias, struct sockaddr *sa); - int (*alias_done_1) /* called before alias deletion */ - (struct net_alias_type *this, struct net_alias *alias); - int (*alias_print_1) - (struct net_alias_type *this, struct net_alias *alias, char *buf, int len); - struct net_alias_type *next; /* link */ + int type; /* aliasing type: address family */ + int n_attach; /* number of aliases attached */ + char name[16]; /* af_name */ + __u32 (*get_addr32) /* get __u32 addr 'representation'*/ + (struct net_alias_type *this, struct sockaddr*); + int (*dev_addr_chk) /* address checking func: */ + (struct net_alias_type *this, struct device *, struct sockaddr *); + struct device * (*dev_select) /* closest alias selector*/ + (struct net_alias_type *this, struct device *, struct sockaddr *sa); + int (*alias_init_1) /* called after alias creation: */ + (struct net_alias_type *this,struct net_alias *alias, struct sockaddr *sa); + int (*alias_done_1) /* called before alias deletion */ + (struct net_alias_type *this, struct net_alias *alias); + int (*alias_print_1) + (struct net_alias_type *this, struct net_alias *alias, char *buf, int len); + struct net_alias_type *next; /* link */ }; /* - * is dev an alias? + * is dev an alias? */ -static __inline__ int -net_alias_is(struct device *dev) +#ifdef CONFIG_NET_ALIAS + +extern __inline__ int net_alias_is(struct device *dev) +{ + return (dev->my_alias != NULL); +} + +/* + * Does dev have aliases? + */ + +extern __inline__ int net_alias_has(struct device *dev) +{ + return (dev->alias_info != NULL); +} + +/* + * Returns MY 'true' main device + * intended for alias devices + */ + +extern __inline__ struct device *net_alias_main_dev(struct device *dev) { - return (dev->my_alias != NULL); + return (net_alias_is(dev))? dev->my_alias->main_dev : dev; } /* - * does dev have aliases? + * Returns NEXT 'true' device + * intended for true devices + */ + +extern __inline__ struct device *net_alias_nextdev(struct device *dev) +{ + return (dev->alias_info)? dev->alias_info->taildev->next : dev->next; +} + +/* + * Sets NEXT 'true' device + * Intended for main devices (treat main device as block: dev+aliases). */ -static __inline__ int -net_alias_has(struct device *dev) +extern __inline__ struct device *net_alias_nextdev_set(struct device *dev, struct device *nextdev) { - return (dev->alias_info != NULL); + struct device *pdev = dev; + if (net_alias_has(dev)) + { + pdev = dev->alias_info->taildev; /* point to last dev alias */ + } + pdev->next = nextdev; + return nextdev; } +#else + +#define net_alias_has(dev) (0) +#define net_alias_is(dev) (0) +#define net_alias_main_dev(dev) (dev) +#endif + extern void net_alias_init(void); @@ -129,44 +173,5 @@ extern struct device * net_alias_dev_rcv_sel(struct device *main_dev, struct soc extern struct device * net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst); -/* - * returns MY 'true' main device - * intended for alias devices - */ - -static __inline__ struct device *net_alias_main_dev(struct device *dev) -{ - return (net_alias_is(dev))? dev->my_alias->main_dev : dev; -} - - -/* - * returns NEXT 'true' device - * intended for true devices - */ - -static __inline__ struct device * -net_alias_nextdev(struct device *dev) -{ - return (dev->alias_info)? dev->alias_info->taildev->next : dev->next; -} - - -/* - * sets NEXT 'true' device - * intended for main devices (treat main device as block: dev+aliases). - */ - -static __inline__ struct device * -net_alias_nextdev_set(struct device *dev, struct device *nextdev) -{ - struct device *pdev = dev; - if (net_alias_has(dev)) - { - pdev = dev->alias_info->taildev; /* point to last dev alias */ - } - pdev->next = nextdev; - return nextdev; -} #endif /* _NET_ALIAS_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9e1722b6799d..4a99e5062715 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -10,8 +10,8 @@ * Authors: Ross Biro, * Fred N. van Kempen, * Corey Minyard - * Donald J. Becker, - * Alan Cox, + * Donald J. Becker, + * Alan Cox, * Bjorn Ekwall. * * This program is free software; you can redistribute it and/or @@ -28,10 +28,18 @@ #include #include -/* for future expansion when we will have different priorities. */ -#define DEV_NUMBUFFS 3 -#define MAX_ADDR_LEN 7 +/* + * For future expansion when we will have different priorities. + */ + +#define DEV_NUMBUFFS 3 /* Number of queues per device */ +#define MAX_ADDR_LEN 7 /* Largest hardware address length */ +/* + * Compute the worst case header length according to the protocols + * used. + */ + #if !defined(CONFIG_AX25) && !defined(CONFIG_AX25_MODULE) && !defined(CONFIG_TR) #define LL_MAX_HEADER 32 #else @@ -79,11 +87,11 @@ struct dev_mc_list struct hh_cache { - struct hh_cache *hh_next; - int hh_refcnt; /* number of users */ + struct hh_cache *hh_next; /* Next entry */ + int hh_refcnt; /* number of users */ unsigned short hh_type; /* protocol identifier, f.e ETH_P_IP */ - char hh_uptodate; /* hh_data is valid */ - /* cached hardware header; allow for machine alignment needs. */ + char hh_uptodate; /* hh_data is valid */ + /* cached hardware header; allow for machine alignment needs. */ unsigned long hh_data[16/sizeof(unsigned long)]; }; @@ -140,6 +148,12 @@ struct device unsigned char if_port; /* Selectable AUI, TP,..*/ unsigned char dma; /* DMA channel */ + /* + * FIXME: + * The description 'enet_statistics' is misleading. We + * should change this. + */ + struct enet_statistics* (*get_stats)(struct device *dev); #ifdef CONFIG_NET_RADIO struct iw_statistics* (*get_wireless_stats)(struct device *dev); @@ -179,8 +193,8 @@ struct device int mc_count; /* Number of installed mcasts */ struct ip_mc_list *ip_mc_list; /* IP multicast filter chain */ - unsigned ip_flags; - __u8 hash; + unsigned ip_flags; /* IP layer control flags */ + __u8 hash; /* Hashing index */ __u32 tx_queue_len; /* Max frames per queue allowed */ /* For load balancing driver pair support */ @@ -229,12 +243,13 @@ struct device }; -struct packet_type { - unsigned short type; /* This is really htons(ether_type). */ - struct device *dev; +struct packet_type +{ + unsigned short type; /* This is really htons(ether_type). */ + struct device *dev; /* NULL is wildcarded here */ int (*func) (struct sk_buff *, struct device *, struct packet_type *); - void *data; + void *data; /* Private to the packet type */ struct packet_type *next; }; @@ -242,16 +257,14 @@ struct packet_type { #include #include -/* Used by dev_rint */ -#define IN_SKBUFF 1 - -extern struct device loopback_dev; -extern struct device *dev_base; -extern struct packet_type *ptype_base[16]; +extern struct device loopback_dev; /* The loopback */ +extern struct device *dev_base; /* All devices */ +extern struct packet_type *ptype_base[16]; /* Hashed types */ /* NOTE: move to INET specific header; __ip_chk_addr is deprecated, do not use if it's possible. */ + extern int __ip_chk_addr(unsigned long addr); extern struct device *ip_dev_find(unsigned long addr, char *name); /* This is the wrong place but it'll do for the moment */ @@ -311,7 +324,7 @@ extern __inline__ void dev_lock_wait(void) /* NOTE: about to be replaced with if_index */ -static __inline__ __u8 dev_hash_name(char *name) +extern __inline__ __u8 dev_hash_name(char *name) { __u8 hash = 0; __u8 *p; @@ -320,7 +333,7 @@ static __inline__ __u8 dev_hash_name(char *name) return hash; } -static __inline__ __u8 dev_hash_mc_name(char *name) +extern __inline__ __u8 dev_hash_mc_name(char *name) { int i; __u8 hash = 0; @@ -334,6 +347,21 @@ static __inline__ __u8 dev_hash_mc_name(char *name) return hash; } +/* + * Buffer initialisation function. This used to appear in all the + * drivers but is now an inline in case we ever want to change the + * schemes used. + */ + +extern __inline__ void dev_init_buffers(struct device *dev) +{ + int i; + for(i=0;ibuffs[i]); + } +} + /* These functions live elsewhere (drivers/net/net_init.c, but related) */ diff --git a/include/linux/netrom.h b/include/linux/netrom.h index a7557ccb367f..8732761bd007 100644 --- a/include/linux/netrom.h +++ b/include/linux/netrom.h @@ -33,6 +33,8 @@ struct nr_route_struct { char mnemonic[7]; ax25_address neighbour; unsigned int obs_count; + unsigned int ndigis; + ax25_address digipeaters[AX25_MAX_DIGIS]; }; struct nr_ctl_struct { diff --git a/include/linux/tpqic02.h b/include/linux/tpqic02.h index 9f4cb8a58254..fe13ad6e013a 100644 --- a/include/linux/tpqic02.h +++ b/include/linux/tpqic02.h @@ -1,4 +1,4 @@ -/* $Id: tpqic02.h,v 0.25 1994/07/21 02:16:30 root Exp root $ +/* $Id: tpqic02.h,v 1.5 1996/12/14 23:01:38 root Exp root $ * * Include file for QIC-02 driver for Linux. * @@ -12,7 +12,7 @@ #include -#if defined(CONFIG_QIC02_TAPE) || defined (CONFIG_QIC02_TAPE_MODULE) +#if CONFIG_QIC02_TAPE || CONFIG_QIC02_TAPE_MODULE /* need to have QIC02_TAPE_DRIVE and QIC02_TAPE_IFC expand to something */ #include @@ -28,6 +28,9 @@ * * Support for Mountain controllers was added by Erik Jacobson * and severely hacked by me. -- hhb + * + * Support for Emerald controllers by Alan Bain + * with more hacks by me. -- hhb */ #define WANGTEK 1 /* don't know about Wangtek QIC-36 */ #define EVEREX (WANGTEK+1) /* I heard *some* of these are identical */ @@ -39,6 +42,11 @@ #define ARCHIVE_SC499 ARCHIVE /* SC402 and SC499R should be identical */ #define MOUNTAIN 5 /* Mountain Computer Interface */ +#define EMERALD 6 /* Emerald Interface card */ + + + +#define QIC02_TAPE_PORT_RANGE 8 /* number of IO locations to reserve */ /*********** START OF USER CONFIGURABLE SECTION ************/ @@ -205,6 +213,7 @@ #define WT_QIC02_DATA_PORT (QIC02_TAPE_PORT+1) /* status register bits (Active LOW!) */ +#define WT_QIC02_STAT_POLARITY 0 #define WT_QIC02_STAT_READY 0x01 #define WT_QIC02_STAT_EXCEPTION 0x02 #define WT_QIC02_STAT_MASK (WT_QIC02_STAT_READY|WT_QIC02_STAT_EXCEPTION) @@ -221,6 +230,31 @@ #define WT_CTL_DMA3 0x10 /* enable dma chan3 */ #define WT_CTL_DMA1 0x08 /* enable dma chan1 or chan2 */ +/* EMERALD interface card specifics + * Much like Wangtek, only different polarity and bit locations + */ +#define EMR_QIC02_STAT_PORT (QIC02_TAPE_PORT) +#define EMR_QIC02_CTL_PORT (QIC02_TAPE_PORT) +#define EMR_QIC02_CMD_PORT (QIC02_TAPE_PORT+1) +#define EMR_QIC02_DATA_PORT (QIC02_TAPE_PORT+1) + +/* status register bits (Active High!) */ +#define EMR_QIC02_STAT_POLARITY 1 +#define EMR_QIC02_STAT_READY 0x01 +#define EMR_QIC02_STAT_EXCEPTION 0x02 +#define EMR_QIC02_STAT_MASK (EMR_QIC02_STAT_READY|EMR_QIC02_STAT_EXCEPTION) + +#define EMR_QIC02_STAT_RESETMASK 0x07 +#define EMR_QIC02_STAT_RESETVAL (EMR_QIC02_STAT_RESETMASK & ~EMR_QIC02_STAT_EXCEPTION) + +/* controller register (QIC02_CTL_PORT) bits */ +#define EMR_QIC02_CTL_RESET 0x02 +#define EMR_QIC02_CTL_REQUEST 0x04 +#define EMR_CTL_ONLINE 0x01 +#define EMR_CTL_CMDOFF 0xC0 + +#define EMR_CTL_DMA3 0x10 /* enable dma chan3 */ +#define EMR_CTL_DMA1 0x08 /* enable dma chan1 or chan2 */ @@ -234,6 +268,7 @@ #define AR_RESET_DMA_PORT (QIC02_TAPE_PORT+3) /* STAT port bits */ +#define AR_QIC02_STAT_POLARITY 0 #define AR_STAT_IRQF 0x80 /* active high, interrupt request flag */ #define AR_QIC02_STAT_READY 0x40 /* active low */ #define AR_QIC02_STAT_EXCEPTION 0x20 /* active low */ @@ -266,6 +301,7 @@ #define MTN_W_DMA_WRITE_PORT (QIC02_TAPE_PORT+3) /* STAT port bits */ +#define MTN_QIC02_STAT_POLARITY 0 #define MTN_QIC02_STAT_READY 0x02 /* active low */ #define MTN_QIC02_STAT_EXCEPTION 0x04 /* active low */ #define MTN_QIC02_STAT_MASK (MTN_QIC02_STAT_READY|MTN_QIC02_STAT_EXCEPTION) @@ -274,7 +310,7 @@ #define MTN_QIC02_STAT_RESETMASK 0x07 /* check RDY,EXC,DMADONE */ #define MTN_QIC02_STAT_RESETVAL ((MTN_QIC02_STAT_RESETMASK & ~MTN_QIC02_STAT_EXCEPTION) | MTN_STAT_DMADONE) - /* CTL port bits */ +/* CTL port bits */ #define MTN_QIC02_CTL_RESET_NOT 0x80 /* drive reset, active low */ #define MTN_QIC02_CTL_RESET 0x80 /* Fodder #definition to keep gcc happy */ @@ -294,6 +330,7 @@ # define QIC02_TAPE_DEBUG (qic02_tape_debug) # if QIC02_TAPE_IFC == WANGTEK +# define QIC02_STAT_POLARITY WT_QIC02_STAT_POLARITY # define QIC02_STAT_PORT WT_QIC02_STAT_PORT # define QIC02_CTL_PORT WT_QIC02_CTL_PORT # define QIC02_CMD_PORT WT_QIC02_CMD_PORT @@ -320,7 +357,36 @@ # error Unsupported or incorrect DMA configuration. # endif +# elif QIC02_TAPE_IFC == EMERALD +# define QIC02_STAT_POLARITY EMR_QIC02_STAT_POLARITY +# define QIC02_STAT_PORT EMR_QIC02_STAT_PORT +# define QIC02_CTL_PORT EMR_QIC02_CTL_PORT +# define QIC02_CMD_PORT EMR_QIC02_CMD_PORT +# define QIC02_DATA_PORT EMR_QIC02_DATA_PORT + +# define QIC02_STAT_READY EMR_QIC02_STAT_READY +# define QIC02_STAT_EXCEPTION EMR_QIC02_STAT_EXCEPTION +# define QIC02_STAT_MASK EMR_QIC02_STAT_MASK +# define QIC02_STAT_RESETMASK EMR_QIC02_STAT_RESETMASK +# define QIC02_STAT_RESETVAL EMR_QIC02_STAT_RESETVAL + +# define QIC02_CTL_RESET EMR_QIC02_CTL_RESET +# define QIC02_CTL_REQUEST EMR_QIC02_CTL_REQUEST + +# if QIC02_TAPE_DMA == 3 +# ifdef QIC02_TAPE_DMA3_FIX +# define EMR_CTL_DMA EMR_CTL_DMA1 +# else +# define EMR_CTL_DMA EMR_CTL_DMA3 +# endif +# elif QIC02_TAPE_DMA == 1 +# define EMR_CTL_DMA EMR_CTL_DMA1 +# else +# error Unsupported or incorrect DMA configuration. +# endif + # elif QIC02_TAPE_IFC == ARCHIVE +# define QIC02_STAT_POLARITY AR_QIC02_STAT_POLARITY # define QIC02_STAT_PORT AR_QIC02_STAT_PORT # define QIC02_CTL_PORT AR_QIC02_CTL_PORT # define QIC02_CMD_PORT AR_QIC02_CMD_PORT @@ -340,6 +406,7 @@ # endif # elif QIC02_TAPE_IFC == MOUNTAIN +# define QIC02_STAT_POLARITY MTN_QIC02_STAT_POLARITY # define QIC02_STAT_PORT MTN_QIC02_STAT_PORT # define QIC02_CTL_PORT MTN_QIC02_CTL_PORT # define QIC02_CMD_PORT MTN_QIC02_CMD_PORT @@ -393,6 +460,7 @@ # define QIC02_CMD_PORT (qic02_tape_ccb.port_cmd) # define QIC02_DATA_PORT (qic02_tape_ccb.port_data) +# define QIC02_STAT_POLARITY (qic02_tape_ccb.stat_polarity) # define QIC02_STAT_READY (qic02_tape_ccb.stat_ready) # define QIC02_STAT_EXCEPTION (qic02_tape_ccb.stat_exception) # define QIC02_STAT_MASK (qic02_tape_ccb.stat_mask) @@ -617,10 +685,10 @@ typedef char flag; /* NR_BLK_BUF is a `tuneable parameter'. If you're really low on * kernel space, you could decrease it to 1, or if you got a very - * slow machine, you could increase it up to 128 blocks. Less kernel + * slow machine, you could increase it up to 127 blocks. Less kernel * buffer blocks result in more context-switching. */ -#define NR_BLK_BUF 20 /* max 128 blocks */ +#define NR_BLK_BUF 20 /* max 127 blocks */ #define TAPE_BLKSIZE 512 /* streamer tape block size (fixed) */ #define TPQBUF_SIZE (TAPE_BLKSIZE*NR_BLK_BUF) /* buffer size */ @@ -642,6 +710,7 @@ struct qic02_ccb { unsigned short port_data; /* Data port address */ /* status register bits */ + unsigned short stat_polarity; /* invert status bits or not */ unsigned short stat_ready; /* drive ready */ unsigned short stat_exception; /* drive signals exception */ unsigned short stat_mask; @@ -656,8 +725,12 @@ struct qic02_ccb { unsigned short dma_enable_value; }; - +#if MODULE +static int qic02_tape_init(void); +#else extern int qic02_tape_init(void); /* for mem.c */ +#endif + #endif /* CONFIG_QIC02_TAPE */ diff --git a/include/linux/tty.h b/include/linux/tty.h index 0c03eb5e55d5..1483f40d1860 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -294,7 +294,7 @@ extern int cy_init(void); extern int stl_init(void); extern int stli_init(void); extern int riscom8_init(void); -extern int esp_init(void); +extern int espserial_init(void); extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device, const char *routine); diff --git a/include/net/lapb.h b/include/net/lapb.h new file mode 100644 index 000000000000..e974dc27686b --- /dev/null +++ b/include/net/lapb.h @@ -0,0 +1,121 @@ +#ifndef _LAPB_H +#define _LAPB_H +#include + +#define LAPB_SLOWHZ 10 /* Run timing at 1/10 second */ + +#define LAPB_HEADER_LEN 20 /* LAPB over Ethernet + a bit more */ + +#define LAPB_ACK_PENDING_CONDITION 0x01 +#define LAPB_REJECT_CONDITION 0x02 +#define LAPB_PEER_RX_BUSY_CONDITION 0x04 + +/* Control field templates */ +#define LAPB_I 0x00 /* Information frames */ +#define LAPB_S 0x01 /* Supervisory frames */ +#define LAPB_U 0x03 /* Unnumbered frames */ + +#define LAPB_RR 0x01 /* Receiver ready */ +#define LAPB_RNR 0x05 /* Receiver not ready */ +#define LAPB_REJ 0x09 /* Reject */ + +#define LAPB_SABM 0x2F /* Set Asynchronous Balanced Mode */ +#define LAPB_SABME 0x6F /* Set Asynchronous Balanced Mode Extended */ +#define LAPB_DISC 0x43 /* Disconnect */ +#define LAPB_DM 0x0F /* Disconnected mode */ +#define LAPB_UA 0x63 /* Unnumbered acknowledge */ +#define LAPB_FRMR 0x87 /* Frame reject */ + +#define LAPB_ILLEGAL 0x100 /* Impossible to be a real frame type */ + +#define LAPB_SPF 0x10 /* Poll/final bit for standard LAPB */ +#define LAPB_EPF 0x01 /* Poll/final bit for extended LAPB */ + +#define LAPB_POLLOFF 0 +#define LAPB_POLLON 1 + +/* LAPB C-bit */ +#define LAPB_COMMAND 1 +#define LAPB_RESPONSE 2 + +#define LAPB_ADDR_A 0x03 +#define LAPB_ADDR_B 0x01 +#define LAPB_ADDR_C 0x0F +#define LAPB_ADDR_D 0x07 + +/* Define Link State constants. */ +#define LAPB_STATE_0 0 +#define LAPB_STATE_1 1 +#define LAPB_STATE_2 2 +#define LAPB_STATE_3 3 +#define LAPB_STATE_4 4 + +#define LAPB_DEFAULT_MODE (LAPB_STANDARD | LAPB_SLP | LAPB_DTE) +#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */ +#define LAPB_DEFAULT_T1 (5 * LAPB_SLOWHZ) /* T1=5s */ +#define LAPB_DEFAULT_T2 (1 * LAPB_SLOWHZ) /* T2=1s */ +#define LAPB_DEFAULT_N2 20 /* N2=20 */ + +#define LAPB_SMODULUS 8 +#define LAPB_EMODULUS 128 + +typedef struct lapb_cb { + struct lapb_cb *next; + void *token; + unsigned int mode; + unsigned char state; + unsigned short vs, vr, va; + unsigned char condition; + unsigned short n2, n2count; + unsigned short t1, t2; + unsigned short t1timer, t2timer; + struct sk_buff_head write_queue; + struct sk_buff_head ack_queue; + unsigned char window; + struct timer_list timer; + struct lapb_register_struct callbacks; +} lapb_cb; + +/* lapb_iface.c */ +extern lapb_cb *lapb_tokentostruct(void *); +extern void lapb_connect_confirmation(lapb_cb *, int); +extern void lapb_connect_indication(lapb_cb *, int); +extern void lapb_disconnect_confirmation(lapb_cb *, int); +extern void lapb_disconnect_indication(lapb_cb *, int); +extern int lapb_data_indication(lapb_cb *, struct sk_buff *); +extern int lapb_data_transmit(lapb_cb *, struct sk_buff *); + +/* lapb_in.c */ + +/* lapb_out.c */ +extern void lapb_kick(lapb_cb *); +extern void lapb_transmit_buffer(lapb_cb *, struct sk_buff *, int); +extern void lapb_nr_error_recovery(lapb_cb *); +extern void lapb_establish_data_link(lapb_cb *); +extern void lapb_transmit_enquiry(lapb_cb *); +extern void lapb_enquiry_response(lapb_cb *); +extern void lapb_timeout_response(lapb_cb *); +extern void lapb_check_iframes_acked(lapb_cb *, unsigned short); +extern void lapb_check_need_response(lapb_cb *, int, int); + +/* lapb_subr.c */ +extern void lapb_clear_queues(lapb_cb *); +extern void lapb_frames_acked(lapb_cb *, unsigned short); +extern void lapb_requeue_frames(lapb_cb *); +extern int lapb_validate_nr(lapb_cb *, unsigned short); +extern int lapb_decode(lapb_cb *, struct sk_buff *, int *, int *, int *, int *); +extern void lapb_send_control(lapb_cb *, int, int, int); + +/* lapb_timer.c */ +extern void lapb_set_timer(lapb_cb *); + +/* + * Debug levels. + * 0 = Off + * 1 = State Changes + * 2 = Packets I/O and State Changes + * 3 = Hex dumps, Packets I/O and State Changes. + */ +#define LAPB_DEBUG 2 + +#endif diff --git a/include/net/lapbcall.h b/include/net/lapbcall.h new file mode 100644 index 000000000000..825e7f2c7ed7 --- /dev/null +++ b/include/net/lapbcall.h @@ -0,0 +1,2 @@ +/* Separate to keep compilation of protocols.c simpler */ +extern void lapb_proto_init(struct net_proto *pro); diff --git a/include/net/llc.h b/include/net/llc.h index 8784b62b2929..37abe50af9ee 100644 --- a/include/net/llc.h +++ b/include/net/llc.h @@ -5,34 +5,6 @@ typedef struct llc_struct llc; typedef struct llc_struct *llcptr; -/* - * LLC operations object. - */ - -typedef struct -{ - void (* data_indication_ep) (llcptr llc, struct sk_buff *skb); - /* unit data returns 0 to keep the data 1 to free it */ - int (* unit_data_indication_ep) (llcptr llc, int ll, char *xid_data); - void (* connect_indication_ep) (llcptr llc); - void (* connect_confirm_ep) (llcptr llc); - void (* data_connect_indication_ep) (llcptr llc); - void (* data_connect_confirm_ep) (llcptr llc); - void (* disconnect_indication_ep) (llcptr llc); - void (* disconnect_confirm_ep) (llcptr llc); - void (* reset_confirm_ep) (llcptr llc, char lr); - void (* reset_indication_ep) (llcptr llc, char lr); -#define LOCAL 0 -#define REMOTE 1 - void (* xid_indication_ep) (llcptr llc, int ll, char *xid_data); - void (* test_indication_ep) (llcptr llc, int ll, char *test_data); - void (* report_status_ep) (llcptr llc, char status); -#define FRMR_RECEIVED 0 -#define FRMR_SENT 1 -#define REMOTE_BUSY 2 -#define REMOTE_NOT_BUSY 3 -} llc_ops; - /* * LLC private data area structure. */ @@ -84,10 +56,10 @@ struct llc_struct struct timer_list tl[4]; /* - * Client entry points, called by the LLC + * Client entry point, called by the LLC. */ - llc_ops *ops; + void (*llc_event)(struct llc_struct *); /* * Mux and Demux variables @@ -106,18 +78,20 @@ struct llc_struct #define MODE_ABM 2 int llc_callbacks; /* Pending callbacks */ -#define LLC_CONNECT_INDICATION 1 -#define LLC_CONNECT_CONFIRM 2 -#define LLC_DATA_INDICATION 4 +#define LLC_CONN_INDICATION 1 /* We have to ensure the names don't */ +#define LLC_CONN_CONFIRM 2 /* mix up with the 802 state table */ +#define LLC_DATA_INDIC 4 #define LLC_DISC_INDICATION 8 #define LLC_RESET_INDIC_LOC 16 #define LLC_RESET_INDIC_REM 32 -#define LLC_RESET_CONFIRM 64 +#define LLC_RST_CONFIRM 64 #define LLC_FRMR_RECV 128 #define LLC_FRMR_SENT 256 #define LLC_REMOTE_BUSY 512 #define LLC_REMOTE_NOTBUSY 1024 -#define LLC_SET_REMOTE_BUSY 2048 +#define LLC_TEST_INDICATION 2048 +#define LLC_XID_INDICATION 4096 +#define LLC_UI_DATA 8192 struct sk_buff *inc_skb; /* Saved data buffer for indications */ @@ -156,5 +130,5 @@ void llc_connect_request(llcptr lp); void llc_xid_request(llcptr lp, char opt, int data_len, char *pdu_data); void llc_test_request(llcptr lp, int data_len, char *pdu_data); -int register_cl2llc_client(llcptr llc, const char *device, llc_ops *ops, u8 *rmac, u8 ssap, u8 dsap); +int register_cl2llc_client(llcptr llc, const char *device, void (*ops)(llcptr), u8 *rmac, u8 ssap, u8 dsap); void unregister_cl2llc_client(llcptr lp); diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 4f9e351eeb02..0043b4fcf6c6 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -23,7 +23,7 @@ #define NDISC_QUEUE_LEN 3 #define NCF_NOARP 0x0100 /* no ARP needed on this device */ -#define NCF_SUBNET 0x0200 /* NC entry for subnet */ +#define NCF_SUBNET 0x0200 /* NC entry for subnet */ #define NCF_INVALID 0x0400 #define NCF_DELAY_EXPIRED 0x0800 /* time to move to PROBE */ #define NCF_ROUTER 0x1000 /* neighbour is a router */ diff --git a/include/net/neighbour.h b/include/net/neighbour.h index dc3a27a85723..c0af9b1269df 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -119,15 +119,13 @@ static __inline__ void neigh_insert(struct neigh_table *tbl, if (tbl->tbl_lock == 1) { neigh_table_ins(tbl, neigh); - end_bh_atomic(); } else { - end_bh_atomic(); tbl->tbl_bh_mask |= NT_MASK_QUEUE; neigh_queue_ins(tbl, neigh); } - + end_bh_atomic(); } @@ -164,7 +162,9 @@ static __inline__ void neigh_table_unlock(struct neigh_table *tbl) { start_bh_atomic(); if (atomic_dec_and_test(&tbl->tbl_lock) && tbl->tbl_bh_mask) + { neigh_tbl_run_bh(tbl); + } end_bh_atomic(); } diff --git a/include/net/netbeui.h b/include/net/netbeui.h index 3a48e538989e..c981d7531a29 100644 --- a/include/net/netbeui.h +++ b/include/net/netbeui.h @@ -23,11 +23,17 @@ struct nb_ses struct nb_link { + struct llc llc; /* Must be first */ u8 mac[6]; /* Mac address of remote */ struct device *dev; /* Device we heard him on */ - struct llc *llc; /* 802.2 link layer */ struct nb_ses *sessions;/* Netbeui sessions on this LLC link */ struct wait_queue *wait;/* Wait queue for this netbios LLC */ + int busy; /* Track the LLC busy state */ + int state; /* Link state */ +#define NETBEUI_OPEN 1 /* Up and going */ +#define NETBEUI_CONNWAIT 2 /* Waiting to come up */ +#define NETBEUI_DISCWAIT 3 /* Waiting to drop and recover */ +#define NETBEUI_DEADWAIT 4 /* Trying to die */ }; diff --git a/include/net/sock.h b/include/net/sock.h index 1d6f03239e06..d4043ca84697 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -689,6 +689,10 @@ extern struct sk_buff *sock_alloc_send_skb(struct sock *skb, int noblock, int *errcode); +extern void sklist_remove_socket(struct sock **list, struct sock *sk); +extern void sklist_insert_socket(struct sock **list, struct sock *sk); +extern void sklist_destroy_socket(struct sock **list, struct sock *sk); + /* * Queue a received datagram if it will fit. Stream and sequenced * protocols can't normally use this as they need to fit buffers in diff --git a/include/net/x25.h b/include/net/x25.h index 6878076ab34e..fedc80a61443 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -59,6 +59,11 @@ #define X25_STATE_3 3 /* Data Transfer */ #define X25_STATE_4 4 /* Awaiting Reset Confirmation */ +#define X25_LINK_STATE_0 0 +#define X25_LINK_STATE_1 1 +#define X25_LINK_STATE_2 2 +#define X25_LINK_STATE_3 3 + #define X25_DEFAULT_T20 (180 * X25_SLOWHZ) /* Default T20 value */ #define X25_DEFAULT_T21 (200 * X25_SLOWHZ) /* Default T21 value */ #define X25_DEFAULT_T22 (180 * X25_SLOWHZ) /* Default T22 value */ @@ -148,10 +153,11 @@ extern int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned i #include /* x25_dev.c */ -extern int x25_link_up(struct device *); -extern void x25_send_frame(struct sk_buff *, struct device *); +extern void x25_send_frame(struct sk_buff *, struct x25_neigh *); extern int x25_lapb_receive_frame(struct sk_buff *, struct device *, struct packet_type *); extern int x25_llc_receive_frame(struct sk_buff *, struct device *, struct packet_type *); +extern void x25_establish_link(struct x25_neigh *); +extern void x25_terminate_link(struct x25_neigh *); /* x25_in.c */ extern int x25_process_rx_frame(struct sock *, struct sk_buff *); @@ -160,6 +166,8 @@ extern int x25_process_rx_frame(struct sock *, struct sk_buff *); extern void x25_link_control(struct sk_buff *, struct x25_neigh *, unsigned short); extern void x25_link_device_up(struct device *); extern void x25_link_device_down(struct device *); +extern void x25_link_established(struct x25_neigh *); +extern void x25_link_terminated(struct x25_neigh *); extern void x25_transmit_restart_request(struct x25_neigh *); extern void x25_transmit_restart_confirmation(struct x25_neigh *); extern void x25_transmit_diagnostic(struct x25_neigh *, unsigned char); diff --git a/init/main.c b/init/main.c index 7284e0e8b476..14788c570472 100644 --- a/init/main.c +++ b/init/main.c @@ -97,6 +97,7 @@ extern void aha1542_setup(char *str, int *ints); extern void aic7xxx_setup(char *str, int *ints); extern void AM53C974_setup(char *str, int *ints); extern void BusLogic_Setup(char *str, int *ints); +extern void ncr53c8xx_setup(char *str, int *ints); extern void eata2x_setup(char *str, int *ints); extern void u14_34f_setup(char *str, int *ints); extern void fdomain_setup(char *str, int *ints); @@ -192,7 +193,9 @@ extern void baycom_setup(char *str, int *ints); #ifdef CONFIG_SOUNDMODEM extern void sm_setup(char *str, int *ints); #endif - +#ifdef CONFIG_WDT +extern void wdt_setup(char *str, int *ints); +#endif #if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD) extern void ipc_init(void); @@ -345,6 +348,9 @@ struct { #ifdef CONFIG_SCSI_BUSLOGIC { "BusLogic=", BusLogic_Setup}, #endif +#ifdef CONFIG_SCSI_NCR53C8XX + { "ncr53c8xx=", ncr53c8xx_setup}, +#endif #ifdef CONFIG_SCSI_EATA { "eata=", eata2x_setup}, #endif @@ -460,6 +466,9 @@ struct { #endif #ifdef CONFIG_SOUNDMODEM { "soundmodem=", sm_setup }, +#endif +#ifdef CONFIG_WDT + { "wdt=", wdt_setup }, #endif { 0, 0 } }; diff --git a/kernel/fork.c b/kernel/fork.c index 5a013983f657..7ce05ee75304 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -82,10 +82,12 @@ static inline int dup_mmap(struct mm_struct * mm) mm->mmap = NULL; p = &mm->mmap; + flush_cache_mm(current->mm); for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { tmp = (struct vm_area_struct *) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL); if (!tmp) { exit_mmap(mm); + flush_tlb_mm(current->mm); return -ENOMEM; } *tmp = *mpnt; @@ -101,6 +103,7 @@ static inline int dup_mmap(struct mm_struct * mm) } if (copy_page_range(mm, current->mm, tmp)) { exit_mmap(mm); + flush_tlb_mm(current->mm); return -ENOMEM; } if (tmp->vm_ops && tmp->vm_ops->open) @@ -108,6 +111,7 @@ static inline int dup_mmap(struct mm_struct * mm) *p = tmp; p = &tmp->vm_next; } + flush_tlb_mm(current->mm); build_mmap_avl(mm); return 0; } diff --git a/mm/memory.c b/mm/memory.c index c6a13d5ed083..a1bdfef2ef89 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -137,10 +137,8 @@ void clear_page_tables(struct task_struct * tsk) printk("%s trying to clear kernel page-directory: not good\n", tsk->comm); return; } - flush_cache_mm(tsk->mm); for (i = 0 ; i < USER_PTRS_PER_PGD ; i++) free_one_pgd(page_dir + i); - flush_tlb_mm(tsk->mm); } /* @@ -171,10 +169,8 @@ int new_page_tables(struct task_struct * tsk) if (!(new_pg = pgd_alloc())) return -ENOMEM; page_dir = pgd_offset(&init_mm, 0); - flush_cache_mm(tsk->mm); memcpy(new_pg + USER_PTRS_PER_PGD, page_dir + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof (pgd_t)); - flush_tlb_mm(tsk->mm); SET_PAGE_DIR(tsk, new_pg); tsk->mm->pgd = new_pg; return 0; @@ -286,17 +282,12 @@ int copy_page_range(struct mm_struct *dst, struct mm_struct *src, cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE; src_pgd = pgd_offset(src, address); dst_pgd = pgd_offset(dst, address); - flush_cache_range(src, vma->vm_start, vma->vm_end); - flush_cache_range(dst, vma->vm_start, vma->vm_end); while (address < end) { error = copy_pmd_range(dst_pgd++, src_pgd++, address, end - address, cow); if (error) break; address = (address + PGDIR_SIZE) & PGDIR_MASK; } - /* Note that the src ptes get c-o-w treatment, so they change too. */ - flush_tlb_range(src, vma->vm_start, vma->vm_end); - flush_tlb_range(dst, vma->vm_start, vma->vm_end); return error; } @@ -380,20 +371,17 @@ static inline void zap_pmd_range(pgd_t * dir, unsigned long address, unsigned lo /* * remove user pages in a given range. */ -int zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size) +void zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size) { pgd_t * dir; unsigned long end = address + size; dir = pgd_offset(mm, address); - flush_cache_range(mm, end - size, end); while (address < end) { zap_pmd_range(dir, address, end - address); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } - flush_tlb_range(mm, end - size, end); - return 0; } static inline void zeromap_pte_range(pte_t * pte, unsigned long address, unsigned long size, pte_t zero_pte) @@ -580,7 +568,7 @@ unsigned long put_dirty_page(struct task_struct * tsk, unsigned long page, unsig } flush_page_to_ram(page); set_pte(pte, pte_mkwrite(pte_mkdirty(mk_pte(page, PAGE_COPY)))); -/* no need for invalidate */ +/* no need for flush_tlb */ return page; } @@ -731,13 +719,17 @@ void vmtruncate(struct inode * inode, unsigned long offset) return; mpnt = inode->i_mmap; do { + struct mm_struct *mm = mpnt->vm_mm; unsigned long start = mpnt->vm_start; - unsigned long len = mpnt->vm_end - start; + unsigned long end = mpnt->vm_end; + unsigned long len = end - start; unsigned long diff; /* mapping wholly truncated? */ if (mpnt->vm_offset >= offset) { - zap_page_range(mpnt->vm_mm, start, len); + flush_cache_range(mm, start, end); + zap_page_range(mm, start, len); + flush_tlb_range(mm, start, end); continue; } /* mapping wholly unaffected? */ @@ -751,7 +743,9 @@ void vmtruncate(struct inode * inode, unsigned long offset) partial_clear(mpnt, start); start = (start + ~PAGE_MASK) & PAGE_MASK; } - zap_page_range(mpnt->vm_mm, start, len); + flush_cache_range(mm, start, end); + zap_page_range(mm, start, len); + flush_tlb_range(mm, start, end); } while ((mpnt = mpnt->vm_next_share) != inode->i_mmap); } diff --git a/mm/mmap.c b/mm/mmap.c index 387a11bc951f..56d71ec1ef79 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -833,7 +833,7 @@ int do_munmap(unsigned long addr, size_t len) * it will put new vm_area_struct(s) into the address space. */ do { - unsigned long st, end; + unsigned long st, end, size; mpnt = free; free = free->vm_next; @@ -843,11 +843,14 @@ int do_munmap(unsigned long addr, size_t len) st = addr < mpnt->vm_start ? mpnt->vm_start : addr; end = addr+len; end = end > mpnt->vm_end ? mpnt->vm_end : end; + size = end - st; if (mpnt->vm_ops && mpnt->vm_ops->unmap) - mpnt->vm_ops->unmap(mpnt, st, end-st); - zap_page_range(current->mm, st, end-st); - unmap_fixup(mpnt, st, end-st); + mpnt->vm_ops->unmap(mpnt, st, size); + flush_cache_range(current->mm, st, end); + zap_page_range(current->mm, st, size); + flush_tlb_range(current->mm, st, end); + unmap_fixup(mpnt, st, size); kfree(mpnt); } while (free); @@ -879,14 +882,18 @@ void exit_mmap(struct mm_struct * mm) mm->locked_vm = 0; while (mpnt) { struct vm_area_struct * next = mpnt->vm_next; + unsigned long start = mpnt->vm_start; + unsigned long end = mpnt->vm_end; + unsigned long size = end - start; + if (mpnt->vm_ops) { if (mpnt->vm_ops->unmap) - mpnt->vm_ops->unmap(mpnt, mpnt->vm_start, mpnt->vm_end-mpnt->vm_start); + mpnt->vm_ops->unmap(mpnt, start, size); if (mpnt->vm_ops->close) mpnt->vm_ops->close(mpnt); } remove_shared_vm_struct(mpnt); - zap_page_range(mm, mpnt->vm_start, mpnt->vm_end-mpnt->vm_start); + zap_page_range(mm, start, size); if (mpnt->vm_inode) iput(mpnt->vm_inode); kfree(mpnt); diff --git a/mm/mremap.c b/mm/mremap.c index a3e941055542..65c0ff5351ed 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -119,8 +119,8 @@ oops_we_failed: flush_cache_range(mm, new_addr, new_addr + len); while ((offset += PAGE_SIZE) < len) move_one_page(mm, new_addr + offset, old_addr + offset); - flush_tlb_range(mm, new_addr, new_addr + len); zap_page_range(mm, new_addr, new_addr + len); + flush_tlb_range(mm, new_addr, new_addr + len); return -1; } diff --git a/net/802/cl2llc.c b/net/802/cl2llc.c index 957098b61020..185cd960a5c3 100644 --- a/net/802/cl2llc.c +++ b/net/802/cl2llc.c @@ -97,6 +97,11 @@ int llc_data_request(llcptr lp, struct sk_buff *skb) break; default: } + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } return 0; } } @@ -118,6 +123,14 @@ void disconnect_request(llcptr lp) { lp->state = D_CONN; llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may be invalid after the callback + */ } } @@ -132,6 +145,14 @@ void connect_request(llcptr lp) { lp->state = SETUP; llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may be invalid after the callback + */ } } @@ -196,53 +217,41 @@ void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, case 2: lp->state = NORMAL; /* needed to eliminate connect_response() */ lp->llc_mode = MODE_ABM; - if (lp->ops->connect_indication_ep != NULL) - lp->ops->connect_indication_ep(lp); + lp->llc_callbacks|=LLC_CONN_INDICATION; break; case 3: lp->llc_mode = MODE_ABM; - if (lp->ops->connect_confirm_ep != NULL) - lp->ops->connect_confirm_ep(lp); + lp->llc_callbacks|=LLC_CONN_CONFIRM; break; case 4: - if (lp->ops->data_indication_ep != NULL) - { - skb_pull(skb, 4); - lp->ops->data_indication_ep(lp, skb); - } + skb_pull(skb, 4); + lp->inc_skb=skb; + lp->llc_callbacks|=LLC_DATA_INDIC; break; case 5: lp->llc_mode = MODE_ADM; - if (lp->ops->disconnect_indication_ep != NULL) - lp->ops->disconnect_indication_ep(lp); + lp->llc_callbacks|=LLC_DISC_INDICATION; break; case 70: - if (lp->ops->reset_indication_ep != NULL) - lp->ops->reset_indication_ep(lp, LOCAL); + lp->llc_callbacks|=LLC_RESET_INDIC_LOC; break; case 71: - if (lp->ops->reset_indication_ep != NULL) - lp->ops->reset_indication_ep(lp, REMOTE); + lp->llc_callbacks|=LLC_RESET_INDIC_REM; break; case 7: - if (lp->ops->reset_confirm_ep != NULL) - lp->ops->reset_confirm_ep(lp, REMOTE); + lp->llc_callbacks|=LLC_RST_CONFIRM; break; case 66: - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, FRMR_RECEIVED); + lp->llc_callbacks|=LLC_FRMR_RECV; break; case 67: - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, FRMR_SENT); + lp->llc_callbacks|=LLC_FRMR_SENT; break; case 68: - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, REMOTE_BUSY); + lp->llc_callbacks|=LLC_REMOTE_BUSY; break; case 69: - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, REMOTE_NOT_BUSY); + lp->llc_callbacks|=LLC_REMOTE_NOTBUSY; break; case 11: llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL); @@ -322,8 +331,7 @@ void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, { lp->remote_busy = 1; llc_start_timer(lp, BUSY_TIMER); - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, REMOTE_BUSY); + lp->llc_callbacks|=LLC_REMOTE_BUSY; } else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE) { @@ -559,6 +567,15 @@ void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type) if (pc_label != 0) { llc_interpret_pseudo_code(lp, pc_label, skb, type); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may no longer be valid after this point. Be + * careful what is added! + */ } } @@ -585,5 +602,13 @@ void llc_timer_expired(llcptr lp, int t) lp->state = timertr_entry[idx +1]; } lp->timer_state[t] = TIMER_IDLE; + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * And lp may have vanished in the event callback + */ } diff --git a/net/802/cl2llc.pre b/net/802/cl2llc.pre index 0f2e34008728..6d4f0c852182 100644 --- a/net/802/cl2llc.pre +++ b/net/802/cl2llc.pre @@ -21,6 +21,10 @@ * Changes * Alan Cox : Chainsawed into Linux format * Modified to use llc_ names + * Changed callbacks + * + * Note: TST/XID stuff is broken at the moment. The + * buffer is freed before being passed up. * * This file must be processed by sed before it can be compiled. */ @@ -97,6 +101,11 @@ int llc_data_request(llcptr lp, struct sk_buff *skb) break; default: } + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } return 0; } } @@ -118,6 +127,14 @@ void disconnect_request(llcptr lp) { lp->state = D_CONN; llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may be invalid after the callback + */ } } @@ -132,6 +149,14 @@ void connect_request(llcptr lp) { lp->state = SETUP; llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may be invalid after the callback + */ } } @@ -196,53 +221,41 @@ void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, case CONNECT_INDICATION: lp->state = NORMAL; /* needed to eliminate connect_response() */ lp->llc_mode = MODE_ABM; - if (lp->ops->connect_indication_ep != NULL) - lp->ops->connect_indication_ep(lp); + lp->llc_callbacks|=LLC_CONN_INDICATION; break; case CONNECT_CONFIRM: lp->llc_mode = MODE_ABM; - if (lp->ops->connect_confirm_ep != NULL) - lp->ops->connect_confirm_ep(lp); + lp->llc_callbacks|=LLC_CONN_CONFIRM; break; case DATA_INDICATION: - if (lp->ops->data_indication_ep != NULL) - { - skb_pull(skb, 4); - lp->ops->data_indication_ep(lp, skb); - } + skb_pull(skb, 4); + lp->inc_skb=skb; + lp->llc_callbacks|=LLC_DATA_INDIC; break; case DISCONNECT_INDICATION: lp->llc_mode = MODE_ADM; - if (lp->ops->disconnect_indication_ep != NULL) - lp->ops->disconnect_indication_ep(lp); + lp->llc_callbacks|=LLC_DISC_INDICATION; break; case RESET_INDICATION(LOCAL): - if (lp->ops->reset_indication_ep != NULL) - lp->ops->reset_indication_ep(lp, LOCAL); + lp->llc_callbacks|=LLC_RESET_INDIC_LOC; break; case RESET_INDICATION(REMOTE): - if (lp->ops->reset_indication_ep != NULL) - lp->ops->reset_indication_ep(lp, REMOTE); + lp->llc_callbacks|=LLC_RESET_INDIC_REM; break; case RESET_CONFIRM: - if (lp->ops->reset_confirm_ep != NULL) - lp->ops->reset_confirm_ep(lp, REMOTE); + lp->llc_callbacks|=LLC_RST_CONFIRM; break; case REPORT_STATUS(FRMR_RECEIVED): - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, FRMR_RECEIVED); + lp->llc_callbacks|=LLC_FRMR_RECV; break; case REPORT_STATUS(FRMR_SENT): - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, FRMR_SENT); + lp->llc_callbacks|=LLC_FRMR_SENT; break; case REPORT_STATUS(REMOTE_BUSY): - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, REMOTE_BUSY); + lp->llc_callbacks|=LLC_REMOTE_BUSY; break; case REPORT_STATUS(REMOTE_NOT_BUSY): - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, REMOTE_NOT_BUSY); + lp->llc_callbacks|=LLC_REMOTE_NOTBUSY; break; case SEND_DISC_CMD(P=X): llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL); @@ -322,8 +335,7 @@ void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, { lp->remote_busy = 1; llc_start_timer(lp, BUSY_TIMER); - if (lp->ops->report_status_ep != NULL) - lp->ops->report_status_ep(lp, REMOTE_BUSY); + lp->llc_callbacks|=LLC_REMOTE_BUSY; } else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE) { @@ -559,6 +571,15 @@ void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type) if (pc_label != NOP) { llc_interpret_pseudo_code(lp, pc_label, skb, type); + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * lp may no longer be valid after this point. Be + * careful what is added! + */ } } @@ -585,5 +606,13 @@ void llc_timer_expired(llcptr lp, int t) lp->state = timertr_entry[idx +1]; } lp->timer_state[t] = TIMER_IDLE; + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } + /* + * And lp may have vanished in the event callback + */ } diff --git a/net/802/fddi.c b/net/802/fddi.c index 24fcff1276d9..9b644c8ce56e 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -20,7 +20,11 @@ * 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. + * + * Changes + * Alan Cox : New arp/rebuild header */ + #include #include #include @@ -45,21 +49,14 @@ * daddr=NULL means leave destination address (eg unresolved arp) */ -int fddi_header( - struct sk_buff *skb, - struct device *dev, - unsigned short type, - void *daddr, - void *saddr, - unsigned len - ) - - { +int fddi_header(struct sk_buff *skb, struct device *dev, unsigned short type, + void *daddr, void *saddr, unsigned len) +{ struct fddihdr *fddi = (struct fddihdr *)skb_push(skb, FDDI_K_SNAP_HLEN); /* Fill in frame header - assume 802.2 SNAP frames for now */ - fddi->fc = FDDI_FC_K_ASYNC_LLC_DEF; + fddi->fc = FDDI_FC_K_ASYNC_LLC_DEF; fddi->hdr.llc_snap.dsap = FDDI_EXTENDED_SAP; fddi->hdr.llc_snap.ssap = FDDI_EXTENDED_SAP; fddi->hdr.llc_snap.ctrl = FDDI_UI_CMD; @@ -76,12 +73,12 @@ int fddi_header( memcpy(fddi->saddr, dev->dev_addr, dev->addr_len); if (daddr != NULL) - { + { memcpy(fddi->daddr, daddr, dev->addr_len); return(FDDI_K_SNAP_HLEN); - } - return(-FDDI_K_SNAP_HLEN); } + return(-FDDI_K_SNAP_HLEN); +} /* @@ -90,32 +87,22 @@ int fddi_header( * this sk_buff. We now let ARP fill in the other fields. */ -int fddi_rebuild_header( - void *buff, - struct device *dev, - unsigned long dest, - struct sk_buff *skb - ) - - { - struct fddihdr *fddi = (struct fddihdr *)buff; +int fddi_rebuild_header(struct sk_buff *skb) +{ + struct fddihdr *fddi = (struct fddihdr *)skb->data; /* Only ARP/IP is currently supported */ if (fddi->hdr.llc_snap.ethertype != htons(ETH_P_IP)) - { + { printk("fddi_rebuild_header: Don't know how to resolve type %04X addresses?\n", (unsigned int)htons(fddi->hdr.llc_snap.ethertype)); return(0); - } + } /* Try to get ARP to resolve the header and fill destination address */ - if (arp_find(fddi->daddr, dest, dev, dev->pa_addr, skb)) - return(1); - else - return(0); - } - + return arp_find(fddi->daddr, skb) ? 1 : 0; +} /* * Determine the packet's protocol ID and fill in skb fields. @@ -124,12 +111,8 @@ int fddi_rebuild_header( * the proper pointer to the start of packet data (skb->data). */ -unsigned short fddi_type_trans( - struct sk_buff *skb, - struct device *dev - ) - - { +unsigned short fddi_type_trans(struct sk_buff *skb, struct device *dev) +{ struct fddihdr *fddi = (struct fddihdr *)skb->data; /* @@ -143,20 +126,20 @@ unsigned short fddi_type_trans( /* Set packet type based on destination address and flag settings */ if (*fddi->daddr & 0x01) - { + { if (memcmp(fddi->daddr, dev->broadcast, FDDI_K_ALEN) == 0) skb->pkt_type = PACKET_BROADCAST; else skb->pkt_type = PACKET_MULTICAST; - } + } else if (dev->flags & IFF_PROMISC) - { + { if (memcmp(fddi->daddr, dev->dev_addr, FDDI_K_ALEN)) skb->pkt_type = PACKET_OTHERHOST; - } + } /* Assume 802.2 SNAP frames, for now */ return(fddi->hdr.llc_snap.ethertype); - } +} diff --git a/net/802/llc_macinit.c b/net/802/llc_macinit.c index a934db5124c3..8e3374106ef3 100644 --- a/net/802/llc_macinit.c +++ b/net/802/llc_macinit.c @@ -84,11 +84,8 @@ int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb, struct device *dev, st fr->u_hdr.u_info); break; case TEST_RSP: - if (lp->ops->test_indication_ep != NULL) - { - lp->ops->test_indication_ep(lp, - ll -3, fr->u_hdr.u_info); - } + lp->llc_callbacks|=LLC_TEST_INDICATION; + lp->inc_skb=skb; break; case XID_CMD: /* @@ -110,19 +107,14 @@ int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb, struct device *dev, st { lp->k = fr->u_hdr.u_info[2]; } - if (lp->ops->xid_indication_ep != NULL) - { - lp->ops->xid_indication_ep(lp, - ll-3, fr->u_hdr.u_info); - } + lp->llc_callbacks|=LLC_XID_INDICATION; + lp->inc_skb=skb; break; case UI_CMD: - if(lp->ops->unit_data_indication_ep != NULL) - { - free=lp->ops->unit_data_indication_ep(lp, - ll-3, fr->u_hdr.u_info); - } + lp->llc_callbacks|=LLC_UI_DATA; + skb_pull(skb,3); + lp->inc_skb=skb; break; default: @@ -140,6 +132,12 @@ int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb, struct device *dev, st skb->sk = NULL; kfree_skb(skb, FREE_READ); } + + if(lp->llc_callbacks) + { + lp->llc_event(lp); + lp->llc_callbacks=0; + } return 0; } @@ -149,7 +147,7 @@ int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb, struct device *dev, st * LLC's on device down, the device list must be locked before this call. */ -int register_cl2llc_client(llcptr lp, const char *device, llc_ops *ops, u8 *rmac, u8 ssap, u8 dsap) +int register_cl2llc_client(llcptr lp, const char *device, void (*event)(llcptr), u8 *rmac, u8 ssap, u8 dsap) { char eye_init[] = "LLC\0"; @@ -167,7 +165,7 @@ int register_cl2llc_client(llcptr lp, const char *device, llc_ops *ops, u8 *rmac lp->timer_interval[ACK_TIMER] = HZ/8; lp->timer_interval[BUSY_TIMER] = HZ*2; lp->local_sap = ssap; - lp->ops = ops; + lp->llc_event = event; lp->remote_mac_len = lp->dev->addr_len; memcpy(lp->remote_mac, rmac, lp->remote_mac_len); lp->state = 0; diff --git a/net/802/p8022.c b/net/802/p8022.c index 968603b204d4..132df34fd1cc 100644 --- a/net/802/p8022.c +++ b/net/802/p8022.c @@ -1,3 +1,21 @@ +/* + * NET3: Support for 802.2 demultiplexing off ethernet (Token ring + * is kept seperate see p8022tr.c) + * 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. + * + * Demultiplex 802.2 encoded protocols. We match the entry by the + * SSAP/DSAP pair and then deliver to the registered datalink that + * matches. The control byte is ignored and handling of such items + * is up to the routine passed the frame. + * + * Unlike the 802.3 datalink we have a list of 802.2 entries as there + * are multiple protocols to demux. The list is currently short (3 or + * 4 entries at most). The current demux assumes this. + */ + #include #include #include @@ -12,11 +30,11 @@ static struct datalink_proto *p8022_list = NULL; * We don't handle the loopback SAP stuff, the extended * 802.2 command set, multicast SAP identifiers and non UI * frames. We have the absolute minimum needed for IPX, - * IP and Appletalk phase 2. + * IP and Appletalk phase 2. See the llc_* routines for + * support libraries if your protocol needs these. */ -static struct datalink_proto * -find_8022_client(unsigned char type) +static struct datalink_proto *find_8022_client(unsigned char type) { struct datalink_proto *proto; @@ -28,13 +46,13 @@ find_8022_client(unsigned char type) return proto; } -int -p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +int p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { struct datalink_proto *proto; proto = find_8022_client(*(skb->h.raw)); - if (proto != NULL) { + if (proto != NULL) + { skb->h.raw += 3; skb_pull(skb,3); return proto->rcvfunc(skb, dev, pt); @@ -45,8 +63,7 @@ p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) return 0; } -static void -p8022_datalink_header(struct datalink_proto *dl, +static void p8022_datalink_header(struct datalink_proto *dl, struct sk_buff *skb, unsigned char *dest_node) { struct device *dev = skb->dev; @@ -77,8 +94,7 @@ void p8022_proto_init(struct net_proto *pro) dev_add_pack(&p8022_packet_type); } -struct datalink_proto * -register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *)) +struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *)) { struct datalink_proto *proto; diff --git a/net/802/p8022tr.c b/net/802/p8022tr.c index 4d7ed668afcc..704b3feddc8c 100644 --- a/net/802/p8022tr.c +++ b/net/802/p8022tr.c @@ -1,3 +1,13 @@ +/* + * NET3: Handling for token ring frames that are not IP. IP is hooked + * early in the token ring support code. + * + * 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. + */ + #include #include #include @@ -14,11 +24,14 @@ static struct datalink_proto *p8022tr_list = NULL; * We don't handle the loopback SAP stuff, the extended * 802.2 command set, multicast SAP identifiers and non UI * frames. We have the absolute minimum needed for IPX, - * IP and Appletalk phase 2. + * IP and Appletalk phase 2. See the llc_* routines for support + * to handle the fun stuff. + * + * We assume the list will be very short (at the moment its normally + * one or two entries). */ -static struct datalink_proto * -find_8022tr_client(unsigned char type) +static struct datalink_proto *find_8022tr_client(unsigned char type) { struct datalink_proto *proto; @@ -30,8 +43,7 @@ find_8022tr_client(unsigned char type) return proto; } -int -p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) +int p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { struct datalink_proto *proto; @@ -47,8 +59,7 @@ p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) return 0; } -static void -p8022tr_datalink_header(struct datalink_proto *dl, +static void p8022tr_datalink_header(struct datalink_proto *dl, struct sk_buff *skb, unsigned char *dest_node) { struct device *dev = skb->dev; @@ -85,8 +96,8 @@ void p8022tr_proto_init(struct net_proto *pro) dev_add_pack(&p8022tr_packet_type); } -struct datalink_proto * -register_8022tr_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *)) +struct datalink_proto *register_8022tr_client(unsigned char type, + int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *)) { struct datalink_proto *proto; diff --git a/net/802/p8023.c b/net/802/p8023.c index 57bd6a74a10a..82a80c3c2c11 100644 --- a/net/802/p8023.c +++ b/net/802/p8023.c @@ -1,34 +1,59 @@ +/* + * NET3: 802.3 data link hooks used for IPX 802.3 + * + * 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. + * + * 802.3 isn't really a protocol data link layer. Some old IPX stuff + * uses it however. Note that there is only one 802.3 protocol layer + * in the system. We don't currently support different protocols + * running raw 802.3 on different devices. Thankfully nobody else + * has done anything like the old IPX. + */ + #include #include #include #include #include -static void -p8023_datalink_header(struct datalink_proto *dl, +/* + * Place an 802.3 header on a packet. The driver will do the mac + * addresses, we just need to give it the buffer length. + */ + +static void p8023_datalink_header(struct datalink_proto *dl, struct sk_buff *skb, unsigned char *dest_node) { struct device *dev = skb->dev; - dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len); } -struct datalink_proto * -make_8023_client(void) +/* + * Create an 802.3 client. Note there can be only one 802.3 client + */ + +struct datalink_proto *make_8023_client(void) { struct datalink_proto *proto; proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC); - if (proto != NULL) { + if (proto != NULL) + { proto->type_len = 0; proto->header_length = 0; proto->datalink_header = p8023_datalink_header; proto->string_name = "802.3"; } - return proto; } +/* + * Destroy the 802.3 client. + */ + void destroy_8023_client(struct datalink_proto *dl) { if (dl) diff --git a/net/802/sysctl_net_802.c b/net/802/sysctl_net_802.c index 96f51588c98b..acfcf3d777bb 100644 --- a/net/802/sysctl_net_802.c +++ b/net/802/sysctl_net_802.c @@ -1,8 +1,13 @@ /* -*- linux-c -*- - * sysctl_net_802.c: sysctl interface to net 802 subsystem. + * sysctl_net_802.c: sysctl interface to net 802 subsystem. * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/802 directory entry (empty =) ). [MS] + * Begun April 1, 1996, Mike Shaver. + * Added /proc/sys/net/802 directory entry (empty =) ). [MS] + * + * 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. */ #include diff --git a/net/802/tr.c b/net/802/tr.c index fde33b79e50a..d429cb4d4b84 100644 --- a/net/802/tr.c +++ b/net/802/tr.c @@ -1,3 +1,14 @@ +/* + * NET3: Token ring device handling subroutines + * + * 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. + * + * Fixes: + */ + #include #include #include @@ -23,7 +34,12 @@ static void rif_check_expire(unsigned long dummy); typedef struct rif_cache_s *rif_cache; -struct rif_cache_s { +/* + * Each RIF entry we learn is kept this way + */ + +struct rif_cache_s +{ unsigned char addr[TR_ALEN]; unsigned short rcf; unsigned short rseg[8]; @@ -32,12 +48,32 @@ struct rif_cache_s { }; #define RIF_TABLE_SIZE 16 + +/* + * We hash the RIF cache 16 ways. We do after all have to look it + * up a lot. + */ + rif_cache rif_table[RIF_TABLE_SIZE]={ NULL, }; #define RIF_TIMEOUT 60*10*HZ #define RIF_CHECK_INTERVAL 60*HZ -static struct timer_list rif_timer={ NULL,NULL,RIF_CHECK_INTERVAL,0L,rif_check_expire }; +/* + * Garbage disposal timer. + */ + +static struct timer_list rif_timer= +{ + NULL,NULL,RIF_CHECK_INTERVAL,0L,rif_check_expire +}; + + +/* + * Put the headers on a token ring packet. Token ring source routing + * makes this a little more exciting than on ethernet. + */ + int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { @@ -53,13 +89,23 @@ int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type, else memset(trh->saddr,0,dev->addr_len); /* Adapter fills in address */ + /* + * This is the stuff needed for IP encoding - IP over 802.2 + * with SNAP. + */ + trllc->dsap=trllc->ssap=EXTENDED_SAP; trllc->llc=UI_CMD; trllc->protid[0]=trllc->protid[1]=trllc->protid[2]=0x00; trllc->ethertype=htons(type); - if(daddr) { + /* + * Build the destination and then source route the frame + */ + + if(daddr) + { memcpy(trh->daddr,daddr,dev->addr_len); tr_source_route(trh,dev); return(dev->hard_header_len); @@ -68,12 +114,21 @@ int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type, } -int tr_rebuild_header(struct sk_buff *skb) { - +/* + * A neighbour discovery of some species (eg arp) has completed. We + * can now send the packet. + */ + +int tr_rebuild_header(struct sk_buff *skb) +{ struct trh_hdr *trh=(struct trh_hdr *)skb->data; struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr)); struct device *dev = skb->dev; + /* + * FIXME: We don't yet support IPv6 over token rings + */ + if(trllc->ethertype != htons(ETH_P_IP)) { printk("tr_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons( trllc->ethertype)); return 0; @@ -82,13 +137,21 @@ int tr_rebuild_header(struct sk_buff *skb) { if(arp_find(trh->daddr, skb)) { return 1; } - else { + else + { tr_source_route(trh,dev); return 0; } } -unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) { +/* + * Some of this is a bit hackish. We intercept RIF information + * used for source routing. We also grab IP directly and don't feed + * it via SNAP. + */ + +unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) +{ struct trh_hdr *trh=(struct trh_hdr *)skb->data; struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr)); @@ -117,31 +180,46 @@ unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) { return trllc->ethertype; } -/* We try to do source routing... */ +/* + * We try to do source routing... + */ -static void tr_source_route(struct trh_hdr *trh,struct device *dev) { +static void tr_source_route(struct trh_hdr *trh,struct device *dev) +{ int i; unsigned int hash; rif_cache entry; - /* Broadcasts are single route as stated in RFC 1042 */ - if(!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN)) { + /* + * Broadcasts are single route as stated in RFC 1042 + */ + if(!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN)) + { trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK) | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); trh->saddr[0]|=TR_RII; } - else { + else + { for(i=0,hash=0;idaddr[i++]); hash&=RIF_TABLE_SIZE-1; + /* + * Walk the hash table and look for an entry + */ for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->daddr[0]),TR_ALEN);entry=entry->next); - if(entry) { + /* + * If we found an entry we can route the frame. + */ + if(entry) + { #if 0 printk("source routing for %02X %02X %02X %02X %02X %02X\n",trh->daddr[0], trh->daddr[1],trh->daddr[2],trh->daddr[3],trh->daddr[4],trh->daddr[5]); #endif - if((ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8) { + if((ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8) + { trh->rcf=entry->rcf; memcpy(&trh->rseg[0],&entry->rseg[0],8*sizeof(unsigned short)); trh->rcf^=htons(TR_RCF_DIR_BIT); @@ -151,40 +229,58 @@ printk("source routing for %02X %02X %02X %02X %02X %02X\n",trh->daddr[0], entry->last_used=jiffies; } } - else { + else + { + /* + * Without the information we simply have to shout + * on the wire. The replies should rapidly clean this + * situation up. + */ trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK) | TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST); trh->saddr[0]|=TR_RII; } } - } -static void tr_add_rif_info(struct trh_hdr *trh) { - +/* + * We have learned some new RIF information for our source + * routing. + */ + +static void tr_add_rif_info(struct trh_hdr *trh) +{ int i; unsigned int hash; rif_cache entry; - + /* + * Firstly see if the entry exists + */ trh->saddr[0]&=0x7f; for(i=0,hash=0;isaddr[i++]); hash&=RIF_TABLE_SIZE-1; -#if 0 - printk("hash: %d\n",hash); -#endif for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);entry=entry->next); - if(entry==NULL) { + if(entry==NULL) + { #if 0 printk("adding rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n", trh->saddr[0],trh->saddr[1],trh->saddr[2], trh->saddr[3],trh->saddr[4],trh->saddr[5], trh->rcf); #endif + /* + * Allocate our new entry. A failure to allocate loses + * use the information. This is harmless. + * + * FIXME: We ought to keep some kind of cache size + * limiting and adjust the timers to suit. + */ entry=kmalloc(sizeof(struct rif_cache_s),GFP_ATOMIC); - if(!entry) { - printk("tr.c: Couldn't malloc rif cache entry !\n"); + if(!entry) + { + printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n"); return; } entry->rcf=trh->rcf; @@ -193,69 +289,92 @@ printk("adding rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n", entry->next=rif_table[hash]; entry->last_used=jiffies; rif_table[hash]=entry; - } -/* Y. Tahara added */ - else { - if ( entry->rcf != trh->rcf ) { - if (!(trh->rcf & htons(TR_RCF_BROADCAST_MASK))) { + } + else /* Y. Tahara added */ + { + /* + * Update existing entries + */ + if ( entry->rcf != trh->rcf ) + { + if (!(trh->rcf & htons(TR_RCF_BROADCAST_MASK))) + { #if 0 printk("updating rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n", trh->saddr[0],trh->saddr[1],trh->saddr[2], trh->saddr[3],trh->saddr[4],trh->saddr[5], trh->rcf); #endif - entry->rcf = trh->rcf; - memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short)); - entry->last_used=jiffies; - } + entry->rcf = trh->rcf; + memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short)); + entry->last_used=jiffies; + } } } - } -static void rif_check_expire(unsigned long dummy) { +/* + * Scan the cache with a timer and see what we need to throw out. + */ +static void rif_check_expire(unsigned long dummy) +{ int i; unsigned long now=jiffies,flags; save_flags(flags); cli(); - for(i=0; i < RIF_TABLE_SIZE;i++) { - - rif_cache entry, *pentry=rif_table+i; - + for(i=0; i < RIF_TABLE_SIZE;i++) + { + rif_cache entry, *pentry=rif_table+i; while((entry=*pentry)) - if((now-entry->last_used) > RIF_TIMEOUT) { + { + /* + * Out it goes + */ + if((now-entry->last_used) > RIF_TIMEOUT) + { *pentry=entry->next; kfree_s(entry,sizeof(struct rif_cache_s)); } else - pentry=&entry->next; + pentry=&entry->next; + } } restore_flags(flags); + /* + * Reset the timer + */ + del_timer(&rif_timer); rif_timer.expires=jiffies+RIF_CHECK_INTERVAL; add_timer(&rif_timer); } -int rif_get_info(char *buffer,char **start, off_t offset, int length) { - - int len=0; - off_t begin=0; - off_t pos=0; - int size,i; +/* + * Generate the /proc/net information for the token ring RIF + * routing. + */ + +int rif_get_info(char *buffer,char **start, off_t offset, int length) +{ + int len=0; + off_t begin=0; + off_t pos=0; + int size,i; - rif_cache entry; + rif_cache entry; size=sprintf(buffer, -" TR address rcf routing segments TTL\n\n"); - pos+=size; - len+=size; + " TR address rcf routing segments TTL\n\n"); + pos+=size; + len+=size; - for(i=0;i < RIF_TABLE_SIZE;i++) { + for(i=0;i < RIF_TABLE_SIZE;i++) + { for(entry=rif_table[i];entry;entry=entry->next) { size=sprintf(buffer+len,"%02X:%02X:%02X:%02X:%02X:%02X %04X %04X %04X %04X %04X %04X %04X %04X %04X %lu\n", entry->addr[0],entry->addr[1],entry->addr[2],entry->addr[3],entry->addr[4],entry->addr[5], @@ -264,27 +383,33 @@ int rif_get_info(char *buffer,char **start, off_t offset, int length) { len+=size; pos=begin+len; - if(posoffset+length) break; - } + } if(pos>offset+length) break; } - *start=buffer+(offset-begin); /* Start of wanted data */ - len-=(offset-begin); /* Start slop */ - if(len>length) - len=length; /* Ending slop */ - return len; + *start=buffer+(offset-begin); /* Start of wanted data */ + len-=(offset-begin); /* Start slop */ + if(len>length) + len=length; /* Ending slop */ + return len; } -void rif_init(struct net_proto *unused) { - +/* + * Called during bootup. We don't actually have to initialise + * too much for this. The timer structure is setup statically. Thats + * probably NOT a good thing if we change the structure. + */ + +void rif_init(struct net_proto *unused) +{ add_timer(&rif_timer); - } diff --git a/net/Config.in b/net/Config.in index d1de8d9cd97f..18fe70d2a7c0 100644 --- a/net/Config.in +++ b/net/Config.in @@ -33,6 +33,7 @@ if [ "$CONFIG_AX25" != "n" ]; then fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25 + tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE bool '802.2 LLC (VERY EXPERIMENTAL)' CONFIG_LLC fi diff --git a/net/Makefile b/net/Makefile index 94af3add9cc1..19514b47a960 100644 --- a/net/Makefile +++ b/net/Makefile @@ -9,7 +9,7 @@ MOD_SUB_DIRS := ipv4 ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \ - netrom rose x25 #decnet + netrom rose lapb x25 #decnet SUB_DIRS := core ethernet unix MOD_LIST_NAME := NET_MISC_MODULES @@ -57,6 +57,14 @@ else endif endif +ifeq ($(CONFIG_LAPB),y) +SUB_DIRS += lapb +else + ifeq ($(CONFIG_LAPB),m) + MOD_SUB_DIRS += lapb + endif +endif + ifeq ($(CONFIG_NETROM),y) SUB_DIRS += netrom else diff --git a/net/README b/net/README index 9c35aaf33999..aae7733dd967 100644 --- a/net/README +++ b/net/README @@ -11,6 +11,7 @@ core alan@lxorguk.ukuu.org.uk ethernet alan@lxorguk.ukuu.org.uk ipv4 alan@lxorguk.ukuu.org.uk ipx alan@lxorguk.ukuu.org.uk,greg@caldera.com +lapb jsn@cs.nott.ac.uk netrom jsn@cs.nott.ac.uk rose jsn@cs.nott.ac.uk unix alan@lxorguk.ukuu.org.uk diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index d1d426ac85b7..26cbdfaa9c72 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -3,15 +3,15 @@ * ethernet 'ELAP'. * * Alan Cox - * * - * This doesn't fit cleanly with the IP arp. This isn't a problem as - * the IP arp wants extracting from the device layer in 1.3.x anyway. - * [see the pre-1.3 test code for details 8)] + * This doesn't fit cleanly with the IP arp. Potentially we can use + * the generic neighbour discovery code to clean this up. * * FIXME: * We ought to handle the retransmits with a single list and a * separate fast timer for when it is needed. + * Use neighbour discovery code. + * Token Ring Support. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 60ee9a2aac36..2d5e3d9fb4fc 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -3,7 +3,6 @@ * ethernet 'ELAP'. * * Alan Cox - * * * With more than a little assistance from * @@ -30,8 +29,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * TODO - * ASYNC I/O */ #include @@ -88,7 +85,7 @@ static struct proto_ops atalk_dgram_ops; * * \***********************************************************************************************************************/ -static struct sock *volatile atalk_socket_list=NULL; +static struct sock *atalk_socket_list=NULL; /* * Note: Sockets may not be removed _during_ an interrupt or inet_bh @@ -96,42 +93,14 @@ static struct sock *volatile atalk_socket_list=NULL; * use this facility. */ -static void atalk_remove_socket(struct sock *sk) +extern inline void atalk_remove_socket(struct sock *sk) { - unsigned long flags; - struct sock *s; - - save_flags(flags); - cli(); - - s=atalk_socket_list; - if(s==sk) - { - atalk_socket_list=s->next; - restore_flags(flags); - return; - } - while(s && s->next) - { - if(s->next==sk) - { - s->next=sk->next; - restore_flags(flags); - return; - } - s=s->next; - } - restore_flags(flags); + sklist_remove_socket(&atalk_socket_list,sk); } -static void atalk_insert_socket(struct sock *sk) +extern inline void atalk_insert_socket(struct sock *sk) { - unsigned long flags; - save_flags(flags); - cli(); - sk->next=atalk_socket_list; - atalk_socket_list=sk; - restore_flags(flags); + sklist_insert_socket(&atalk_socket_list,sk); } static struct sock *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif) @@ -191,53 +160,11 @@ static struct sock *atalk_find_socket(struct sockaddr_at *sat) return( s ); } -/* - * This is only called from user mode. Thus it protects itself against - * interrupt users but doesn't worry about being called during work. - * Once it is removed from the queue no interrupt or bottom half will - * touch it and we are (fairly 8-) ) safe. - */ - -static void atalk_destroy_socket(struct sock *sk); - -/* - * Handler for deferred kills. - */ - -static void atalk_destroy_timer(unsigned long data) -{ - atalk_destroy_socket((struct sock *)data); -} - -static void atalk_destroy_socket(struct sock *sk) +extern inline void atalk_destroy_socket(struct sock *sk) { - struct sk_buff *skb; - atalk_remove_socket(sk); - - while((skb=skb_dequeue(&sk->receive_queue))!=NULL) - { - kfree_skb(skb,FREE_READ); - } - - if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead) - { - sk_free(sk); - MOD_DEC_USE_COUNT; - } - else - { - /* - * Someone is using our buffers still.. defer - */ - init_timer(&sk->timer); - sk->timer.expires=jiffies+10*HZ; - sk->timer.function=atalk_destroy_timer; - sk->timer.data = (unsigned long)sk; - add_timer(&sk->timer); - } + sklist_destroy_socket(&atalk_socket_list,sk); } - /* * Called from proc fs */ @@ -364,9 +291,8 @@ static int atif_probe_device(struct atalk_iface *atif) /* * THIS IS A HACK: Farallon cards want to do their own picking of - * addresses. This needs tidying up post 1.4, but we need it in - * now for the 1.4 release as is. - * + * addresses. This needs tidying up when someone does localtalk + * drivers */ if((atif->dev->type == ARPHRD_LOCALTLK || atif->dev->type == ARPHRD_PPP) && atif->dev->do_ioctl) @@ -1105,7 +1031,16 @@ static void def_callback2(struct sock *sk, int len) if(!sk->dead) { wake_up_interruptible(sk->sleep); - sock_wake_async(sk->socket,0); + sock_wake_async(sk->socket,1); + } +} + +static void def_callback3(struct sock *sk) +{ + if(!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket, 2); } } @@ -1159,7 +1094,7 @@ static int atalk_create(struct socket *sock, int protocol) sk->state_change=def_callback1; sk->data_ready=def_callback2; - sk->write_space=def_callback1; + sk->write_space=def_callback3; sk->error_report=def_callback1; sk->zapped=1; diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index c9d67acb4719..dbb6b692fbdf 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1,5 +1,5 @@ /* - * AX.25 release 034 + * AX.25 release 035 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. @@ -88,6 +88,8 @@ * Alan(GW4PTS) Small POSIXisations * AX.25 035 Alan(GW4PTS) Started fixing to the new * format. + * Hans(PE1AYX) Fixed interface to IP layer. + * Alan(GW4PTS) Added asynchronous support. * * To do: * Restructure the ax25_rcv code to be cleaner/faster and @@ -729,6 +731,8 @@ static ax25_cb *ax25_create_cb(void) MOD_INC_USE_COUNT; + memset(ax25, 0x00, sizeof(*ax25)); + skb_queue_head_init(&ax25->write_queue); skb_queue_head_init(&ax25->frag_queue); skb_queue_head_init(&ax25->ack_queue); @@ -736,8 +740,6 @@ static ax25_cb *ax25_create_cb(void) init_timer(&ax25->timer); - ax25->dama_slave = 0; - ax25->rtt = AX25_DEF_T1 / 2; ax25->t1 = AX25_DEF_T1; ax25->t2 = AX25_DEF_T2; @@ -755,29 +757,8 @@ static ax25_cb *ax25_create_cb(void) ax25->window = AX25_DEF_WINDOW; } - ax25->fragno = 0; - ax25->fraglen = 0; - ax25->hdrincl = 0; - ax25->backoff = AX25_DEF_BACKOFF; - ax25->condition = 0x00; - ax25->t1timer = 0; - ax25->t2timer = 0; - ax25->t3timer = 0; - ax25->n2count = 0; - ax25->idletimer = 0; - - ax25->va = 0; - ax25->vr = 0; - ax25->vs = 0; - - ax25->device = NULL; - ax25->digipeat = NULL; - ax25->sk = NULL; - - ax25->state = AX25_STATE_0; - - memset(&ax25->dest_addr, '\0', AX25_ADDR_LEN); - memset(&ax25->source_addr, '\0', AX25_ADDR_LEN); + ax25->backoff = AX25_DEF_BACKOFF; + ax25->state = AX25_STATE_0; return ax25; } @@ -820,8 +801,6 @@ static void ax25_fillin_cb(ax25_cb *ax25, struct device *dev) ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_MAXQUEUE); ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE); - ax25->dama_slave = 0; - if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) { ax25->modulus = EMODULUS; ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); @@ -1116,7 +1095,19 @@ static void def_callback1(struct sock *sk) static void def_callback2(struct sock *sk, int len) { if (!sk->dead) + { wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket,1); + } +} + +static void def_callback3(struct sock *sk, int len) +{ + if (!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket,2); + } } static int ax25_create(struct socket *sock, int protocol) @@ -1185,7 +1176,7 @@ static int ax25_create(struct socket *sock, int protocol) sk->state_change = def_callback1; sk->data_ready = def_callback2; - sk->write_space = def_callback1; + sk->write_space = def_callback3; sk->error_report = def_callback1; if (sock != NULL) { @@ -1245,7 +1236,7 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev) sk->state_change = def_callback1; sk->data_ready = def_callback2; - sk->write_space = def_callback1; + sk->write_space = def_callback3; sk->error_report = def_callback1; ax25->modulus = osk->protinfo.ax25->modulus; @@ -1367,6 +1358,8 @@ static int ax25_release(struct socket *sock, struct socket *peer) * digipeated via a local address as source. This is a hack until we add * BSD 4.4 ADDIFADDR type support. It is however small and trivially backward * compatible 8) + * + * FIXME: Check family */ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { @@ -1430,6 +1423,11 @@ static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) return 0; } +/* + * FIXME: nonblock behaviour looks like it may have a bug. Also check + * the family in the connect. + */ + static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { @@ -1687,8 +1685,7 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) { struct device *dev_out = dev; - skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ); - if(skb==NULL) + if ((skb = skb_unshare(skb, GFP_ATOMIC, FREE_READ)) == NULL) return 0; /* We are the digipeater. Mark ourselves as repeated @@ -1723,7 +1720,6 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a return 0; } #endif - skb->arp = 1; skb->dev = dev_out; skb->priority = SOPRI_NORMAL; @@ -1764,11 +1760,19 @@ static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_a #ifdef CONFIG_INET case AX25_P_IP: skb_pull(skb,2); /* drop PID/CTRL */ + skb->h.raw = skb->data; + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */ break; case AX25_P_ARP: skb_pull(skb,2); + skb->h.raw = skb->data; + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */ break; #endif @@ -2115,7 +2119,7 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f return -ENOTCONN; /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL) + if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) return er; if (sk->protinfo.ax25->hdrincl) { @@ -2395,6 +2399,7 @@ EXPORT_SYMBOL(ax25cmp); EXPORT_SYMBOL(ax2asc); EXPORT_SYMBOL(asc2ax); EXPORT_SYMBOL(null_ax25_address); +#endif #ifdef CONFIG_PROC_FS static struct proc_dir_entry proc_ax25_route = { @@ -2431,7 +2436,7 @@ void ax25_proto_init(struct net_proto *pro) proc_net_register(&proc_ax25_calls); #endif - printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.34 for Linux NET3.037 (Linux 2.1)\n"); + printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.35 for Linux NET3.038 (Linux 2.1)\n"); } /* @@ -2605,7 +2610,7 @@ void cleanup_module(void) ax25_packet_type.type = htons(ETH_P_AX25); dev_remove_pack(&ax25_packet_type); - sock_unregister(ax25_proto_ops.family); + sock_unregister(AF_AX25); } #endif diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index c1077f46660e..1e94adfda053 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -1,10 +1,10 @@ /* - * AX.25 release 033 + * AX.25 release 035 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or @@ -35,6 +35,7 @@ * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. * AX.25 033 Jonathan(G4KLX) Remove auto-router. * Modularisation changes. + * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. */ #include @@ -161,7 +162,10 @@ static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) #ifdef CONFIG_INET if (pid == AX25_P_IP) { skb_pull(skb, 1); /* Remove PID */ - skb->h.raw = skb->data; + skb->h.raw = skb->data; + skb->nh.raw = skb->data; + skb->dev = ax25->device; + skb->pkt_type = PACKET_HOST; ip_rcv(skb, ax25->device, NULL); /* Wrong ptype */ return 1; } diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index 1eb4d54aa75e..a12f18fa544b 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -1,10 +1,10 @@ /* - * AX.25 release 033 + * AX.25 release 035 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index b4606111ec22..4ad193d3b0d0 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -1,10 +1,10 @@ /* - * AX.25 release 033 + * AX.25 release 035 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index 7c78516431f9..aac8d7eb8849 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -1,10 +1,10 @@ /* - * AX.25 release 033 + * AX.25 release 035 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.3.61 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index f6ce6e00bad4..d07465c94e62 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -1,10 +1,10 @@ /* - * AX.25 release 033 + * AX.25 release 035 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/core/datagram.c b/net/core/datagram.c index 592583731023..35af151aea94 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -188,8 +188,9 @@ int skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to, } /* - * Datagram select: Again totally generic. Moved from udp.c - * Now does seqpacket. + * Datagram select: Again totally generic. This also handles + * sequenced packet sockets providing the socket receive queue + * is only ever holding data ready to receive. */ int datagram_select(struct socket *sock, int sel_type, select_table *wait) diff --git a/net/core/dev.c b/net/core/dev.c index 67f33887c7e9..14addfb7fbef 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -78,9 +78,7 @@ #include #include #include -#ifdef CONFIG_NET_ALIAS #include -#endif #ifdef CONFIG_KERNELD #include #endif @@ -91,14 +89,30 @@ /* * The list of devices, that are able to output. */ + static struct device *dev_up_base; /* * The list of packet types we will receive (as opposed to discard) * and the routines to invoke. + * + * Why 16. Because with 16 the only overlap we get on a hash of the + * low nibble of the protocol value is RARP/SNAP/X.25. + * + * 0800 IP + * 0001 802.3 + * 0002 AX.25 + * 0004 802.2 + * 8035 RARP + * 0005 SNAP + * 0805 X.25 + * 0806 ARP + * 8137 IPX + * 0009 Localtalk + * 86DD IPv6 */ -struct packet_type *ptype_base[16]; +struct packet_type *ptype_base[16]; /* 16 way hashed list */ struct packet_type *ptype_all = NULL; /* Taps */ /* @@ -243,6 +257,7 @@ int dev_open(struct device *dev) /* * Call device private open method */ + if (dev->open) ret = dev->open(dev); @@ -259,11 +274,12 @@ int dev_open(struct device *dev) */ dev_mc_upload(dev); notifier_call_chain(&netdev_chain, NETDEV_UP, dev); -#ifdef CONFIG_NET_ALIAS - if (!net_alias_is(dev) || dev->tx_queue_len) -#else - if (dev->tx_queue_len) -#endif + /* + * FIXME: This logic was wrong before. Now its + * obviously so. I think the change here (removing the + * ! on the net_alias_is) is right. ANK ?? + */ + if (net_alias_is(dev) || dev->tx_queue_len) { cli(); dev->next_up = dev_up_base; @@ -301,6 +317,7 @@ int dev_close(struct device *dev) /* * Tell people we are going down */ + notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev); /* * Flush the multicast chain @@ -318,6 +335,10 @@ int dev_close(struct device *dev) ct++; } + /* + * The device is no longer up. Drop it from the list. + */ + devp = &dev_up_base; while (*devp) { @@ -347,6 +368,34 @@ int unregister_netdevice_notifier(struct notifier_block *nb) return notifier_chain_unregister(&netdev_chain,nb); } +/* + * Support routine. Sends outgoing frames to any network + * taps currently in use. + */ + +static void queue_xmit_nit(struct sk_buff *skb, struct device *dev) +{ + struct packet_type *ptype; + get_fast_time(&skb->stamp); + + for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) + { + /* Never send packets back to the socket + * they originated from - MvS (miquels@drinkel.ow.org) + */ + if ((ptype->dev == dev || !ptype->dev) && + ((struct sock *)ptype->data != skb->sk)) + { + struct sk_buff *skb2; + if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) + break; + skb2->mac.raw = skb2->data; + skb2->nh.raw = skb2->h.raw = skb2->data + dev->hard_header_len; + ptype->func(skb2, skb->dev, ptype); + } + } +} + /* * Send (or queue for sending) a packet. * @@ -403,41 +452,30 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) list = dev->buffs + pri; save_flags(flags); - /* if this isn't a retransmission, use the first packet instead... */ - if (!retransmission) { - if (skb_queue_len(list)) { - /* avoid overrunning the device queue.. */ - if (skb_queue_len(list) > dev->tx_queue_len) { - dev_kfree_skb(skb, FREE_WRITE); - return; - } - } - /* copy outgoing packets to any sniffer packet handlers */ - if (dev_nit) { - struct packet_type *ptype; - - get_fast_time(&skb->stamp); + /* + * If this isn't a retransmission, use the first packet instead. + * Note: We don't do strict priority ordering here. We will in + * fact kick the queue that is our priority. The dev_tint reload + * does strict priority queueing. In effect what we are doing here + * is to add some random jitter to the queues and to do so by + * saving clocks. Doing a perfect priority queue isn't a good idea + * as you get some fascinating timing interactions. + */ - for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) - { - /* Never send packets back to the socket - * they originated from - MvS (miquels@drinkel.ow.org) - */ - if ((ptype->dev == dev || !ptype->dev) && - ((struct sock *)ptype->data != skb->sk)) - { - struct sk_buff *skb2; - if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) - break; - skb2->mac.raw = skb2->data; - skb2->nh.raw = - skb2->h.raw = skb2->data + dev->hard_header_len; - ptype->func(skb2, skb->dev, ptype); - } - } + if (!retransmission) + { + /* avoid overrunning the device queue.. */ + if (skb_queue_len(list) > dev->tx_queue_len) + { + dev_kfree_skb(skb, FREE_WRITE); + return; } + /* copy outgoing packets to any sniffer packet handlers */ + if (dev_nit) + queue_xmit_nit(skb,dev); + if (skb_queue_len(list)) { cli(); __skb_queue_tail(list, skb); @@ -461,13 +499,16 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) restore_flags(flags); } +/* + * Entry point for transmitting frames. + */ + int dev_queue_xmit(struct sk_buff *skb) { struct device *dev = skb->dev; start_bh_atomic(); - #if CONFIG_SKB_CHECK IS_SKB(skb); #endif @@ -477,13 +518,23 @@ int dev_queue_xmit(struct sk_buff *skb) * This can cover all protocols and technically not just ARP either. */ - if (!skb->arp) { - if (dev->rebuild_header) { - if (dev->rebuild_header(skb)) { + if (!skb->arp) + { + /* + * FIXME: we should make the printk for no rebuild + * header a default rebuild_header routine and drop + * this call. Similarly we should make hard_header + * have a default NULL operation not check conditions. + */ + if (dev->rebuild_header) + { + if (dev->rebuild_header(skb)) + { end_bh_atomic(); return 0; } - } else + } + else printk("%s: !skb->arp & !rebuild_header!\n", dev->name); } @@ -495,16 +546,18 @@ int dev_queue_xmit(struct sk_buff *skb) * */ -#ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) skb->dev = dev = net_alias_main_dev(dev); -#endif do_dev_queue_xmit(skb, dev, skb->priority); end_bh_atomic(); return 0; } +/* + * Fast path for loopback frames. + */ + void dev_loopback_xmit(struct sk_buff *skb) { struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC); @@ -521,8 +574,7 @@ void dev_loopback_xmit(struct sk_buff *skb) /* * Receive a packet from a device driver and queue it for the upper - * (protocol) levels. It always succeeds. This is the recommended - * interface to use. + * (protocol) levels. It always succeeds. */ void netif_rx(struct sk_buff *skb) @@ -582,7 +634,8 @@ static void dev_transmit(void) for (dev = dev_up_base; dev != NULL; dev = dev->next_up) { - if (dev->flags != 0 && !dev->tbusy) { + if (dev->flags != 0 && !dev->tbusy) + { /* * Kick the device */ @@ -636,7 +689,8 @@ void net_bh(void) * disabling interrupts. */ - while (!skb_queue_empty(&backlog)) { + while (!skb_queue_empty(&backlog)) + { struct sk_buff * skb = backlog.next; /* @@ -658,8 +712,6 @@ void net_bh(void) continue; } - - #ifdef CONFIG_BRIDGE /* @@ -783,9 +835,7 @@ void net_bh(void) * One last output flush. */ -#ifdef XMIT_AFTER dev_transmit(); -#endif } @@ -804,12 +854,11 @@ void dev_tint(struct device *dev) * aliases do not transmit (for now :) ) */ -#ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) { printk("net alias %s transmits\n", dev->name); return; } -#endif + head = dev->buffs; save_flags(flags); cli(); @@ -848,7 +897,7 @@ void dev_tint(struct device *dev) /* * Perform a SIOCGIFCONF call. This structure will change - * size shortly, and there is nothing I can do about it. + * size eventually, and there is nothing I can do about it. * Thus we will need a 'compatibility mode'. */ @@ -1426,6 +1475,7 @@ extern void dlci_setup(void); extern int pt_init(void); extern int sm_init(void); extern int baycom_init(void); +extern int lapbeth_init(void); #ifdef CONFIG_PROC_FS static struct proc_dir_entry proc_net_dev = { @@ -1496,6 +1546,9 @@ int net_dev_init(void) #endif #if defined(CONFIG_SOUNDMODEM) sm_init(); +#endif +#if defined(CONFIG_LAPBETHER) + lapbeth_init(); #endif /* * SLHC if present needs attaching so other people see it diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 710ed09c1584..f86bc158b398 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -43,19 +42,17 @@ #include #include #include -#ifdef CONFIG_NET_ALIAS #include -#endif /* - * Device multicast list maintenance. This knows about such little matters as promiscuous mode and - * converting from the list to the array the drivers use. At least until I fix the drivers up. + * Device multicast list maintenance. * - * This is used both by IP and by the user level maintenance functions. Unlike BSD we maintain a usage count - * on a given multicast address so that a casual user application can add/delete multicasts used by protocols - * without doing damage to the protocols when it deletes the entries. It also helps IP as it tracks overlapping - * maps. + * This is used both by IP and by the user level maintenance functions. + * Unlike BSD we maintain a usage count on a given multicast address so + * that a casual user application can add/delete multicasts used by + * protocols without doing damage to the protocols when it deletes the + * entries. It also helps IP as it tracks overlapping maps. */ @@ -72,9 +69,13 @@ void dev_mc_upload(struct device *dev) if(!(dev->flags&IFF_UP)) return; -#ifdef CONFIG_NET_ALIAS + /* + * An aliased device should end up with the combined + * multicast list of all its aliases. + * [Check this is still ok -AC] + */ + dev = net_alias_main_dev(dev); -#endif /* * Devices with no set multicast don't get set @@ -93,19 +94,29 @@ void dev_mc_upload(struct device *dev) void dev_mc_delete(struct device *dev, void *addr, int alen, int all) { struct dev_mc_list **dmi; -#ifdef CONFIG_NET_ALIAS dev = net_alias_main_dev(dev); -#endif + for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next) { + /* + * Find the entry we want to delete. The device could + * have variable length entries so check these too. + */ if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen) { struct dev_mc_list *tmp= *dmi; if(--(*dmi)->dmi_users && !all) return; + /* + * Last user. So delete the entry. + */ *dmi=(*dmi)->next; dev->mc_count--; kfree_s(tmp,sizeof(*tmp)); + /* + * We have altered the list, so the card + * loaded filter is now wrong. Fix it + */ dev_mc_upload(dev); return; } @@ -119,9 +130,9 @@ void dev_mc_delete(struct device *dev, void *addr, int alen, int all) void dev_mc_add(struct device *dev, void *addr, int alen, int newonly) { struct dev_mc_list *dmi; -#ifdef CONFIG_NET_ALIAS + dev = net_alias_main_dev(dev); -#endif + for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next) { if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen) @@ -149,10 +160,8 @@ void dev_mc_add(struct device *dev, void *addr, int alen, int newonly) void dev_mc_discard(struct device *dev) { -#ifdef CONFIG_NET_ALIAS if (net_alias_is(dev)) return; -#endif while(dev->mc_list!=NULL) { struct dev_mc_list *tmp=dev->mc_list; diff --git a/net/core/firewall.c b/net/core/firewall.c index 791d64d0e9bf..ef00387f3ebf 100644 --- a/net/core/firewall.c +++ b/net/core/firewall.c @@ -29,6 +29,10 @@ int register_firewall(int pf, struct firewall_ops *fw) * Don't allow two people to adjust at once. */ + /* + * FIXME: Swap for a kernel semaphore object + */ + while(firewall_lock) schedule(); firewall_lock=1; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 4266c9bf3648..0650d7da6c89 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -137,17 +137,17 @@ struct neighbour * neigh_lookup(struct neigh_table *tbl, void *pkey, if (neigh) { do { - if (memcmp(&neigh->primary_key, pkey, key_len) == 0) + if (memcmp(neigh->primary_key, pkey, key_len) == 0) { if (!dev || dev == neigh->dev) - break; + return neigh; } neigh = neigh->next; } while (neigh != head); } - return neigh; + return NULL; } /* diff --git a/net/core/net_alias.c b/net/core/net_alias.c index ccbb293115e2..54aafeadf90e 100644 --- a/net/core/net_alias.c +++ b/net/core/net_alias.c @@ -51,7 +51,7 @@ #endif /* - * Only allow the following flags to pass from main device to aliases + * Only allow the following flags to pass from main device to aliases */ #define NET_ALIAS_IFF_MASK (IFF_UP|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MULTICAST) @@ -61,7 +61,6 @@ static int nat_attach_chg(struct net_alias_type *nat, int delta); static int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa); static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias); - static int net_alias_devinit(struct device *dev); static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev); static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa); @@ -71,54 +70,53 @@ static struct device *net_alias_dev_delete(struct device *main_dev, int slot, in static void net_alias_free(struct device *dev); /* - * net_alias_type base array, will hold net_alias_type obj hashed list heads. + * net_alias_type base array, will hold net_alias_type obj hashed list + * heads. */ struct net_alias_type *nat_base[16]; /* - * get net_alias_type ptr by type + * Get net_alias_type ptr by type */ -static __inline__ struct net_alias_type * -nat_getbytype(int type) +extern __inline__ struct net_alias_type *nat_getbytype(int type) { - struct net_alias_type *nat; - for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next) - { - if (nat->type == type) return nat; - } - return NULL; + struct net_alias_type *nat; + for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next) + { + if (nat->type == type) + return nat; + } + return NULL; } /* - * get addr32 representation (pre-hashing) of address. - * if NULL nat->get_addr32, assume sockaddr_in struct (IP-ish). + * Get addr32 representation (pre-hashing) of address. + * If NULL nat->get_addr32, assume sockaddr_in struct (IP-ish). */ -static __inline__ __u32 -nat_addr32(struct net_alias_type *nat, struct sockaddr *sa) +extern __inline__ __u32 nat_addr32(struct net_alias_type *nat, struct sockaddr *sa) { - if (nat->get_addr32) - return nat->get_addr32(nat, sa); - else - return (*(struct sockaddr_in *)sa).sin_addr.s_addr; + if (nat->get_addr32) + return nat->get_addr32(nat, sa); + else + return (*(struct sockaddr_in *)sa).sin_addr.s_addr; } /* - * hashing code for alias_info->hash_tab entries - * 4 bytes -> 1/2 byte using xor complemented by af + * Hashing code for alias_info->hash_tab entries + * 4 bytes -> 1/2 byte using xor complemented by af */ -static __inline__ unsigned -HASH(__u32 addr, int af) +extern __inline__ unsigned HASH(__u32 addr, int af) { - unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */ - tmp ^= (tmp>>8); /* 2 -> 1 */ - return (tmp^(tmp>>4)^af) & 0x0f; /* 1 -> 1/2 */ + unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */ + tmp ^= (tmp>>8); /* 2 -> 1 */ + return (tmp^(tmp>>4)^af) & 0x0f; /* 1 -> 1/2 */ } @@ -129,969 +127,1014 @@ HASH(__u32 addr, int af) * address to a hash code. */ -static __inline__ int -nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa) +extern __inline__ int nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa) { - return HASH(nat_addr32(nat,sa), sa->sa_family); + return HASH(nat_addr32(nat,sa), sa->sa_family); } /* - * change net_alias_type number of attachments (bindings) + * Change net_alias_type number of attachments (bindings) */ -static int -nat_attach_chg(struct net_alias_type *nat, int delta) +static int nat_attach_chg(struct net_alias_type *nat, int delta) { - unsigned long flags; - int n_at; - if (!nat) return -1; - save_flags(flags); - cli(); - n_at = nat->n_attach + delta; - if (n_at < 0) - { - restore_flags(flags); - printk(KERN_WARNING "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n", - nat->type); - return -1; - } - nat->n_attach = n_at; - restore_flags(flags); - return 0; + unsigned long flags; + int n_at; + if (!nat) + return -1; + save_flags(flags); + cli(); + n_at = nat->n_attach + delta; + if (n_at < 0) + { + restore_flags(flags); + printk(KERN_WARNING + "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n", + nat->type); + return -1; + } + nat->n_attach = n_at; + restore_flags(flags); + return 0; } /* - * bind alias to its type (family) object and call initialization hook + * Bind alias to its type (family) object and call initialization hook */ -static __inline__ int -nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa) +extern __inline__ int nat_bind(struct net_alias_type *nat, + struct net_alias *alias, struct sockaddr *sa) { - if (nat->alias_init_1) nat->alias_init_1(nat, alias, sa); - return nat_attach_chg(nat, +1); + if (nat->alias_init_1) + nat->alias_init_1(nat, alias, sa); + return nat_attach_chg(nat, +1); } /* - * unbind alias from type object and call alias destructor + * Unbind alias from type object and call alias destructor */ -static __inline__ int -nat_unbind(struct net_alias_type *nat, struct net_alias *alias) +extern __inline__ int nat_unbind(struct net_alias_type *nat, + struct net_alias *alias) { - if (nat->alias_done_1) nat->alias_done_1(nat, alias); - return nat_attach_chg(nat, -1); + if (nat->alias_done_1) + nat->alias_done_1(nat, alias); + return nat_attach_chg(nat, -1); } /* - * compare device address with given. if NULL nat->dev_addr_chk, - * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish) + * Compare device address with given. if NULL nat->dev_addr_chk, + * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish) */ static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat, struct device *dev, struct sockaddr *sa) { - if (nat->dev_addr_chk) - return nat->dev_addr_chk(nat, dev, sa); - else - return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr); + if (nat->dev_addr_chk) + return nat->dev_addr_chk(nat, dev, sa); + else + return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr); } /* - * alias device init() - * do nothing. + * Alias device init() + * do nothing. */ -static int -net_alias_devinit(struct device *dev) +static int net_alias_devinit(struct device *dev) { #ifdef ALIAS_USER_LAND_DEBUG - printk("net_alias_devinit(%s) called.\n", dev->name); + printk("net_alias_devinit(%s) called.\n", dev->name); #endif - return 0; + return 0; } /* - * hard_start_xmit() should not be called. - * ignore ... but shout!. + * Hard_start_xmit() should not be called. + * ignore ... but shout!. */ -static int -net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev) +static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev) { - printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name); - dev_kfree_skb(skb, FREE_WRITE); - return 0; + printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name); + dev_kfree_skb(skb, FREE_WRITE); + return 0; } -static int -net_alias_open(struct device * dev) +static int net_alias_open(struct device * dev) { - return 0; + return 0; } -static int -net_alias_close(struct device * dev) +static int net_alias_close(struct device * dev) { - return 0; + return 0; } /* * setups a new (alias) device */ -static int -net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, - struct sockaddr *sa) +static int net_alias_devsetup(struct net_alias *alias, + struct net_alias_type *nat, struct sockaddr *sa) { - struct device *main_dev; - struct device *dev; - int family; - int i; - - /* - * - * generic device setup based on main_dev info - * - * FIXME: is NULL bitwise 0 for all Linux platforms? - */ - - main_dev = alias->main_dev; - dev = &alias->dev; - memset(dev, '\0', sizeof(struct device)); - family = (sa)? sa->sa_family : main_dev->family; - - dev->alias_info = NULL; /* no aliasing recursion */ - dev->my_alias = alias; /* point to alias */ - dev->name = alias->name; - dev->type = main_dev->type; - dev->open = net_alias_open; - dev->stop = net_alias_close; - dev->hard_header_len = main_dev->hard_header_len; - memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN); - memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN); - dev->addr_len = main_dev->addr_len; - dev->init = net_alias_devinit; - dev->hard_start_xmit = net_alias_hard_start_xmit; - dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP; - - /* - * only makes sense if same family - */ - - if (family == main_dev->family) - { - dev->metric = main_dev->metric; - dev->mtu = main_dev->mtu; - dev->pa_alen = main_dev->pa_alen; - dev->hard_header = main_dev->hard_header; - dev->hard_header_cache = main_dev->hard_header_cache; - dev->header_cache_update = main_dev->header_cache_update; - dev->rebuild_header = main_dev->rebuild_header; - } - - /* - * Fill in the generic fields of the device structure. - * not actually used, avoids some dev.c #ifdef's - */ - - for (i = 0; i < DEV_NUMBUFFS; i++) - skb_queue_head_init(&dev->buffs[i]); - - dev->family = family; - return 0; + struct device *main_dev; + struct device *dev; + int family; + int i; + + /* + * + * generic device setup based on main_dev info + * + * FIXME: is NULL bitwise 0 for all Linux platforms? + */ + + main_dev = alias->main_dev; + dev = &alias->dev; + memset(dev, '\0', sizeof(struct device)); + family = (sa)? sa->sa_family : main_dev->family; + + dev->alias_info = NULL; /* no aliasing recursion */ + dev->my_alias = alias; /* point to alias */ + dev->name = alias->name; + dev->type = main_dev->type; + dev->open = net_alias_open; + dev->stop = net_alias_close; + dev->hard_header_len = main_dev->hard_header_len; + memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN); + memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN); + dev->addr_len = main_dev->addr_len; + dev->init = net_alias_devinit; + dev->hard_start_xmit = net_alias_hard_start_xmit; + dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP; + + /* + * Only makes sense if same family (arguable) + */ + + if (family == main_dev->family) + { + dev->metric = main_dev->metric; + dev->mtu = main_dev->mtu; + dev->pa_alen = main_dev->pa_alen; + dev->hard_header = main_dev->hard_header; + dev->hard_header_cache = main_dev->hard_header_cache; + dev->header_cache_update = main_dev->header_cache_update; + dev->rebuild_header = main_dev->rebuild_header; + } + + /* + * Fill in the generic fields of the device structure. + * not actually used, avoids some dev.c #ifdef's + */ + + for (i = 0; i < DEV_NUMBUFFS; i++) + skb_queue_head_init(&dev->buffs[i]); + + dev->family = family; + return 0; } /* - * slow alias find (parse the whole hash_tab) - * returns: alias' pointer address + * Slow alias find (parse the whole hash_tab) + * returns: alias' pointer address */ -static struct net_alias ** -net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias) +static struct net_alias **net_alias_slow_findp(struct net_alias_info + *alias_info, struct net_alias *alias) { - unsigned idx, n_aliases; - struct net_alias **aliasp; - - /* - * for each alias_info's hash_tab entry, for every alias ... - */ - - n_aliases = alias_info->n_aliases; - for (idx=0; idx < 16 ; idx++) - for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next) - if (*aliasp == alias) - return aliasp; - else - if (--n_aliases == 0) break; /* faster give up */ - return NULL; + unsigned idx, n_aliases; + struct net_alias **aliasp; + + /* + * For each alias_info's hash_tab entry, for every alias ... + */ + + n_aliases = alias_info->n_aliases; + for (idx=0; idx < 16 ; idx++) + { + for (aliasp = &alias_info->hash_tab[idx];*aliasp; + aliasp = &(*aliasp)->next) + { + if (*aliasp == alias) + return aliasp; + else + if (--n_aliases == 0) + break; /* faster give up */ + } + } + return NULL; } /* - * create alias device for main_dev with given slot num. - * if sa==NULL will create a same_family alias device + * Create alias device for main_dev with given slot num. + * if sa==NULL will create a same_family alias device. */ -static struct device * -net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data) +static struct device *net_alias_dev_create(struct device *main_dev, int slot, + int *err, struct sockaddr *sa, void *data) { - struct net_alias_info *alias_info; - struct net_alias *alias, **aliasp; - struct net_alias_type *nat; - struct device *dev; - unsigned long flags; - int family; - __u32 addr32; - - /* FIXME: lock */ - alias_info = main_dev->alias_info; + struct net_alias_info *alias_info; + struct net_alias *alias, **aliasp; + struct net_alias_type *nat; + struct device *dev; + unsigned long flags; + int family; + __u32 addr32; + + /* FIXME: lock */ + + alias_info = main_dev->alias_info; - /* - * if NULL address given, take family from main_dev - */ + /* + * If NULL address given, take family from main_dev + */ - family = (sa)? sa->sa_family : main_dev->family; + family = (sa)? sa->sa_family : main_dev->family; - /* - * check if wanted family has a net_alias_type object registered - */ + /* + * Check if wanted family has a net_alias_type object registered + */ - nat = nat_getbytype(family); - if (!nat) { + nat = nat_getbytype(family); + if (!nat) + { #ifdef CONFIG_KERNELD - char modname[20]; - sprintf (modname,"netalias-%d", family); - request_module(modname); + char modname[20]; + sprintf (modname,"netalias-%d", family); + request_module(modname); - nat = nat_getbytype(family); - if (!nat) { + nat = nat_getbytype(family); + if (!nat) + { #endif - printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n", - main_dev->name, slot, family); - /* *err = -EAFNOSUPPORT; */ - *err = -EINVAL; - return NULL; + printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n", + main_dev->name, slot, family); + /* *err = -EAFNOSUPPORT; */ + *err = -EINVAL; + return NULL; #ifdef CONFIG_KERNELD - } + } #endif - } + } - /* - * do not allow creation over downed devices - */ + /* + * Do not allow creation over downed devices + */ - *err = -EIO; + *err = -EIO; - if (! (main_dev->flags & IFF_UP) ) - return NULL; + if (! (main_dev->flags & IFF_UP) ) + return NULL; - /* - * if first alias, must also create alias_info - */ + /* + * If first alias, must also create alias_info + */ - *err = -ENOMEM; - - if (!alias_info) - { - alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL); - if (!alias_info) return NULL; /* ENOMEM */ - memset(alias_info, 0, sizeof(struct net_alias_info)); - } - - if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL))) - return NULL; /* ENOMEM */ - - /* - * FIXME: is NULL bitwise 0 for all Linux platforms? - */ - - memset(alias, 0, sizeof(struct net_alias)); - alias->slot = slot; - alias->main_dev = main_dev; - alias->nat = nat; - alias->next = NULL; - alias->data = data; - sprintf(alias->name, "%s:%d", main_dev->name, slot); + *err = -ENOMEM; + + if (!alias_info) + { + alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL); + if (!alias_info) + return NULL; /* ENOMEM */ + memset(alias_info, 0, sizeof(struct net_alias_info)); + } + + if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL))) + return NULL; /* ENOMEM */ + + memset(alias, 0, sizeof(struct net_alias)); + alias->slot = slot; + alias->main_dev = main_dev; + alias->nat = nat; + alias->next = NULL; + alias->data = data; + sprintf(alias->name, "%s:%d", main_dev->name, slot); - /* - * initialise alias' device structure - */ + /* + * Initialise alias' device structure + */ - net_alias_devsetup(alias, nat, sa); + net_alias_devsetup(alias, nat, sa); - dev = &alias->dev; + dev = &alias->dev; - save_flags(flags); - cli(); + save_flags(flags); + cli(); - /* - * bind alias to its object type - * nat_bind calls nat->alias_init_1 - */ + /* + * bind alias to its object type + * nat_bind calls nat->alias_init_1 + */ - nat_bind(nat, alias, sa); + nat_bind(nat, alias, sa); - /* - * if no address passed, take from device (could have been - * set by nat->alias_init_1) - */ + /* + * If no address passed, take from device (could have been + * set by nat->alias_init_1) + */ - addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr; + addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr; - /* - * store hash key in alias: will speed-up rehashing and deletion - */ + /* + * Store hash key in alias: will speed-up rehashing and deletion + */ - alias->hash = HASH(addr32, family); + alias->hash = HASH(addr32, family); - /* - * insert alias in hashed linked list - */ + /* + * Insert alias in hashed linked list + */ - aliasp = &alias_info->hash_tab[alias->hash]; - alias->next = *aliasp; - *aliasp = alias; + aliasp = &alias_info->hash_tab[alias->hash]; + alias->next = *aliasp; + *aliasp = alias; - /* - * if first alias ... - */ + /* + * If first alias ... + */ - if (!alias_info->n_aliases++) - { - alias_info->taildev = main_dev; - main_dev->alias_info = alias_info; - } + if (!alias_info->n_aliases++) + { + alias_info->taildev = main_dev; + main_dev->alias_info = alias_info; + } - /* - * add device at tail (just after last main_dev alias) - */ + /* + * add device at tail (just after last main_dev alias) + */ - dev->next = alias_info->taildev->next; - alias_info->taildev->next = dev; - alias_info->taildev = dev; - restore_flags(flags); - return dev; + dev->next = alias_info->taildev->next; + alias_info->taildev->next = dev; + alias_info->taildev = dev; + restore_flags(flags); + return dev; } /* - * delete one main_dev alias (referred by its slot num) + * Delete one main_dev alias (referred by its slot num) */ -static struct device * -net_alias_dev_delete(struct device *main_dev, int slot, int *err) +static struct device *net_alias_dev_delete(struct device *main_dev, int slot, + int *err) { - struct net_alias_info *alias_info; - struct net_alias *alias, **aliasp; - struct device *dev; - unsigned n_aliases; - unsigned long flags; - struct net_alias_type *nat; - struct device *prevdev; - - /* FIXME: lock */ - *err = -ENODEV; - - if (main_dev == NULL) return NULL; + struct net_alias_info *alias_info; + struct net_alias *alias, **aliasp; + struct device *dev; + unsigned n_aliases; + unsigned long flags; + struct net_alias_type *nat; + struct device *prevdev; - /* - * does main_dev have aliases? - */ + /* FIXME: lock */ + *err = -ENODEV; - alias_info = main_dev->alias_info; - if (!alias_info) return NULL; /* ENODEV */ - - n_aliases = alias_info->n_aliases; + if (main_dev == NULL) + return NULL; + + /* + * Does main_dev have aliases? + */ - /* - * find device that holds the same slot number (could also - * be strcmp() ala dev_get). - */ + alias_info = main_dev->alias_info; + if (!alias_info) + return NULL; /* ENODEV */ - for (prevdev=main_dev, alias = NULL;prevdev->next && n_aliases; prevdev = prevdev->next) - { - if (!(alias = prevdev->next->my_alias)) - { - printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n"); - continue; /* or should give up? */ - } - if (alias->slot == slot) break; - alias = NULL; - n_aliases--; - } + n_aliases = alias_info->n_aliases; - if (!alias) return NULL; /* ENODEV */ + /* + * Find device that holds the same slot number (could also + * be strcmp() ala dev_get). + */ - dev = &alias->dev; + for (prevdev=main_dev, alias = NULL; + prevdev->next && n_aliases; prevdev = prevdev->next) + { + if (!(alias = prevdev->next->my_alias)) + { + printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n"); + continue; /* or should give up? */ + } + if (alias->slot == slot) + break; + alias = NULL; + n_aliases--; + } + + if (!alias) + return NULL; /* ENODEV */ + + dev = &alias->dev; - /* - * find alias hashed entry - */ + /* + * Find alias hashed entry + */ - for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next) - if(*aliasp == alias) break; + for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; + aliasp = &(*aliasp)->next) + { + if(*aliasp == alias) + break; + } - /* - * if not found (???), try a full search - */ - - if (*aliasp != alias) - if ((aliasp = net_alias_slow_findp(alias_info, alias))) - printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name); - else - { - printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name); - return NULL; /* ENODEV */ - } + /* + * If not found (???), try a full search + */ - nat = alias->nat; + if (*aliasp != alias) + { + if ((aliasp = net_alias_slow_findp(alias_info, alias))) + printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name); + else + { + printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name); + return NULL; /* ENODEV */ + } + } + nat = alias->nat; - save_flags(flags); - cli(); + save_flags(flags); + cli(); - /* - * unbind alias from alias_type obj. - */ + /* + * Unbind alias from alias_type obj. + */ - nat_unbind(nat, alias); + nat_unbind(nat, alias); - /* - * is alias at tail? - */ + /* + * Is alias at tail? + */ - if ( dev == alias_info->taildev ) - alias_info->taildev = prevdev; + if ( dev == alias_info->taildev ) + alias_info->taildev = prevdev; - /* - * unlink and close device - */ - prevdev->next = dev->next; - dev_close(dev); + /* + * Unlink and close device + */ + prevdev->next = dev->next; + dev_close(dev); - /* - * unlink alias - */ + /* + * Unlink alias + */ - *aliasp = (*aliasp)->next; + *aliasp = (*aliasp)->next; + if (--alias_info->n_aliases == 0) /* last alias */ + main_dev->alias_info = NULL; - if (--alias_info->n_aliases == 0) /* last alias */ - main_dev->alias_info = NULL; - restore_flags(flags); + restore_flags(flags); - /* - * now free structures - */ + /* + * Now free structures + */ - kfree_s(alias, sizeof(struct net_alias)); - if (main_dev->alias_info == NULL) - kfree_s(alias_info, sizeof(struct net_alias_info)); + kfree_s(alias, sizeof(struct net_alias)); + if (main_dev->alias_info == NULL) + kfree_s(alias_info, sizeof(struct net_alias_info)); - /* - * deletion ok (*err=0), NULL device returned. - */ + /* + * Deletion ok (*err=0), NULL device returned. + */ - *err = 0; - return NULL; + *err = 0; + return NULL; } /* - * free all main device aliasing stuff - * will be called on dev_close(main_dev) + * Free all main device aliasing stuff + * will be called on dev_close(main_dev) */ -static void -net_alias_free(struct device *main_dev) +static void net_alias_free(struct device *main_dev) { - struct net_alias_info *alias_info; - struct net_alias *alias; - struct net_alias_type *nat; - struct device *dev; - unsigned long flags; + struct net_alias_info *alias_info; + struct net_alias *alias; + struct net_alias_type *nat; + struct device *dev; + unsigned long flags; - /* - * do I really have aliases? - */ + /* + * Do I really have aliases? + */ - if (!(alias_info = main_dev->alias_info)) return; + if (!(alias_info = main_dev->alias_info)) + return; - /* - * fast device link "short-circuit": set main_dev->next to - * device after last alias - */ + /* + * Fast device link "short-circuit": set main_dev->next to + * device after last alias + */ - save_flags(flags); - cli(); + save_flags(flags); + cli(); - dev = main_dev->next; - main_dev->next = alias_info->taildev->next; - main_dev->alias_info = NULL; - alias_info->taildev->next = NULL; + dev = main_dev->next; + main_dev->next = alias_info->taildev->next; + main_dev->alias_info = NULL; + alias_info->taildev->next = NULL; - restore_flags(flags); + restore_flags(flags); - /* - * loop over alias devices, free and dev_close() - */ - - while (dev) - { - if (net_alias_is(dev)) - { - alias = dev->my_alias; - if (alias->main_dev == main_dev) - { /* - * unbind alias from alias_type object + * Loop over alias devices, free and dev_close() */ - - nat = alias->nat; - if (nat) + + while (dev) { - nat_unbind(nat, alias); - } /* else error/printk ??? */ + if (net_alias_is(dev)) + { + alias = dev->my_alias; + if (alias->main_dev == main_dev) + { + /* + * unbind alias from alias_type object + */ + nat = alias->nat; + if (nat) + { + nat_unbind(nat, alias); + } /* else error/printk ??? */ + + dev_close(dev); + dev = dev->next; - dev_close(dev); - dev = dev->next; - - kfree_s(alias, sizeof(struct net_alias)); - continue; - } - else - printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n", - main_dev->name, alias->name); - } - else - printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n", - main_dev->name); - dev = dev->next; - } - - kfree_s(alias_info, sizeof(alias_info)); - return; + kfree_s(alias, sizeof(struct net_alias)); + continue; + } + else + printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n", + main_dev->name, alias->name); + } + else + { + printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n", + main_dev->name); + } + dev = dev->next; + } + + kfree_s(alias_info, sizeof(alias_info)); + return; } /* - * dev_get() with added alias naming magic. + * dev_get() with added alias naming magic. */ -struct device * -net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, +struct device *net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, struct sockaddr *sa, void *data) { - struct device *dev; - char *sptr,*eptr; - int slot = 0; - int delete = 0; + struct device *dev; + char *sptr,*eptr; + int slot = 0; + int delete = 0; - *err = -ENODEV; - if ((dev=dev_get(dev_name))) - return dev; + *err = -ENODEV; + if ((dev=dev_get(dev_name))) + return dev; - /* - * want alias naming magic? - */ + /* + * Want alias naming magic? + */ - if (!aliasing_ok) return NULL; + if (!aliasing_ok) + return NULL; - if (!dev_name || !*dev_name) - return NULL; - - /* - * find the first ':' , must be followed by, at least, 1 char - */ + if (!dev_name || !*dev_name) + return NULL; - for (sptr=dev_name ; *sptr ; sptr++) if(*sptr==':') break; - if (!*sptr || !*(sptr+1)) - return NULL; - - /* - * seems to be an alias name, fetch main device - */ + /* + * Find the first ':' , must be followed by, at least, 1 char + */ + + sptr=strchr(dev_name,':'); + if (sptr==NULL || !sptr[1]) + return NULL; + +#if 0 + for (sptr=dev_name ; *sptr ; sptr++) + if(*sptr==':') + break; + if (!*sptr || !*(sptr+1)) + return NULL; +#endif + /* + * Seems to be an alias name, fetch main device + */ - *sptr='\0'; - if (!(dev=dev_get(dev_name))) - return NULL; - *sptr++=':'; + *sptr='\0'; + if (!(dev=dev_get(dev_name))) + return NULL; + *sptr++=':'; - /* - * fetch slot number - */ + /* + * Fetch slot number + */ - slot = simple_strtoul(sptr,&eptr,10); - if (slot >= NET_ALIAS_MAX_SLOT) - return NULL; + slot = simple_strtoul(sptr,&eptr,10); + if (slot >= NET_ALIAS_MAX_SLOT) + return NULL; - /* - * if last char is '-', it is a deletion request - */ + /* + * If last char is '-', it is a deletion request + */ - if (eptr[0] == '-' && !eptr[1] ) delete++; - else if (eptr[0]) - return NULL; + if (eptr[0] == '-' && !eptr[1] ) + delete++; + else if (eptr[0]) + return NULL; - /* - * well... let's work. - */ + /* + * Well... let's work. + */ - if (delete) - return net_alias_dev_delete(dev, slot, err); - else - return net_alias_dev_create(dev, slot, err, sa, data); + if (delete) + return net_alias_dev_delete(dev, slot, err); + else + return net_alias_dev_create(dev, slot, err, sa, data); } /* - * rehash alias device with address supplied. + * Rehash alias device with address supplied. */ -int -net_alias_dev_rehash(struct device *dev, struct sockaddr *sa) +int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa) { - struct net_alias_info *alias_info; - struct net_alias *alias, **aliasp; - struct device *main_dev; - unsigned long flags; - struct net_alias_type *o_nat, *n_nat; - unsigned n_hash; - - /* - * defensive ... - */ - - if (dev == NULL) return -1; - if ( (alias = dev->my_alias) == NULL ) return -1; - - if (!sa) - { - printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n"); - return -1; - } - - /* - * defensive. should not happen. - */ - - if ( (main_dev = alias->main_dev) == NULL ) - { - printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name); - return -1; - } - - /* - * defensive. should not happen. - */ - - if (!(alias_info=main_dev->alias_info)) - { - printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name); - return -1; - } - - /* - * will the request also change device family? - */ - - o_nat = alias->nat; - if (!o_nat) - { - printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name); - return -1; - } - - /* - * point to new alias_type obj. - */ - - if (o_nat->type == sa->sa_family) - n_nat = o_nat; - else - { - n_nat = nat_getbytype(sa->sa_family); - if (!n_nat) - { - printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family); - return -1; - } - } - - /* - * new hash key. if same as old AND same type (family) return; - */ - - n_hash = nat_hash_key(n_nat, sa); - if (n_hash == alias->hash && o_nat == n_nat ) - return 0; - - /* - * find alias in hashed list - */ - - for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next) - if (*aliasp == alias) break; - - /* - * not found (???). try a full search - */ - - if(!*aliasp) - if ((aliasp = net_alias_slow_findp(alias_info, alias))) - printk(KERN_WARNING "net_alias_rehash(%s): bad hashing recovered\n", alias->name); - else - { - printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name); - return -1; - } - - save_flags(flags); - cli(); - - /* - * if type (family) changed, unlink from old type object (o_nat) - * will call o_nat->alias_done_1() - */ - - if (o_nat != n_nat) - nat_unbind(o_nat, alias); - - /* - * if diff hash key, change alias position in hashed list - */ - - if (n_hash != alias->hash) - { - *aliasp = (*aliasp)->next; - alias->hash = n_hash; - aliasp = &alias_info->hash_tab[n_hash]; - alias->next = *aliasp; - *aliasp = alias; - } - - /* - * if type (family) changed link to new type object (n_nat) - * will call n_nat->alias_init_1() - */ - - if (o_nat != n_nat) - nat_bind(n_nat, alias, sa); - - restore_flags(flags); - return 0; + struct net_alias_info *alias_info; + struct net_alias *alias, **aliasp; + struct device *main_dev; + unsigned long flags; + struct net_alias_type *o_nat, *n_nat; + unsigned n_hash; + + /* + * Defensive ... + */ + + if (dev == NULL) + return -1; + if ( (alias = dev->my_alias) == NULL ) + return -1; + + if (!sa) + { + printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n"); + return -1; + } + + /* + * Defensive. should not happen. + */ + + if ( (main_dev = alias->main_dev) == NULL ) + { + printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name); + return -1; + } + + /* + * Defensive. should not happen. + */ + + if (!(alias_info=main_dev->alias_info)) + { + printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name); + return -1; + } + + /* + * Will the request also change device family? + */ + + o_nat = alias->nat; + if (!o_nat) + { + printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name); + return -1; + } + + /* + * Point to new alias_type obj. + */ + + if (o_nat->type == sa->sa_family) + n_nat = o_nat; + else + { + n_nat = nat_getbytype(sa->sa_family); + if (!n_nat) + { + printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family); + return -1; + } + } + + /* + * New hash key. if same as old AND same type (family) return; + */ + + n_hash = nat_hash_key(n_nat, sa); + if (n_hash == alias->hash && o_nat == n_nat ) + return 0; + + /* + * Find alias in hashed list + */ + + for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; + aliasp = &(*aliasp)->next) + { + if (*aliasp == alias) + break; + } + + /* + * Not found (???). try a full search + */ + + if(!*aliasp) + { + if ((aliasp = net_alias_slow_findp(alias_info, alias))) + { + printk(KERN_WARNING + "net_alias_rehash(%s): bad hashing recovered\n", alias->name); + } + else + { + printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name); + return -1; + } + } + + save_flags(flags); + cli(); + + /* + * If type (family) changed, unlink from old type object (o_nat) + * Will call o_nat->alias_done_1() + */ + + if (o_nat != n_nat) + nat_unbind(o_nat, alias); + + /* + * If diff hash key, change alias position in hashed list + */ + + if (n_hash != alias->hash) + { + *aliasp = (*aliasp)->next; + alias->hash = n_hash; + aliasp = &alias_info->hash_tab[n_hash]; + alias->next = *aliasp; + *aliasp = alias; + } + + /* + * If type (family) changed link to new type object (n_nat) + * will call n_nat->alias_init_1() + */ + + if (o_nat != n_nat) + nat_bind(n_nat, alias, sa); + + restore_flags(flags); + return 0; } /* - * implements /proc/net/alias_types entry - * shows net_alias_type objects registered. + * Implements /proc/net/alias_types entry + * Shows net_alias_type objects registered. */ int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length, int dummy) { - off_t pos=0, begin=0; - int len=0; - struct net_alias_type *nat; - unsigned idx; - len=sprintf(buffer,"type name n_attach\n"); - for (idx=0 ; idx < 16 ; idx++) - for (nat = nat_base[idx]; nat ; nat = nat->next) - { - len += sprintf(buffer+len, "%-7d %-15s %-7d\n", - nat->type, nat->name,nat->n_attach); - pos=begin+len; - if(posoffset+length) - break; - } - *start=buffer+(offset-begin); - len-=(offset-begin); - if(len>length) - len=length; - return len; + off_t pos=0, begin=0; + int len=0; + struct net_alias_type *nat; + unsigned idx; + len=sprintf(buffer,"type name n_attach\n"); + for (idx=0 ; idx < 16 ; idx++) + { + for (nat = nat_base[idx]; nat ; nat = nat->next) + { + len += sprintf(buffer+len, "%-7d %-15s %-7d\n", + nat->type, nat->name,nat->n_attach); + pos=begin+len; + if(posoffset+length) + break; + } + } + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; } /* - * implements /proc/net/aliases entry, shows alias devices. - * calls alias nat->alias_print_1 if not NULL and formats everything - * to a fixed rec. size without using local (stack) buffers + * Implements /proc/net/aliases entry, shows alias devices. + * calls alias nat->alias_print_1 if not NULL and formats everything + * to a fixed rec. size without using local (stack) buffers * */ #define NET_ALIASES_RECSIZ 64 -int net_alias_getinfo(char *buffer, char **start, off_t offset, int length, int dummy) + +int net_alias_getinfo(char *buffer, char **start, off_t offset, + int length, int dummy) { - off_t pos=0, begin=0; - int len=0; - int dlen; - struct net_alias_type *nat; - struct net_alias *alias; - struct device *dev; - - len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device family address"); - for (dev = dev_base; dev ; dev = dev->next) - if (net_alias_is(dev)) - { - alias = dev->my_alias; - nat = alias->nat; - dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family); + off_t pos=0, begin=0; + int len=0; + int dlen; + struct net_alias_type *nat; + struct net_alias *alias; + struct device *dev; + + len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device family address"); + for (dev = dev_base; dev ; dev = dev->next) + { + if (net_alias_is(dev)) + { + alias = dev->my_alias; + nat = alias->nat; + dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family); - /* - * call alias_type specific print function. - */ + /* + * Call alias_type specific print function. + */ - if (nat->alias_print_1) - dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen); - else - dlen += sprintf(buffer+len+dlen, "-"); - - /* - * fill with spaces if needed - */ + if (nat->alias_print_1) + dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen); + else + dlen += sprintf(buffer+len+dlen, "-"); + + /* + * Fill with spaces if needed + */ - if (dlen < NET_ALIASES_RECSIZ) memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen); - /* - * truncate to NET_ALIASES_RECSIZ - */ + if (dlen < NET_ALIASES_RECSIZ) + memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen); + + /* + * Truncate to NET_ALIASES_RECSIZ + */ - len += NET_ALIASES_RECSIZ; - buffer[len-1] = '\n'; + len += NET_ALIASES_RECSIZ; + buffer[len-1] = '\n'; - pos=begin+len; - if(posoffset+length) - break; - } - *start=buffer+(offset-begin); - len-=(offset-begin); - if(len>length) - len=length; - return len; + pos=begin+len; + if(posoffset+length) + break; + } + } + *start=buffer+(offset-begin); + len-=(offset-begin); + if(len>length) + len=length; + return len; } /* - * notifier for devices events + * Notifier for devices events */ int net_alias_device_event(struct notifier_block *this, unsigned long event, void *ptr) { - struct device *dev = ptr; + struct device *dev = ptr; - if (event == NETDEV_DOWN) - { + if (event == NETDEV_DOWN) + { #ifdef ALIAS_USER_LAND_DEBUG - printk("net_alias: NETDEV_DOWN for %s received\n", dev->name); + printk("net_alias: NETDEV_DOWN for %s received\n", dev->name); #endif - if (net_alias_has(dev)) - net_alias_free(dev); - } + if (net_alias_has(dev)) + net_alias_free(dev); + } - if (event == NETDEV_UP) - { + if (event == NETDEV_UP) + { #ifdef ALIAS_USER_LAND_DEBUG - printk("net_alias: NETDEV_UP for %s received\n", dev->name); + printk("net_alias: NETDEV_UP for %s received\n", dev->name); #endif - dev->alias_info = 0; - } + dev->alias_info = 0; + } - return NOTIFY_DONE; + return NOTIFY_DONE; } /* - * device aliases address comparison workhorse - * no checks for nat and alias_info, must be !NULL + * Device aliases address comparison workhorse + * No checks for nat and alias_info, must be !NULL */ -static __inline__ struct device * -nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off) +extern __inline__ struct device *nat_addr_chk(struct net_alias_type *nat, + struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off) { - struct net_alias *alias; - for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)]; - alias; alias = alias->next) - { - if (alias->dev.family != sa->sa_family) continue; - - /* - * nat_dev_addr_chk_1 will call type specific address cmp function. - */ + struct net_alias *alias; + for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)]; + alias; alias = alias->next) + { + if (alias->dev.family != sa->sa_family) + continue; + + /* + * Nat_dev_addr_chk_1 will call type specific address + * cmp function. + */ - if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) && - nat_dev_addr_chk_1(nat,&alias->dev,sa)) - return &alias->dev; - } - return NULL; + if (alias->dev.flags & flags_on && + !(alias->dev.flags & flags_off) && + nat_dev_addr_chk_1(nat,&alias->dev,sa)) + return &alias->dev; + } + return NULL; } /* - * nat_addr_chk enough for protocols whose addr is (fully) stored at pa_addr. - * note that nat pointer is ignored because of static comparison. + * Nat_addr_chk enough for protocols whose addr is (fully) stored at + * pa_addr. Note that nat pointer is ignored because of static comparison. */ -static __inline__ struct device * -nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off) +extern __inline__ struct device *nat_addr_chk32(struct net_alias_type *nat, + struct net_alias_info *alias_info, int family, __u32 addr32, + int flags_on, int flags_off) { - struct net_alias *alias; - for (alias=alias_info->hash_tab[HASH(addr32,family)]; - alias; alias=alias->next) - { - if (alias->dev.family != family) continue; - - /* - * "hard" (static) comparison between addr32 and pa_addr. - */ + struct net_alias *alias; + for (alias=alias_info->hash_tab[HASH(addr32,family)]; + alias; alias=alias->next) + { + if (alias->dev.family != family) + continue; + /* + * "hard" (static) comparison between addr32 and pa_addr. + */ - if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) && - addr32 == alias->dev.pa_addr) - return &alias->dev; - } - return NULL; + if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) && + addr32 == alias->dev.pa_addr) + return &alias->dev; + } + return NULL; } /* - * returns alias device with specified address AND flags_on AND flags_off, - * else NULL. - * intended for main devices. + * Returns alias device with specified address AND flags_on AND flags_off, + * else NULL. + * Intended for main devices. */ -struct device * -net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int flags_off) +struct device *net_alias_dev_chk(struct device *main_dev, + struct sockaddr *sa,int flags_on, int flags_off) { - struct net_alias_info *alias_info = main_dev->alias_info; - struct net_alias_type *nat; + struct net_alias_info *alias_info = main_dev->alias_info; + struct net_alias_type *nat; - /* - * only if main_dev has aliases - */ + /* + * Only if main_dev has aliases + */ - if (!alias_info) return NULL; + if (!alias_info) + return NULL; - /* - * get alias_type object for sa->sa_family. - */ + /* + * Get alias_type object for sa->sa_family. + */ - nat = nat_getbytype(sa->sa_family); - if (!nat) - return NULL; + nat = nat_getbytype(sa->sa_family); + if (!nat) + return NULL; - return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off); + return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off); } /* @@ -1099,180 +1142,199 @@ net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int * at pa_addr. */ -struct device * -net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32, - int flags_on, int flags_off) +struct device *net_alias_dev_chk32(struct device *main_dev, int family, + __u32 addr32, int flags_on, int flags_off) { - struct net_alias_info *alias_info = main_dev->alias_info; + struct net_alias_info *alias_info = main_dev->alias_info; - /* - * only if main_dev has aliases - */ + /* + * only if main_dev has aliases + */ - if (!alias_info) return NULL; - - return nat_addr_chk32(NULL, alias_info, family, addr32, flags_on, flags_off); + if (!alias_info) + return NULL; + return nat_addr_chk32(NULL, alias_info, family, addr32, + flags_on, flags_off); } /* - * select closest (main or alias) device to addresses given. if no - * further info is available, return main_dev (for easier calling arrangement). + * Select closest (main or alias) device to addresses given. If + * there is no further info available, return main_dev (for easier + * calling arrangement). * - * Should be called early at xxx_rcv() time for device selection + * Should be called early at xxx_rcv() time for device selection */ -struct device * -net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst) +struct device *net_alias_dev_rcv_sel(struct device *main_dev, + struct sockaddr *sa_src, struct sockaddr *sa_dst) { - int family; - struct net_alias_type *nat; - struct net_alias_info *alias_info; - struct device *dev; + int family; + struct net_alias_type *nat; + struct net_alias_info *alias_info; + struct device *dev; - if (main_dev == NULL) return NULL; + if (main_dev == NULL) + return NULL; - /* - * if not aliased, don't bother any more - */ + /* + * If not aliased, don't bother any more + */ - if ((alias_info = main_dev->alias_info) == NULL) - return main_dev; + if ((alias_info = main_dev->alias_info) == NULL) + return main_dev; - /* - * find out family - */ + /* + * Find out family + */ - family = (sa_src)? sa_src->sa_family : ((sa_dst)? sa_dst->sa_family : AF_UNSPEC); - if (family == AF_UNSPEC) return main_dev; + family = (sa_src)? sa_src->sa_family : + ((sa_dst)? sa_dst->sa_family : AF_UNSPEC); - /* - * get net_alias_type object for this family - */ + if (family == AF_UNSPEC) + return main_dev; - if ( (nat = nat_getbytype(family)) == NULL ) return main_dev; + /* + * Get net_alias_type object for this family + */ + + if ( (nat = nat_getbytype(family)) == NULL ) + return main_dev; - /* - * first step: find out if dst addr is main_dev's or one of its aliases' - */ + /* + * First step: find out if dst addr is main_dev's or one of its + * aliases' + */ - if (sa_dst) - { - if (nat_dev_addr_chk_1(nat, main_dev,sa_dst)) - return main_dev; + if (sa_dst) + { + if (nat_dev_addr_chk_1(nat, main_dev,sa_dst)) + return main_dev; - dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0); + dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0); - if (dev != NULL) return dev; - } + if (dev != NULL) + return dev; + } + + /* + * Second step: find the rcv addr 'closest' alias through nat + * method call + */ - /* - * second step: find the rcv addr 'closest' alias through nat method call - */ + if ( sa_src == NULL || nat->dev_select == NULL) + return main_dev; - if ( sa_src == NULL || nat->dev_select == NULL) return main_dev; - dev = nat->dev_select(nat, main_dev, sa_src); + dev = nat->dev_select(nat, main_dev, sa_src); - if (dev == NULL || dev->family != family) return main_dev; + if (dev == NULL || dev->family != family) + return main_dev; - /* - * dev ok only if it is alias of main_dev - */ + /* + * Dev ok only if it is alias of main_dev + */ - dev = net_alias_is(dev)? - ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; + dev = net_alias_is(dev)? + ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; - /* - * do not return NULL. - */ + /* + * Do not return NULL. + */ - return (dev)? dev : main_dev; + return (dev)? dev : main_dev; } /* - * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols. + * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols. */ -struct device * -net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst) +struct device *net_alias_dev_rcv_sel32(struct device *main_dev, int family, + __u32 src, __u32 dst) { - struct net_alias_type *nat; - struct net_alias_info *alias_info; - struct sockaddr_in sin_src; - struct device *dev; + struct net_alias_type *nat; + struct net_alias_info *alias_info; + struct sockaddr_in sin_src; + struct device *dev; - if (main_dev == NULL) return NULL; + if (main_dev == NULL) + return NULL; - /* - * if not aliased, don't bother any more - */ + /* + * If not aliased, don't bother any more + */ - if ((alias_info = main_dev->alias_info) == NULL) - return main_dev; + if ((alias_info = main_dev->alias_info) == NULL) + return main_dev; - /* - * early return if dst is main_dev's address - */ + /* + * Early return if dst is main_dev's address + */ - if (dst == main_dev->pa_addr) - return main_dev; + if (dst == main_dev->pa_addr) + return main_dev; - if (family == AF_UNSPEC) return main_dev; + if (family == AF_UNSPEC) + return main_dev; - /* - * get net_alias_type object for this family - */ + /* + * Get net_alias_type object for this family + */ - if ( (nat = nat_getbytype(family)) == NULL ) return main_dev; + if ( (nat = nat_getbytype(family)) == NULL ) + return main_dev; - /* - * first step: find out if dst address one of main_dev aliases' - */ + /* + * First step: find out if dst address one of main_dev aliases' + */ - if (dst) - { - dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0); - if (dev) return dev; - } + if (dst) + { + dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0); + if (dev) + return dev; + } - /* - * second step: find the rcv addr 'closest' alias through nat method call - */ + /* + * Second step: find the rcv addr 'closest' alias through nat + * method call + */ - if ( src == 0 || nat->dev_select == NULL) return main_dev; + if ( src == 0 || nat->dev_select == NULL) + return main_dev; - sin_src.sin_family = family; - sin_src.sin_addr.s_addr = src; + sin_src.sin_family = family; + sin_src.sin_addr.s_addr = src; - dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src); + dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src); - if (dev == NULL || dev->family != family) return main_dev; + if (dev == NULL || dev->family != family) + return main_dev; - /* - * dev ok only if it is alias of main_dev - */ + /* + * Dev ok only if it is alias of main_dev + */ - dev = net_alias_is(dev)? - ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; + dev = net_alias_is(dev)? + ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL; - /* - * do not return NULL. - */ - - return (dev)? dev : main_dev; + /* + * Do not return NULL. + */ + return (dev)? dev : main_dev; } /* - * device event hook + * Device event hook */ -static struct notifier_block net_alias_dev_notifier = { - net_alias_device_event, - NULL, - 0 +static struct notifier_block net_alias_dev_notifier = +{ + net_alias_device_event, + NULL, + 0 }; #ifndef ALIAS_USER_LAND_DEBUG @@ -1293,93 +1355,95 @@ static struct proc_dir_entry proc_net_aliases = { #endif /* - * net_alias initialisation - * called from net_dev_init(). + * Net_alias initialisation called from net_dev_init(). */ void net_alias_init(void) { - /* - * register dev events notifier - */ + /* + * Register device events notifier + */ - register_netdevice_notifier(&net_alias_dev_notifier); + register_netdevice_notifier(&net_alias_dev_notifier); - /* - * register /proc/net entries - */ + /* + * Register /proc/net entries + */ #ifndef ALIAS_USER_LAND_DEBUG #ifdef CONFIG_PROC_FS - proc_net_register(&proc_net_alias_types); - proc_net_register(&proc_net_aliases); + proc_net_register(&proc_net_alias_types); + proc_net_register(&proc_net_aliases); #endif #endif } /* - * net_alias type object registering func. + * Net_alias type object registering func. */ + int register_net_alias_type(struct net_alias_type *nat, int type) { - unsigned hash; - unsigned long flags; - if (!nat) - { - printk(KERN_ERR "register_net_alias_type(): NULL arg\n"); - return -EINVAL; - } - nat->type = type; - nat->n_attach = 0; - hash = nat->type & 0x0f; - save_flags(flags); - cli(); - nat->next = nat_base[hash]; - nat_base[hash] = nat; - restore_flags(flags); - return 0; + unsigned hash; + unsigned long flags; + if (!nat) + { + printk(KERN_ERR "register_net_alias_type(): NULL arg\n"); + return -EINVAL; + } + nat->type = type; + nat->n_attach = 0; + hash = nat->type & 0x0f; + save_flags(flags); + cli(); + nat->next = nat_base[hash]; + nat_base[hash] = nat; + restore_flags(flags); + return 0; } /* - * net_alias type object unreg. + * Net_alias type object unreg. */ + int unregister_net_alias_type(struct net_alias_type *nat) { - struct net_alias_type **natp; - unsigned hash; - unsigned long flags; - - if (!nat) - { - printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n"); - return -EINVAL; - } - - /* - * only allow unregistration if it has no attachments - */ - if (nat->n_attach) - { - printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n", - nat->n_attach); - return -EINVAL; - } - hash = nat->type & 0x0f; - save_flags(flags); - cli(); - for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next) - { - if (nat==(*natp)) - { - *natp = nat->next; - restore_flags(flags); - return 0; - } - } - restore_flags(flags); - printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type); - return -EINVAL; + struct net_alias_type **natp; + unsigned hash; + unsigned long flags; + + if (!nat) + { + printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n"); + return -EINVAL; + } + + /* + * Only allow unregistration if it has no attachments + */ + + if (nat->n_attach) + { + printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n", + nat->n_attach); + return -EINVAL; + } + hash = nat->type & 0x0f; + save_flags(flags); + cli(); + for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next) + { + if (nat==(*natp)) + { + *natp = nat->next; + restore_flags(flags); + return 0; + } + } + restore_flags(flags); + printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type); + return -EINVAL; } diff --git a/net/core/scm.c b/net/core/scm.c index 32bc6744eabc..2ca00d5b2038 100644 --- a/net/core/scm.c +++ b/net/core/scm.c @@ -37,7 +37,8 @@ /* - * Allow to send credentials, that user could set with setu(g)id. + * Only allow a user to send credentials, that they could set with + * setu(g)id. */ static __inline__ int scm_check_creds(struct ucred *creds) @@ -54,8 +55,7 @@ static __inline__ int scm_check_creds(struct ucred *creds) } -static int -scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) +static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) { int num; struct scm_fp_list *fpl = *fplp; @@ -124,7 +124,7 @@ void __scm_destroy(struct scm_cookie *scm) -static __inline__ int not_one_bit(unsigned val) +extern __inline__ int not_one_bit(unsigned val) { return (val-1) & val; } @@ -138,16 +138,18 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) int acc_fd; unsigned scm_flags=0; - for (cmsg = KCMSG_FIRSTHDR(msg); cmsg; cmsg = KCMSG_NXTHDR(msg, cmsg)) { + for (cmsg = KCMSG_FIRSTHDR(msg); cmsg; cmsg = KCMSG_NXTHDR(msg, cmsg)) + { if (kcm.cmsg_level != SOL_SOCKET) continue; err = -EINVAL; /* - * Temporary hack: no protocols except for AF_UNIX - * undestand scm now. + * Temporary hack: no protocols except for AF_UNIX + * undestand scm now. */ + if (sock->ops->family != AF_UNIX) goto error; @@ -285,7 +287,7 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) scm->fp = NULL; } -struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl) +struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl) { int i; struct scm_fp_list *new_fpl; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 1904a681dbac..98d06802744e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -87,7 +87,7 @@ void show_net_buffers(void) #if CONFIG_SKB_CHECK /* - * Debugging paranoia. Can go later when this crud stack works + * Debugging paranoia. Used for debugging network stacks. */ int skb_check(struct sk_buff *skb, int head, int line, char *file) @@ -114,20 +114,6 @@ int skb_check(struct sk_buff *skb, int head, int line, char *file) file,line); return -1; } -#if 0 - { - struct sk_buff *skb2 = skb->next; - int i = 0; - while (skb2 != skb && i < 5) { - if (skb_check(skb2, 0, line, file) < 0) { - printk("bad queue element in whole queue\n"); - return -1; - } - i++; - skb2 = skb2->next; - } - } -#endif return 0; } if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB @@ -573,9 +559,15 @@ void skb_trim(struct sk_buff *skb, unsigned int len) #endif +/************************************************************************** + + Stuff below this point isn't debugging duplicates of the inlines + used for buffer handling + +***************************************************************************/ + /* - * Free an sk_buff. This still knows about things it should - * not need to like protocols and sockets. + * Free an sk_buff. Release anything attached to the buffer. */ void __kfree_skb(struct sk_buff *skb) @@ -603,7 +595,12 @@ void __kfree_skb(struct sk_buff *skb) /* * Allocate a new skbuff. We do this ourselves so we can fill in a few 'private' * fields and also do memory statistics to find all the [BEEP] leaks. + * + * Note: For now we put the header after the data to get better cache + * usage. Once we have a good cache aware kmalloc this will cease + * to be a good idea. */ + struct sk_buff *alloc_skb(unsigned int size,int priority) { struct sk_buff *skb; @@ -620,6 +617,11 @@ struct sk_buff *alloc_skb(unsigned int size,int priority) } } + /* + * FIXME: We could do with an architecture dependant + * 'alignment mask'. + */ + size=(size+15)&~15; /* Allow for alignments. Make a multiple of 16 bytes */ len = size; @@ -684,7 +686,7 @@ struct sk_buff *alloc_skb(unsigned int size,int priority) * Free an skbuff by memory */ -static inline void __kfree_skbmem(struct sk_buff *skb) +extern inline void __kfree_skbmem(struct sk_buff *skb) { /* don't do anything if somebody still uses us */ if (atomic_dec_and_test(&skb->count)) { diff --git a/net/core/sock.c b/net/core/sock.c index 4e03664d1cd1..b822b3fb93f2 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -367,6 +367,11 @@ int sock_getsockopt(struct socket *sock, int level, int optname, return err; } +/* + * All socket objects are allocated here. This is for future + * usage. + */ + struct sock *sk_alloc(int priority) { struct sock *sk=(struct sock *)kmalloc(sizeof(*sk), priority); @@ -381,6 +386,10 @@ void sk_free(struct sock *sk) kfree_s(sk,sizeof(*sk)); } +/* + * Simple resource managers for sockets. + */ + void sock_wfree(struct sk_buff *skb) { struct sock *sk = skb->sk; @@ -498,7 +507,11 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne } if(sk->shutdown&SEND_SHUTDOWN) - { + { + /* + * FIXME: Check 1003.1g should we deliver + * a signal here ??? + */ *errcode=-EPIPE; return NULL; } @@ -590,3 +603,101 @@ void __release_sock(struct sock *sk) end_bh_atomic(); #endif } + + +/* + * Generic socket manager library. Most simpler socket families + * use this to manage their socket lists. At some point we should + * hash these. By making this generic we get the lot hashed for free. + */ + +void sklist_remove_socket(struct sock **list, struct sock *sk) +{ + unsigned long flags; + struct sock *s; + + save_flags(flags); + cli(); + + s= *list; + if(s==sk) + { + *list = s->next; + restore_flags(flags); + return; + } + while(s && s->next) + { + if(s->next==sk) + { + s->next=sk->next; + restore_flags(flags); + return; + } + s=s->next; + } + restore_flags(flags); +} + +void sklist_insert_socket(struct sock **list, struct sock *sk) +{ + unsigned long flags; + save_flags(flags); + cli(); + sk->next= *list; + *list=sk; + restore_flags(flags); +} + +/* + * This is only called from user mode. Thus it protects itself against + * interrupt users but doesn't worry about being called during work. + * Once it is removed from the queue no interrupt or bottom half will + * touch it and we are (fairly 8-) ) safe. + */ + +void sklist_destroy_socket(struct sock **list, struct sock *sk); + +/* + * Handler for deferred kills. + */ + +static void sklist_destroy_timer(unsigned long data) +{ + struct sock *sk=(struct sock *)data; + sklist_destroy_socket(NULL,sk); +} + +/* + * Destroy a socket. We pass NULL for a list if we know the + * socket is not on a list. + */ + +void sklist_destroy_socket(struct sock **list,struct sock *sk) +{ + struct sk_buff *skb; + if(list) + sklist_remove_socket(list, sk); + + while((skb=skb_dequeue(&sk->receive_queue))!=NULL) + { + kfree_skb(skb,FREE_READ); + } + + if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead) + { + sk_free(sk); + } + else + { + /* + * Someone is using our buffers still.. defer + */ + init_timer(&sk->timer); + sk->timer.expires=jiffies+10*HZ; + sk->timer.function=sklist_destroy_timer; + sk->timer.data = (unsigned long)sk; + add_timer(&sk->timer); + } +} + diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 4b686cf4f9ac..e49ff05824f2 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -240,10 +240,6 @@ int devinet_ioctl(unsigned int cmd, void *arg) addr = (*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr.s_addr; - if (addr == dev->pa_brdaddr) { - dev->ip_flags |= IFF_IP_BRD_OK; - return 0; - } if (dev->flags & IFF_UP) ip_rt_change_broadcast(dev, addr); dev->pa_brdaddr = addr; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 52ac22cfe678..fdb375ea73f8 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -467,13 +467,31 @@ static struct open_request *tcp_find_established(struct tcp_opt *tp) static void tcp_close_pending (struct sock *sk) { - struct sk_buff *skb; + struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + struct open_request *req; - while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) - { - tcp_close(skb->sk, 0); - kfree_skb(skb, FREE_READ); - } + req = tp->syn_wait_queue; + + if (!req) + return; + + do { + struct open_request *iter; + + if (req->sk) + tcp_close(req->sk, 0); + + iter = req; + req = req->dl_next; + + (*iter->class->destructor)(iter); + tcp_dec_slow_timer(TCP_SLT_SYNACK); + sk->ack_backlog--; + kfree(iter); + + } while (req != tp->syn_wait_queue); + + tp->syn_wait_queue = NULL; return; } @@ -945,8 +963,9 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, actual_win = tp->snd_wnd - (tp->snd_nxt - tp->snd_una); - if (copy > actual_win && - (((long) actual_win) >= (sk->max_window >> 1))) + if (copy > actual_win && + (((long) actual_win) >= (sk->max_window >> 1)) + && actual_win) { copy = actual_win; } @@ -1172,12 +1191,15 @@ static void cleanup_rbuf(struct sock *sk) { struct sk_buff *skb; struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); + unsigned long pspace, rspace; /* * NOTE! The socket must be locked, so that we don't get * a messed-up receive queue. */ + pspace = sock_rspace(sk); + while ((skb=skb_peek(&sk->receive_queue)) != NULL) { if (!skb->used || skb->users>1) break; @@ -1192,14 +1214,16 @@ static void cleanup_rbuf(struct sock *sk) * else let tcp_data deal with the acking policy. */ - if (sock_rspace(sk) > tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup) && + rspace = sock_rspace(sk); + + if ((rspace > pspace) && + (rspace > tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup)) && (tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup) < sk->mss)) { /* Send an ack right now. */ sk->delayed_acks++; tcp_read_wakeup(sk); - } - + } } @@ -1747,7 +1771,7 @@ struct sock *tcp_accept(struct sock *sk, int flags) got_new_connect: tcp_synq_unlink(tp, req); newsk = req->sk; - kfree(req); + kfree(req); sk->ack_backlog--; error = 0; out: @@ -1852,10 +1876,3 @@ void tcp_set_keepalive(struct sock *sk, int val) tcp_dec_slow_timer(TCP_SLT_KEEPALIVE); } } - -/* - * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -c -o tcp.o tcp.c" - * c-file-style: "Linux" - * End: - */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2a4e19d81116..1495fd44c344 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -567,10 +567,11 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq, kfree_skb(skb, FREE_WRITE); } - if (acked && !sk->dead) + if (acked) { tp->retrans_head = NULL; - sk->write_space(sk); + if (!sk->dead) + sk->write_space(sk); } return acked; @@ -1744,8 +1745,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, if (tp->snd_una == sk->write_seq) { tcp_time_wait(sk); - if (!sk->dead) - sk->state_change(sk); } break; @@ -1812,7 +1811,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, case TCP_ESTABLISHED: queued = tcp_data(skb, sk, len); - break; + break; } /* diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 6b7ec0ad4254..f812ab0a35a0 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -851,34 +851,47 @@ static int addrconf_ifdown(struct device *dev) int addrconf_set_dstaddr(void *arg) { struct in6_ifreq ireq; + struct inet6_dev *idev; struct device *dev; - int err; + int err = -EINVAL; - err = copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)); - - if (err) - return -EFAULT; + if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) + { + err = -EFAULT; + goto err_exit; + } - dev = dev_get(ireq.devname); + idev = ipv6_dev_by_index(ireq.ifr6_ifindex); + + if (idev == NULL) + { + err = -ENODEV; + goto err_exit; + } + + dev = idev->dev; if (dev->type == ARPHRD_SIT) { struct device *dev; - if (!(ipv6_addr_type(&ireq.addr) & IPV6_ADDR_COMPATv4)) + if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4)) { return -EADDRNOTAVAIL; } - dev = sit_add_tunnel(ireq.addr.s6_addr32[3]); + dev = sit_add_tunnel(ireq.ifr6_addr.s6_addr32[3]); if (dev == NULL) - return -ENODEV; - - return 0; + { + err = -ENODEV; + } + else + err = 0; } - - return -EINVAL; + +err_exit: + return err; } /* @@ -934,20 +947,17 @@ int addrconf_add_ifaddr(void *arg) if (err) return -EFAULT; - dev = dev_get(ireq.devname); - - if (dev == NULL) - return -EINVAL; - - in6_dev = ipv6_get_idev(dev); + in6_dev = ipv6_dev_by_index(ireq.ifr6_ifindex); if (in6_dev == NULL) return -EINVAL; - addr_type = ipv6_addr_type(&ireq.addr); + dev = in6_dev->dev; + + addr_type = ipv6_addr_type(&ireq.ifr6_addr); addr_type &= IPV6_ADDR_SCOPE_MASK; - ifp = ipv6_add_addr(in6_dev, &ireq.addr, addr_type); + ifp = ipv6_add_addr(in6_dev, &ireq.ifr6_addr, addr_type); if (ifp == NULL) return -ENOMEM; @@ -959,12 +969,12 @@ int addrconf_add_ifaddr(void *arg) struct in6_addr maddr; /* join to solicited addr multicast group */ - addrconf_addr_solict_mult(&ireq.addr, &maddr); + addrconf_addr_solict_mult(&ireq.ifr6_addr, &maddr); ipv6_dev_mc_inc(dev, &maddr); } - ifp->prefix_len = ireq.prefix_len; + ifp->prefix_len = ireq.ifr6_prefixlen; ifp->flags |= ADDR_PERMANENT; if (!(dev->flags & (IFF_NOARP|IFF_LOOPBACK))) @@ -1150,7 +1160,7 @@ static void addrconf_dad_timer(unsigned long data) ifp = (struct inet6_ifaddr *) data; - if (ifp->probes-- == 0) + if (--ifp->probes == 0) { /* * DAD was successful @@ -1415,9 +1425,3 @@ void addrconf_cleanup(void) proc_unregister(&proc_net, iface_proc_entry.low_ino); } - -/* - * Local variables: - * c-file-style: "Linux" - * End: - */ diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index ccd487b5e955..a5c67c518a99 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -163,13 +163,19 @@ static void ndisc_periodic_timer(unsigned long arg) if (nd_tbl.tbl_lock == 1) { ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL); + ndisc_gc_timer.expires = now + nd_gc_interval; + } + else + { +#if ND_DEBUG >= 2 + printk(KERN_DEBUG "ndisc_gc delayed: table locked\n"); +#endif + ndisc_gc_timer.expires = now + HZ; } end_bh_atomic(); neigh_table_unlock(&nd_tbl); - ndisc_gc_timer.expires = now + HZ; - ndisc_gc_timer.expires = now + nd_gc_interval; add_timer(&ndisc_gc_timer); } @@ -178,19 +184,18 @@ static int ndisc_gc_func(struct neighbour *neigh, void *arg) struct nd_neigh *ndn = (struct nd_neigh *) neigh; unsigned long now = jiffies; - if (ndn->ndn_refcnt == 0 && - ((ndn->ndn_nud_state == NUD_FAILED) || - ((ndn->ndn_nud_state == NUD_REACHABLE) && - (ndn->ndn_tstamp <= (now - nd_gc_staletime)) - ) - ) - ) + if (ndn->ndn_refcnt == 0) { - /* - * Release unused entries - */ + switch (ndn->ndn_nud_state) { - return 1; + case NUD_REACHABLE: + case NUD_STALE: + if (now - ndn->ndn_tstamp < nd_gc_staletime) + break; + case NUD_FAILED: + return 1; + default: + } } return 0; } @@ -198,17 +203,16 @@ static int ndisc_gc_func(struct neighbour *neigh, void *arg) static __inline__ void ndisc_add_timer(struct nd_neigh *ndn, int timer) { unsigned long now = jiffies; - unsigned long tval; + unsigned long tval = ~0UL; ndn->ndn_expires = now + timer; - tval = del_timer(&ndisc_timer); - - if (tval) + + if (del_timer(&ndisc_timer)) { - tval = min(tval, ndn->ndn_expires); + tval = ndisc_timer.expires; } - else - tval = ndn->ndn_expires; + + tval = min(tval, ndn->ndn_expires); ndisc_timer.expires = tval; add_timer(&ndisc_timer); @@ -216,12 +220,15 @@ static __inline__ void ndisc_add_timer(struct nd_neigh *ndn, int timer) static void ndisc_del_timer(struct nd_neigh *ndn) { - unsigned long tval; + unsigned long tval = ~0UL; if (!(ndn->ndn_nud_state & NUD_IN_TIMER)) return; - tval = del_timer(&ndisc_timer); + if (del_timer(&ndisc_timer)) + { + tval = ndisc_timer.expires; + } if (tval == ndn->ndn_expires) { @@ -362,6 +369,9 @@ struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr) if (neigh == NULL) { neigh = ndisc_new_neigh(dev, addr); + + if (neigh == NULL) + return NULL; } neigh_table_unlock(&nd_tbl); @@ -475,6 +485,7 @@ void ndisc_send_na(struct device *dev, struct nd_neigh *ndn, if (skb == NULL) { printk(KERN_DEBUG "send_na: alloc skb failed\n"); + return; } if (ipv6_bld_hdr_2(sk, skb, dev, (struct neighbour *) ndn, @@ -631,6 +642,7 @@ void ndisc_send_rs(struct device *dev, struct in6_addr *saddr, if (skb == NULL) { printk(KERN_DEBUG "send_ns: alloc skb failed\n"); + return; } if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6, @@ -723,14 +735,14 @@ static void ndisc_timer_handler(unsigned long arg) { if (ndn->ndn_nud_state & NUD_IN_TIMER) { - long time; + unsigned long time; - if ((ndn->ndn_expires - now) <= 0) + time = ndn->ndn_expires - now; + + if ((long) time <= 0) { time = ndisc_event_timer(ndn); } - else - time = ndn->ndn_expires - now; if (time) { @@ -744,7 +756,7 @@ static void ndisc_timer_handler(unsigned long arg) if (ntimer != (~0UL)) { - ndisc_timer.expires = jiffies + ntimer; + ndisc_timer.expires = now + ntimer; add_timer(&ndisc_timer); } @@ -1854,10 +1866,3 @@ void ndisc_cleanup(void) del_timer(&ndisc_timer); } #endif - -/* - * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o ndisc.o ndisc.c" - * c-file-style: "Linux" - * End: - */ diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 012fe4a57cb4..8c3ba63b5488 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -57,10 +57,13 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb, __u8 *nhptr, struct frag_hdr *fhdr) { - __u32 expires; + __u32 expires = jiffies + IPV6_FRAG_TIMEOUT; int nh; - expires = del_timer(&fq->timer); + if (del_timer(&fq->timer)) + { + expires = fq->timer.expires; + } /* * We queue the packet even if it's the last. @@ -83,7 +86,7 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb, if ((nh = reasm_frag_1(fq, skb))) return nh; } - + fq->timer.expires = expires; add_timer(&fq->timer); diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index b88e0dd84ff7..6d08d51c748d 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -46,6 +46,7 @@ * Handles WIN95 discovery packets * Revision 0.36: Internal bump up for 2.1 * Revision 0.37: Began adding POSIXisms. + * Revision 0.38: Asynchronous socket stuff made current. * * Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT * pair. Also, now usage count is managed this way @@ -115,13 +116,16 @@ static ipx_interface *ipx_interfaces = NULL; static ipx_interface *ipx_primary_net = NULL; static ipx_interface *ipx_internal_net = NULL; -static int -ipxcfg_set_auto_create(char val) +static int ipxcfg_set_auto_create(char val) { - if (ipxcfg_auto_create_interfaces != val){ - if (val){ + if (ipxcfg_auto_create_interfaces != val) + { + if (val) + { MOD_INC_USE_COUNT; - }else{ + } + else + { MOD_DEC_USE_COUNT; } ipxcfg_auto_create_interfaces = val; @@ -129,8 +133,7 @@ ipxcfg_set_auto_create(char val) return 0; } -static int -ipxcfg_set_auto_select(char val) +static int ipxcfg_set_auto_select(char val) { ipxcfg_auto_select_primary = val; if (val && (ipx_primary_net == NULL)) @@ -138,8 +141,7 @@ ipxcfg_set_auto_select(char val) return 0; } -static int -ipxcfg_get_config_data(ipx_config_data *arg) +static int ipxcfg_get_config_data(ipx_config_data *arg) { ipx_config_data vals; @@ -161,8 +163,7 @@ ipxcfg_get_config_data(ipx_config_data *arg) * use this facility. */ -static void -ipx_remove_socket(struct sock *sk) +static void ipx_remove_socket(struct sock *sk) { struct sock *s; ipx_interface *intrfc; @@ -203,8 +204,7 @@ ipx_remove_socket(struct sock *sk) * touch it and we are (fairly 8-) ) safe. */ -static void -ipx_destroy_socket(struct sock *sk) +static void ipx_destroy_socket(struct sock *sk) { struct sk_buff *skb; @@ -223,8 +223,7 @@ ipx_destroy_socket(struct sock *sk) static ipx_route * ipxrtr_lookup(unsigned long); -static void -ipxitf_clear_primary_net(void) +static void ipxitf_clear_primary_net(void) { if (ipxcfg_auto_select_primary && (ipx_interfaces != NULL)) ipx_primary_net = ipx_interfaces; @@ -232,8 +231,7 @@ ipxitf_clear_primary_net(void) ipx_primary_net = NULL; } -static ipx_interface * -ipxitf_find_using_phys(struct device *dev, unsigned short datalink) +static ipx_interface *ipxitf_find_using_phys(struct device *dev, unsigned short datalink) { ipx_interface *i; @@ -244,8 +242,7 @@ ipxitf_find_using_phys(struct device *dev, unsigned short datalink) return i; } -static ipx_interface * -ipxitf_find_using_net(unsigned long net) +static ipx_interface *ipxitf_find_using_net(unsigned long net) { ipx_interface *i; @@ -259,8 +256,7 @@ ipxitf_find_using_net(unsigned long net) } /* Sockets are bound to a particular IPX interface. */ -static void -ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk) +static void ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk) { struct sock *s; @@ -275,8 +271,7 @@ ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk) } } -static struct sock * -ipxitf_find_socket(ipx_interface *intrfc, unsigned short port) +static struct sock *ipxitf_find_socket(ipx_interface *intrfc, unsigned short port) { struct sock *s; @@ -290,8 +285,7 @@ ipxitf_find_socket(ipx_interface *intrfc, unsigned short port) #ifdef CONFIG_IPX_INTERN -static struct sock * -ipxitf_find_internal_socket(ipx_interface *intrfc, +static struct sock *ipxitf_find_internal_socket(ipx_interface *intrfc, unsigned char *node, unsigned short port) { struct sock *s = intrfc->if_sklist; @@ -311,8 +305,7 @@ ipxitf_find_internal_socket(ipx_interface *intrfc, static void ipxrtr_del_routes(ipx_interface *); -static void -ipxitf_down(ipx_interface *intrfc) +static void ipxitf_down(ipx_interface *intrfc) { ipx_interface *i; struct sock *s, *t; @@ -359,8 +352,7 @@ ipxitf_down(ipx_interface *intrfc) return; } -static int -ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr) +static int ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr) { struct device *dev = ptr; ipx_interface *i, *tmp; @@ -400,8 +392,7 @@ static int ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb) */ #ifdef CONFIG_IPX_INTERN -static int -ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) +static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) { struct ipxhdr *ipx = skb->nh.ipxh; struct sock *s; @@ -464,8 +455,7 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) #else -static int -ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) +static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) { struct ipxhdr *ipx = skb->nh.ipxh; struct sock *sock1 = NULL, *sock2 = NULL; @@ -562,8 +552,7 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) } #endif -static struct sk_buff * -ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb) +static struct sk_buff *ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb) { struct sk_buff *skb2; int in_offset = skb->h.raw - skb->head; @@ -765,69 +754,77 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb) } #ifdef CONFIG_IPX_PPROP_ROUTING - if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 ) { - int i; - ipx_interface *ifcs; - struct sk_buff *skb2; - long *l; - char *c; + if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 ) + { + int i; + ipx_interface *ifcs; + struct sk_buff *skb2; + long *l; + char *c; #ifdef DEBUG_IPX_PPROP_ROUTING - printk(KERN_INFO "IPX: PPROP packet received\n" - " Src: %8x:%02x:%02x:%02x:%02x:%02x:%02x:%d/%d\n", - htonl(ipx->ipx_source.net), - ipx->ipx_source.node[0], ipx->ipx_source.node[1], - ipx->ipx_source.node[2], ipx->ipx_source.node[3], - ipx->ipx_source.node[4], ipx->ipx_source.node[5], - htons(ipx->ipx_source.sock), - htons(ipx->ipx_dest.sock) - ); + printk(KERN_INFO "IPX: PPROP packet received\n" + " Src: %8x:%02x:%02x:%02x:%02x:%02x:%02x:%d/%d\n", + htonl(ipx->ipx_source.net), + ipx->ipx_source.node[0], ipx->ipx_source.node[1], + ipx->ipx_source.node[2], ipx->ipx_source.node[3], + ipx->ipx_source.node[4], ipx->ipx_source.node[5], + htons(ipx->ipx_source.sock), + htons(ipx->ipx_dest.sock) + ); #endif - c = (char *) skb->data; - c += sizeof( struct ipxhdr ); + c = (char *) skb->data; + c += sizeof( struct ipxhdr ); - l = (long *) c; + l = (long *) c; #ifdef DEBUG_IPX_PPROP_ROUTING - printk( "IPX: Routing PPROP from net num %08x\n", (unsigned int) htonl(intrfc->if_netnum) ); - for( i = 0 ; i < ipx->ipx_tctrl ; i++ ) - printk( "IPX: Routing PPROP seen net num %08x\n", (unsigned int) htonl(*l++) ); - l = (long *) c; + printk( "IPX: Routing PPROP from net num %08x\n", (unsigned int) htonl(intrfc->if_netnum) ); + for( i = 0 ; i < ipx->ipx_tctrl ; i++ ) + printk( "IPX: Routing PPROP seen net num %08x\n", (unsigned int) htonl(*l++) ); + l = (long *) c; #endif - i = 0; - /* dump packet if too many hops or already seen this net */ - if( ipx->ipx_tctrl < 8 ) - for( ; i < ipx->ipx_tctrl ; i++ ) - if( *l++ == intrfc->if_netnum ) - break; - - if( i == ipx->ipx_tctrl ) { /* < 8 hops && input itfc not in list */ - *l = intrfc->if_netnum; /* insert recvd netnum into list */ - - /* xmit on all other interfaces... */ - for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) { - /* that aren't in the list */ - l = (long *) c; - for( i = 0 ; i <= ipx->ipx_tctrl ; i++ ) - if( ifcs->if_netnum == *l++ ) - break; - if( i - 1 == ipx->ipx_tctrl ) { - ipx->ipx_dest.net = ifcs->if_netnum; + i = 0; + /* + * Dump packet if too many hops or already seen this net + */ + if( ipx->ipx_tctrl < 8 ) + for( ; i < ipx->ipx_tctrl ; i++ ) + if( *l++ == intrfc->if_netnum ) + break; + + if( i == ipx->ipx_tctrl ) + { + /* < 8 hops && input itfc not in list */ + *l = intrfc->if_netnum; /* insert recvd netnum into list */ + /* xmit on all other interfaces... */ + for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) + { + /* That aren't in the list */ + l = (long *) c; + for( i = 0 ; i <= ipx->ipx_tctrl ; i++ ) + if( ifcs->if_netnum == *l++ ) + break; + if( i - 1 == ipx->ipx_tctrl ) + { + ipx->ipx_dest.net = ifcs->if_netnum; #ifdef DEBUG_IPX_PPROP_ROUTING - printk( "IPX: Forward PPROP onto net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) ); + printk( "IPX: Forward PPROP onto net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) ); #endif - skb2 = skb_clone(skb, GFP_ATOMIC); - ipxrtr_route_skb(skb2); - } + skb2 = skb_clone(skb, GFP_ATOMIC); + ipxrtr_route_skb(skb2); + } #ifdef DEBUG_IPX_PPROP_ROUTING - else - printk( "IPX: Ignoring PPROP for net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) ); + else + printk( "IPX: Ignoring PPROP for net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) ); #endif - } - /* reset netnum in packet */ - ipx->ipx_dest.net = intrfc->if_netnum; - } + } + /* + * Reset network number in packet + */ + ipx->ipx_dest.net = intrfc->if_netnum; + } } #endif @@ -875,8 +872,7 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb) return 0; } -static void -ipxitf_insert(ipx_interface *intrfc) +static void ipxitf_insert(ipx_interface *intrfc) { ipx_interface *i; @@ -895,16 +891,17 @@ ipxitf_insert(ipx_interface *intrfc) return; } -static int -ipxitf_create_internal(ipx_interface_definition *idef) +static int ipxitf_create_internal(ipx_interface_definition *idef) { ipx_interface *intrfc; /* Only one primary network allowed */ - if (ipx_primary_net != NULL) return -EEXIST; + if (ipx_primary_net != NULL) + return -EEXIST; /* Must have a valid network number */ - if (idef->ipx_network == 0L) return -EADDRNOTAVAIL; + if (idef->ipx_network == 0L) + return -EADDRNOTAVAIL; if (ipxitf_find_using_net(idef->ipx_network) != NULL) return -EADDRINUSE; @@ -926,21 +923,25 @@ ipxitf_create_internal(ipx_interface_definition *idef) return ipxitf_add_local_route(intrfc); } -static int -ipx_map_frame_type(unsigned char type) +static int ipx_map_frame_type(unsigned char type) { - switch (type) { - case IPX_FRAME_ETHERII: return htons(ETH_P_IPX); - case IPX_FRAME_8022: return htons(ETH_P_802_2); - case IPX_FRAME_TR_8022: return htons(ETH_P_TR_802_2); - case IPX_FRAME_SNAP: return htons(ETH_P_SNAP); - case IPX_FRAME_8023: return htons(ETH_P_802_3); + switch (type) + { + case IPX_FRAME_ETHERII: + return htons(ETH_P_IPX); + case IPX_FRAME_8022: + return htons(ETH_P_802_2); + case IPX_FRAME_TR_8022: + return htons(ETH_P_TR_802_2); + case IPX_FRAME_SNAP: + return htons(ETH_P_SNAP); + case IPX_FRAME_8023: + return htons(ETH_P_802_3); } return 0; } -static int -ipxitf_create(ipx_interface_definition *idef) +static int ipxitf_create(ipx_interface_definition *idef) { struct device *dev; unsigned short dlink_type = 0; @@ -957,30 +958,31 @@ ipxitf_create(ipx_interface_definition *idef) (ipxitf_find_using_net(idef->ipx_network) != NULL)) return -EADDRINUSE; - switch (idef->ipx_dlink_type) { - case IPX_FRAME_ETHERII: - dlink_type = htons(ETH_P_IPX); - datalink = pEII_datalink; - break; - case IPX_FRAME_TR_8022: - dlink_type = htons(ETH_P_TR_802_2); - datalink = p8022tr_datalink; - break; - case IPX_FRAME_8022: - dlink_type = htons(ETH_P_802_2); - datalink = p8022_datalink; - break; - case IPX_FRAME_SNAP: - dlink_type = htons(ETH_P_SNAP); - datalink = pSNAP_datalink; - break; - case IPX_FRAME_8023: - dlink_type = htons(ETH_P_802_3); - datalink = p8023_datalink; - break; - case IPX_FRAME_NONE: - default: - break; + switch (idef->ipx_dlink_type) + { + case IPX_FRAME_ETHERII: + dlink_type = htons(ETH_P_IPX); + datalink = pEII_datalink; + break; + case IPX_FRAME_TR_8022: + dlink_type = htons(ETH_P_TR_802_2); + datalink = p8022tr_datalink; + break; + case IPX_FRAME_8022: + dlink_type = htons(ETH_P_802_2); + datalink = p8022_datalink; + break; + case IPX_FRAME_SNAP: + dlink_type = htons(ETH_P_SNAP); + datalink = pSNAP_datalink; + break; + case IPX_FRAME_8023: + dlink_type = htons(ETH_P_802_3); + datalink = p8023_datalink; + break; + case IPX_FRAME_NONE: + default: + break; } if (datalink == NULL) @@ -1032,15 +1034,16 @@ ipxitf_create(ipx_interface_definition *idef) return ipxitf_add_local_route(intrfc); } -static int -ipxitf_delete(ipx_interface_definition *idef) +static int ipxitf_delete(ipx_interface_definition *idef) { struct device *dev = NULL; unsigned short dlink_type = 0; ipx_interface *intrfc; - if (idef->ipx_special == IPX_INTERNAL) { - if (ipx_internal_net != NULL) { + if (idef->ipx_special == IPX_INTERNAL) + { + if (ipx_internal_net != NULL) + { ipxitf_down(ipx_internal_net); return 0; } @@ -1062,19 +1065,31 @@ ipxitf_delete(ipx_interface_definition *idef) return -EINVAL; } -static ipx_interface * -ipxitf_auto_create(struct device *dev, unsigned short dlink_type) +static ipx_interface *ipxitf_auto_create(struct device *dev, + unsigned short dlink_type) { struct datalink_proto *datalink = NULL; ipx_interface *intrfc; - switch (htons(dlink_type)) { - case ETH_P_IPX: datalink = pEII_datalink; break; - case ETH_P_802_2: datalink = p8022_datalink; break; - case ETH_P_TR_802_2: datalink = p8022tr_datalink; break; - case ETH_P_SNAP: datalink = pSNAP_datalink; break; - case ETH_P_802_3: datalink = p8023_datalink; break; - default: return NULL; + switch (htons(dlink_type)) + { + case ETH_P_IPX: + datalink = pEII_datalink; + break; + case ETH_P_802_2: + datalink = p8022_datalink; + break; + case ETH_P_TR_802_2: + datalink = p8022tr_datalink; + break; + case ETH_P_SNAP: + datalink = pSNAP_datalink; + break; + case ETH_P_802_3: + datalink = p8023_datalink; + break; + default: + return NULL; } if (dev == NULL) @@ -1084,7 +1099,8 @@ ipxitf_auto_create(struct device *dev, unsigned short dlink_type) if(dev->addr_len>IPX_NODE_LEN) return NULL; intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC); - if (intrfc!=NULL) { + if (intrfc!=NULL) + { intrfc->if_dev=dev; intrfc->if_netnum=0L; intrfc->if_dlink_type = dlink_type; @@ -1103,12 +1119,12 @@ ipxitf_auto_create(struct device *dev, unsigned short dlink_type) return intrfc; } -static int -ipxitf_ioctl_real(unsigned int cmd, void *arg) +static int ipxitf_ioctl_real(unsigned int cmd, void *arg) { switch(cmd) { - case SIOCSIFADDR: { + case SIOCSIFADDR: + { struct ifreq ifr; struct sockaddr_ipx *sipx; ipx_interface_definition f; @@ -1128,7 +1144,8 @@ ipxitf_ioctl_real(unsigned int cmd, void *arg) else return ipxitf_create(&f); } - case SIOCGIFADDR: { + case SIOCGIFADDR: + { struct ifreq ifr; struct sockaddr_ipx *sipx; ipx_interface *ipxif; @@ -1152,14 +1169,16 @@ ipxitf_ioctl_real(unsigned int cmd, void *arg) return -EFAULT; return err; } - case SIOCAIPXITFCRT: { + case SIOCAIPXITFCRT: + { int err, val; err = get_user(val, (unsigned char *) arg); if (err) return err; return ipxcfg_set_auto_create(val); } - case SIOCAIPXPRISLT: { + case SIOCAIPXPRISLT: + { int err, val; err = get_user(val, (unsigned char *) arg); if (err) @@ -1171,8 +1190,7 @@ ipxitf_ioctl_real(unsigned int cmd, void *arg) } } -static int -ipxitf_ioctl(unsigned int cmd, void *arg) +static int ipxitf_ioctl(unsigned int cmd, void *arg) { int ret; MOD_INC_USE_COUNT; @@ -1180,14 +1198,14 @@ ipxitf_ioctl(unsigned int cmd, void *arg) MOD_DEC_USE_COUNT; return ret; } + /*******************************************************************************************************************\ * * * Routing tables for the IPX socket layer * * * \*******************************************************************************************************************/ -static ipx_route * -ipxrtr_lookup(unsigned long net) +static ipx_route *ipxrtr_lookup(unsigned long net) { ipx_route *r; @@ -1197,14 +1215,14 @@ ipxrtr_lookup(unsigned long net) return r; } -static int -ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node) +static int ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node) { ipx_route *rt; /* Get a route structure; either existing or create */ rt = ipxrtr_lookup(network); - if (rt==NULL) { + if (rt==NULL) + { rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC); if(rt==NULL) return -EAGAIN; @@ -1226,8 +1244,7 @@ ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *no return 0; } -static void -ipxrtr_del_routes(ipx_interface *intrfc) +static void ipxrtr_del_routes(ipx_interface *intrfc) { ipx_route **r, *tmp; @@ -1241,8 +1258,7 @@ ipxrtr_del_routes(ipx_interface *intrfc) } } -static int -ipxrtr_create(ipx_route_definition *rd) +static int ipxrtr_create(ipx_route_definition *rd) { ipx_interface *intrfc; @@ -1255,18 +1271,18 @@ ipxrtr_create(ipx_route_definition *rd) } -static int -ipxrtr_delete(long net) +static int ipxrtr_delete(long net) { ipx_route **r; ipx_route *tmp; - for (r = &ipx_routes; (tmp = *r) != NULL; ) { - if (tmp->ir_net == net) { - if (!(tmp->ir_routed)) { + for (r = &ipx_routes; (tmp = *r) != NULL; ) + { + if (tmp->ir_net == net) + { + if (!(tmp->ir_routed)) /* Directly connected; can't lose route */ return -EPERM; - } *r = tmp->ir_next; kfree_s(tmp, sizeof(ipx_route)); return 0; @@ -1359,9 +1375,9 @@ static int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, stru else { rt = ipxrtr_lookup(usipx->sipx_network); - if (rt==NULL) { + if (rt==NULL) return -ENETUNREACH; - } + intrfc = rt->ir_intrfc; } @@ -1433,15 +1449,15 @@ static int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, stru rt->ir_router_node : ipx->ipx_dest.node); } -static int -ipxrtr_route_skb(struct sk_buff *skb) +static int ipxrtr_route_skb(struct sk_buff *skb) { struct ipxhdr *ipx = skb->nh.ipxh; ipx_route *r; ipx_interface *i; r = ipxrtr_lookup(ipx->ipx_dest.net); - if (r == NULL) { + if (r == NULL) + { /* no known route */ kfree_skb(skb,FREE_READ); return 0; @@ -1493,21 +1509,26 @@ static int ipxrtr_ioctl(unsigned int cmd, void *arg) } } -static const char * -ipx_frame_name(unsigned short frame) +static const char *ipx_frame_name(unsigned short frame) { - switch (ntohs(frame)) { - case ETH_P_IPX: return "EtherII"; - case ETH_P_802_2: return "802.2"; - case ETH_P_SNAP: return "SNAP"; - case ETH_P_802_3: return "802.3"; - case ETH_P_TR_802_2: return "802.2TR"; - default: return "None"; + switch (ntohs(frame)) + { + case ETH_P_IPX: + return "EtherII"; + case ETH_P_802_2: + return "802.2"; + case ETH_P_SNAP: + return "SNAP"; + case ETH_P_802_3: + return "802.3"; + case ETH_P_TR_802_2: + return "802.2TR"; + default: + return "None"; } } -static const char * -ipx_device_name(ipx_interface *intrfc) +static const char *ipx_device_name(ipx_interface *intrfc) { return (intrfc->if_internal ? "Internal" : (intrfc->if_dev ? intrfc->if_dev->name : "Unknown")); @@ -1772,6 +1793,15 @@ static void def_callback2(struct sock *sk, int len) } } +static void def_callback3(struct sock *sk, int len) +{ + if(!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket, 2); + } +} + static int ipx_create(struct socket *sock, int protocol) { struct sock *sk; @@ -1807,7 +1837,7 @@ static int ipx_create(struct socket *sock, int protocol) sk->state_change=def_callback1; sk->data_ready=def_callback2; - sk->write_space=def_callback1; + sk->write_space=def_callback3; sk->error_report=def_callback1; sk->zapped=1; @@ -1833,8 +1863,7 @@ static int ipx_dup(struct socket *newsock,struct socket *oldsock) return(ipx_create(newsock,SOCK_DGRAM)); } -static unsigned short -ipx_first_free_socketnum(ipx_interface *intrfc) +static unsigned short ipx_first_free_socketnum(ipx_interface *intrfc) { unsigned short socketNum = intrfc->if_sknum; @@ -2052,56 +2081,66 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, /* * User to dump IPX packets (debugging) */ -void dump_data(char *str,unsigned char *d, int len) { - static char h2c[] = "0123456789ABCDEF"; - int l,i; - char *p, b[64]; - for (l=0;len > 0 && l<16;l++) { - p = b; - for (i=0; i < 8 ; i++, --len) { - if (len > 0) { - *(p++) = h2c[(d[i] >> 4) & 0x0f]; - *(p++) = h2c[d[i] & 0x0f]; - } - else { - *(p++) = ' '; - *(p++) = ' '; - } - *(p++) = ' '; - } - *(p++) = '-'; - *(p++) = ' '; - len += 8; - for (i=0; i < 8 ; i++, --len) - if (len > 0) - *(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.'; - else +void dump_data(char *str,unsigned char *d, int len) +{ + static char h2c[] = "0123456789ABCDEF"; + int l,i; + char *p, b[64]; + for (l=0;len > 0 && l<16;l++) + { + p = b; + for (i=0; i < 8 ; i++, --len) + { + if (len > 0) + { + *(p++) = h2c[(d[i] >> 4) & 0x0f]; + *(p++) = h2c[d[i] & 0x0f]; + } + else + { + *(p++) = ' '; + *(p++) = ' '; + } *(p++) = ' '; - *p = '\000'; - d += i; - printk(KERN_DEBUG"%s-%04X: %s\n",str,l*8,b); - } + } + *(p++) = '-'; + *(p++) = ' '; + len += 8; + for (i=0; i < 8 ; i++, --len) + { + if (len > 0) + *(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.'; + else + *(p++) = ' '; + } + *p = '\000'; + d += i; + printk(KERN_DEBUG"%s-%04X: %s\n",str,l*8,b); + } } -void dump_addr(char *str,ipx_address *p) { - printk(KERN_DEBUG"%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n", - str,ntohl(p->net),p->node[0],p->node[1],p->node[2], - p->node[3],p->node[4],p->node[5],ntohs(p->sock)); +void dump_addr(char *str,ipx_address *p) +{ + printk(KERN_DEBUG"%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n", + str,ntohl(p->net),p->node[0],p->node[1],p->node[2], + p->node[3],p->node[4],p->node[5],ntohs(p->sock)); } -void dump_hdr(char *str,struct ipxhdr *p) { - printk(KERN_DEBUG"%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n", - str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize), - p->ipx_tctrl,p->ipx_tctrl,p->ipx_type); - dump_addr(" IPX-DST",&p->ipx_dest); - dump_addr(" IPX-SRC",&p->ipx_source); +void dump_hdr(char *str,struct ipxhdr *p) +{ + printk(KERN_DEBUG"%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n", + str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize), + p->ipx_tctrl,p->ipx_tctrl,p->ipx_type); + dump_addr(" IPX-DST",&p->ipx_dest); + dump_addr(" IPX-SRC",&p->ipx_source); } -void dump_pkt(char *str,struct ipxhdr *p) { - int len = ntohs(p->ipx_pktsize); - dump_hdr(str,p); - if (len > 30) - dump_data(str,(unsigned char *)p + 30, len - 30); +void dump_pkt(char *str,struct ipxhdr *p) +{ + int len = ntohs(p->ipx_pktsize); + dump_hdr(str,p); + if (len > 30) + dump_data(str,(unsigned char *)p + 30, len - 30); } #endif @@ -2253,6 +2292,10 @@ static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, return(copied); } +/* + * FIXME: We have to support shutdown really. + */ + static int ipx_shutdown(struct socket *sk,int how) { return -EOPNOTSUPP; @@ -2427,7 +2470,7 @@ ipx_proto_init(struct net_proto *pro) proc_net_register(&ipx_rt_procinfo); #endif - printk(KERN_INFO "Swansea University Computer Society IPX 0.35 for NET3.037\n"); + printk(KERN_INFO "Swansea University Computer Society IPX 0.38 for NET3.037\n"); printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n"); } @@ -2445,8 +2488,7 @@ ipx_proto_init(struct net_proto *pro) * sockets be closed from user space. */ -static void -ipx_proto_finito(void) +static void ipx_proto_finito(void) { ipx_interface *ifc; while (ipx_interfaces) { diff --git a/net/lapb/Makefile b/net/lapb/Makefile new file mode 100644 index 000000000000..434e79aa031d --- /dev/null +++ b/net/lapb/Makefile @@ -0,0 +1,20 @@ +# +# Makefile for the Linux LAPB layer. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + + +O_TARGET := lapb.o +O_OBJS := lapb_in.o lapb_out.o lapb_subr.o lapb_timer.o +M_OBJS := $(O_TARGET) + +OX_OBJS += lapb_iface.o + +include $(TOPDIR)/Rules.make + +tar: + tar -cvf /dev/f1 . diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c new file mode 100644 index 000000000000..12b48c991c14 --- /dev/null +++ b/net/lapb/lapb_iface.c @@ -0,0 +1,409 @@ +/* + * LAPB release 001 + * + * This is ALPHA test software. This code may break your machine, randomly fail to work with new + * releases, misbehave and/or generally screw up. It might even work. + * + * This code REQUIRES 2.1.15 or higher/ NET3.038 + * + * This module: + * This module 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. + * + * History + * LAPB 001 Jonathan Naylor Started Coding + */ + +#include +#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static lapb_cb *volatile lapb_list = NULL; + +/* + * Free an allocated lapb control block. This is done to centralise + * the MOD count code. + */ +static void lapb_free_cb(lapb_cb *lapb) +{ + del_timer(&lapb->timer); + + kfree_s(lapb, sizeof(lapb_cb)); + + MOD_DEC_USE_COUNT; +} + +/* + * Socket removal during an interrupt is now safe. + */ +static void lapb_remove_cb(lapb_cb *lapb) +{ + lapb_cb *s; + unsigned long flags; + + save_flags(flags); + cli(); + + if ((s = lapb_list) == lapb) { + lapb_list = s->next; + restore_flags(flags); + return; + } + + while (s != NULL && s->next != NULL) { + if (s->next == lapb) { + s->next = lapb->next; + restore_flags(flags); + return; + } + + s = s->next; + } + + restore_flags(flags); +} + +/* + * Add a socket to the bound sockets list. + */ +static void lapb_insert_cb(lapb_cb *lapb) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + lapb->next = lapb_list; + lapb_list = lapb; + + restore_flags(flags); +} + +/* + * Convert the integer token used by the device driver into a pointer + * to a LAPB control structure. + */ +lapb_cb *lapb_tokentostruct(void *token) +{ + lapb_cb *lapb; + + for (lapb = lapb_list; lapb != NULL; lapb = lapb->next) + if (lapb->token == token) + return lapb; + + return NULL; +} + +/* + * Create an empty LAPB control block. + */ +static lapb_cb *lapb_create_cb(void) +{ + lapb_cb *lapb; + + if ((lapb = (lapb_cb *)kmalloc(sizeof(*lapb), GFP_ATOMIC)) == NULL) + return NULL; + + MOD_INC_USE_COUNT; + + memset(lapb, 0x00, sizeof(*lapb)); + + skb_queue_head_init(&lapb->write_queue); + skb_queue_head_init(&lapb->ack_queue); + + init_timer(&lapb->timer); + + lapb->t1 = LAPB_DEFAULT_T1; + lapb->t2 = LAPB_DEFAULT_T2; + lapb->n2 = LAPB_DEFAULT_N2; + lapb->mode = LAPB_DEFAULT_MODE; + lapb->window = LAPB_DEFAULT_WINDOW; + lapb->state = LAPB_STATE_0; + + return lapb; +} + +int lapb_register(void *token, struct lapb_register_struct *callbacks) +{ + lapb_cb *lapb; + + if (lapb_tokentostruct(token) != NULL) + return LAPB_BADTOKEN; + + if ((lapb = lapb_create_cb()) == NULL) + return LAPB_NOMEM; + + lapb->token = token; + lapb->callbacks = *callbacks; + + lapb_insert_cb(lapb); + + return LAPB_OK; +} + +int lapb_unregister(void *token) +{ + lapb_cb *lapb; + + if ((lapb = lapb_tokentostruct(token)) == NULL) + return LAPB_BADTOKEN; + + lapb_clear_queues(lapb); + + lapb_remove_cb(lapb); + + lapb_free_cb(lapb); + + return LAPB_OK; +} + +int lapb_getparms(void *token, struct lapb_parms_struct *parms) +{ + lapb_cb *lapb; + + if ((lapb = lapb_tokentostruct(token)) == NULL) + return LAPB_BADTOKEN; + + parms->t1 = lapb->t1; + parms->t1timer = lapb->t1timer; + parms->t2 = lapb->t2; + parms->t2timer = lapb->t2timer; + parms->n2 = lapb->n2; + parms->n2count = lapb->n2count; + parms->state = lapb->state; + parms->window = lapb->window; + parms->mode = lapb->mode; + + return LAPB_OK; +} + +int lapb_setparms(void *token, struct lapb_parms_struct *parms) +{ + lapb_cb *lapb; + + if ((lapb = lapb_tokentostruct(token)) == NULL) + return LAPB_BADTOKEN; + + if (parms->t1 < 1) + return LAPB_INVALUE; + + if (parms->t2 < 1) + return LAPB_INVALUE; + + if (parms->n2 < 1) + return LAPB_INVALUE; + + if (lapb->state == LAPB_STATE_0) { + if (parms->mode & LAPB_EXTENDED) { + if (parms->window < 1 || parms->window > 127) + return LAPB_INVALUE; + } else { + if (parms->window < 1 || parms->window > 7) + return LAPB_INVALUE; + } + + lapb->mode = parms->mode; + lapb->window = parms->window; + + if (lapb->mode & LAPB_DCE) { + lapb_set_timer(lapb); + } else { + lapb->t1timer = 0; + } + } + + lapb->t1 = parms->t1; + lapb->t2 = parms->t2; + lapb->n2 = parms->n2; + + return LAPB_OK; +} + +int lapb_connect_request(void *token) +{ + lapb_cb *lapb; + + if ((lapb = lapb_tokentostruct(token)) == NULL) + return LAPB_BADTOKEN; + + switch (lapb->state) { + case LAPB_STATE_1: + return LAPB_OK; + case LAPB_STATE_3: + case LAPB_STATE_4: + return LAPB_CONNECTED; + } + + lapb_establish_data_link(lapb); + +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S0 -> S1\n", lapb->token); +#endif + + lapb->state = LAPB_STATE_1; + + lapb_set_timer(lapb); + + return LAPB_OK; +} + +int lapb_disconnect_request(void *token) +{ + lapb_cb *lapb; + + if ((lapb = lapb_tokentostruct(token)) == NULL) + return LAPB_BADTOKEN; + + switch (lapb->state) { + case LAPB_STATE_0: + return LAPB_NOTCONNECTED; + + case LAPB_STATE_1: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 TX DISC(1)\n", lapb->token); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); +#endif + lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + return LAPB_NOTCONNECTED; + + case LAPB_STATE_2: + return LAPB_OK; + } + + lapb_clear_queues(lapb); + lapb->n2count = 0; + lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); + lapb->t1timer = lapb->t1; + lapb->t2timer = 0; + lapb->state = LAPB_STATE_2; + +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->token); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S2\n", lapb->token); +#endif + + return LAPB_OK; +} + +int lapb_data_request(void *token, struct sk_buff *skb) +{ + lapb_cb *lapb; + + if ((lapb = lapb_tokentostruct(token)) == NULL) + return LAPB_BADTOKEN; + + if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4) + return LAPB_NOTCONNECTED; + + skb_queue_tail(&lapb->write_queue, skb); + + lapb_kick(lapb); + + return LAPB_OK; +} + +void lapb_connect_confirmation(lapb_cb *lapb, int reason) +{ + if (lapb->callbacks.connect_confirmation != NULL) + (lapb->callbacks.connect_confirmation)(lapb->token, reason); +} + +void lapb_connect_indication(lapb_cb *lapb, int reason) +{ + if (lapb->callbacks.connect_indication != NULL) + (lapb->callbacks.connect_indication)(lapb->token, reason); +} + +void lapb_disconnect_confirmation(lapb_cb *lapb, int reason) +{ + if (lapb->callbacks.disconnect_confirmation != NULL) + (lapb->callbacks.disconnect_confirmation)(lapb->token, reason); +} + +void lapb_disconnect_indication(lapb_cb *lapb, int reason) +{ + if (lapb->callbacks.disconnect_indication != NULL) + (lapb->callbacks.disconnect_indication)(lapb->token, reason); +} + +int lapb_data_indication(lapb_cb *lapb, struct sk_buff *skb) +{ + int used = 0; + + if (lapb->callbacks.data_indication != NULL) { + (lapb->callbacks.data_indication)(lapb->token, skb); + used = 1; + } + + return used; +} + +int lapb_data_transmit(lapb_cb *lapb, struct sk_buff *skb) +{ + int used = 0; + + if (lapb->callbacks.data_transmit != NULL) { + (lapb->callbacks.data_transmit)(lapb->token, skb); + used = 1; + } + + return used; +} + +EXPORT_SYMBOL(lapb_register); +EXPORT_SYMBOL(lapb_unregister); +EXPORT_SYMBOL(lapb_getparms); +EXPORT_SYMBOL(lapb_setparms); +EXPORT_SYMBOL(lapb_connect_request); +EXPORT_SYMBOL(lapb_disconnect_request); +EXPORT_SYMBOL(lapb_data_request); +EXPORT_SYMBOL(lapb_data_received); + +void lapb_proto_init(struct net_proto *pro) +{ + printk(KERN_INFO "LAPB for Linux. Version 0.01 for Linux NET3.038 (Linux 2.1)\n"); +} + +#ifdef MODULE +int init_module(void) +{ + lapb_proto_init(NULL); + + return 0; +} + +void cleanup_module(void) +{ +} +#endif + +#endif diff --git a/net/lapb/lapb_in.c b/net/lapb/lapb_in.c new file mode 100644 index 000000000000..91d61db93df3 --- /dev/null +++ b/net/lapb/lapb_in.c @@ -0,0 +1,799 @@ +/* + * LAPB release 001 + * + * This is ALPHA test software. This code may break your machine, randomly fail to work with new + * releases, misbehave and/or generally screw up. It might even work. + * + * This code REQUIRES 2.1.15 or higher/ NET3.038 + * + * This module: + * This module 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. + * + * History + * LAPB 001 Jonathan Naulor Started Coding + */ + +#include +#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * State machine for state 0, Disconnected State. + * The handling of the timer(s) is in file lapb_timer.c. + */ +static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf) +{ + switch (frametype) { + case LAPB_SABM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 RX SABM(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->t1timer = 0; + lapb->t2timer = 0; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_indication(lapb, LAPB_OK); + } + break; + + case LAPB_SABME: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 RX SABME(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->t1timer = 0; + lapb->t2timer = 0; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_indication(lapb, LAPB_OK); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } + break; + + case LAPB_DISC: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S0 RX DISC(%d)\n", lapb->token, pf); + printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + break; + + default: + break; + } + + kfree_skb(skb, FREE_READ); +} + +/* + * State machine for state 1, Awaiting Connection State. + * The handling of the timer(s) is in file lapb_timer.c. + */ +static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf) +{ + switch (frametype) { + case LAPB_SABM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 RX SABM(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + } + break; + + case LAPB_SABME: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 RX SABME(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } + break; + + case LAPB_DISC: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 RX DISC(%d)\n", lapb->token, pf); + printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + break; + + case LAPB_UA: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 RX UA(%d)\n", lapb->token, pf); +#endif + if (pf) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->token); +#endif + lapb->state = LAPB_STATE_3; + lapb->condition = 0x00; + lapb->t1timer = 0; + lapb->t2timer = 0; + lapb->n2count = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + lapb_connect_confirmation(lapb, LAPB_OK); + } + break; + + case LAPB_DM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 RX DM(%d)\n", lapb->token, pf); +#endif + if (pf) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); +#endif + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_indication(lapb, LAPB_REFUSED); + } + break; + + default: + break; + } + + kfree_skb(skb, FREE_READ); +} + +/* + * State machine for state 2, Awaiting Release State. + * The handling of the timer(s) is in file lapb_timer.c + */ +static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int pf) +{ + switch (frametype) { + case LAPB_SABM: + case LAPB_SABME: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S2 RX {SABM,SABME}(%d)\n", lapb->token, pf); + printk(KERN_DEBUG "lapb: (%p) S2 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + break; + + case LAPB_DISC: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S2 RX DISC(%d)\n", lapb->token, pf); + printk(KERN_DEBUG "lapb: (%p) S2 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, C_RESPONSE); + break; + + case LAPB_UA: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S2 RX UA(%d)\n", lapb->token, pf); +#endif + if (pf) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); +#endif + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_confirmation(lapb, LAPB_OK); + } + break; + + case LAPB_DM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, pf); +#endif + if (pf) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); +#endif + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_confirmation(lapb, LAPB_NOTCONNECTED); + } + break; + + case LAPB_I: + case LAPB_REJ: + case LAPB_RNR: + case LAPB_RR: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S2 RX {I,REJ,RNR,RR}(%d)\n", lapb->token, pf); + printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, pf); +#endif + if (pf) lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + break; + + default: + break; + } + + kfree_skb(skb, FREE_READ); +} + +/* + * State machine for state 3, Connected State. + * The handling of the timer(s) is in file lapb_timer.c + */ +static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) +{ + int queued = 0; + int modulus; + + modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; + + switch (frametype) { + case LAPB_SABM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX SABM(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->condition = 0x00; + lapb->t1timer = 0; + lapb->t2timer = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + } + break; + + case LAPB_SABME: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX SABME(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->condition = 0x00; + lapb->t1timer = 0; + lapb->t2timer = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } + break; + + case LAPB_DISC: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX DISC(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token); +#endif + lapb_clear_queues(lapb); + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_indication(lapb, LAPB_OK); + break; + + case LAPB_DM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX DM(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token); +#endif + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED); + break; + + case LAPB_RNR: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX RNR(%d) R%d\n", lapb->token, pf, nr); +#endif + lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION; + lapb_check_need_response(lapb, type, pf); + if (lapb_validate_nr(lapb, nr)) { + lapb_check_iframes_acked(lapb, nr); + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + + case LAPB_RR: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX RR(%d) R%d\n", lapb->token, pf, nr); +#endif + lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; + lapb_check_need_response(lapb, type, pf); + if (lapb_validate_nr(lapb, nr)) { + lapb_check_iframes_acked(lapb, nr); + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + + case LAPB_REJ: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX REJ(%d) R%d\n", lapb->token, pf, nr); +#endif + lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; + lapb_check_need_response(lapb, type, pf); + if (lapb_validate_nr(lapb, nr)) { + lapb_frames_acked(lapb, nr); + lapb->t1timer = 0; + lapb_requeue_frames(lapb); + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + + case LAPB_I: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX I(%d) S%d R%d\n", lapb->token, pf, ns, nr); +#endif + if (type != LAPB_COMMAND) + break; + if (!lapb_validate_nr(lapb, nr)) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + break; + } + if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) { + lapb_frames_acked(lapb, nr); + } else { + lapb_check_iframes_acked(lapb, nr); + } + if (ns == lapb->vr) { + lapb->vr = (lapb->vr + 1) % modulus; + queued = lapb_data_indication(lapb, skb); + lapb->condition &= ~LAPB_REJECT_CONDITION; + if (pf) { + lapb_enquiry_response(lapb); + } else { + if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) { + lapb->t2timer = lapb->t2; + lapb->condition |= LAPB_ACK_PENDING_CONDITION; + } + } + } else { + if (lapb->condition & LAPB_REJECT_CONDITION) { + if (pf) + lapb_enquiry_response(lapb); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 TX REJ(%d) R%d\n", lapb->token, pf, lapb->vr); +#endif + lapb->condition |= LAPB_REJECT_CONDITION; + lapb_send_control(lapb, LAPB_REJ, pf, LAPB_RESPONSE); + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; + } + } + break; + + case LAPB_FRMR: + case LAPB_ILLEGAL: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S3 RX {FRMR,ILLEGAL}(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token); +#endif + lapb_establish_data_link(lapb); + lapb->state = LAPB_STATE_1; + break; + + default: + break; + } + + if (!queued) + kfree_skb(skb, FREE_READ); +} + +/* + * State machine for state 4, Timer Recovery State. + * The handling of the timer(s) is in file lapb_timer.c + */ +static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) +{ + int queued = 0; + int modulus; + + modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; + + switch (frametype) { + case LAPB_SABM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX SABM(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->condition = 0x00; + lapb->t1timer = 0; + lapb->t2timer = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + } + break; + + case LAPB_SABME: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX SABME(%d)\n", lapb->token, pf); +#endif + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->condition = 0x00; + lapb->t1timer = 0; + lapb->t2timer = 0; + lapb->vs = 0; + lapb->vr = 0; + lapb->va = 0; + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, pf); +#endif + lapb_send_control(lapb, LAPB_DM, pf, LAPB_RESPONSE); + } + break; + + case LAPB_DISC: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX DISC(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token); +#endif + lapb_clear_queues(lapb); + lapb_send_control(lapb, LAPB_UA, pf, LAPB_RESPONSE); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_indication(lapb, LAPB_OK); + break; + + case LAPB_DM: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX DM(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token); +#endif + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED); + break; + + case LAPB_RNR: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX RNR(%d) R%d\n", lapb->token, pf, nr); +#endif + lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION; + if (type == LAPB_RESPONSE && pf) { + lapb->t1timer = 0; + if (lapb_validate_nr(lapb, nr)) { + lapb_frames_acked(lapb, nr); + if (lapb->vs == lapb->va) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token); +#endif + lapb->n2count = 0; + lapb->state = LAPB_STATE_3; + } + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + } + + lapb_check_need_response(lapb, type, pf); + if (lapb_validate_nr(lapb, nr)) { + lapb_frames_acked(lapb, nr); + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + + case LAPB_RR: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX RR(%d) R%d\n", lapb->token, pf, nr); +#endif + lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; + if (pf && type == LAPB_RESPONSE) { + lapb->t1timer = 0; + if (lapb_validate_nr(lapb, nr)) { + lapb_frames_acked(lapb, nr); + if (lapb->vs == lapb->va) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token); +#endif + lapb->n2count = 0; + lapb->state = LAPB_STATE_3; + } else { + lapb_requeue_frames(lapb); + } + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + } + + lapb_check_need_response(lapb, type, pf); + if (lapb_validate_nr(lapb, nr)) { + lapb_frames_acked(lapb, nr); + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + + case LAPB_REJ: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX REJ(%d) R%d\n", lapb->token, pf, nr); +#endif + lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION; + if (pf && type == LAPB_RESPONSE) { + lapb->t1timer = 0; + if (lapb_validate_nr(lapb, nr)) { + lapb_frames_acked(lapb, nr); + if (lapb->vs == lapb->va) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token); +#endif + lapb->n2count = 0; + lapb->state = LAPB_STATE_3; + } else { + lapb_requeue_frames(lapb); + } + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + } + + lapb_check_need_response(lapb, type, pf); + if (lapb_validate_nr(lapb, nr)) { + lapb_frames_acked(lapb, nr); + if (lapb->vs != lapb->va) + lapb_requeue_frames(lapb); + } else { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + } + break; + + case LAPB_I: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 RX I(%d) S%d R%d\n", lapb->token, pf, ns, nr); +#endif + if (type != LAPB_COMMAND) + break; + if (!lapb_validate_nr(lapb, nr)) { +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_nr_error_recovery(lapb); + lapb->state = LAPB_STATE_1; + break; + } + lapb_frames_acked(lapb, nr); + if (ns == lapb->vr) { + lapb->vr = (lapb->vr + 1) % modulus; + queued = lapb_data_indication(lapb, skb); + lapb->condition &= ~LAPB_REJECT_CONDITION; + if (pf) { + lapb_enquiry_response(lapb); + } else { + if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) { + lapb->t2timer = lapb->t2; + lapb->condition |= LAPB_ACK_PENDING_CONDITION; + } + } + } else { + if (lapb->condition & LAPB_REJECT_CONDITION) { + if (pf) + lapb_enquiry_response(lapb); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 TX REJ(%d) R%d\n", lapb->token, pf, lapb->vr); +#endif + lapb->condition |= LAPB_REJECT_CONDITION; + lapb_send_control(lapb, LAPB_REJ, pf, LAPB_RESPONSE); + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; + } + } + break; + + case LAPB_FRMR: + case LAPB_ILLEGAL: +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S4 TX {FRMR,ILLEGAL}(%d)\n", lapb->token, pf); +#endif +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S1\n", lapb->token); +#endif + lapb_establish_data_link(lapb); + lapb->state = LAPB_STATE_1; + break; + + default: + break; + } + + if (!queued) + kfree_skb(skb, FREE_READ); +} + +/* + * Process an incoming LAPB frame + */ +int lapb_data_received(void *token, struct sk_buff *skb) +{ + int frametype, ns, nr, pf, type; + lapb_cb *lapb; + + if ((lapb = lapb_tokentostruct(token)) == NULL) + return LAPB_BADTOKEN; + + del_timer(&lapb->timer); + + frametype = lapb_decode(lapb, skb, &ns, &nr, &pf, &type); + + switch (lapb->state) { + case LAPB_STATE_0: + lapb_state0_machine(lapb, skb, frametype, pf); + break; + case LAPB_STATE_1: + lapb_state1_machine(lapb, skb, frametype, pf); + break; + case LAPB_STATE_2: + lapb_state2_machine(lapb, skb, frametype, pf); + break; + case LAPB_STATE_3: + lapb_state3_machine(lapb, skb, frametype, ns, nr, pf, type); + break; + case LAPB_STATE_4: + lapb_state4_machine(lapb, skb, frametype, ns, nr, pf, type); + break; + } + + lapb_set_timer(lapb); + + return LAPB_OK; +} + +#endif diff --git a/net/lapb/lapb_out.c b/net/lapb/lapb_out.c new file mode 100644 index 000000000000..43481e416416 --- /dev/null +++ b/net/lapb/lapb_out.c @@ -0,0 +1,252 @@ +/* + * LAPB release 001 + * + * This is ALPHA test software. This code may break your machine, randomly fail to work with new + * releases, misbehave and/or generally screw up. It might even work. + * + * This code REQUIRES 2.1.15 or higher/ NET3.038 + * + * This module: + * This module 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. + * + * History + * LAPB 001 Jonathan Naylor Started Coding + */ + +#include +#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This procedure is passed a buffer descriptor for an iframe. It builds + * the rest of the control part of the frame and then writes it out. + */ +static void lapb_send_iframe(lapb_cb *lapb, struct sk_buff *skb, int poll_bit) +{ + unsigned char *frame; + + if (skb == NULL) + return; + + if (lapb->mode & LAPB_EXTENDED) { + frame = skb_push(skb, 2); + + frame[0] = I; + frame[0] |= (lapb->vs << 1); + frame[1] = (poll_bit) ? LAPB_EPF : 0; + frame[1] |= (lapb->vr << 1); + } else { + frame = skb_push(skb, 1); + + *frame = I; + *frame |= (poll_bit) ? LAPB_SPF : 0; + *frame |= (lapb->vr << 5); + *frame |= (lapb->vs << 1); + } + + lapb_transmit_buffer(lapb, skb, LAPB_COMMAND); +} + +void lapb_kick(lapb_cb *lapb) +{ + struct sk_buff *skb, *skbn; + int modulus, last = 1; + unsigned short start, end, next; + + del_timer(&lapb->timer); + + modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; + + start = (skb_peek(&lapb->ack_queue) == NULL) ? lapb->va : lapb->vs; + end = (lapb->va + lapb->window) % modulus; + + if (!(lapb->condition & PEER_RX_BUSY_CONDITION) && + start != end && + skb_peek(&lapb->write_queue) != NULL) { + + lapb->vs = start; + + /* + * Dequeue the frame and copy it. + */ + skb = skb_dequeue(&lapb->write_queue); + + do { + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { + skb_queue_head(&lapb->write_queue, skb); + break; + } + + next = (lapb->vs + 1) % modulus; +#ifdef notdef + last = (next == end) || skb_peek(&lapb->write_queue) == NULL; +#else + last = (next == end); +#endif + /* + * Transmit the frame copy. + */ + lapb_send_iframe(lapb, skbn, POLLOFF); + + lapb->vs = next; + + /* + * Requeue the original data frame. + */ + skb_queue_tail(&lapb->ack_queue, skb); +#ifdef notdef + } while (!last); +#else + } while (!last && (skb = skb_dequeue(&lapb->write_queue)) != NULL); +#endif + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; + + if (lapb->t1timer == 0) + lapb->t1timer = lapb->t1; + } + + lapb_set_timer(lapb); +} + +void lapb_transmit_buffer(lapb_cb *lapb, struct sk_buff *skb, int type) +{ + unsigned char *ptr; + + ptr = skb_push(skb, 1); + + if (lapb->mode & LAPB_MLP) { + if (lapb->mode & LAPB_DCE) { + if (type == LAPB_COMMAND) + *ptr = LAPB_ADDR_C; + if (type == LAPB_RESPONSE) + *ptr = LAPB_ADDR_D; + } else { + if (type == LAPB_COMMAND) + *ptr = LAPB_ADDR_D; + if (type == LAPB_RESPONSE) + *ptr = LAPB_ADDR_C; + } + } else { + if (lapb->mode & LAPB_DCE) { + if (type == LAPB_COMMAND) + *ptr = LAPB_ADDR_A; + if (type == LAPB_RESPONSE) + *ptr = LAPB_ADDR_B; + } else { + if (type == LAPB_COMMAND) + *ptr = LAPB_ADDR_B; + if (type == LAPB_RESPONSE) + *ptr = LAPB_ADDR_A; + } + } + +#if LAPB_DEBUG > 2 + printk(KERN_DEBUG "lapb: (%p) S%d TX %02X %02X %02X\n", lapb->token, lapb->state, skb->data[0], skb->data[1], skb->data[2]); +#endif + + if (!lapb_data_transmit(lapb, skb)) + kfree_skb(skb, FREE_WRITE); +} + +void lapb_nr_error_recovery(lapb_cb *lapb) +{ + lapb_establish_data_link(lapb); +} + +void lapb_establish_data_link(lapb_cb *lapb) +{ + lapb->condition = 0x00; + lapb->n2count = 0; + + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S%d TX SABME(1)\n", lapb->token, lapb->state); +#endif + lapb_send_control(lapb, SABME, POLLON, LAPB_COMMAND); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S%d TX SABM(1)\n", lapb->token, lapb->state); +#endif + lapb_send_control(lapb, SABM, POLLON, LAPB_COMMAND); + } + + lapb->t2timer = 0; + lapb->t1timer = lapb->t1; +} + +void lapb_transmit_enquiry(lapb_cb *lapb) +{ +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S%d TX RR(1) R%d\n", lapb->token, lapb->state, lapb->vr); +#endif + + lapb_send_control(lapb, RR, POLLON, C_COMMAND); + + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; + + lapb->t1timer = lapb->t1; +} + +void lapb_enquiry_response(lapb_cb *lapb) +{ +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S%d TX RR(1) R%d\n", lapb->token, lapb->state, lapb->vr); +#endif + + lapb_send_control(lapb, RR, POLLON, LAPB_RESPONSE); + + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; +} + +void lapb_timeout_response(lapb_cb *lapb) +{ +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S%d TX RR(0) R%d\n", lapb->token, lapb->state, lapb->vr); +#endif + + lapb_send_control(lapb, RR, POLLOFF, LAPB_RESPONSE); + + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; +} + +void lapb_check_iframes_acked(lapb_cb *lapb, unsigned short nr) +{ + if (lapb->vs == nr) { + lapb_frames_acked(lapb, nr); + lapb->t1timer = 0; + } else { + if (lapb->va != nr) { + lapb_frames_acked(lapb, nr); + lapb->t1timer = lapb->t1; + } + } +} + +void lapb_check_need_response(lapb_cb *lapb, int type, int pf) +{ + if (type == LAPB_COMMAND && pf) + lapb_enquiry_response(lapb); +} + +#endif diff --git a/net/lapb/lapb_subr.c b/net/lapb/lapb_subr.c new file mode 100644 index 000000000000..56117293ee0b --- /dev/null +++ b/net/lapb/lapb_subr.c @@ -0,0 +1,236 @@ +/* + * LAPB release 001 + * + * This is ALPHA test software. This code may break your machine, randomly fail to work with new + * releases, misbehave and/or generally screw up. It might even work. + * + * This code REQUIRES 2.1.15 or higher/ NET3.038 + * + * This module: + * This module 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. + * + * History + * LAPB 001 Jonathan Naylor Started Coding + */ + +#include +#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This routine purges all the queues of frames. + */ +void lapb_clear_queues(lapb_cb *lapb) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&lapb->write_queue)) != NULL) + kfree_skb(skb, FREE_WRITE); + + while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL) + kfree_skb(skb, FREE_WRITE); +} + +/* + * This routine purges the input queue of those frames that have been + * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the + * SDL diagram. + */ +void lapb_frames_acked(lapb_cb *lapb, unsigned short nr) +{ + struct sk_buff *skb; + int modulus; + + modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; + + /* + * Remove all the ack-ed frames from the ack queue. + */ + if (lapb->va != nr) { + while (skb_peek(&lapb->ack_queue) != NULL && lapb->va != nr) { + skb = skb_dequeue(&lapb->ack_queue); + kfree_skb(skb, FREE_WRITE); + lapb->va = (lapb->va + 1) % modulus; + } + } +} + +void lapb_requeue_frames(lapb_cb *lapb) +{ + struct sk_buff *skb, *skb_prev = NULL; + + /* + * Requeue all the un-ack-ed frames on the output queue to be picked + * up by lapb_kick called from the timer. This arrangement handles the + * possibility of an empty output queue. + */ + while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL) { + if (skb_prev == NULL) + skb_queue_head(&lapb->write_queue, skb); + else + skb_append(skb_prev, skb); + skb_prev = skb; + } +} + +/* + * Validate that the value of nr is between va and vs. Return true or + * false for testing. + */ +int lapb_validate_nr(lapb_cb *lapb, unsigned short nr) +{ + unsigned short vc = lapb->va; + int modulus; + + modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS; + + while (vc != lapb->vs) { + if (nr == vc) return 1; + vc = (vc + 1) % modulus; + } + + if (nr == lapb->vs) return 1; + + return 0; +} + +/* + * This routine is the centralised routine for parsing the control + * information for the different frame formats. + */ +int lapb_decode(lapb_cb *lapb, struct sk_buff *skb, int *ns, int *nr, int *pf, int *type) +{ + int frametype = LAPB_ILLEGAL; + + *ns = *nr = *pf = *type = 0; + +#if LAPB_DEBUG > 2 + printk(KERN_DEBUG "lapb: (%p) S%d RX %02X %02X %02X\n", lapb->token, lapb->state, skb->data[0], skb->data[1], skb->data[2]); +#endif + + if (lapb->mode & LAPB_MLP) { + if (lapb->mode & LAPB_DCE) { + if (skb->data[0] == LAPB_ADDR_C) + *type = LAPB_COMMAND; + if (skb->data[0] == LAPB_ADDR_D) + *type = LAPB_RESPONSE; + } else { + if (skb->data[0] == LAPB_ADDR_D) + *type = LAPB_COMMAND; + if (skb->data[0] == LAPB_ADDR_C) + *type = LAPB_RESPONSE; + } + } else { + if (lapb->mode & LAPB_DCE) { + if (skb->data[0] == LAPB_ADDR_A) + *type = LAPB_COMMAND; + if (skb->data[0] == LAPB_ADDR_B) + *type = LAPB_RESPONSE; + } else { + if (skb->data[0] == LAPB_ADDR_B) + *type = LAPB_COMMAND; + if (skb->data[0] == LAPB_ADDR_A) + *type = LAPB_RESPONSE; + } + } + + skb_pull(skb, 1); + + if (lapb->mode & LAPB_EXTENDED) { + if ((skb->data[0] & LAPB_S) == 0) { + frametype = LAPB_I; /* I frame - carries NR/NS/PF */ + *ns = (skb->data[0] >> 1) & 0x7F; + *nr = (skb->data[1] >> 1) & 0x7F; + *pf = skb->data[1] & LAPB_EPF; + skb_pull(skb, 2); + } else if ((skb->data[0] & LAPB_U) == 1) { /* S frame - take out PF/NR */ + frametype = skb->data[0] & 0x0F; + *nr = (skb->data[1] >> 1) & 0x7F; + *pf = skb->data[1] & LAPB_EPF; + skb_pull(skb, 2); + } else if ((skb->data[0] & LAPB_U) == 3) { /* U frame - take out PF */ + frametype = skb->data[0] & ~LAPB_SPF; + *pf = skb->data[0] & LAPB_SPF; + skb_pull(skb, 1); + } + } else { + if ((skb->data[0] & LAPB_S) == 0) { + frametype = LAPB_I; /* I frame - carries NR/NS/PF */ + *ns = (skb->data[0] >> 1) & 0x07; + *nr = (skb->data[0] >> 5) & 0x07; + *pf = skb->data[0] & LAPB_SPF; + } else if ((skb->data[0] & LAPB_U) == 1) { /* S frame - take out PF/NR */ + frametype = skb->data[0] & 0x0F; + *nr = (skb->data[0] >> 5) & 0x07; + *pf = skb->data[0] & LAPB_SPF; + } else if ((skb->data[0] & LAPB_U) == 3) { /* U frame - take out PF */ + frametype = skb->data[0] & ~PF; + *pf = skb->data[0] & LAPB_SPF; + } + + skb_pull(skb, 1); + } + + return frametype; +} + +/* + * This routine is called when the HDLC layer internally generates a + * command or response for the remote machine ( eg. RR, UA etc. ). + * Only supervisory or unnumbered frames are processed. + */ +void lapb_send_control(lapb_cb *lapb, int frametype, int poll_bit, int type) +{ + struct sk_buff *skb; + unsigned char *dptr; + + if ((skb = alloc_skb(LAPB_HEADER_LEN + 3, GFP_ATOMIC)) == NULL) + return; + + skb_reserve(skb, LAPB_HEADER_LEN + 1); + + /* Assume a response - address structure for DTE */ + if (lapb->mode & LAPB_EXTENDED) { + if ((frametype & LAPB_U) == LAPB_U) { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? LAPB_SPF : 0; + } else { + dptr = skb_put(skb, 2); + dptr[0] = frametype; + dptr[1] = (lapb->vr << 1); + dptr[1] |= (poll_bit) ? LAPB_EPF : 0; + } + } else { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? LAPB_SPF : 0; + if ((frametype & LAPB_U) == LAPB_S) /* S frames carry NR */ + *dptr |= (lapb->vr << 5); + } + + lapb_transmit_buffer(lapb, skb, type); +} + +#endif diff --git a/net/lapb/lapb_timer.c b/net/lapb/lapb_timer.c new file mode 100644 index 000000000000..6586056c6515 --- /dev/null +++ b/net/lapb/lapb_timer.c @@ -0,0 +1,183 @@ +/* + * LAPB release 001 + * + * This is ALPHA test software. This code may break your machine, randomly fail to work with new + * releases, misbehave and/or generally screw up. It might even work. + * + * This code REQUIRES 2.1.15 or higher/ NET3.038 + * + * This module: + * This module 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. + * + * History + * LAPB 001 Jonathan Naylor Started Coding + */ + +#include +#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void lapb_timer(unsigned long); + +/* + * Linux set/reset timer routines + */ +void lapb_set_timer(lapb_cb *lapb) +{ + unsigned long flags; + + save_flags(flags); + cli(); + del_timer(&lapb->timer); + restore_flags(flags); + + lapb->timer.next = lapb->timer.prev = NULL; + lapb->timer.data = (unsigned long)lapb; + lapb->timer.function = &lapb_timer; + + lapb->timer.expires = jiffies + 10; + add_timer(&lapb->timer); +} + +static void lapb_reset_timer(lapb_cb *lapb) +{ + unsigned long flags; + + save_flags(flags); + cli(); + del_timer(&lapb->timer); + restore_flags(flags); + + lapb->timer.data = (unsigned long)lapb; + lapb->timer.function = &lapb_timer; + lapb->timer.expires = jiffies + 10; + add_timer(&lapb->timer); +} + +/* + * LAPB TIMER + * + * This routine is called every 100ms. Decrement timer by this + * amount - if expired then process the event. + */ +static void lapb_timer(unsigned long param) +{ + lapb_cb *lapb = (lapb_cb *)param; + + if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4) + lapb_kick(lapb); + + if (lapb->t2timer > 0 && --lapb->t2timer == 0) { + if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4) { + if (lapb->condition & LAPB_ACK_PENDING_CONDITION) { + lapb->condition &= ~LAPB_ACK_PENDING_CONDITION; + lapb_timeout_response(lapb); + } + } + } + + if (lapb->t1timer == 0 || --lapb->t1timer > 0) { + lapb_reset_timer(lapb); + return; + } + + switch (lapb->state) { + case LAPB_STATE_0: + if (lapb->mode & LAPB_DCE) + lapb_send_control(lapb, LAPB_DM, LAPB_POLLOFF, LAPB_RESPONSE); + break; + case LAPB_STATE_1: + if (lapb->n2count == lapb->n2) { + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token); +#endif + } else { + lapb->n2count++; + if (lapb->mode & LAPB_EXTENDED) { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 TX SABME(1)\n", lapb->token); +#endif + lapb_send_control(lapb, LAPB_SABME, LAPB_POLLON, LAPB_COMMAND); + } else { +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S1 TX SABM(1)\n", lapb->token); +#endif + lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND); + } + } + break; + + case LAPB_STATE_2: + if (lapb->n2count == lapb->n2) { + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT); +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token); +#endif + } else { + lapb->n2count++; +#if LAPB_DEBUG > 1 + printk(KERN_DEBUG "lapb: (%p) S2 TX DISC(1)\n", lapb->token); +#endif + lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); + } + break; + + case LAPB_STATE_3: + lapb->n2count = 1; + lapb_transmit_enquiry(lapb); + lapb->state = LAPB_STATE_4; + break; + + case LAPB_STATE_4: + if (lapb->n2count == lapb->n2) { + lapb_clear_queues(lapb); + lapb->state = LAPB_STATE_0; + lapb->t1timer = (lapb->mode & LAPB_DCE) ? lapb->t1 : 0; + lapb->t2timer = 0; + lapb_disconnect_indication(lapb, LAPB_TIMEDOUT); +#if LAPB_DEBUG > 0 + printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token); +#endif + } else { + lapb->n2count++; + lapb_transmit_enquiry(lapb); + } + break; + } + + lapb->t1timer = lapb->t1; + + lapb_set_timer(lapb); +} + +#endif diff --git a/net/netbeui/netbeui.c b/net/netbeui/netbeui.c index 93807d0a4eda..48be8e54586d 100644 --- a/net/netbeui/netbeui.c +++ b/net/netbeui/netbeui.c @@ -49,7 +49,7 @@ * * \***********************************************************************************************************************/ -static netbeui_socket *volatile netbeui_socket_list=NULL; +static netbeui_socket *netbeui_socket_list=NULL; /* * Note: Sockets may not be removed _during_ an interrupt or inet_bh @@ -57,91 +57,42 @@ static netbeui_socket *volatile netbeui_socket_list=NULL; * use this facility. */ -static void netbeui_remove_socket(netbeui_socket *sk) +extern inline void netbeui_remove_socket(netbeui_socket *sk) { - unsigned long flags; - netbeui_socket *s; - - save_flags(flags); - cli(); - - s=netbeui_socket_list; - if(s==sk) - { - netbeui_socket_list=s->next; - restore_flags(flags); - return; - } - while(s && s->next) - { - if(s->next==sk) - { - s->next=sk->next; - restore_flags(flags); - return; - } - s=s->next; - } - restore_flags(flags); + sklist_remove_socket(&netbeui_socket_list,sk); } -static void netbeui_insert_socket(netbeui_socket *sk) +extenr inline void netbeui_insert_socket(netbeui_socket *sk) { - unsigned long flags; - save_flags(flags); - cli(); - sk->next=netbeui_socket_list; + sklist_insert_socket(&netbeui_socket_list,sk); netbeui_socket_list=sk; restore_flags(flags); } -/* - * This is only called from user mode. Thus it protects itself against - * interrupt users but doesn't worry about being called during work. - * Once it is removed from the queue no interrupt or bottom half will - * touch it and we are (fairly 8-) ) safe. - */ - -static void netbeui_destroy_socket(netbeui_socket *sk); - -/* - * Handler for deferred kills. - */ - -static void netbeui_destroy_timer(unsigned long data) -{ - netbeui_destroy_socket((netbeui_socket *)data); -} - static void netbeui_destroy_socket(netbeui_socket *sk) { - struct sk_buff *skb; - netbeui_remove_socket(sk); - - while((skb=skb_dequeue(&sk->receive_queue))!=NULL) + /* + * Release netbios logical channels first + */ + if(sk->af_nb.nb_link) { - kfree_skb(skb,FREE_READ); + netbeui_delete_channel(sk->af_nb.nb_link); + sk->af_nb.nb_link=NULL; } - - if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead) + if(sk->af_nb.src_name) { - sk_free(sk); - MOD_DEC_USE_COUNT; + netbeui_release_name(sk->af_nb.src_name); + sk->af_nb.src_name=NULL; } - else + if(sk->af_nb.dst_name) { - /* - * Someone is using our buffers still.. defer - */ - init_timer(&sk->timer); - sk->timer.expires=jiffies+10*HZ; - sk->timer.function=netbeui_destroy_timer; - sk->timer.data = (unsigned long)sk; - add_timer(&sk->timer); + netbeui_release_name(sk->af_nb.dst_name); + sk->af_nb.dst_name=NULL; } + netbeui_remove_listener(sk); + sklist_destroy_socket(&netbeui_socket,sk); } - /* * Called from proc fs */ @@ -161,14 +112,10 @@ int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int d for (s = netbeui_socket_list; s != NULL; s = s->next) { len += sprintf (buffer+len,"%02X ", s->type); - len += sprintf (buffer+len,"%04X:%02X:%02X ", - ntohs(s->protinfo.af_at.src_net), - s->protinfo.af_at.src_node, - s->protinfo.af_at.src_port); - len += sprintf (buffer+len,"%04X:%02X:%02X ", - ntohs(s->protinfo.af_at.dest_net), - s->protinfo.af_at.dest_node, - s->protinfo.af_at.dest_port); + len += sprintf (buffer+len,"%s ", + s->af_nb.src_name->text); + len += sprintf (buffer+len,"%s ", + s->af_nb.dst_name->text); len += sprintf (buffer+len,"%08X:%08X ", s->wmem_alloc, s->rmem_alloc); len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid); @@ -258,9 +205,6 @@ static int netbeui_setsockopt(struct socket *sock, int level, int optname, char } break; - case SOL_SOCKET: - return sock_setsockopt(sk,level,optname,optval,optlen); - default: return -EOPNOTSUPP; } @@ -291,9 +235,6 @@ static int netbeui_getsockopt(struct socket *sock, int level, int optname, } break; - case SOL_SOCKET: - return sock_getsockopt(sk,level,optname,optval,optlen); - default: return -EOPNOTSUPP; } @@ -320,7 +261,7 @@ static int netbeui_listen(struct socket *sock, int backlog) sk->backlog=128; sk->state=TCP_LISTEN; sk->state_change(sk); - netbeui_llc_listen(sk); + netbeui_add_listener(sk); return 0; } @@ -339,7 +280,16 @@ static void def_callback2(struct sock *sk, int len) if(!sk->dead) { wake_up_interruptible(sk->sleep); - sock_wake_async(sk->socket,0); + sock_wake_async(sk->socket,1); + } +} + +static void def_callback3(struct sock *sk, int len) +{ + if(!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket,2); } } @@ -365,13 +315,6 @@ static int netbeui_create(struct socket *sock, int protocol) return(-ESOCKTNOSUPPORT); } - sk->llc802=llc_alloc(GFP_KERNEL); - if(sk->llc802==NULL) - { - sk_free((void *)sk); - return -ENOBUFS: - } - MOD_INC_USE_COUNT; sk->allocation=GFP_KERNEL; @@ -395,7 +338,7 @@ static int netbeui_create(struct socket *sock, int protocol) sk->state_change=def_callback1; sk->data_ready=def_callback2; - sk->write_space=def_callback1; + sk->write_space=def_callback3; sk->error_report=def_callback1; sk->zapped=1; return(0); @@ -435,21 +378,40 @@ static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_ { netbeui_socket *sk; struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr; + int err; sk=(netbeui_socket *)sock->data; if(sk->zapped==0) return(-EINVAL); - if(addr_len!=sizeof(struct sockaddr_at)) + if(addr_len!=sizeof(struct sockaddr_netbeui)) return -EINVAL; - if(addr->sat_family!=AF_NETBEUI) + if(addr->snb_family!=AF_NETBEUI) return -EAFNOSUPPORT; - if(netbeui_find_socket(addr)!=NULL) - return -EADDRINUSE; - + /* + * This will sleep. To meet POSIX it is non interruptible. + * Someone should give the 1003.1g authors an injection of + * imagination... + */ + + if(sk->af_nb.src_name!=NULL) + return -EINVAL; + + /* + * Try and get the name. It may return various 'invalid' name + * problem reports or EADDRINUSE if we or another node holds + * the desired name. + */ + + sk->af_nb.src_name=netbeui_alloc_name(addr, &err); + if(sk->af_nb.src_name==NULL) + return err; + /* + * Add us to the active socket list + */ netbeui_insert_socket(sk); sk->zapped=0; return(0); @@ -463,28 +425,44 @@ static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr, size_t addr_len, int flags) { netbeui_socket *sk=(netbeui_socket *)sock->data; - struct sockaddr_netbeui *addr; - - sk->state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - if(addr_len!=sizeof(*addr)) - return(-EINVAL); - addr=(struct sockaddr_netbeui *)uaddr; - - if(addr->sat_family!=AF_NETBEUI) - return -EAFNOSUPPORT; - -/* FIXME - Netbios broadcast semantics ?? */ - if(addr->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast) - return -EACCES; - - if(sk->zapped) - return -EINVAL; - - if(atrtr_get_dev(&addr->sat_addr)==NULL) - return -ENETUNREACH; + struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr; + /* + * Check pending operations + */ + + if(sk->state==TCP_ESTABLISHED && sock->state == SS_CONNECTING) + { + sock->state==SS_CONNECTED; + return 0; + } + + if(sk->state == TCP_CLOSE & sock->state == SS_CONNECTING) + { + sock->state==SS_UNCONNECTED; + return -ECONNREFUSED; + } + + if(sock->state == SS_CONNECTING && (flags & O_NONBLOCK)) + return -EINPROGRESS; + + if(sk->state==TCP_ESTABLISHED) + return -EISCONN; + + /* + * If this is new it must really be new... + */ + + if(sk->af_nb.dst_name==NULL) + { + if(addr_len != sizeof(struct sockaddr_nb)) + return -EINVAL; + if(addr->snb_family!=AF_NETBEUI) + return -EAFNOSUPPORT; + /* + * Try and find the name + */ + } } /* diff --git a/net/netbeui/netbeui_llc.c b/net/netbeui/netbeui_llc.c index 999f8d2779eb..62b9f569fa77 100644 --- a/net/netbeui/netbeui_llc.c +++ b/net/netbeui/netbeui_llc.c @@ -1,124 +1,169 @@ /* - * Maintain 802.2 LLC logical channels being used by NetBEUI + * NET3: 802.2 LLC supervisor for the netbeui protocols. + * + * The basic aim is to provide a self managing link layer supervisor + * for netbeui. It creates and destroys the 802.2 virtual connections + * as needed, and copes with the various races when a link goes down + * just as its requested etc. + * + * The upper layers are presented with the notion of an nb_link which + * is a potentially shared object that represents a logical path + * between two hosts. Each nb_link has usage counts and users can + * treat it as if its their own. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include - -void netbeui_disc_indication(llcptr llc) -{ - struct nb_link *nb=LLC_TO_NB(llc); - if(nb->users>0) - llc_connect_request(&nb->llc); -} - -void netbeui_disc_confirm(llcptr llc) +/* + * When this routine is called the netbeui layer has decided to + * drop the link. There is a tiny risk that we might reuse the + * link after we decide. Thus before we blast the link into little + * tiny pieces we must check.... + */ + +static void netbeui_do_destroy(struct nb_link *nb) { - struct nb_link *nb=LLC_TO_NB(llc); + /* + * Are we wanted again. Bring it back. Sigh, wish people + * would make up their minds 8) + */ if(nb->users>0) - llc_connect_request(&nb->llc); - else { - netbeui_destroy_channel(nb); + nb->state=NETBEUI_CONNWAIT; + llc_connect_request(&nb->llc); + return; } + /* + * Blam.... into oblivion it goes + */ + + llc_unregister(&nb->llc); + netbeui_free_link(nb); } -void netbeui_connect_confirm(llcptr llc) -{ - struct nb_link *nb=LLC_TO_NB(llc); - nb->status=SS_CONNECTED; - wakeup(&nb->wait_queue); -} - -void netbeui_connect_indication(llcptr llc) -{ - struct nb_link *nb=LLC_TO_NB(llc); - nb->status=SS_CONNECTED; - wakeup(&nb->wait_queue); -} - -void netbeui_reset_confirm(llcptr llc) -{ - struct nb_link *nb=LLC_TO_NB(llc); - /* Good question .. */ -} +/* + * Handle netbeui events. Basically that means keep it up when it + * should be up, down when it should be down and handle all the data. + */ -void netbeui_reset_indication(llcptr llc, char lr) +static void netbeui_event(llcptr llc) { - struct nb_link *nb=LLC_TO_NB(llc); - printk("netbeui: 802.2LLC reset.\n"); - /* Good question too */ -} + struct nb_link *nb=(struct nb_link *)llc; + + /* + * See what has occured + */ -void netbeui_data_indication(llcptr llc, struct sk_buff *skb) -{ - netbeui_rcv_seq(LLC_TO_NB(llc),skb); -} - -int netbeui_unit_data_indication(llcptr llc, struct sk_buff *skb) -{ - return netbeui_rcv_dgram(LLC_TO_NB(llc),skb); -} - -int netbeui_xid_indication(llcptr llc, int ll, char *data) -{ - struct nb_link *nb=LLC_TO_NB(llc); - /* No action needed */ -} + if(llc->llc_callbacks&LLC_CONN_CONFIRM) + { + /* + * Link up if desired. Otherwise try frantically + * to close it. + */ + if(nb->state!=NETBEUI_DEADWAIT) + { + /* + * Wake pending writers + */ + nb->state=NETBEUI_OPEN; + netbeui_wakeup(nb); + } + else + llc_disconnect_request(llc); + } + + /* + * Data is passed to the upper netbeui layer + */ -int netbeui_test_indication(llcptr llc, int ll, char *data) -{ - struct nb_link *nb=LLC_TO_NB(llc); - /* No action needed */ -} + if(llc->llc_callbacks&LLC_DATA_INDIC) + netbeu_rcv_stream(llc,llc->inc_skb); -void netbeui_report_status(llcptr llc, char status) -{ - struct nb_link *nb=LLC_TO_NB(llc); - switch(status) + /* + * We got disconnected + */ + + if(llc->llc_callbacks&LLC_DISC_INDICATION) { - case FRMR_RECEIVED: - case FRMR_SENT: - printk("netbeui: FRMR event %d\n",status); - break; /* FRMR's - shouldnt occur - debug log */ - case REMOTE_BUSY: - nb->busy=1; - break; - case REMOTE_NOT_BUSY: - nb->busy=0; - wakeup(&nb->wakeup); - break; - default: - printk("LLC passed netbeui bogus state %d\n",status); - break; + if(nb->state==NETBEUI_DEADWAIT) + { + netbeui_do_destroy(nb); + return; + } + if(nb->state==NETBEUI_DISCWAIT) + { + llc_connect_request(llc); + nb->state=NETBEUI_CONNWAIT; + } + } + + if(llc->llc_callbacks&(LLC_RESET_INDIC_LOC|LLC_RESET_INDIC_REM| + LLC_RST_CONFIRM)) + { + /* + * Reset. + * Q: Is tearing the link down the right answer ? + * + * For now we just carry on + */ } -} -struct llc_ops netbeui_ops= -{ - netbeui_data_indication, /* Sequenced frame */ - netbeui_unit_data_indication, /* Datagrams */ - netbeui_connect_indication, /* They called us */ - netbeui_connect_confirm, /* We called them, they OK'd */ - netbeui_data_connect_indication, /* Erm ?????? */ - netbeui_data_connect_confirm, /* Erm ?????? */ - netbeui_disc_indication, /* They closed */ - netbeui_disc_confirm, /* We closed they OK'd */ - netbeui_reset_confirm, /* Our reset worked */ - netbeui_reset_indication, /* They reset on us */ - netbeui_xid_indication, /* An XID frame */ - netbeui_test_indication, /* A TEST frame */ - netbeui_report_status /* Link state change */ -}; + if(llc->llc_callbacks&LLC_REMOTE_BUSY) + nb->busy=1; /* Send no more for a bit */ + if(llc->llc_callbacks&LLC_REMOTE_NOTBUSY) + { + /* Coming unbusy may wake sending threads */ + nb->busy=0; + netbeui_wakeup(nb); + } + /* + * UI frames are passed to the upper netbeui layer. + */ + if(llc->llc_callbacks&LLC_UI_DATA) + netbeui_rcv_dgram(llc,llc->inc_skb); + + /* We ignore TST, XID, FRMR stuff */ + /* FIXME: We need to free frames here once I fix the callback! */ +} /* - * Create a new outgoing session + * Netbeui has created a new logical link. As a result we will + * need to find or create a suitable 802.2 LLC session and join + * it. */ - + struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int pri) { struct nb_link *nb=netbeui_find_channel(dev,remote_mac); if(nb) { + if(nb->state==NETBEUI_DEADWAIT) + { + /* + * We had commenced a final shutdown. We + * cannot abort that (we sent the packet) but + * we can shift the mode to DISCWAIT. That will + * cause the disconnect event to bounce us + * back into connected state. + */ + nb->state==NETBEUI_DISCWAIT; + } nb->users++; return nb; } @@ -131,19 +176,17 @@ struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int p */ nb->dev=dev; - init_timer(&nb->timer); - nb->timer.function=netbeui_link_timer; nb->users=1; nb->busy=0; nb->wakeup=NULL; - nb->status=SS_CONNECTING; + nb->state=NETBEUI_CONNWAIT; memcpy(nb->remote_mac, remote_mac, ETH_ALEN); /* * Now try and attach an LLC. */ - if(register_cl2llc_client(&nb->llc,dev->name,&nebeui_llcops, + if(register_cl2llc_client(&nb->llc,dev->name,netbeui_event, remote_mac, NETBEUI_SAP, NETBEUI_SAP)<0) { netbeui_free_link(nb); @@ -165,21 +208,33 @@ struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int p return nb; } + +/* + * A logical netbeui channel has died. If the channel has no + * further users we commence shutdown. + */ int netbeui_delete_channel(struct nb_link *nb) { nb->users--; + + /* + * FIXME: Must remove ourselves from the nb_link chain when + * we add that bit + */ + if(nb->users) return 0; - llc_disconnect_request(lp); /* * Ensure we drop soon. The disconnect confirm will let - * us fix the deletion + * us fix the deletion. If someone wants the link at + * the wrong moment nothing bad will occur. The create + * or the do_destroy will sort it. */ - nb->state = SS_DISCONNECTING; - nb->timer.expires=jiffies+NB_DROP_TIMEOUT; - add_timer(&nb->timer); + + nb->state = NETBEUI_DEADWAIT; + llc_disconnect_request(lp); return 0; } diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 44ee9198589e..e911abf88ea1 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.15 or higher/ NET3.039 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or @@ -34,6 +34,7 @@ #include #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) +#include #include #include #include @@ -82,6 +83,37 @@ static struct sock *volatile nr_list = NULL; static struct proto_ops nr_proto_ops; +static void nr_free_sock(struct sock *sk) +{ + kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr)); + + sk_free(sk); + + MOD_DEC_USE_COUNT; +} + +static struct sock *nr_alloc_sock(void) +{ + struct sock *sk; + nr_cb *nr; + + if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) + return NULL; + + if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) { + sk_free(sk); + return NULL; + } + + MOD_INC_USE_COUNT; + + memset(nr, 0x00, sizeof(*nr)); + + sk->protinfo.nr = nr; + nr->sk = sk; + + return sk; +} /* * Socket removal during an interrupt is now safe. @@ -283,8 +315,7 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer sk->timer.data = (unsigned long)sk; add_timer(&sk->timer); } else { - kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr)); - sk_free(sk); + nr_free_sock(sk); } restore_flags(flags); @@ -543,7 +574,19 @@ static void def_callback1(struct sock *sk) static void def_callback2(struct sock *sk, int len) { if (!sk->dead) + { + sock_wake_async(sk->socket,1); wake_up_interruptible(sk->sleep); + } +} + +static void def_callback3(struct sock *sk, int len) +{ + if (!sk->dead) + { + sock_wake_async(sk->socket,2); + wake_up_interruptible(sk->sleep); + } } static int nr_create(struct socket *sock, int protocol) @@ -554,13 +597,10 @@ static int nr_create(struct socket *sock, int protocol) if (sock->type != SOCK_SEQPACKET || protocol != 0) return -ESOCKTNOSUPPORT; - if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) + if ((sk = nr_alloc_sock()) == NULL) return -ENOMEM; - if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return -ENOMEM; - } + nr = sk->protinfo.nr; skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); @@ -583,7 +623,7 @@ static int nr_create(struct socket *sock, int protocol) sk->state_change = def_callback1; sk->data_ready = def_callback2; - sk->write_space = def_callback1; + sk->write_space = def_callback3; sk->error_report = def_callback1; if (sock != NULL) { @@ -595,8 +635,6 @@ static int nr_create(struct socket *sock, int protocol) skb_queue_head_init(&nr->reseq_queue); skb_queue_head_init(&nr->frag_queue); - nr->my_index = 0; - nr->my_id = 0; nr->rtt = sysctl_netrom_transport_timeout / 2; nr->t1 = sysctl_netrom_transport_timeout; nr->t2 = sysctl_netrom_transport_acknowledge_delay; @@ -606,35 +644,8 @@ static int nr_create(struct socket *sock, int protocol) nr->paclen = sysctl_netrom_transport_packet_length; nr->window = sysctl_netrom_transport_requested_window_size; - nr->t1timer = 0; - nr->t2timer = 0; - nr->t4timer = 0; - nr->idletimer = 0; - nr->n2count = 0; - - nr->va = 0; - nr->vr = 0; - nr->vs = 0; - nr->vl = 0; - - nr->your_index = 0; - nr->your_id = 0; - - nr->my_index = 0; - nr->my_id = 0; - nr->bpqext = 1; - nr->fraglen = 0; - nr->hdrincl = 0; nr->state = NR_STATE_0; - nr->device = NULL; - - memset(&nr->source_addr, '\0', AX25_ADDR_LEN); - memset(&nr->user_addr, '\0', AX25_ADDR_LEN); - memset(&nr->dest_addr, '\0', AX25_ADDR_LEN); - - nr->sk = sk; - sk->protinfo.nr = nr; return 0; } @@ -647,13 +658,10 @@ static struct sock *nr_make_new(struct sock *osk) if (osk->type != SOCK_SEQPACKET) return NULL; - if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) + if ((sk = nr_alloc_sock()) == NULL) return NULL; - if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return NULL; - } + nr = sk->protinfo.nr; skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); @@ -675,7 +683,7 @@ static struct sock *nr_make_new(struct sock *osk) sk->state_change = def_callback1; sk->data_ready = def_callback2; - sk->write_space = def_callback1; + sk->write_space = def_callback3; sk->error_report = def_callback1; skb_queue_head_init(&nr->ack_queue); @@ -694,21 +702,6 @@ static struct sock *nr_make_new(struct sock *osk) nr->device = osk->protinfo.nr->device; nr->bpqext = osk->protinfo.nr->bpqext; nr->hdrincl = osk->protinfo.nr->hdrincl; - nr->fraglen = 0; - - nr->t1timer = 0; - nr->t2timer = 0; - nr->t4timer = 0; - nr->idletimer = 0; - nr->n2count = 0; - - nr->va = 0; - nr->vr = 0; - nr->vs = 0; - nr->vl = 0; - - sk->protinfo.nr = nr; - nr->sk = sk; return sk; } @@ -1248,7 +1241,7 @@ static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size, return -ENOTCONN; /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL) + if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) return er; if (!sk->protinfo.nr->hdrincl) { @@ -1413,7 +1406,7 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length, int return(len); } -static struct net_proto_family netrom_family_ops = +static struct net_proto_family nr_family_ops = { AF_NETROM, nr_create @@ -1440,7 +1433,7 @@ static struct proto_ops nr_proto_ops = { nr_recvmsg }; -struct notifier_block nr_dev_notifier = { +static struct notifier_block nr_dev_notifier = { nr_device_event, 0 }; @@ -1466,9 +1459,18 @@ static struct proc_dir_entry proc_net_nr_nodes = { }; #endif +static struct device dev_nr[] = { + {"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}, + {"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}, + {"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}, + {"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init} +}; + void nr_proto_init(struct net_proto *pro) { - sock_register(&netrom_family_ops); + int i; + + sock_register(&nr_family_ops); register_netdevice_notifier(&nr_dev_notifier); printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.034 Linux 2.1\n"); @@ -1477,6 +1479,9 @@ void nr_proto_init(struct net_proto *pro) if (!ax25_linkfail_register(nr_link_failed)) printk(KERN_ERR "NET/ROM unable to register linkfail handler with AX.25\n"); + for (i = 0; i < 4; i++) + register_netdev(&dev_nr[i]); + nr_register_sysctl(); #ifdef CONFIG_PROC_FS @@ -1486,4 +1491,45 @@ void nr_proto_init(struct net_proto *pro) #endif } +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + nr_proto_init(NULL); + + return 0; +} + +void cleanup_module(void) +{ + int i; + +#ifdef CONFIG_PROC_FS + proc_net_unregister(PROC_NET_NR); + proc_net_unregister(PROC_NET_NR_NEIGH); + proc_net_unregister(PROC_NET_NR_NODES); +#endif + nr_rt_free(); + + ax25_protocol_release(AX25_P_NETROM); + ax25_linkfail_release(nr_link_failed); + + unregister_netdevice_notifier(&nr_dev_notifier); + + nr_unregister_sysctl(); + + sock_unregister(AF_NETROM); + + for (i = 0; i < 4; i++) { + if (dev_nr[i].priv != NULL) { + kfree(dev_nr[i].priv); + dev_nr[i].priv = NULL; + unregister_netdev(&dev_nr[i]); + } + } +} + +#endif + #endif diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 59e92b5ba07c..997011304c94 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -1,10 +1,10 @@ /* - * NET/ROM release 004 + * NET/ROM release 006 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.3.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or @@ -18,11 +18,11 @@ * NET/ROM 003 Jonathan(G4KLX) Put nr_rebuild_header into line with * ax25_rebuild_header * NET/ROM 004 Jonathan(G4KLX) Callsign registration with AX.25. + * NET/ROM 006 Hans(PE1AYX) Fixed interface to IP layer. */ #include #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) -#include #include #include #include @@ -70,9 +70,11 @@ int nr_rx_ip(struct sk_buff *skb, struct device *dev) skb->protocol = htons(ETH_P_IP); /* Spoof incoming device */ - skb->dev = dev; + skb->dev = dev; + skb->h.raw = skb->data; + skb->nh.raw = skb->data; + skb->pkt_type = PACKET_HOST; - skb->h.raw = skb->data; ip_rcv(skb, skb->dev, NULL); return 1; @@ -169,8 +171,6 @@ static int nr_open(struct device *dev) dev->tbusy = 0; dev->start = 1; - MOD_INC_USE_COUNT; - ax25_listen_register((ax25_address *)dev->dev_addr, NULL); return 0; @@ -183,8 +183,6 @@ static int nr_close(struct device *dev) ax25_listen_release((ax25_address *)dev->dev_addr, NULL); - MOD_DEC_USE_COUNT; - return 0; } @@ -268,60 +266,4 @@ int nr_init(struct device *dev) return 0; }; -#ifdef MODULE -extern struct proto_ops nr_proto_ops; -extern struct notifier_block nr_dev_notifier; - -static struct device dev_nr[] = { - {"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}, - {"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}, - {"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}, - {"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init} -}; - -EXPORT_NO_SYMBOLS; - -int init_module(void) -{ - int i; - - for (i = 0; i < 4; i++) - register_netdev(&dev_nr[i]); - - nr_proto_init(NULL); - - return 0; -} - -void cleanup_module(void) -{ - int i; - -#ifdef CONFIG_PROC_FS - proc_net_unregister(PROC_NET_NR); - proc_net_unregister(PROC_NET_NR_NEIGH); - proc_net_unregister(PROC_NET_NR_NODES); -#endif - nr_rt_free(); - - ax25_protocol_release(AX25_P_NETROM); - ax25_linkfail_release(nr_link_failed); - - unregister_netdevice_notifier(&nr_dev_notifier); - - nr_unregister_sysctl(); - - sock_unregister(nr_proto_ops.family); - - for (i = 0; i < 4; i++) { - if (dev_nr[i].priv != NULL) { - kfree(dev_nr[i].priv); - dev_nr[i].priv = NULL; - unregister_netdev(&dev_nr[i]); - } - } -} - -#endif - #endif diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c index a20f97070dd1..d2e9d53f0b1f 100644 --- a/net/netrom/nr_in.c +++ b/net/netrom/nr_in.c @@ -1,10 +1,10 @@ /* - * NET/ROM release 004 + * NET/ROM release 006 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c index c1f6f6ea5463..777cf91398c8 100644 --- a/net/netrom/nr_out.c +++ b/net/netrom/nr_out.c @@ -1,10 +1,10 @@ /* - * NET/ROM release 004 + * NET/ROM release 006 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index 73f5f0ba41cb..afd3fd0beb43 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -1,10 +1,10 @@ /* - * NET/ROM release 004 + * NET/ROM release 006 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or @@ -20,6 +20,7 @@ * Change default quality for new neighbour when same * as node callsign. * Alan Cox(GW4PTS) Added the firewall hooks. + * NET/ROM 006 Jonathan(G4KLX) Added the setting of digipeated neighbours. */ #include @@ -100,14 +101,14 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_neigh->count = 0; nr_neigh->number = nr_neigh_no++; - if (ax25_digi != NULL) { + if (ax25_digi != NULL && ax25_digi->ndigi > 0) { if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { kfree_s(nr_neigh, sizeof(*nr_neigh)); return -ENOMEM; } *nr_neigh->digipeat = *ax25_digi; } - + save_flags(flags); cli(); @@ -351,7 +352,7 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct d /* * Lock a neighbour with a quality. */ -static int nr_add_neigh(ax25_address *callsign, struct device *dev, unsigned int quality) +static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct device *dev, unsigned int quality) { struct nr_neigh *nr_neigh; unsigned long flags; @@ -375,6 +376,14 @@ static int nr_add_neigh(ax25_address *callsign, struct device *dev, unsigned int nr_neigh->count = 0; nr_neigh->number = nr_neigh_no++; + if (ax25_digi != NULL && ax25_digi->ndigi > 0) { + if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { + kfree_s(nr_neigh, sizeof(*nr_neigh)); + return -ENOMEM; + } + *nr_neigh->digipeat = *ax25_digi; + } + save_flags(flags); cli(); @@ -556,6 +565,25 @@ struct device *nr_dev_get(ax25_address *addr) return NULL; } +static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters) +{ + static ax25_digi ax25_digi; + int i; + + if (ndigis == 0) + return NULL; + + for (i = 0; i < ndigis; i++) { + ax25_digi.calls[i] = digipeaters[i]; + ax25_digi.repeated[i] = 0; + } + + ax25_digi.ndigi = ndigis; + ax25_digi.lastrepeat = 0; + + return &ax25_digi; +} + /* * Handle the ioctls that control the routing functions. */ @@ -573,15 +601,19 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)); if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) return -EINVAL; + if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) + return -EINVAL; switch (nr_route.type) { case NETROM_NODE: return nr_add_node(&nr_route.callsign, nr_route.mnemonic, &nr_route.neighbour, - NULL, dev, nr_route.quality, + nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), + dev, nr_route.quality, nr_route.obs_count); case NETROM_NEIGH: return nr_add_neigh(&nr_route.callsign, + nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), dev, nr_route.quality); default: return -EINVAL; @@ -750,13 +782,14 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int len = 0; off_t pos = 0; off_t begin = 0; + int i; cli(); - len += sprintf(buffer, "addr callsign dev qual lock count\n"); + len += sprintf(buffer, "addr callsign dev qual lock count digipeaters\n"); for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { - len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d\n", + len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d", nr_neigh->number, ax2asc(&nr_neigh->callsign), nr_neigh->dev ? nr_neigh->dev->name : "???", @@ -764,6 +797,13 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, nr_neigh->locked, nr_neigh->count); + if (nr_neigh->digipeat != NULL) { + for (i = 0; i < nr_neigh->digipeat->ndigi; i++) + len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i])); + } + + len += sprintf(buffer + len, "\n"); + pos = begin + len; if (pos < offset) { diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c index 7820cc156b9c..ee8a4c4c1e30 100644 --- a/net/netrom/nr_subr.c +++ b/net/netrom/nr_subr.c @@ -1,10 +1,10 @@ /* - * NET/ROM release 004 + * NET/ROM release 006 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c index 0149851fd41c..db5de2c53691 100644 --- a/net/netrom/nr_timer.c +++ b/net/netrom/nr_timer.c @@ -1,10 +1,10 @@ /* - * NET/ROM release 004 + * NET/ROM release 006 * * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 1.2.1 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/protocols.c b/net/protocols.c index 7444ee63be5f..24e67cde18b6 100644 --- a/net/protocols.c +++ b/net/protocols.c @@ -33,6 +33,10 @@ extern void inet6_proto_init(struct net_proto *pro); #include #endif +#ifdef CONFIG_LAPB +#include +#endif + #ifdef CONFIG_AX25 #include #ifdef CONFIG_NETROM @@ -119,6 +123,10 @@ struct net_proto protocols[] = { { "DDP", atalk_proto_init }, /* Netatalk Appletalk driver */ #endif +#ifdef CONFIG_LAPB + { "LAPB", lapb_proto_init }, /* LAPB protocols */ +#endif + #ifdef CONFIG_X25 { "X.25", x25_proto_init }, /* CCITT X.25 Packet Layer */ #endif diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 08134953864e..79f0150e0d51 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or @@ -21,6 +21,7 @@ #include #if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE) +#include #include #include #include @@ -62,10 +63,10 @@ int sysctl_rose_routing_control = 1; static unsigned int lci = 1; -struct proto_ops rose_proto_ops; - static struct sock *volatile rose_list = NULL; +static struct proto_ops rose_proto_ops; + /* * Convert a Rose address into text. */ @@ -127,6 +128,38 @@ int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask) return 0; } +static void rose_free_sock(struct sock *sk) +{ + kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose)); + + sk_free(sk); + + MOD_DEC_USE_COUNT; +} + +static struct sock *rose_alloc_sock(void) +{ + struct sock *sk; + rose_cb *rose; + + if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) + return NULL; + + if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { + sk_free(sk); + return NULL; + } + + MOD_INC_USE_COUNT; + + memset(rose, 0x00, sizeof(*rose)); + + sk->protinfo.rose = rose; + rose->sk = sk; + + return sk; +} + /* * Socket removal during an interrupt is now safe. */ @@ -328,8 +361,7 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time sk->timer.data = (unsigned long)sk; add_timer(&sk->timer); } else { - kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose)); - sk_free(sk); + rose_free_sock(sk); } restore_flags(flags); @@ -593,7 +625,18 @@ static void def_callback1(struct sock *sk) static void def_callback2(struct sock *sk, int len) { if (!sk->dead) + { wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket,1); + } +} +static void def_callback3(struct sock *sk, int len) +{ + if (!sk->dead) + { + wake_up_interruptible(sk->sleep); + sock_wake_async(sk->socket,2); + } } static int rose_create(struct socket *sock, int protocol) @@ -604,13 +647,10 @@ static int rose_create(struct socket *sock, int protocol) if (sock->type != SOCK_SEQPACKET || protocol != 0) return -ESOCKTNOSUPPORT; - if ((sk = sk_alloc(GFP_ATOMIC)) == NULL) + if ((sk = rose_alloc_sock()) == NULL) return -ENOMEM; - if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return -ENOMEM; - } + rose = sk->protinfo.rose; skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); @@ -643,39 +683,13 @@ static int rose_create(struct socket *sock, int protocol) skb_queue_head_init(&rose->ack_queue); skb_queue_head_init(&rose->frag_queue); - rose->lci = 0; - - rose->t1 = sysctl_rose_call_request_timeout; - rose->t2 = sysctl_rose_reset_request_timeout; - rose->t3 = sysctl_rose_clear_request_timeout; - rose->hb = sysctl_rose_ack_hold_back_timeout; - rose->idle = sysctl_rose_no_activity_timeout; - - rose->timer = 0; - - rose->va = 0; - rose->vr = 0; - rose->vs = 0; - rose->vl = 0; - - rose->fraglen = 0; - rose->hdrincl = 0; - rose->state = ROSE_STATE_0; - rose->neighbour = NULL; - rose->device = NULL; - - rose->source_ndigis = 0; - rose->dest_ndigis = 0; - - memset(&rose->source_addr, '\0', ROSE_ADDR_LEN); - memset(&rose->dest_addr, '\0', ROSE_ADDR_LEN); - memset(&rose->source_call, '\0', AX25_ADDR_LEN); - memset(&rose->dest_call, '\0', AX25_ADDR_LEN); - memset(&rose->source_digi, '\0', AX25_ADDR_LEN); - memset(&rose->dest_digi, '\0', AX25_ADDR_LEN); + rose->t1 = sysctl_rose_call_request_timeout; + rose->t2 = sysctl_rose_reset_request_timeout; + rose->t3 = sysctl_rose_clear_request_timeout; + rose->hb = sysctl_rose_ack_hold_back_timeout; + rose->idle = sysctl_rose_no_activity_timeout; - rose->sk = sk; - sk->protinfo.rose = rose; + rose->state = ROSE_STATE_0; return 0; } @@ -688,13 +702,10 @@ static struct sock *rose_make_new(struct sock *osk) if (osk->type != SOCK_SEQPACKET) return NULL; - if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL) + if ((sk = rose_alloc_sock()) == NULL) return NULL; - if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return NULL; - } + rose = sk->protinfo.rose; skb_queue_head_init(&sk->receive_queue); skb_queue_head_init(&sk->write_queue); @@ -722,25 +733,14 @@ static struct sock *rose_make_new(struct sock *osk) skb_queue_head_init(&rose->ack_queue); skb_queue_head_init(&rose->frag_queue); - rose->t1 = osk->protinfo.rose->t1; - rose->t2 = osk->protinfo.rose->t2; - rose->t3 = osk->protinfo.rose->t3; - rose->hb = osk->protinfo.rose->hb; - rose->idle = osk->protinfo.rose->idle; - - rose->device = osk->protinfo.rose->device; - rose->hdrincl = osk->protinfo.rose->hdrincl; - rose->fraglen = 0; + rose->t1 = osk->protinfo.rose->t1; + rose->t2 = osk->protinfo.rose->t2; + rose->t3 = osk->protinfo.rose->t3; + rose->hb = osk->protinfo.rose->hb; + rose->idle = osk->protinfo.rose->idle; - rose->timer = 0; - - rose->va = 0; - rose->vr = 0; - rose->vs = 0; - rose->vl = 0; - - sk->protinfo.rose = rose; - rose->sk = sk; + rose->device = osk->protinfo.rose->device; + rose->hdrincl = osk->protinfo.rose->hdrincl; return sk; } @@ -1241,7 +1241,7 @@ static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, return -ENOTCONN; /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL) + if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) return er; if (!sk->protinfo.rose->hdrincl) { @@ -1419,12 +1419,12 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i return(len); } -struct net_proto_family rose_family_ops = { +static struct net_proto_family rose_family_ops = { AF_ROSE, rose_create }; -struct proto_ops rose_proto_ops = { +static struct proto_ops rose_proto_ops = { AF_ROSE, rose_dup, @@ -1445,51 +1445,113 @@ struct proto_ops rose_proto_ops = { rose_recvmsg }; -struct notifier_block rose_dev_notifier = { +static struct notifier_block rose_dev_notifier = { rose_device_event, 0 }; +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry proc_net_rose = { + PROC_NET_RS, 4, "rose", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + rose_get_info +}; +static struct proc_dir_entry proc_net_rose_neigh = { + PROC_NET_RS_NEIGH, 10, "rose_neigh", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + rose_neigh_get_info +}; +static struct proc_dir_entry proc_net_rose_nodes = { + PROC_NET_RS_NODES, 10, "rose_nodes", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + rose_nodes_get_info +}; +static struct proc_dir_entry proc_net_rose_routes = { + PROC_NET_RS_ROUTES, 11, "rose_routes", + S_IFREG | S_IRUGO, 1, 0, 0, + 0, &proc_net_inode_operations, + rose_routes_get_info +}; +#endif + +static struct device dev_rose[] = { + {"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, + {"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init} +}; + void rose_proto_init(struct net_proto *pro) { + int i; + sock_register(&rose_family_ops); register_netdevice_notifier(&rose_dev_notifier); - printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.034 Linux 2.1\n"); + printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.035 Linux 2.1\n"); if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame)) printk(KERN_ERR "Rose unable to register protocol with AX.25\n"); if (!ax25_linkfail_register(rose_link_failed)) printk(KERN_ERR "Rose unable to register linkfail handler with AX.25\n"); + for (i = 0; i < 6; i++) + register_netdev(&dev_rose[i]); + rose_register_sysctl(); #ifdef CONFIG_PROC_FS - proc_net_register(&(struct proc_dir_entry) { - PROC_NET_RS, 4, "rose", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - rose_get_info - }); - proc_net_register(&(struct proc_dir_entry) { - PROC_NET_RS_NEIGH, 10, "rose_neigh", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - rose_neigh_get_info - }); - proc_net_register(&(struct proc_dir_entry) { - PROC_NET_RS_NODES, 10, "rose_nodes", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - rose_nodes_get_info - }); - - proc_net_register(&(struct proc_dir_entry) { - PROC_NET_RS_ROUTES, 11, "rose_routes", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_net_inode_operations, - rose_routes_get_info - }); + proc_net_register(&proc_net_rose); + proc_net_register(&proc_net_rose_neigh); + proc_net_register(&proc_net_rose_nodes); + proc_net_register(&proc_net_rose_routes); #endif } +#ifdef MODULE +EXPORT_NO_SYMBOLS; + +int init_module(void) +{ + rose_proto_init(NULL); + + return 0; +} + +void cleanup_module(void) +{ + int i; + +#ifdef CONFIG_PROC_FS + proc_net_unregister(PROC_NET_RS); + proc_net_unregister(PROC_NET_RS_NEIGH); + proc_net_unregister(PROC_NET_RS_NODES); + proc_net_unregister(PROC_NET_RS_ROUTES); +#endif + rose_rt_free(); + + ax25_protocol_release(AX25_P_ROSE); + ax25_linkfail_release(rose_link_failed); + + rose_unregister_sysctl(); + + unregister_netdevice_notifier(&rose_dev_notifier); + + sock_unregister(AF_ROSE); + + for (i = 0; i < 6; i++) { + if (dev_rose[i].priv != NULL) { + kfree(dev_rose[i].priv); + dev_rose[i].priv = NULL; + unregister_netdev(&dev_rose[i]); + } + } +} + +#endif + #endif diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 8d703d81caee..b21994894643 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or @@ -14,11 +14,11 @@ * * History * Rose 001 Jonathan(G4KLX) Cloned from nr_dev.c. + * Hans(PE1AYX) Fixed interface to IP layer. */ #include #if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE) -#include #include #include #include @@ -66,9 +66,11 @@ int rose_rx_ip(struct sk_buff *skb, struct device *dev) skb->protocol = htons(ETH_P_IP); /* Spoof incoming device */ - skb->dev = dev; + skb->dev = dev; + skb->h.raw = skb->data; + skb->nh.raw = skb->data; + skb->pkt_type = PACKET_HOST; - skb->h.raw = skb->data; ip_rcv(skb, skb->dev, NULL); return 1; @@ -141,8 +143,6 @@ static int rose_open(struct device *dev) dev->tbusy = 0; dev->start = 1; - MOD_INC_USE_COUNT; - ax25_listen_register((ax25_address *)dev->dev_addr, NULL); return 0; @@ -155,8 +155,6 @@ static int rose_close(struct device *dev) ax25_listen_release((ax25_address *)dev->dev_addr, NULL); - MOD_DEC_USE_COUNT; - return 0; } @@ -240,57 +238,4 @@ int rose_init(struct device *dev) return 0; }; -#ifdef MODULE -extern struct proto_ops rose_proto_ops; -extern struct notifier_block rose_dev_notifier; - -static struct device dev_rose[] = { - {"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}, - {"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init} -}; - -EXPORT_NO_SYMBOLS; - -int init_module(void) -{ - register_netdev(&dev_rose[0]); - register_netdev(&dev_rose[1]); - - rose_proto_init(NULL); - - return 0; -} - -void cleanup_module(void) -{ - int i; - -#ifdef CONFIG_PROC_FS - proc_net_unregister(PROC_NET_RS); - proc_net_unregister(PROC_NET_RS_NEIGH); - proc_net_unregister(PROC_NET_RS_NODES); - proc_net_unregister(PROC_NET_RS_ROUTES); -#endif - rose_rt_free(); - - ax25_protocol_release(AX25_P_ROSE); - ax25_linkfail_release(rose_link_failed); - - rose_unregister_sysctl(); - - unregister_netdevice_notifier(&rose_dev_notifier); - - sock_unregister(rose_proto_ops.family); - - for (i = 0; i < 2; i++) { - if (dev_rose[i].priv != NULL) { - kfree(dev_rose[i].priv); - dev_rose[i].priv = NULL; - unregister_netdev(&dev_rose[i]); - } - } -} - -#endif - #endif diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c index 4e2b23431c85..a7d2c49d2258 100644 --- a/net/rose/rose_in.c +++ b/net/rose/rose_in.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 32f53e68e428..4b1d63c4482a 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c index 3a68c4886583..b58edeb706a8 100644 --- a/net/rose/rose_out.c +++ b/net/rose/rose_out.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 9e8cf70288d2..bc1073a9d81f 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index fd70918378ff..8a166f3aef74 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c index a5b3956e21fc..e56f9edb1ede 100644 --- a/net/rose/rose_timer.c +++ b/net/rose/rose_timer.c @@ -4,7 +4,7 @@ * This is ALPHA test software. This code may break your machine, randomly fail to work with new * releases, misbehave and/or generally screw up. It might even work. * - * This code REQUIRES 2.1.0 or higher/ NET3.029 + * This code REQUIRES 2.1.15 or higher/ NET3.038 * * This module: * This module is free software; you can redistribute it and/or diff --git a/net/socket.c b/net/socket.c index 926bb77843f3..35c3bd4c3575 100644 --- a/net/socket.c +++ b/net/socket.c @@ -44,16 +44,15 @@ * * * This module is effectively the top level interface to the BSD socket - * paradigm. Because it is very simple it works well for Unix domain sockets, - * but requires a whole layer of substructure for the other protocols. - * - * In addition it lacks an effective kernel -> kernel interface to go with - * the user one. + * paradigm. * * PROBLEMS: * - CLONE_FILES. Big problem, cloned thread can close file, * while other thread sleeps in kernel. It can be solved * by increasing f_count and releasing it on exit from syscall. + * _HAS_ to be fixed before 2.2 is released. I assume whoever is + * working on the CLONE stuff will fix that pile of accidents. If + * you find this comment in a 2.2-preXXX kernel scream loudly. * */ @@ -130,11 +129,13 @@ static struct file_operations socket_file_ops = { /* * The protocol list. Each protocol is registered in here. */ + static struct net_proto_family *net_families[NPROTO]; /* * Statistics counters of the socket lists */ + static int sockets_in_use = 0; /* @@ -215,7 +216,7 @@ static int get_fd(struct inode *inode) return fd; } -static __inline__ struct socket *socki_lookup(struct inode *inode) +extern __inline__ struct socket *socki_lookup(struct inode *inode) { return &inode->u.socket_i; } @@ -462,12 +463,13 @@ static int sock_select(struct inode *inode, struct file *file, int sel_type, sel void sock_close(struct inode *inode, struct file *filp) { /* - * It's possible the inode is NULL if we're closing an unfinished socket. + * It was possible the inode is NULL we were + * closing an unfinished socket. */ if (!inode) { - printk(KERN_DEBUG "Nope. It is impossible!\n"); + printk(KERN_DEBUG "sock_close: NULL inode\n"); return; } sock_fasync(inode, filp, 0); @@ -617,8 +619,8 @@ asmlinkage int sys_socket(int family, int type, int protocol) if (!(sock = sock_alloc())) { printk(KERN_WARNING "socket: no more sockets\n"); - return(-ENOSR); /* Was: EAGAIN, but we are out of - system resources! */ + return(-ENFILE); /* Not exactly a match, but its the + closest posix thing */ } sock->type = type; @@ -752,7 +754,7 @@ asmlinkage int sys_listen(int fd, int backlog) * space and move it to user at the very end. This is unclean because * we open the socket then return an error. * - * 1003.1g addcs the ability to recvmsg() to query connection pending + * 1003.1g adds the ability to recvmsg() to query connection pending * status to recvmsg. We need to add that support in a way thats * clean when we restucture accept also. */ @@ -1212,9 +1214,10 @@ asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags) return -EINVAL; /* - * save the user-mode address (verify_iovec will change the - * kernel msghdr to use the kernel address space) + * Save the user-mode address (verify_iovec will change the + * kernel msghdr to use the kernel address space) */ + uaddr = msg_sys.msg_name; uaddr_len = &msg->msg_namelen; err=verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE); diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index da6fc4b0c0a3..450396b0a380 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1,7 +1,7 @@ /* * NET3: Implementation of BSD Unix domain sockets. * - * Authors: Alan Cox, + * Authors: Alan Cox, * * Currently this contains all but the file descriptor passing code. * Before that goes in the odd bugs in the iovec handlers need @@ -50,8 +50,6 @@ * Bug fixes and improvements. * - client shutdown killed server socket. * - removed all useless cli/sti pairs. - * - (suspicious!) not allow connect/send to connected not to us - * socket, return EPERM. * * Semantic changes/extensions. * - generic control message passing. @@ -100,7 +98,7 @@ unix_socket *unix_socket_table[UNIX_HASH_SIZE+1]; #define UNIX_ABSTRACT(sk) ((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE) -static __inline__ unsigned unix_hash_fold(unsigned hash) +extern __inline__ unsigned unix_hash_fold(unsigned hash) { hash ^= hash>>16; hash ^= hash>>8; @@ -110,32 +108,32 @@ static __inline__ unsigned unix_hash_fold(unsigned hash) #define unix_peer(sk) ((sk)->pair) -static __inline__ int unix_our_peer(unix_socket *sk, unix_socket *osk) +extern __inline__ int unix_our_peer(unix_socket *sk, unix_socket *osk) { return unix_peer(osk) == sk; } -static __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk) +extern __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk) { - return !unix_peer(osk) || unix_peer(osk) == sk; + return (sk->type==osk->type); } -static __inline__ void unix_lock(unix_socket *sk) +extern __inline__ void unix_lock(unix_socket *sk) { sk->users++; } -static __inline__ int unix_unlock(unix_socket *sk) +extern __inline__ int unix_unlock(unix_socket *sk) { return sk->users--; } -static __inline__ int unix_locked(unix_socket *sk) +extern __inline__ int unix_locked(unix_socket *sk) { return sk->users; } -static __inline__ void unix_release_addr(struct unix_address *addr) +extern __inline__ void unix_release_addr(struct unix_address *addr) { if (addr) { @@ -300,10 +298,6 @@ static void unix_destroy_socket(unix_socket *sk) } } -/* - * Fixme: We need async I/O on AF_UNIX doing next. - */ - static int unix_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg) { return -EINVAL; @@ -657,7 +651,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, if (!unix_may_send(sk, other)) { unix_unlock(other); - return -EPERM; + return -EINVAL; } /* @@ -1001,18 +995,9 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, other = unix_peer(sk); if (other && other->dead) { - /* Alan said: + /* * Check with 1003.1g - what should * datagram error - * - * Pardon, if POSIX says, that we should return - * error here, it is wrong. - * It is the main idea of SOCK_DGRAM sockets, - * they could die and be borned, and clients - * should not care about it. - * If you want SOCK_STREAM semantics, - * use SOCK_STREAM. - * --ANK */ unix_unlock(other); unix_peer(sk)=NULL; @@ -1035,7 +1020,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len, { unix_unlock(other); kfree_skb(skb, FREE_WRITE); - return -EPERM; + return -EINVAL; } } @@ -1102,8 +1087,8 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len, * much. */ - if (size > 4000) - limit = 4000; /* Fall back to 4K if we can't grab a big buffer this instant */ + if (size > 3500) + limit = 3500; /* Fall back to a page if we can't grab a big buffer this instant */ else limit = 0; /* Otherwise just grab and wait */ @@ -1232,6 +1217,11 @@ retry: apparently wrong) - clone fds (I choosed it for now, it is the most universal solution) + + POSIX 1003.1g does not actually define this clearly + at all. POSIX 1003.1g doesn't define a lot of things + clearly however! + */ if (UNIXCB(skb).fp) scm->fp = scm_fp_dup(UNIXCB(skb).fp); @@ -1278,11 +1268,10 @@ static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size { if (copied >= target) break; -#if 0 - /* ANK: sk->err is never set for UNIX */ + if (sk->err) return sock_error(sk); -#endif + if (sk->shutdown & RCV_SHUTDOWN) break; up(&sk->protinfo.af_unix.readsem); @@ -1348,8 +1337,8 @@ static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size } else { - /* It is questionable, - see note in unix_dgram_recvmsg. + /* It is questionable, see note in unix_dgram_recvmsg. + */ if (UNIXCB(skb).fp) scm->fp = scm_fp_dup(UNIXCB(skb).fp); @@ -1415,7 +1404,7 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) if(sk->state==TCP_LISTEN) return -EINVAL; /* - * These two are safe on a single CPU system as + * These two are safe on current systems as * only user tasks fiddle here */ if((skb=skb_peek(&sk->receive_queue))!=NULL) @@ -1544,7 +1533,7 @@ static struct proc_dir_entry proc_net_unix = { void unix_proto_init(struct net_proto *pro) { struct sk_buff *dummy_skb; - printk(KERN_INFO "NET3: Unix domain sockets 0.14 for Linux NET3.037.\n"); + printk(KERN_INFO "NET3: Unix domain sockets 0.15 for Linux NET3.038.\n"); if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb)) { printk(KERN_CRIT "unix_proto_init: panic\n"); diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c index b436aabb318a..5af2df443df6 100644 --- a/net/unix/sysctl_net_unix.c +++ b/net/unix/sysctl_net_unix.c @@ -1,8 +1,14 @@ -/* -*- linux-c -*- - * sysctl_net_unix.c: sysctl interface to net af_unix subsystem. +/* + * NET3: Sysctl interface to net af_unix subsystem. * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/unix directory entry (empty =) ). [MS] + * Authors: Mike Shaver. + * + * Added /proc/sys/net/unix directory entry (empty =) ). + * + * 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. */ #include diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index cbcdb77a0bee..843ca54a337d 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -55,10 +55,10 @@ int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2; static unsigned int lci = 1; -struct proto_ops x25_proto_ops; - static struct sock *volatile x25_list = NULL; +static struct proto_ops x25_proto_ops; + int x25_addr_ntoa(unsigned char *p, x25_address *called_addr, x25_address *calling_addr) { int called_len, calling_len; @@ -136,31 +136,9 @@ int x25_addr_aton(unsigned char *p, x25_address *called_addr, x25_address *calli /* * Socket removal during an interrupt is now safe. */ -static void x25_remove_socket(struct sock *sk) +extern inline void x25_remove_socket(struct sock *sk) { - struct sock *s; - unsigned long flags; - - save_flags(flags); - cli(); - - if ((s = x25_list) == sk) { - x25_list = s->next; - restore_flags(flags); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == sk) { - s->next = sk->next; - restore_flags(flags); - return; - } - - s = s->next; - } - - restore_flags(flags); + sklist_remove_socket(&x25_list,sk); } /* @@ -189,7 +167,11 @@ static int x25_device_event(struct notifier_block *this, unsigned long event, vo { struct device *dev = (struct device *)ptr; - if (dev->type == ARPHRD_X25 || dev->type == ARPHRD_ETHER) { + if (dev->type == ARPHRD_X25 +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) + || dev->type == ARPHRD_ETHER +#endif + ) { switch (event) { case NETDEV_UP: x25_link_device_up(dev); @@ -208,17 +190,10 @@ static int x25_device_event(struct notifier_block *this, unsigned long event, vo /* * Add a socket to the bound sockets list. */ -static void x25_insert_socket(struct sock *sk) + +extern inline void x25_insert_socket(struct sock *sk) { - unsigned long flags; - - save_flags(flags); - cli(); - - sk->next = x25_list; - x25_list = sk; - - restore_flags(flags); + sklist_insert_socket(&x25_list,sk); } /* @@ -504,6 +479,8 @@ static struct sock *x25_alloc_socket(void) return NULL; } + memset(x25, 0x00, sizeof(*x25)); + x25->sk = sk; sk->protinfo.x25 = x25; @@ -524,14 +501,6 @@ static struct sock *x25_alloc_socket(void) skb_queue_head_init(&x25->fragment_queue); skb_queue_head_init(&x25->interrupt_queue); - x25->condition = 0x00; - x25->timer = 0; - - x25->va = 0; - x25->vr = 0; - x25->vs = 0; - x25->vl = 0; - return sk; } @@ -566,16 +535,11 @@ static int x25_create(struct socket *sock, int protocol) sk->sleep = &sock->wait; } - x25->lci = 0; - x25->t21 = sysctl_x25_call_request_timeout; x25->t22 = sysctl_x25_reset_request_timeout; x25->t23 = sysctl_x25_clear_request_timeout; x25->t2 = sysctl_x25_ack_holdback_timeout; - x25->fraglen = 0; - x25->qbitincl = 0; - x25->intflg = 0; x25->state = X25_STATE_0; x25->facilities.window_size = X25_DEFAULT_WINDOW_SIZE; @@ -583,11 +547,6 @@ static int x25_create(struct socket *sock, int protocol) x25->facilities.throughput = X25_DEFAULT_THROUGHPUT; x25->facilities.reverse = X25_DEFAULT_REVERSE; - x25->neighbour = NULL; - - memset(&x25->source_addr, '\0', X25_ADDR_LEN); - memset(&x25->dest_addr, '\0', X25_ADDR_LEN); - return 0; } @@ -623,9 +582,7 @@ static struct sock *x25_make_new(struct sock *osk) x25->facilities = osk->protinfo.x25->facilities; - x25->qbitincl = osk->protinfo.x25->qbitincl; - x25->intflg = 0; - x25->fraglen = 0; + x25->qbitincl = osk->protinfo.x25->qbitincl; return sk; } @@ -1103,7 +1060,7 @@ static int x25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int fl return -ENOTCONN; /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags & MSG_DONTWAIT, &er)) == NULL) + if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) return er; qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT; @@ -1272,7 +1229,7 @@ struct net_proto_family x25_family_ops = { x25_create }; -struct proto_ops x25_proto_ops = { +static struct proto_ops x25_proto_ops = { AF_X25, x25_dup, @@ -1345,11 +1302,10 @@ void x25_proto_init(struct net_proto *pro) } #ifdef MODULE +EXPORT_NO_SYMBOLS; int init_module(void) { - register_symtab(NULL); - x25_proto_init(NULL); return 0; @@ -1373,7 +1329,7 @@ void cleanup_module(void) dev_remove_pack(&x25_packet_type); - sock_unregister(x25_proto_ops.family); + sock_unregister(AF_X25); } #endif diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index a0d02cd78c24..5fca44cc86f0 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -47,9 +47,8 @@ #include #include -static int x25_receive_data(struct sk_buff *skb, struct device *dev) +static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) { - struct x25_neigh *neigh; struct sock *sk; unsigned short frametype; unsigned int lci; @@ -61,14 +60,6 @@ static int x25_receive_data(struct sk_buff *skb, struct device *dev) } #endif - /* - * Packet received from unrecognised device, throw it away. - */ - if ((neigh = x25_get_neigh(dev)) == NULL) { - kfree_skb(skb, FREE_READ); - return 0; - } - frametype = skb->data[2]; lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); @@ -107,12 +98,40 @@ static int x25_receive_data(struct sk_buff *skb, struct device *dev) int x25_lapb_receive_frame(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) { + struct x25_neigh *neigh; + skb->sk = NULL; - switch (*skb->data) { + /* + * Packet received from unrecognised device, throw it away. + */ + if ((neigh = x25_get_neigh(dev)) == NULL) { + kfree_skb(skb, FREE_READ); + return 0; + } + + switch (skb->data[0]) { case 0x00: skb_pull(skb, 1); - return x25_receive_data(skb, dev); + return x25_receive_data(skb, neigh); + + case 0x01: + x25_link_established(neigh); + kfree_skb(skb, FREE_READ); + return 0; + + case 0x02: + x25_link_terminated(neigh); + kfree_skb(skb, FREE_READ); + return 0; + + case 0x03: + kfree_skb(skb, FREE_READ); + return 0; + + case 0x04: + kfree_skb(skb, FREE_READ); + return 0; default: kfree_skb(skb, FREE_READ); @@ -122,60 +141,108 @@ int x25_lapb_receive_frame(struct sk_buff *skb, struct device *dev, struct packe int x25_llc_receive_frame(struct sk_buff *skb, struct device *dev, struct packet_type *ptype) { - unsigned int len; + struct x25_neigh *neigh; skb->sk = NULL; - - memcpy(&len, skb->data, sizeof(int)); - - skb_pull(skb, sizeof(int)); - - skb_trim(skb, len); - return x25_receive_data(skb, dev); + /* + * Packet received from unrecognised device, throw it away. + */ + if ((neigh = x25_get_neigh(dev)) == NULL) { + kfree_skb(skb, FREE_READ); + return 0; + } + + return x25_receive_data(skb, neigh); } -int x25_link_up(struct device *dev) +void x25_establish_link(struct x25_neigh *neigh) { - switch (dev->type) { - case ARPHRD_ETHER: - return 1; + struct sk_buff *skb; + unsigned char *ptr; + + switch (neigh->dev->type) { case ARPHRD_X25: - return 0; + if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) { + printk(KERN_ERR "x25_dev: out of memory\n"); + return; + } + ptr = skb_put(skb, 1); + *ptr = 0x01; + break; + +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) + case ARPHRD_ETHER: + return; +#endif default: - return 0; + return; } + + skb->protocol = htons(ETH_P_X25); + skb->priority = SOPRI_NORMAL; + skb->dev = neigh->dev; + skb->arp = 1; + + dev_queue_xmit(skb); } -void x25_send_frame(struct sk_buff *skb, struct device *dev) +void x25_terminate_link(struct x25_neigh *neigh) { - unsigned char *dptr; - unsigned int len; - static char bcast_addr[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + struct sk_buff *skb; + unsigned char *ptr; + + switch (neigh->dev->type) { + case ARPHRD_X25: + if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) { + printk(KERN_ERR "x25_dev: out of memory\n"); + return; + } + ptr = skb_put(skb, 1); + *ptr = 0x02; + break; + +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) + case ARPHRD_ETHER: + return; +#endif + default: + return; + } skb->protocol = htons(ETH_P_X25); skb->priority = SOPRI_NORMAL; - skb->dev = dev; + skb->dev = neigh->dev; skb->arp = 1; - switch (dev->type) { - case ARPHRD_ETHER: - len = skb->len; - dptr = skb_push(skb, sizeof(int)); - memcpy(dptr, &len, sizeof(int)); - dev->hard_header(skb, dev, ETH_P_X25, bcast_addr, NULL, 0); - break; + dev_queue_xmit(skb); +} +void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh) +{ + unsigned char *dptr; + + switch (neigh->dev->type) { case ARPHRD_X25: dptr = skb_push(skb, 1); *dptr = 0x00; break; +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) + case ARPHRD_ETHER: + kfree_skb(skb, FREE_WRITE); + return; +#endif default: kfree_skb(skb, FREE_WRITE); return; } + skb->protocol = htons(ETH_P_X25); + skb->priority = SOPRI_NORMAL; + skb->dev = neigh->dev; + skb->arp = 1; + dev_queue_xmit(skb); } diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c index c4240ec49f59..af75513f40e0 100644 --- a/net/x25/x25_link.c +++ b/net/x25/x25_link.c @@ -115,14 +115,14 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned sho switch (frametype) { case X25_RESTART_REQUEST: neigh->t20timer = 0; - neigh->state = 1; + neigh->state = X25_LINK_STATE_3; del_timer(&neigh->timer); x25_transmit_restart_confirmation(neigh); break; case X25_RESTART_CONFIRMATION: neigh->t20timer = 0; - neigh->state = 1; + neigh->state = X25_LINK_STATE_3; del_timer(&neigh->timer); break; @@ -135,9 +135,9 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned sho break; } - if (neigh->state == 1) { + if (neigh->state == X25_LINK_STATE_3) { while ((skbn = skb_dequeue(&neigh->queue)) != NULL) - x25_send_frame(skbn, neigh->dev); + x25_send_frame(skbn, neigh); } } @@ -167,7 +167,7 @@ void x25_transmit_restart_request(struct x25_neigh *neigh) skb->sk = NULL; - x25_send_frame(skb, neigh->dev); + x25_send_frame(skb, neigh); } /* @@ -194,7 +194,7 @@ void x25_transmit_restart_confirmation(struct x25_neigh *neigh) skb->sk = NULL; - x25_send_frame(skb, neigh->dev); + x25_send_frame(skb, neigh); } /* @@ -222,7 +222,7 @@ void x25_transmit_diagnostic(struct x25_neigh *neigh, unsigned char diag) skb->sk = NULL; - x25_send_frame(skb, neigh->dev); + x25_send_frame(skb, neigh); } /* @@ -252,7 +252,7 @@ void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsig skb->sk = NULL; - x25_send_frame(skb, neigh->dev); + x25_send_frame(skb, neigh); } void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh) @@ -262,24 +262,42 @@ void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh) return; #endif - if (!x25_link_up(neigh->dev)) - neigh->state = 0; - - skb->arp = 1; + switch (neigh->state) { + case X25_LINK_STATE_0: + skb_queue_tail(&neigh->queue, skb); + neigh->state = X25_LINK_STATE_1; + x25_establish_link(neigh); + break; + case X25_LINK_STATE_1: + case X25_LINK_STATE_2: + skb_queue_tail(&neigh->queue, skb); + break; + case X25_LINK_STATE_3: + x25_send_frame(skb, neigh); + break; + } +} - if (neigh->state == 1) { - x25_send_frame(skb, neigh->dev); - } else { - skb_queue_tail(&neigh->queue, skb); - - if (neigh->t20timer == 0) { +void x25_link_established(struct x25_neigh *neigh) +{ + switch (neigh->state) { + case X25_LINK_STATE_0: + neigh->state = X25_LINK_STATE_2; + break; + case X25_LINK_STATE_1: x25_transmit_restart_request(neigh); + neigh->state = X25_LINK_STATE_2; neigh->t20timer = neigh->t20; x25_link_set_timer(neigh); - } + break; } } +void x25_link_terminated(struct x25_neigh *neigh) +{ + neigh->state = X25_LINK_STATE_0; +} + /* * Add a new device. */ @@ -295,7 +313,7 @@ void x25_link_device_up(struct device *dev) init_timer(&x25_neigh->timer); x25_neigh->dev = dev; - x25_neigh->state = 0; + x25_neigh->state = X25_LINK_STATE_0; x25_neigh->extended = 0; x25_neigh->t20timer = 0; x25_neigh->t20 = sysctl_x25_restart_request_timeout; @@ -412,10 +430,10 @@ int x25_link_get_info(char *buffer, char **start, off_t offset, int length, int cli(); - len += sprintf(buffer, "device st t20 ext\n"); + len += sprintf(buffer, "device st t20 ext\n"); for (x25_neigh = x25_neigh_list; x25_neigh != NULL; x25_neigh = x25_neigh->next) { - len += sprintf(buffer + len, "%-15s %2d %3d/%03d %d\n", + len += sprintf(buffer + len, "%-6s %2d %3d/%03d %d\n", x25_neigh->dev->name, x25_neigh->state, x25_neigh->t20timer / X25_SLOWHZ, diff --git a/net/x25/x25_route.c b/net/x25/x25_route.c index 4458c91d6249..7a012374823e 100644 --- a/net/x25/x25_route.c +++ b/net/x25/x25_route.c @@ -62,7 +62,9 @@ static int x25_add_route(x25_address *address, unsigned int sigdigits, struct de if ((x25_route = (struct x25_route *)kmalloc(sizeof(*x25_route), GFP_ATOMIC)) == NULL) return -ENOMEM; - x25_route->address = *address; + strcpy(x25_route->address.x25_addr, "000000000000000"); + memcpy(x25_route->address.x25_addr, address->x25_addr, sigdigits); + x25_route->sigdigits = sigdigits; x25_route->dev = dev; @@ -143,7 +145,11 @@ struct device *x25_dev_get(char *devname) if ((dev = dev_get(devname)) == NULL) return NULL; - if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25 || dev->type == ARPHRD_ETHER)) + if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25 +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) + || dev->type == ARPHRD_ETHER +#endif + )) return dev; return NULL; @@ -217,10 +223,10 @@ int x25_routes_get_info(char *buffer, char **start, off_t offset, int length, in cli(); - len += sprintf(buffer, "address digits device\n"); + len += sprintf(buffer, "address digits device\n"); for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) { - len += sprintf(buffer + len, "%-15s %2d %-4s\n", + len += sprintf(buffer + len, "%-15s %-6d %-5s\n", x25_route->address.x25_addr, x25_route->sigdigits, (x25_route->dev != NULL) ? x25_route->dev->name : "???"); -- 2.39.5