S: The Netherlands
N: Kai Harrekilde-Petersen
-E: khp@dolphinics.no
+E: khp@olicom.dk
D: Original author of the ftape-HOWTO, i82078 fdc detection code.
-S: Peder Holters vei 13
-S: 1168 Oslo
-S: Norway
N: Andrew Haylett
E: ajh@primag.co.uk
S: Germany
N: David Mosberger-Tang
-E: David.Mosberger@acm.org
-D: Linux/Alpha
+E: davidm@hpl.hp.com if IA-64 related, else David.Mosberger@acm.org
+D: Linux/Alpha and Linux/ia64
S: 35706 Runckel Lane
S: Fremont, California 94536
S: USA
--- /dev/null
+-----BEGIN PGP SIGNED MESSAGE-----
+
+First:
+
+ HiSax is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+However, if you wish to modify the HiSax sources, please note the following:
+
+HiSax has passed the ITU approval test suite with ELSA Quickstep ISDN cards.
+The certification is only valid for the combination of the tested software
+version and the tested hardware. Any changes to the HiSax source code may
+therefore affect the certification.
+
+If you change the main files of the HiSax ISDN stack, the certification will
+become invalid. Because in most countries it is illegal to connect
+unapproved ISDN equipment to the public network, I have to guarantee that
+changes in HiSax do not affect the certification.
+
+In order to make a valid certification apparent to the user, I have built in
+some validation checks that are made during the make process. The HiSax main
+files are protected by md5 checksums and the md5sum file is pgp signed by
+myself:
+
+KeyID 1024/FF992F6D 1997/01/16 Karsten Keil <keil@temic-ech.spacenet.de>
+Key fingerprint = 92 6B F7 58 EE 86 28 C8 C4 1A E6 DC 39 89 F2 AA
+
+Only if the checksums are OK, and the signature of the file
+"drivers/isdn/hisax/md5sums.asc" match, is the certification valid; a
+message confirming this is then displayed during the hisax init process.
+
+The affected files are:
+
+drivers/isdn/hisax/isac.c
+drivers/isdn/hisax/isdnl1.c
+drivers/isdn/hisax/isdnl2.c
+drivers/isdn/hisax/isdnl3.c
+drivers/isdn/hisax/tei.c
+drivers/isdn/hisax/callc.c
+drivers/isdn/hisax/l3dss1.c
+drivers/isdn/hisax/l3_1tr6.c
+drivers/isdn/hisax/cert.c
+drivers/isdn/hisax/elsa.c
+
+Please send any changes, bugfixes and patches to me rather than implementing
+them directly into the HiSax sources.
+
+This does not reduce your rights granted by the GNU General Public License.
+If you wish to change the sources, go ahead; but note that then the
+certification is invalid even if you use ELSA Quickstep cards.
+
+Here are the certification registration numbers for ELSA Quickstep cards:
+German D133361J CETECOM ICT Services GmbH 0682
+European D133362J CETECOM ICT Services GmbH 0682
+
+
+Karsten Keil
+keil@isdn4linux.de
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBNj5OKDpxHvX/mS9tAQFHuQP/WeImlqCcDZ2d132yAvRBWFULlJoSf1P/
+c1lVTeaWvsSaY5Cu9hrKhXXhPzeEaitUbcUBPXdpzFWCA5CE902lnz7AhgRC+HF1
+0qiKgkZZyc/5HKasFymR1+IWSLw30GesP3Di/ZMR3NJi8SlY9PIjx7hnEMunGSRO
+1ufPvfWWuu8=
+=nGJk
+-----END PGP SIGNATURE-----
-$Id: INTERFACE,v 1.8 1998/02/20 17:38:20 fritz Exp $
+$Id: INTERFACE,v 1.11 1999/03/02 12:14:51 armin Exp $
Description of the Interface between Linklevel and Hardwarelevel
of isdn4linux:
1 = At least one device matching this call (RING on ttyI).
HL-driver may send ALERTING on the D-channel in this case.
2 = Call will be rejected.
+ 3 = Incomplete number.
+ The CalledNumber would match, if more digits are appended.
+ This feature is needed for Number-Blocks assigned to
+ a line. In this case, the LL driver should assemble the
+ CalledNumber by handling keypad protocol and try again
+ later with a longer CalledNumber.
+ HL drivers serving ordinary lines should interpret this
+ return code like 0 (nothing matches).
-1 = An error happened. (Invalid parameters for example.)
ISDN_STAT_RUN:
driver = driver-Id
command = ISDN_STAT_BCONN
arg = channel-number, locally to the driver. (starting with 0)
- para = unused.
+ para.num = ASCII-String, containing type of connection (for analog
+ modem only). This will be appended to the CONNECT message
+ e.g. 14400/V.32bis
ISDN_STAT_DHUP:
This mailinglist is bidirectionally gated to the newsgroup
de.alt.comm.isdn4linux
-
+
There is also a well maintained FAQ (both english and german) available
at ftp.franken.de in /pub/isdn4linux/FAQ/
This FAQ is also available at http://www.lrz-muenchen.de/~ui161ab/www/isdn/
In the following Text, the terms MSN and EAZ are used.
MSN is the abbreviation for (M)ultiple(S)ubscriber(N)umber, and applies
- to Euro(EDSS1)-type lines. Usually it is simply the phone-number.
+ to Euro(EDSS1)-type lines. Usually it is simply the phone number.
EAZ is the abbreviation of (E)ndgeraete(A)uswahl(Z)iffer and
applies to German 1TR6-type lines. This is a one-digit string,
- simply appended to the base phone-number
+ simply appended to the base phone number
The internal handling is nearly identical, so replace the appropriate
term to that one, which applies to your local ISDN-environment.
A low-level-driver can register itself through an interface (which is
defined in isdnif.h) and gets assigned a slot.
The following char-devices are made available for each channel:
-
+
A raw-control-device with the following functions:
write: raw D-channel-messages (format: depends on driver).
read: raw D-channel-messages (format: depends on driver).
ioctl: depends on driver, i.e. for the ICN-driver, the base-address of
the ports and the shared memory on the card can be set and read
- also the boot-code and the protocol software can be loaded into
+ also the boot-code and the protocol software can be loaded into
the card.
O N L Y !!! for debugging (no locking against other devices):
128 tty-devices (64 cuix and 64 ttyIx) with integrated modem-emulator:
The functionality is almost the same as that of a serial device
- (the line-discs are handled by the kernel), which lets you run
- SLIP, CSLIP and asynchronous PPP through the devices. We have tested
+ (the line-discs are handled by the kernel), which lets you run
+ SLIP, CSLIP and asynchronous PPP through the devices. We have tested
Seyon, minicom, CSLIP (uri-dip) PPP and mgetty (compiled with NO_FAX),
XCept.
The modem-emulation supports the following:
1.3.1 Commands:
- ATA Answer incoming call.
- ATD<No.> Dial, the number may contain:
+ ATA Answer incoming call.
+ ATD<No.> Dial, the number may contain:
[0-9] and [,#.*WPT-S]
the latter are ignored until 'S'.
- The 'S' must precede the number, if
+ The 'S' must precede the number, if
the line is a SPV (German 1TR6).
- ATE0 Echo off.
- ATE1 Echo on (default).
+ ATE0 Echo off.
+ ATE1 Echo on (default).
ATH Hang-up.
- ATH1 Off hook (ignored).
+ ATH1 Off hook (ignored).
ATH0 Hang-up.
- ATI Return "ISDN for Linux...".
+ ATI Return "ISDN for Linux...".
ATI0 "
ATI1 "
- ATI2 Report of last connection.
+ ATI2 Report of last connection.
ATO On line (data mode).
ATQ0 Enable result codes (default).
ATQ1 Disable result codes (default).
- ATSx=y Set register x to y.
- ATSx? Show contents of register x.
+ ATSx=y Set register x to y.
+ ATSx? Show contents of register x.
ATV0 Numeric responses.
ATV1 English responses (default).
- ATZ Load registers and EAZ/MSN from Profile.
- AT&Bx Set Send-Packet-size to x (max. 4000)
+ ATZ Load registers and EAZ/MSN from Profile.
+ AT&Bx Set Send-Packet-size to x (max. 4000)
The real packet-size may be limited by the
low-level-driver used. e.g. the HiSax-Module-
limit is 2000. You will get NO Error-Message,
driver may not be selected (see "Automatic
Assignment") however the size of outgoing packets
will be limited correctly.
- AT&D0 Ignore DTR
- AT&D2 DTR-low-edge: Hang up and return to
+ AT&D0 Ignore DTR
+ AT&D2 DTR-low-edge: Hang up and return to
command mode (default).
AT&D3 Same as AT&D2 but also resets all registers.
- AT&Ex Set the EAZ/MSN for this channel to x.
- AT&F Reset all registers and profile to "factory-defaults"
- AT&Rx Select V.110 bitrate adaption.
+ AT&Ex Set the EAZ/MSN for this channel to x.
+ AT&F Reset all registers and profile to "factory-defaults"
+ AT&Rx Select V.110 bitrate adaption.
This command enables V.110 protocol with 9600 baud
(x=9600), 19200 baud (x=19200) or 38400 baud
(x=38400). A value of x=0 disables V.110 switching
The value 198 is choosen arbitrarily. Users
_MUST_ negotiate this value before establishing
a connection.
- AT&Sx Set window-size (x = 1..8) (not yet implemented)
- AT&V Show all settings.
+ AT&Sx Set window-size (x = 1..8) (not yet implemented)
+ AT&V Show all settings.
AT&W0 Write registers and EAZ/MSN to profile. See also
iprofd (5.c in this README).
- AT&X0 BTX-mode and T.70-mode off (default)
- AT&X1 BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0)
- AT&X2 T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0)
+ AT&X0 BTX-mode and T.70-mode off (default)
+ AT&X1 BTX-mode on. (S13.1=1, S13.5=0 S14=0, S16=7, S18=7, S19=0)
+ AT&X2 T.70-mode on. (S13.1=1, S13.5=1, S14=0, S16=7, S18=7, S19=0)
For voice-mode commands refer to README.audio
- 1.3.2 Escape sequence:
+ 1.3.2 Escape sequence:
During a connection, the emulation reacts just like
a normal modem to the escape sequence <DELAY>+++<DELAY>.
- (The escape character - default '+' - can be set in the
+ (The escape character - default '+' - can be set in the
register 2).
- The DELAY must at least be 1.5 seconds long and delay
+ The DELAY must at least be 1.5 seconds long and delay
between the escape characters must not exceed 0.5 seconds.
-
+
1.3.3 Registers:
Nr. Default Description
4 10 Line feed character (ASCII).
5 8 Backspace character (ASCII).
6 3 Delay in seconds before dialing.
- 7 60 Wait for carrier (ignored).
+ 7 60 Wait for carrier.
8 2 Pause time for comma (ignored)
9 6 Carrier detect time (ignored)
10 7 Carrier loss to disconnect time (ignored).
1 = T.70 protocol (Only for BTX!) on
Bit 2: 0 = Don't hangup on DTR low.
1 = Hangup on DTR low.
- Bit 3: 0 = Standard response messages
- 1 = Extended response messages
+ Bit 3: 0 = Standard response messages
+ 1 = Extended response messages
Bit 4: 0 = CALLER NUMBER before every RING.
1 = CALLER NUMBER after first RING.
Bit 5: 0 = T.70 extended protocol off
an incoming call happened (RING) and
the remote party hung up before any
local ATA was given.
- 14 0 Layer-2 protocol:
- 0 = X75/LAPB with I-frames
- 1 = X75/LAPB with UI-frames
+ 14 0 Layer-2 protocol:
+ 0 = X75/LAPB with I-frames
+ 1 = X75/LAPB with UI-frames
2 = X75/LAPB with BUI-frames
3 = HDLC
4 = Transparent (audio)
7 = V.110, 9600 baud
8 = V.110, 19200 baud
9 = V.110, 38400 baud
+ 10 = Analog Modem (only if hardware supports this)
15 0 Layer-3 protocol: (at the moment always 0)
0 = transparent
- 16 250 Send-Packet-size/16
+ 16 250 Send-Packet-size/16
17 8 Window-size (not yet implemented)
18 4 Bit coded register, Service-Octet-1 to accept,
or to be used on dialout:
All inactive physical lines are listening to all EAZs for incoming
calls and are NOT assigned to a specific tty or network interface.
- When an incoming call is detected, the driver looks first for a network
+ When an incoming call is detected, the driver looks first for a network
interface and then for an opened tty which:
1. is configured for the same EAZ.
3. (only for network interfaces if the security flag is set)
contains the caller number in its access list.
4. Either the channel is not bound exclusively to another Net-interface, or
- it is bound AND the other checks apply to exactly this Interface.
+ it is bound AND the other checks apply to exactly this interface.
(For usage of the bind-features, refer to the isdnctrl-man-page)
Only when a matching interface or tty is found is the call accepted
Always use the latest module utilities. The current version is
named in Documentation/Changes. Some old versions of insmod
are not capable of setting the driver-Ids correctly.
-
+
3. Lowlevel-driver configuration.
Configuration depends on how the drivers are built. See the
4. Device-inodes
The major and minor numbers and their names are described in
- Documentation/devices.txt. The major-numbers are:
+ Documentation/devices.txt. The major numbers are:
43 for the ISDN-tty's.
44 for the ISDN-callout-tty's.
g) Set the timeout for hang-up:
isdnctrl huptimeout isdn0 <timeout_in_seconds>
- h) additionally you may activate charge-hang-up (= Hang up before
+ h) additionally you may activate charge-hang-up (= Hang up before
next charge-info, this only works, if your isdn-provider transmits
the charge-info during and after the connection):
isdnctrl chargehup isdn0 on
- i) Setup the interface with ifconfig as usual, and set a route to it.
-
- j) (optional) If you run X11 and have Tcl/Tk-wish Version 4.0, you can use
+ i) Set the dial mode of the interface:
+ isdnctrl dialmode isdn0 auto
+ "off" means that you (or the system) cannot make any connection
+ (neither incoming or outgoing connections are possible). Use
+ this if you want to be sure that no connections will be made.
+ "auto" means that the interface is in auto-dial mode, and will
+ attempt to make a connection whenever a network data packet needs
+ the interface's link. Note that this can cause unexpected dialouts,
+ and lead to a high phone bill! Some daemons or other pc's that use
+ this interface can cause this.
+ Incoming connections are also possible.
+ "manual" is a dial mode created to prevent the unexpected dialouts.
+ In this mode, the interface will never make any connections on its
+ own. You must explicitly initiate a connection with "isdnctrl dial
+ isdn0". However, after an idle time of no traffic as configured for
+ the huptimeout value with isdnctrl, the connection _will_ be ended.
+ If you don't want any automatic hangup, set the huptimeout value to 0.
+ "manual" is the default.
+
+ j) Setup the interface with ifconfig as usual, and set a route to it.
+
+ k) (optional) If you run X11 and have Tcl/Tk-wish version 4.0, you can use
the script tools/tcltk/isdnmon. You can add actions for line-status
changes. See the comments at the beginning of the script for how to
do that. There are other tty-based tools in the tools-subdirectory
contributed by Michael Knigge (imon), Volker Götz (imontty) and
Andreas Kool (isdnmon).
- k) For initial testing, you can set the verbose-level to 2 (default: 0).
+ l) For initial testing, you can set the verbose-level to 2 (default: 0).
Then all incoming calls are logged, even if they are not addressed
to one of the configured net-interfaces:
isdnctrl verbose 2
- Now you are ready! A ping to the set address should now result in a
- dial-out (look at syslog kernel-messages).
- The phone-numbers and EAZs can be assigned at any time with isdnctrl.
+ Now you are ready! A ping to the set address should now result in an
+ automatic dial-out (look at syslog kernel-messages).
+ The phone numbers and EAZs can be assigned at any time with isdnctrl.
You can add as many interfaces as you like with addif following the
- directions above. Of course, there may be some limitations. But we have
- tested as many as 20 interfaces without any problem. However, if you
- don't give an interface name to addif, the kernel will assign a name
+ directions above. Of course, there may be some limitations. But we have
+ tested as many as 20 interfaces without any problem. However, if you
+ don't give an interface name to addif, the kernel will assign a name
which starts with "eth". The number of "eth"-interfaces is limited by
the kernel.
5. Additional options for isdnctrl:
- "isdnctrl secure <InterfaceName> on"
+ "isdnctrl secure <InterfaceName> on"
Only incoming calls, for which the caller-id is listed in the access
list of the interface are accepted. You can add caller-id's With the
command "isdnctrl addphone <InterfaceName> in <caller-id>"
Euro-ISDN does not transmit the leading '0' of the caller-id for an
incoming call, therefore you should configure it accordingly.
If the real number for the dialout e.g. is "09311234567" the number
- to configure here is "9311234567". The pattern-match function
+ to configure here is "9311234567". The pattern-match function
works similar to the shell mechanism.
? one arbitrary digit
a '^' as the first character in a list inverts the list
- "isdnctrl secure <InterfaceName> off"
+ "isdnctrl secure <InterfaceName> off"
Switch off secure operation (default).
- "isdnctrl ihup <InterfaceName> [on|off]"
+ "isdnctrl ihup <InterfaceName> [on|off]"
Switch the hang-up-timer for incoming calls on or off.
- "isdnctrl eaz <InterfaceName>"
+ "isdnctrl eaz <InterfaceName>"
Returns the EAZ of an interface.
- "isdnctrl delphone <InterfaceName> in|out <number>"
+ "isdnctrl delphone <InterfaceName> in|out <number>"
Deletes a number from one of the access-lists of the interface.
- "isdnctrl delif <InterfaceName>"
+ "isdnctrl delif <InterfaceName>"
Removes the interface (and possible slaves) from the kernel.
(You have to unregister it with "ifconfig <InterfaceName> down" before).
- "isdnctrl callback <InterfaceName> [on|off]"
+ "isdnctrl callback <InterfaceName> [on|off]"
Switches an interface to callback-mode. In this mode, an incoming call
will be rejected and after this the remote-station will be called. If
you test this feature by using ping, some routers will re-dial very
only while an interface is down.
At the moment the following values are supported:
-
+
rawip (Default) Selects raw-IP-encapsulation. This means, MAC-headers
- are stripped off.
+ are stripped off.
ip IP with type-field. Same as IP but the type-field of the MAC-header
is preserved.
x25iface X.25 interface encapsulation (first byte semantics as defined in
- ../networking/x25-iface.txt). Use this for running the linux
- X.25 network protocol stack (AF_X25 sockets) on top of isdn.
+ ../networking/x25-iface.txt). Use this for running the linux
+ X.25 network protocol stack (AF_X25 sockets) on top of isdn.
cisco-h A special-mode for communicating with a Cisco, which is configured
to do "hdlc"
ethernet No stripping. Packets are sent with full MAC-header.
NOTE: x25iface encapsulation is currently experimental. Please
- read README.x25 for further details
+ read README.x25 for further details
Watching packets, using standard-tcpdump will fail for all encapsulations
without MAC-header. A patch for tcpdump is included in the utility-package
mentioned above.
- "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>"
- Selects a layer-2-protocol.
+ "isdnctrl l2_prot <InterfaceName> <L2-ProtocolName>"
+ Selects a layer-2-protocol.
(With the ICN-driver and the HiSax-driver, "x75i" and "hdlc" is available.
With other drivers, "x75ui", "x75bui", "x25dte", "x25dce" may be
possible too. See README.x25 for x25 related l2 protocols.)
- isdnctrl l3_prot <InterfaceName> <L3-ProtocolName>
+ isdnctrl l3_prot <InterfaceName> <L3-ProtocolName>
The same for layer-3. (At the moment only "trans" is allowed)
- "isdnctrl list <InterfaceName>"
+ "isdnctrl list <InterfaceName>"
Shows all parameters of an interface and the charge-info.
Try "all" as the interface name.
Forces hangup of an interface.
"isdnctrl bind <InterfaceName> <DriverId>,<ChannelNumber> [exclusive]"
- If you are using more than one ISDN-Card, it is sometimes necessary to
- dial out using a specific Card or even preserve a specific Channel for
- Dialout of a specific net-interface. This can be done with the above
+ If you are using more than one ISDN card, it is sometimes necessary to
+ dial out using a specific card or even preserve a specific channel for
+ dialout of a specific net-interface. This can be done with the above
command. Replace <DriverId> by whatever you assigned while loading the
- module. The <ChannelNumber> is counting from zero. The upper Limit
- depends on the card used. At the Moment no card supports more than
- 2 Channels, so the upper limit is one.
+ module. The <ChannelNumber> is counted from zero. The upper limit
+ depends on the card used. At the moment no card supports more than
+ 2 channels, so the upper limit is one.
"isdnctrl unbind <InterfaceName>"
unbinds a previously bound interface.
If switched on, isdn4linux replies a REJECT to incoming calls, it
cannot match to any configured interface.
If switched off, nothing happens in this case.
- You normally should NOT enable this feature, if the ISDN-adaptor is not
- the only device, connected to the S0-bus. Otherwise it could happen, that
+ You normally should NOT enable this feature, if the ISDN adapter is not
+ the only device connected to the S0-bus. Otherwise it could happen that
isdn4linux rejects an incoming call, which belongs to another device on
- the bus.
+ the bus.
"isdnctrl addslave <InterfaceName> <SlaveName>
Creates a slave interface for channel-bundling. Slave interfaces are
isdnctrl eaz isdn1 4 # listen on 12345674(1tr6) only.
...
isdnctrl eaz isdn2 987654 # listen on 987654(euro) only.
-
+
Same scheme is used with AT&E... at the tty's.
-
+
6. If you want to write a new low-level-driver, you are welcome.
The interface to the link-level-module is described in the file INTERFACE.
If the interface should be expanded for any reason, don't do it
HiSax is a Linux hardware-level driver for passive ISDN cards with Siemens
chipset (ISAC_S 2085/2086/2186, HSCX SAB 82525). It is based on the Teles
driver from Jan den Ouden.
-It is meant to be used with isdn4linux, an ISDN link-level module for Linux
+It is meant to be used with isdn4linux, an ISDN link-level module for Linux
written by Fritz Elfert.
This program is free software; you can redistribute it and/or modify
Teles 8.0/16.0/16.3 and compatible ones
Teles 16.3c
Teles S0/PCMCIA
-Creatix PnP S0
+Teles PCI
+Teles S0Box
+Creatix S0Box
+Creatix PnP S0
Compaq ISDN S0 ISA card
AVM A1 (Fritz, Teledat 150)
+AVM Fritz PCMCIA
+AVM Fritz PnP
+AVM Fritz PCI
ELSA Microlink PCC-16, PCF, PCF-Pro, PCC-8
ELSA Quickstep 1000
ELSA Quickstep 1000PCI
ELSA Quickstep 3000 (same settings as QS1000)
+ELSA Quickstep 3000PCI
ELSA PCMCIA
ITK ix1-micro Rev.2
Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
Eicon.Diehl Diva Piccola
ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D)
Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter)
+PCBIT-DP (OEM version of ASUSCOM NETWORK INC. ISDNLink)
HFC-2BS0 based cards (TeleInt SA1)
-Sedlbauer Speed Card (Speed Win, Teledat 100)
-Sedlbauer Speed Star (PCMCIA)
+Sedlbauer Speed Card (Speed Win, Teledat 100, PCI, Fax+)
+Sedlbauer Speed Star/Speed Star2 (PCMCIA)
+Sedlbauer ISDN-Controller PC/104
USR Sportster internal TA (compatible Stollmann tina-pp V3)
-ith Kommunikationstechnik GmbH MIC 16 ISA card
+ith Kommunikationstechnik GmbH MIC 16 ISA card
Traverse Technologie NETjet PCI S0 card
Dr. Neuhaus Niccy PnP/PCI
PCC-8: not tested yet
Teles PCMCIA is EXPERIMENTAL
Teles 16.3c is EXPERIMENTAL
+ Teles PCI is EXPERIMENTAL
+ Teles S0Box is EXPERIMENTAL
Eicon.Diehl Diva U interface not tested
If you know other passive cards with the Siemens chipset, please let me know.
correct one during kernel config. Valid values are "1" for German 1TR6,
"2" for EDSS1 (Euro ISDN) and "3" for leased lines (no D-Channel).
-The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying
+The Creatix/Teles PnP cards use io1= and io2= instead of io= for specifying
the I/O addresses of the ISAC and HSCX chips, respectively.
Card types:
11 Eicon.Diehl Diva ISA PnP irq, io
11 Eicon.Diehl Diva PCI no parameter
12 ASUS COM ISDNLink irq, io (from isapnp setup)
- 13 HFC-2BS0 based cards irq, io
+ 13 HFC-2BS0 based cards irq, io
14 Teles 16.3c PnP irq, io
- 15 Sedlbauer Speed Card irq, io
- 16 USR Sportster internal irq, io
- 17 MIC card irq, io
+ 15 Sedlbauer Speed Card irq, io
+ 15 Sedlbauer PC/104 irq, io
+ 15 Sedlbauer Speed PCI no parameter
+ 16 USR Sportster internal irq, io
+ 17 MIC card irq, io
18 ELSA Quickstep 1000PCI no parameter
19 Compaq ISDN S0 ISA card irq, io0, io1, io (from isapnp setup io=IO2)
20 NETjet PCI card no parameter
+ 21 Teles PCI no parameter
22 Sedlbauer Speed Star (PCMCIA) irq, io (set with card manager)
24 Dr. Neuhaus Niccy PnP irq, io0, io1 (from isapnp setup)
24 Dr. Neuhaus Niccy PCI no parameter
+ 25 Teles S0Box irq, io (of the used lpt port)
+ 26 AVM A1 PCMCIA (Fritz!) irq, io (set with card manager)
+ 27 AVM PnP (Fritz!PnP) irq, io (from isapnp setup)
+ 27 AVM PCI (Fritz!PCI) no parameter
+ 28 Sedlbauer Speed Fax+ irq, io (from isapnp setup)
-
-At the moment IRQ sharing is not possible. Please make sure that your IRQ
-is free and enabled for ISA use.
-Note: For using the ELSA PCMCIA you need the cardmanager under MSDOS for
-enabling at the moment, then boot linux with loadlin.
+At the moment IRQ sharing is only possible with PCI cards. Please make sure
+that your IRQ is free and enabled for ISA use.
Examples for module loading
4. Any ELSA PCC/PCF card, Euro ISDN
modprobe hisax type=6 protocol=2
-5. Teles 16.3 PnP, Euro ISDN, with isapnp configured
+5. Teles 16.3 PnP, Euro ISDN, with isapnp configured
isapnp config: (INT 0 (IRQ 10 (MODE +E)))
(IO 0 (BASE 0x0580))
(IO 1 (BASE 0x0180))
Note: the ID string must start with an alphabetical character!
Card types:
-
- type
+
+type
1 Teles 16.0 pa=irq pb=membase pc=iobase
2 Teles 8.0 pa=irq pb=membase
3 Teles 16.3 pa=irq pb=iobase
12 ASUS COM ISDNLink ONLY WORKS AS A MODULE !
13 HFC-2BS0 based cards pa=irq pb=io
14 Teles 16.3c PnP ONLY WORKS AS A MODULE !
- 15 Sedlbauer Speed Card pa=irq pb=io (Speed Win only as module !)
+ 15 Sedlbauer Speed Card pa=irq pb=io (Speed Win only as module !)
+ 15 Sedlbauer PC/104 pa=irq pb=io
+ 15 Sedlbauer Speed PCI no parameter
16 USR Sportster internal pa=irq pb=io
17 MIC card pa=irq pb=io
18 ELSA Quickstep 1000PCI no parameter
19 Compaq ISDN S0 ISA card ONLY WORKS AS A MODULE !
20 NETjet PCI card no parameter
- 21 Sedlbauer Speed Star (PCMCIA) pa=irq, pb=io (set with card manager)
+ 21 Teles PCI no parameter
+ 22 Sedlbauer Speed Star (PCMCIA) pa=irq, pb=io (set with card manager)
+ 24 Dr. Neuhaus Niccy PnP ONLY WORKS AS A MODULE !
+ 24 Dr. Neuhaus Niccy PCI no parameter
+ 25 Teles S0Box irq, io (of the used lpt port)
+ 26 AVM A1 PCMCIA (Fritz!) irq, io (set with card manager)
+ 27 AVM PnP (Fritz!PnP) ONLY WORKS AS A MODULE !
+ 27 AVM PCI (Fritz!PCI) no parameter
+ 28 Sedlbauer Speed Fax+ ONLY WORKS AS A MODULE !
+
Running the driver
------------------
-When you insmod isdn.o and hisax.o (or with the in-kernel version, during
+When you insmod isdn.o and hisax.o (or with the in-kernel version, during
boot time), a few lines should appear in your syslog. Look for something like:
Apr 13 21:01:59 kke01 kernel: HiSax: Driver for Siemens chip set ISDN cards
-Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.1
+Apr 13 21:01:59 kke01 kernel: HiSax: Version 2.9
Apr 13 21:01:59 kke01 kernel: HiSax: Revisions 1.14/1.9/1.10/1.25/1.8
Apr 13 21:01:59 kke01 kernel: HiSax: Total 1 card defined
Apr 13 21:01:59 kke01 kernel: HiSax: Card 1 Protocol EDSS1 Id=HiSax1 (0)
Apr 13 21:01:59 kke01 kernel: HiSax: 2 channels added
This means that the card is ready for use.
-Cabling problems or line-downs are not detected, and only some ELSA cards can
+Cabling problems or line-downs are not detected, and only some ELSA cards can
detect the S0 power.
Remember that, according to the new strategy for accessing low-level drivers
insmod: Simply append hisax_id=<SomeString> to the insmod command line. This
string MUST NOT start with a digit or a small 'x'!
-At this point you can run a 'cat /dev/isdnctrl0' and view debugging
-messages.
+At this point you can run a 'cat /dev/isdnctrl0' and view debugging messages.
At the moment, debugging messages are enabled with the hisaxctrl tool:
With DebugCmd set to 1:
- 1 Link-level <--> hardware-level communication
- 2 Top state machine
- 4 D-Channel Q.931 (call control messages)
- 8 D-Channel Q.921
- 16 B-Channel X.75
- 32 D-Channel l2
- 64 B-Channel l2
- 128 D-Channel link state debugging
- 256 B-Channel link state debugging
- 512 TEI debug
- 1024 LOCK debug in callc.c
- 2048 More paranoid debug in callc.c (not for normal use)
+ 0x0001 Link-level <--> hardware-level communication
+ 0x0002 Top state machine
+ 0x0004 D-Channel Frames for isdnlog
+ 0x0008 D-Channel Q.921
+ 0x0010 B-Channel X.75
+ 0x0020 D-Channel l2
+ 0x0040 B-Channel l2
+ 0x0080 D-Channel link state debugging
+ 0x0100 B-Channel link state debugging
+ 0x0200 TEI debug
+ 0x0400 LOCK debug in callc.c
+ 0x0800 More paranoid debug in callc.c (not for normal use)
+ 0x1000 D-Channel l1 state debugging
+ 0x2000 B-Channel l1 state debugging
With DebugCmd set to 11:
- 1 Warnings (default: on)
- 2 IRQ status
- 4 ISAC
- 8 ISAC FIFO
- 16 HSCX
- 32 HSCX FIFO (attention: full B-Channel output!)
- 64 D-Channel LAPD frame types
+ 0x0001 Warnings (default: on)
+ 0x0002 IRQ status
+ 0x0004 ISAC
+ 0x0008 ISAC FIFO
+ 0x0010 HSCX
+ 0x0020 HSCX FIFO (attention: full B-Channel output!)
+ 0x0040 D-Channel LAPD frame types
+ 0x0080 IPAC debug
+ 0x0100 HFC receive debug
+ 0x0200 ISAC monitor debug
+ 0x0400 D-Channel frames for isdnlog (set with 1 0x4 too)
+ 0x0800 D-Channel message verbose
With DebugCmd set to 13:
Because of some obscure problems with some switch equipment, the delay
between the CONNECT message and sending the first data on the B-channel is now
-configurable with
+configurable with
hisaxctrl <DriverId> 2 <delay>
<delay> in ms Value between 50 and 800 ms is recommended.
+Downloading Firmware
+--------------------
+At the moment, the Sedlbauer speed fax+ is the only card, which
+needs to download firmware.
+The firmware is downloaded with the hisaxctrl tool:
+
+ hisaxctrl <DriverId> 9 <firmware_filename>
+
+<DriverId> default is HiSax, if you didn't specify one,
+
+where <firmware_filename> is the filename of the firmware file.
+
+For example, 'hisaxctrl HiSax 9 ISAR.BIN' downloads the firmware for
+ISAR based cards (like the Sedlbauer speed fax+).
Warning
-------
-HiSax is a work in progress and may crash your machine. It has not been
-certified and therefore operation on your PTT's ISDN network is probably
-illegal.
-
+HiSax is a work in progress and may crash your machine.
+For certification look at HiSax.cert file.
Limitations
-----------
At this time, HiSax only works on Euro ISDN lines and German 1TR6 lines.
-For leased lines see appendix.
+For leased lines see appendix.
-Bugs
+Bugs
----
-If you find any, please let me know.
+If you find any, please let me know.
Thanks
Stephan von Krawczynski
Juergen Quade for the Leased Line part
Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE), for ELSA PCMCIA support
+ Enrik Berkhan (enrik@starfleet.inka.de) for S0BOX specific stuff
+ Ton van Rosmalen for Teles PCI
and more people who are hunting bugs. (If I forgot somebody, please
- send me a mail).
+ send me a mail).
Firma ELSA GmbH
Firma Eicon.Diehl GmbH
Firma S.u.S.E
Firma ith Kommunikationstechnik GmbH
Firma Traverse Technologie Australia
-
+ Firma Medusa GmbH (www.medusa.de).
+ Firma Quant-X Austria for sponsoring a DEC Alpha board+CPU
+ Firma Cologne Chip Designs GmbH
+
My girl friend and partner in life Ute for her patience with me.
Enjoy,
-Karsten Keil
-keil@temic-ech.spacenet.de
+Karsten Keil
+keil@isdn4linux.de
Appendix: Teles PCMCIA driver
-----------------------------
-See
+See
http://www.stud.uni-wuppertal.de/~ea0141/pcmcia.html
for instructions.
/sbin/isdnctrl l2_prot isdn0 hdlc
# Attention you must not set an outgoing number !!! This won't work !!!
# The incomming number is LEASED0 for the first card, LEASED1 for the
- # second and so on.
+ # second and so on.
/sbin/isdnctrl addphone isdn0 in LEASED0
# Here is no need to bind the channel.
c) in case the remote partner is a CISCO:
e) set the routes
/sbin/route add -host ${REMOTE_IP} isdn0
/sbin/route add default gw ${REMOTE_IP}
- f) switch the card into leased mode for each used B-channel
+ f) switch the card into leased mode for each used B-channel
/sbin/hisaxctrl HiSax 5 1
-
+
Remarks:
a) If you have a CISCO don't forget to switch off the KEEP ALIVE option!
+b) Use state of the art isdn4k-utils
Here an example script:
#!/bin/sh
/sbin/isdnctrl encap isdn0s cisco-h
fi
fi
+ /sbin/isdnctrl dialmode isdn0 auto
# configure tcp/ip
/sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
/sbin/route add -host ${REMOTE_IP} isdn0
--- /dev/null
+--( Version: 11.10.1997 )--
+
+TimRu-Erweiterungen:
+====================
+
+1. erfolglose Anwahlversuche per ICMP zurueckmelden
+---------------------------------------------------
+
+isdnctrl dialtimeout <name> <timeout>
+ name: Name des Interfaces
+ timeout: -1: kein Waehl-Timeout
+ 0: jede Telefonnummer dialmax-mal probieren
+ >0: Zeitraum in Sekunden, in dem die Anwahl versucht wird
+
+ isdnctrl dialtimeout bewirkt, dass die Anwahl der Gegenstelle im Fehler-
+ fall nicht unbegrenzt versucht wird, sondern entweder nach einer bestimmten
+ Anzahl von Versuchen oder nach einem bestimmten Zeitraum abgebrochen wird
+ und alle ausstehenden Pakete fuer den durch isdnctrl dialwait bestimmten
+ Zeitraum mit ICMP_NET_UNREACHABLE beantwortet werden.
+
+
+isdnctrl dialwait <name> <seconds>
+ name: Name des Interfaces
+ seconds: 0: keine Waehl-Unterdrueckung im Fehlerfall
+ >0: Zeit in Sekunden, in der nach einem erfolglosen Anwahl-
+ versuch Pakete mit ICMP_NET_UNREACHABLE beantwortet werden
+
+
+1.1 einzelne Interfaces stoppen / starten
+-----------------------------------------
+
+isdnctrl status <name> <status>
+ name: Name des Interfaces
+ status: on: Interface einschalten
+ off: Interface ausschalten
+
+ Dieser Befehl wirkt wie 'isdnctrl system on/off' auf Interface-Ebene.
+
+
+2. bessere Kontrolle ueber Aufbau und Hangup einer Verbindung
+-------------------------------------------------------------
+
+isdnctrl <cmd> <name> bringup <seconds> <rule>
+isdnctrl <cmd> <name> keepup in <seconds> <rule>
+isdnctrl <cmd> <name> keepup out <seconds> <rule>
+isdnctrl <cmd> <name> keepup both <seconds> <rule>
+ cmd: addrule: Regel am Ende der Regelliste anfuegen
+ insrule: Regel am Anfang der Regelliste einfuegen
+ delrule: Regel loeschen
+ default: Was tun, wenn keine Regel passt?
+ showrules: alle Regeln anzeigen
+ flushrules: alle Regeln einer Art loeschen (bringup, ...)
+ flushallrules: alle Regeln loeschen
+
+ name: Name des Interfaces
+ seconds: Mindester Hangup-Timeout ab jetzt
+ rule: Regel, auf die ein Paket passen muss, damit die Verbindung
+ aufgebaut oder der Hangup-Timeout verlaengert wird
+
+
+ Eine passende Bringup-Regel erlaubt den Aufbau der Verbindung,
+ wenn ihr Timeout > 0 ist. Eine Bringup-Regel mit einen Timeout == 0
+ verhindert einen Verbindungsaufbau. Dieser Timeout muss eigentlich nur
+ so gross sein, dass die Verbindung zum Gegenrechner erfolgreich
+ zustande kommt, da das ausloesende Datenpaket danach durch die
+ Keepup-Logik 'geht' und der Hangup-Timeout erneut berechnet wird.
+
+ Eine passende Keepup-Regel verlaengert den Hangup-Timeout, wobei
+ nach eingehenden und ausgehenden Paketen unterschieden werden kann.
+
+ Die Kontrolle eines Paketes stoppt bei der ersten passenden Regel, falls
+ keine Regel anwendbar ist, gilt die Default-Regel.
+
+ Die Regeln haben folgenden Aufbau:
+ ip/icmp <src>/<mask> <type> <dst>/<mask>
+ ip/tcp <src>/<mask> <port> <dst>/<mask> <port>
+ ip/udp <src>/<mask> <port> <dst>/<mask> <port>
+ ip/* <src>/<mask> <dst>/<mask>
+ ip/any <src>/<mask> <dst>/<mask>
+
+ ipx/*
+ ipx/any
+
+ ppp/ipcp
+ ppp/ipxcp
+ ppp/ccp
+ ppp/lcp
+ ppp/chap
+ ppp/pap
+ ppp/lqr
+ ppp/*
+ ppp/any
+
+ */*
+ any
+
+
+ src: Absender-Adresse des Paketes als Nummer oder Name
+ dst: Empfaenger-Adresse des Paketes als Nummer oder Name
+ mask: Subnet-Maske als Anzahl Bits oder als Maske
+ type: ICMP-Message-Type als Nummer
+ port: Portnummer als Nummer oder Name
+
+
+ Wildcards ('*', 'any') sind ueberall erlaubt.
+
+ Fuer <src> und <dst> gilt:
+ <host>: Host-Adresse als Nummer oder Name,
+ Subnet-Mask /32
+ <host>/<mask>: Host-Adresse als Nummer oder Name,
+ Subnet-Mask als Anzahl Bits oder Maske
+ <network>: Netzwerk-Adresse als Nummer oder Name,
+ Subnet-Mask /32
+ <network>/<mask>: Host-Adresse als Nummer oder Name,
+ Subnet-Mask als Anzahl Bits oder Maske
+ 0.0.0.0/0
+ 0/0
+ *
+ any: Wildcard, passt auf jede Adresse.
+
+ Fuer <ports> gilt:
+ <from>-<to>: alle Ports von <from> bis <to>, numerische Angabe
+ <from>-: alle Ports von <from> bis 65535, numerische Angabe
+ -<to>: alle Ports von 0 bis <to>, numerische Angabe
+ <port>: ein Port als Nummer oder Name
+ -, *
+ oder any: alle Ports
+
+ Beginnt die Regel mit einem '!' oder mit 'not', so dreht sich ihre Aussage um:
+ falls sie NICHT passt, wird die Verbindung aufgebaut, bzw. der
+ Hangup-Timeout verlaengert.
+
+
+ Default-Regeln werden folgendermassen angegeben:
+
+ isdnctrl default <name> bringup <timeout>
+ name: Name des Interfaces
+ timeout: 0: Verbindung wird nicht aufgebaut
+ >0: Verbindung wird aufgebaut, anfaenglicher Timeout
+ ist <timeout>.
+
+ isdnctrl default <name> keepup in <seconds>
+ isdnctrl default <name> keepup out <seconds>
+ isdnctrl default <name> keepup both <seconds>
+ name: Name des Interfaces
+ seconds: Mindester Hangup-Timeout
+
+
+3. Budget-Erweiterungen
+-----------------------
+
+Diese Erweiterung erlaubt es, bestimmte 'Verbrauchswerte' pro
+Zeitraum zu limitieren. Falls ein Budget aufgebraucht ist, findet
+keine Anwahl mehr statt und eine bestehende Verbindung wird unterbrochen.
+Sobald wieder alle Budgets verfuegbar sind, werden wieder Verbindungen
+zugelassen.
+Die Gebuehrenimpuls-Zaehlung setzt momentan auf der Uebertragung der
+CINF-Pakete, also der Gebuehreninformation waehrend der Verbindung auf.
+Unmittelbar nach Aufbau der Verbindung wird der erste Gebuehrenimpuls
+'angenommen'.
+Fuer ISDN-Anschluesse, bei denen die Gebuehren erst am Ende der Ver-
+bindung uebertragen werden, faellt uns bestimmt auch noch was ein ;-).
+
+isdnctrl budget <name> <budget-type> <amount> <period>
+ setzt die Werte eines bestimmten Budgets. Ein aufgebrauchtes
+ Budget wird unmittelbar wieder aktiviert.
+
+ budget-type: dial: Anzahl der Anwahlversuche (erfolgreich oder
+ erfolglos) pro Periode
+ charge: Anzahl der Gebuehreneinheiten pro Periode
+ online: Online-Zeit pro Periode
+
+ amount: Hoehe des Budgets. Bei <budget-type> 'dial' oder 'charge'
+ ist das die Anzahl Anwahlen oder Einheiten, bei 'online'
+ eine Zeitangabe (s.u.)
+
+ period: Dauer der Periode in folgendem Format (<n> steht fuer eine
+ natuerliche Zahl):
+
+ <n>
+ <n>s
+ <n>sec, <n> Sekunden
+
+ <n>m
+ <n>min, <n> Minuten
+
+ <n>h
+ <n>hour, <n> Stunden
+
+ <n>d
+ <n>day, <n> Tage
+
+ <n>w
+ <n>week, <n> Wochen (a 7 Tage)
+
+ <n>M
+ <n>month, <n> Monate (a 30 Tage)
+
+ <n>y
+ <n>year, <n> Jahre (a 365 Tage)
+
+ Diese Angaben koennen miteinander kombiniert werden, z.B.:
+
+ 2h30m15
+ 2hour,30min,15sec
+ 1M2week,1day,8h
+
+
+isdnctrl budget <name> <budget-type> off
+ schaltet die Kontrolle des entsprechenden Budgets aus
+
+isdnctrl budget <name> showbudgets
+ zeigt die momentaten Budgets an
+
+isdnctrl budget <name> savebudgets
+ gibt die momentaten Budgets in einen Format aus, das vom Befehl 'restore-
+ budgets' wieder eingelesen kann
+
+isdnctrl budget <name> restorebudgets <saved-budget> ...
+ setzt die Budgets wieder auf die vorher mit 'savebudgets' ausgegebenen
+ Werte. Damit koennen Budgets auch ueber den Reboot der Maschine hinaus
+ Gueltigkeit haben.
+
+
+Hier ein Beispiel fuer die TimRu-Erweiterung:
+
+ # Alle Regeln loeschen
+ isdnctrl flushallrules ippp0
+
+ # Default: jeder Datenverkehr setzt den Hangup-Timeout auf 60 Sek.
+ isdnctrl default ippp0 keepup both 60
+
+ # FTP und Telnet erhoehen den Timeout auf 5 Minuten
+ isdnctrl addrule ippp0 keepup both 300 ip/tcp 0/0 20-23 0/0 -
+
+ # Anzeige der Regeln:
+ isdnctrl showrules ippp0
+
+ # ... erzeugt folgende Ausgabe:
+
+Timeout rules for interface ippp0:
+Default bringup policy: true
+Default huptimeout for incoming packets: 60 sec.
+Default huptimeout for outgoing packets: 60 sec.
+
+Current huptimeout: 60 sec.
+Time until hangup: 45 sec.
+
+Keepup-rules for incoming ip-packets:
+1-1-0 keepup_in 300 ip/tcp 0/0 20-23 0/0 *
+
+Keepup-rules for outgoing ip-packets:
+2-1-0 keepup_out 300 ip/tcp 0/0 * 0/0 20-23
+
+
+Hier ein Beispiel fuer die Budget-Erweiterung:
+
+ # Hoechstens 60 Anwahlversuche pro Stunde
+ isdnctrl budget ippp0 dial 60 1h
+
+ # Hoechstens 3000 Einheiten pro Monat
+ isdnctrl budget ippp0 charge 3000 1M
+
+ # Hoechstens 8 Stunden online pro Tag
+ isdnctrl budget ippp0 online 8h 1d
+
+ # Anzeige der Budgets:
+ isdnctrl showbudgets ippp0
+
+ # ... erzeugt nach kurzer Zeit folgende Ausgabe:
+
+
+Budgets for interface ippp0:
+
+TYPE AMOUNT PERIOD USED SINCE
+dial 60 1h 1 01.07.1997, 17:43:40
+charge 3000 1M 2 01.07.1997, 17:43:49
+online 8h 1d 2m26s 01.07.1997, 17:43:57
+
+
+Zum Nachfuehren der Budgets ueber einen laengeren Zeitraum koennte man
+folgende Methode verwenden:
+
+beim Herunterfahren oder per 'cron' jede Minute:
+
+ INTERFACES="ippp0 ippp1 ippp2"
+ for i in $INTERFACES; do
+ isdnctrl savebudgets $i > /var/isdn/saved-budgets/$i
+ done
+
+
+und dann beim Neustart:
+
+ for f in /var/isdn/saved-budgets/*; do
+ isdnctrl restorebudgets ${f##*/} `cat $f`
+ done
-$Id: README.act2000,v 1.1 1997/09/24 23:50:16 fritz Exp $
+$Id: README.act2000,v 1.2 1998/04/29 19:49:06 he Exp $
This document describes the ACT2000 driver for the
IBM Active 2000 ISDN card.
Version. Currently, only the ISA-Bus version of the card is supported.
However MCA and PCMCIA will follow soon.
-The ISA-Bus Version uses 8 IO-ports. The base port address has to be set
+The ISA-Bus Version uses 8 IO-ports. The base port adress has to be set
manually using the DIP switches.
Setting up the DIP switches for the IBM Active 2000 ISDN card:
-$Id: README.audio,v 1.5 1997/02/23 23:53:46 fritz Exp $
+$Id: README.audio,v 1.7 1998/07/26 18:45:34 armin Exp $
ISDN subsystem for Linux.
Description of audio mode.
the application. See below for data format
AT+VSD=x,y Set silence-detection parameters.
- NO EFFECT, supported for compatibility
- only. Possible parameters:
- x = 0 ... 31
- y = 0 ... 255
+ Possible parameters:
+ x = 0 ... 31 sensitivity threshold level.
+ (default 0 , deactivated)
+ y = 0 ... 255 range of interval in units
+ of 0.1 second. (default 70)
AT+VSD=? Report possible parameters.
AT+VSD? Show current parameters.
<DLE>C Touchtone "C" received.
<DLE>D Touchtone "D" received.
+ <DLE>q quiet. Silence detected after non-silence.
+ <DLE>s silence. Silence detected from the
+ start of recording.
+
Currently unsupported DLE sequences:
<DLE>c FAX calling tone received.
<DLE>b busy tone received.
- <DLE>q quiet. Silence detected after non-silence.
- <DLE>s silence. Silence detected from the
- start of recording.
Audio playback.
of the concap interface when a trivial concap protocol is used.
Nevertheless, the device remains able to support encapsulation
protocol configuration.
+
--- /dev/null
+$Id: README.eicon,v 1.3 1999/03/29 11:10:04 armin Exp $
+
+(c) 1999 Cytronics & Melware
+
+This document describes the eicon driver for the
+Eicon.Diehl active ISDN cards.
+
+It is meant to be used with isdn4linux, an ISDN link-level module for Linux.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+NOTE : Since the eicon driver is still experimental, this README file
+ may be incomplete and not up to date.
+
+
+However, the driver should work under following conditions :
+
+Supported Cards
+---------------
+
+- S-Card ISA
+- SX-Card ISA
+- SXn-Card ISA
+- SCOM-Card ISA
+- Quadro-Card ISA
+- S2M-Card ISA
+- DIVA Server BRI/PCI 2M
+- DIVA Server PRI/PCI 2M (9M 23M 30M)
+ (Only analog modem functions of the DSPs are currently implemented)
+
+ISDN D-Channel Protocols
+------------------------
+
+- ETSI (Euro-DSS1)
+- 1TR6 (German ISDN) *not testet*
+
+
+
+You can load the module simply by using the insmod or modprobe function :
+
+ insmod eicon [id=driverid] [membase=<membase>] [irq=<irq>]
+
+
+The module will automatically probe the PCI-cards. If the id-options
+is omitted, the driver will assume 'eicon0' for the first pci card and
+increases the digit with each further card. With a given driver-id
+the module appends a number starting with '0'.
+
+For ISA-cards you have to specify membase, irq and id. If id or
+membase is missing/invalid, the driver will not be loaded except
+PCI-cards were found. Additional ISA-cards and irq/membase changes
+can be done with the eiconctrl utility.
+
+After loading the module, you have to download the protocol and
+dsp-code by using the eiconctrl utility of isdn4k-utils.
+
+
+Example for loading and starting a BRI card with E-DSS1 Protocol.
+
+ eiconctrl [-d DriverId] load etsi
+
+
+Example for loading and starting a PRI card with E-DSS1 Protocol.
+
+ eiconctrl [-d DriverId] load etsi -s2 -n
+
+
+Details about using the eiconctrl utility are in 'man eiconctrl'
+or will be printed by starting eiconctrl without any parameters.
+
+
+
+Any reports about bugs, errors and even wishes are welcome.
+
+
+Have fun !
+
+Armin Schindler
+mac@melware.de
+http://www.melware.de
-$Id: README.icn,v 1.5 1997/04/23 18:55:55 fritz Exp $
+$Id: README.icn,v 1.6 1998/04/29 19:49:08 he Exp $
You can get the ICN-ISDN-card from:
-
-X25 support within isdn4linux
-
-
-This is experimental code and should be used with linux version 2.1.72.
-or later. Use it completely at your own risk.
-
+
+X.25 support within isdn4linux
+==============================
+This is alpha/beta test code. Use it completely at your own risk.
As new versions appear, the stuff described here might suddenly change
or become invalid without notice.
Keep in mind:
-You are using an experimental kernel (2.1.x series) with an experimental
-x25 protocol implementation and experimental x25-on-top-of-isdn extensions.
-Thus, be prepared to face problems related therefrom.
+You are using several new parts of the 2.2.x kernel series which
+have not been tested in a large scale. Therefore, you might encounter
+more bugs as usual.
-- If you connect to an x25 neighbour not operated by yourself, ASK the
+- If you connect to an X.25 neighbour not operated by yourself, ASK the
other side first. Be prepared that bugs in the protocol implementation
- might result in problems (even crashing the peer, however such ugly events
- should only happen if your peer's protocol implementation has serious bugs).
+ might result in problems.
- This implementation has never wiped out my whole hard disk yet. But as
- this is experimental code, don't blame me if that happened to you. Take
- appropriate actions (such as backing up important data) before
- trying this code.
+ this is experimental code, don't blame me if that happened to you.
+ Backing up important data will never harm.
- Monitor your isdn connections while using this software. This should
prevent you from undesired phone bills in case of driver problems.
How to configure the kernel
-
+===========================
The ITU-T (former CCITT) X.25 network protocol layer has been implemented
in the Linux source tree since version 2.1.16. The isdn subsystem might be
compilation. You currently also need to enable
"Prompt for development and/or incomplete code/drivers" from the
"Code maturity level options" menu. For the x25trace utility to work
-you also need to enable "Packet socket" (I recommend to choose "y",
-not "m" for testing) from the networking options.
+you also need to enable "Packet socket".
+For local testing it is also recommended to enable the isdnloop driver
+from the isdn subsystem's configuration menu.
-For testing you should also select the isdnloop driver from the
-isdn subsystem's configuration menu.
+For testing, it is recommended that all isdn drivers and the X.25 PLP
+protocol are compiled as loadable modules. Like this, you can recover
+from certain errors by simply unloading and reloading the modules.
What's it for? How to use it?
+=============================
-
-X25 on top of isdn might be useful with two different scenarios:
+X.25 on top of isdn might be useful with two different scenarios:
- You might want to access a public X.25 data network from your Linux box.
You can use i4l if you were physically connected to the X.25 switch
- by an ISDN line (leased line as well as dial up connection should work,
- but connecting to x.25 network switches is currently untested. Testing
- needs to be done by somebody with access to such a switch.)
+ by an ISDN line (leased line as well as dial up connection should work)
-- Or you might want to operate certain ISDN teleservices on
- your linux box. A lot of those teleservices run on top of the ISO-8208
- network layer protocol. ISO-8208 is essentially the same as ITU-T X.25.
+- Or you might want to operate certain ISDN teleservices on your linux
+ box. A lot of those teleservices run on top of the ISO-8208
+ (DTE-DTE mode) network layer protocol. ISO-8208 is essentially the
+ same as ITU-T X.25.
- Popular candidates of such teleservices are EUROFILE transfer or any
- teleservice applying ITU-T recommendation T.90 (i.e., AFAIK, G4 Fax).
+ Popular candidates of such teleservices are EUROfile transfer or any
+ teleservice applying ITU-T recommendation T.90.
To use the X.25 protocol on top of isdn, just create an isdn network
interface as usual, configure your own and/or peer's ISDN numbers,
isdnctrl encap <iface-name> x25iface.
-Once encap is set like this, the device can be used by the x25 packet layer.
+Once encap is set like this, the device can be used by the X.25 packet layer.
-All the stuff needed for x25 is implemented inside the isdn link
+All the stuff needed for X.25 is implemented inside the isdn link
level (mainly isdn_net.c and some new source files). Thus, it should
-work with every existing HL driver. I was able to successfully open x25
+work with every existing HL driver. I was able to successfully open X.25
connections on top of the isdnloop driver and the hisax driver.
"x25iface"-encapsulation bypasses demand dialing. Dialing will be
-initiated when the upper (x25 packet) layer requests the lapb datalink to
-be established. But hangup timeout is still active. The connection
-will not automatically be re-established by the isdn_net module
-itself when new data arrives after the hangup timeout. But
-the x25 network code will re-establish the datalink connection
-(resulting in re-dialing and an x25 protocol reset) when new data is
-to be transmitted. (This currently does not work properly with the
-isdnloop driver, see "known problems" below)
+initiated when the upper (X.25 packet) layer requests the lapb datalink to
+be established. But hangup timeout is still active. Whenever a hangup
+occurs, all existing X.25 connections on that link will be cleared
+It is recommended to use sufficiently large hangup-timeouts for the
+isdn interfaces.
In order to set up a conforming protocol stack you also need to
isdnctrl l2_prot <iface-name> x25dce
However, x25dte or x25dce is currently not supported by any real HL
-level driver. The main difference between x75 and x25dte/dce is that
+level driver. The main difference between x75i and x25dte/dce is that
x25d[tc]e uses fixed lap_b addresses. With x75i, the side which
initiates the isdn connection uses the DTE's lap_b address while the
-called side used the DCE's lap_b address. Thus, l2_prot x75i will
-probably work if you access a public x25 network as long as the
-corresponding isdn connection is set up by you. However, I've never
-tested this.
-
-
+called side used the DCE's lap_b address. Thus, l2_prot x75i might
+probably work if you access a public X.25 network as long as the
+corresponding isdn connection is set up by you. At least one test
+was successful to connect via isdn4linux to an X.25 switch using this
+trick. At the switch side, a terminal adapter X.21 was used to connect
+it to the isdn.
-How to use the test installation?
+How to set up a test installation?
+==================================
-To test x25 on top of isdn, you need to get
+To test X.25 on top of isdn, you need to get
-- a patched version of the "isdnctrl" program that supports setting the new
- x25 specific parameters.
+- a recent version of the "isdnctrl" program that supports setting the new
+ X.25 specific parameters.
-- the x25-utils-2.1.x package from ftp.pspt.fi/pub/ham/linux/ax25
- or any mirror site (i.e. ftp://ftp.gwdg.de/pub/linux/misc/ax25/).
+- the x25-utils-2.X package from
+ ftp://ftp.hes.iki.fi/pub/ham/linux/ax25/x25utils-*
+ (don't confuse the x25-utils with the ax25-utils)
-- a kernel patch that enhances isdn4linux to provide x25 network
- interface support. (This file is part of that kernel patch).
-
-- an application that uses linux AF_X25 sockets program.
+- an application program that uses linux PF_X25 sockets (some are
+ contained in the x25-util package).
Before compiling the user level utilities make sure that the compiler/
-preprocessor will fetch the proper (patched) kernel header files. Either make
-/usr/include/linux a symbolic link pointing to your developer kernel's
-include/linux directory or set the appropriate compiler flags.
-
-It is recommended that all isdn drivers and the x25 PLP protocol
-are compiled as loadable modules. Like this, you can recover
-from certain errors by simply unloading and reloading the modules.
+preprocessor will fetch the proper kernel header files of this kernel
+source tree. Either make /usr/include/linux a symbolic link pointing to
+this kernel's include/linux directory or set the appropriate compiler flags.
When all drivers and interfaces are loaded and configured you need to
-ifconfig the network interfaces up and add x25-routes to them. Use
+ifconfig the network interfaces up and add X.25-routes to them. Use
the usual ifconfig tool.
ifconfig <iface-name> up
But a special x25route tool (distributed with the x25-util package)
-is needed to set up x25 routes. I.e.
+is needed to set up X.25 routes. I.e.
x25route add 01 <iface-name>
-will cause all x.25 connections to the destination x.25-address
+will cause all x.25 connections to the destination X.25-address
"01" to be routed to your created isdn network interface.
-
-There are currently no real x25 applications available. However, for
+There are currently no real X.25 applications available. However, for
tests, the x25-utils package contains a modified version of telnet
-and telnetd that uses x25 sockets instead of tcp/ip sockets. Use
-this for your first tests. Furthermore, there is an x25.echod and a client
-named "eftp" (which contains some experimental code to download files
-from a remote eft server using the EUROfile transfer protocol).
-It available at ftp://ftp.hamburg.pop.de/pub/LOCAL/linux/i4l-eft/eftp4linux-*
+and telnetd that uses X.25 sockets instead of tcp/ip sockets. You can
+use those for your first tests. Furthermore, you might check
+ftp://ftp.hamburg.pop.de/pub/LOCAL/linux/i4l-eft/ which contains some
+alpha-test implementation ("eftp4linux") of the EUROfile transfer
+protocol.
+
+The scripts distributed with the eftp4linux test releases might also
+provide useful examples for setting up X.25 on top of isdn.
The x25-utility package also contains an x25trace tool that can be
-used to monitor x25 packets received by the network interfaces.
+used to monitor X.25 packets received by the network interfaces.
The /proc/net/x25* files also contain useful information.
-The eftp4linux test release also contains an "ix25test" script that can
-be used for testing x25 on top of isdn4linux. Edit
-this script according to your local needs and then call it as
-
-ix25test start
-
-This will set up a sample configuration using the isdnloop and hisax
-driver and create some isdn network interfaces.
-It is recommended that all other isdn drivers and the
-x25 module are unloaded before calling this script.
-
-
-
-Known problems and deficiencies:
-
-The isdnloop HL driver apparently has problems to re-establish a
-connection that has been hung up from the outgoing device. You have to
-unload the isdnloop driver after the faked isdn-connection is closed
-and insmod it again. With the Hisax driver, this problem is not present.
-
-Sometimes the x25 module cannot be unloaded (decrementation of its use
-count seems to get lost occasionally).
-
-Using the x25 based telnet and telnetd programm to establish connection
-from your own to your own computer repeatedly sometimes totally locked
-up my system. However, this kernel patch also modifies
-net/x25/af_x25.c to include a workaround. With this workaround
-enabled, my system is stable. (If you want to disable the
-workaround, just undefine ISDN_X25_FIXES in af_x25.c).
-
-The latter problem could be reproduced by using hisax as well as the
-isdnloop driver. It seems that it is not caused by the isdn code.
-Somehow, the inode of a socket is freed while a process still refers
-the socket's wait queue. This causes problems when the process tries to
-remove itself from the wait queue (referred by the dangling
-sock->sleep pointer) before returning from a select() system call.
-
- Henner
-
VERSION = 2
PATCHLEVEL = 3
-SUBLEVEL = 2
+SUBLEVEL = 4
EXTRAVERSION =
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
- If you install the full sources, do a
cd /usr/src
- gzip -cd linux-2.3.XX.tar.gz | tar xfv -
+ gzip -cd linux-2.3.XX.tar.gz | tar xvf -
to get it all put in place. Replace "XX" with the version number of the
latest kernel.
static struct file * init_fd_array[NR_OPEN] = { NULL, };
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS;
-struct mm_struct init_mm = INIT_MM;
+struct mm_struct init_mm = INIT_MM(init_mm);
union task_union init_task_union __attribute__((section("init_task")))
- = { task: INIT_TASK };
+ = { task: INIT_TASK(init_task_union.task) };
/*
* No need to acquire the kernel lock, we're entirely local..
(current->uid != child->uid) ||
(current->gid != child->egid) ||
(current->gid != child->sgid) ||
- (current->gid != child->gid))
+ (current->gid != child->gid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)))
&& !capable(CAP_SYS_PTRACE))
goto out;
/* the same process cannot be attached many times */
(current->uid != child->uid) ||
(current->gid != child->egid) ||
(current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
(current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
goto out;
/* the same process cannot be attached many times */
CONFIG_USB_KBD=y
# CONFIG_USB_AUDIO is not set
# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
#
# Filesystems
(current->uid != child->uid) ||
(current->gid != child->egid) ||
(current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
(current->gid != child->gid)) &&
!capable(CAP_SYS_PTRACE)) {
res = -EPERM;
CFLAGS := $(CFLAGS) -mcpu=860
endif
+ifdef CONFIG_PPC64
+CFLAGS := $(CFLAGS) -Wa,-mppc64bridge #-Wa,-mppc64
+#CFLAGS := $(CFLAGS) -Wa,-mppc64 -mpowerpc64
+endif
HEAD := arch/ppc/kernel/head.o
ISZ = 0
ifeq ($(CONFIG_SMP),y)
-TFTPIMAGE=/tftpboot/zImage.prep.smp
+TFTPIMAGE=/tftpboot/zImage.prep.smp$(MSIZE)
else
-TFTPIMAGE=/tftpboot/zImage.prep
+TFTPIMAGE=/tftpboot/zImage.prep$(MSIZE)
+endif
+
+ifeq ($(CONFIG_PPC64),y)
+MSIZE=.64
+else
+MSIZE=
endif
ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000
/*
* misc.c
*
- * $Id: misc.c,v 1.64 1999/04/30 05:52:46 cort Exp $
+ * $Id: misc.c,v 1.65 1999/05/17 19:11:13 cort Exp $
*
* Adapted for PowerPC by Gary Thomas
*
if (board_type == 0xe0) {
base_mod = inb(0x803);
/* if a MVME2300/2400 or a Sitka then no keyboard */
- if((base_mod == 0x9) || (base_mod == 0xF9) ||
+ if((base_mod == 0xFA) || (base_mod == 0xF9) ||
(base_mod == 0xE1)) {
keyb_present = 0; /* no keyboard */
}
OBJS = crt0.o start.o main.o misc.o ../coffboot/string.o ../coffboot/zlib.o image.o # initrd.o
LIBS = $(TOPDIR)/lib/lib.a
+ifeq ($(CONFIG_PPC64),y)
+MSIZE=.64
+else
+MSIZE=
+endif
+
ifeq ($(CONFIG_ALL_PPC),y)
# yes, we want to build chrp stuff
CONFIG_CHRP = y
endif
ifeq ($(CONFIG_SMP),y)
-TFTPIMAGE=/tftpboot/zImage.chrp.smp
+TFTPIMAGE=/tftpboot/zImage.chrp.smp$(MSIZE)
else
-TFTPIMAGE=/tftpboot/zImage.chrp
+TFTPIMAGE=/tftpboot/zImage.chrp$(MSIZE)
endif
all: $(TOPDIR)/zImage
CONFIG_PMAC = y
endif
+ifeq ($(CONFIG_PPC64),y)
+MSIZE=.64
+else
+MSIZE=
+endif
+
ifeq ($(CONFIG_SMP),y)
-TFTPIMAGE=/tftpboot/zImage.pmac.smp
+TFTPIMAGE=/tftpboot/zImage.pmac.smp$(MSIZE)
else
-TFTPIMAGE=/tftpboot/zImage.pmac
+TFTPIMAGE=/tftpboot/zImage.pmac$(MSIZE)
endif
ifeq ($(CONFIG_PMAC),y)
#
CONFIG_PPC=y
CONFIG_6xx=y
+# CONFIG_PPC64 is not set
# CONFIG_8xx is not set
-# CONFIG_PMAC is not set
+CONFIG_PMAC=y
# CONFIG_PREP is not set
# CONFIG_CHRP is not set
-CONFIG_ALL_PPC=y
+# CONFIG_ALL_PPC is not set
# CONFIG_APUS is not set
# CONFIG_MBX is not set
# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_6xx=y
#
# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_MODULES=y
-# CONFIG_MODVERSIONS is not set
+CONFIG_MODVERSIONS=y
CONFIG_KMOD=y
CONFIG_PCI=y
# CONFIG_PCI_QUIRKS is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_BINFMT_ELF=y
CONFIG_KERNEL_ELF=y
-CONFIG_BINFMT_MISC=m
+# CONFIG_BINFMT_MISC is not set
# CONFIG_BINFMT_JAVA is not set
# CONFIG_PARPORT is not set
CONFIG_VGA_CONSOLE=y
# CONFIG_TOTALMP is not set
CONFIG_BOOTX_TEXT=y
# CONFIG_MOTOROLA_HOTSWAP is not set
-# CONFIG_CMDLINE_BOOL is not set
#
# Plug and Play support
#
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
CONFIG_BLK_DEV_IDECD=y
+# CONFIG_IDECD_SLOTS is not set
# CONFIG_BLK_DEV_IDETAPE is not set
CONFIG_BLK_DEV_IDEFLOPPY=y
# CONFIG_BLK_DEV_IDESCSI is not set
# CONFIG_BLK_DEV_IDEPCI is not set
# CONFIG_BLK_DEV_SL82C105 is not set
CONFIG_BLK_DEV_IDE_PMAC=y
-# CONFIG_BLK_DEV_IDEDMA_PMAC is not set
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_PMAC_IDEDMA_AUTO=y
# CONFIG_IDE_CHIPSETS is not set
#
# Additional Block Devices
#
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
CONFIG_BLK_DEV_RAM=y
# CONFIG_BLK_DEV_XD is not set
CONFIG_PARIDE_PARPORT=y
# CONFIG_PARIDE is not set
+CONFIG_BLK_DEV_IDE_MODES=y
# CONFIG_BLK_DEV_HD is not set
#
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
-CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SG is not set
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
-CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SCSI_CONSTANTS=y
# CONFIG_SCSI_LOGGING is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_SYM53C416 is not set
# CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX is not set
# CONFIG_SCSI_SYM53C8XX is not set
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_PCI2000 is not set
# CONFIG_SCSI_PCI2220I is not set
# CONFIG_ACENIC is not set
# CONFIG_NET_ISA is not set
CONFIG_NET_EISA=y
-CONFIG_PCNET32=y
+# CONFIG_PCNET32 is not set
# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
CONFIG_FB_CONTROL=y
CONFIG_FB_PLATINUM=y
CONFIG_FB_VALKYRIE=y
-# CONFIG_FB_ATY is not set
+CONFIG_FB_ATY=y
CONFIG_FB_IMSTT=y
CONFIG_FB_CT65550=y
# CONFIG_FB_S3TRIO is not set
-CONFIG_FB_MATROX=y
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-CONFIG_FB_MATROX_G100=y
-# CONFIG_FB_MATROX_MULTIHEAD is not set
-# CONFIG_FB_ATY is not set
+# CONFIG_FB_MATROX is not set
+CONFIG_FB_ATY=y
# CONFIG_FB_VIRTUAL is not set
-CONFIG_FBCON_ADVANCED=y
-# CONFIG_FBCON_MFB is not set
-# CONFIG_FBCON_CFB2 is not set
-# CONFIG_FBCON_CFB4 is not set
+# CONFIG_FBCON_ADVANCED is not set
CONFIG_FBCON_CFB8=y
CONFIG_FBCON_CFB16=y
CONFIG_FBCON_CFB24=y
CONFIG_FBCON_CFB32=y
-# CONFIG_FBCON_AFB is not set
-# CONFIG_FBCON_ILBM is not set
-# CONFIG_FBCON_IPLAN2P2 is not set
-# CONFIG_FBCON_IPLAN2P4 is not set
-# CONFIG_FBCON_IPLAN2P8 is not set
-# CONFIG_FBCON_MAC is not set
-# CONFIG_FBCON_VGA is not set
# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
CONFIG_FBCON_FONTS=y
# CONFIG_FONT_8x8 is not set
#
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=m
+# CONFIG_SERIAL is not set
# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
-CONFIG_MOUSE=y
-
-#
-# Mice
-#
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_BUSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-CONFIG_PSMOUSE=y
-# CONFIG_82C710_MOUSE is not set
-# CONFIG_PC110_PAD is not set
+# CONFIG_MOUSE is not set
# CONFIG_QIC02_TAPE is not set
# CONFIG_WATCHDOG is not set
-# CONFIG_NVRAM is not set
+CONFIG_NVRAM=y
# CONFIG_RTC is not set
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
CONFIG_HFS_FS=y
-CONFIG_FAT_FS=m
-CONFIG_MSDOS_FS=m
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
# CONFIG_UMSDOS_FS is not set
-CONFIG_VFAT_FS=m
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
# CONFIG_MINIX_FS is not set
#
CONFIG_MAGIC_SYSRQ=y
# CONFIG_KGDB is not set
-# CONFIG_XMON is not set
+CONFIG_XMON=y
-# $Id: config.in,v 1.92 1999/04/30 05:41:43 cort Exp $
+# $Id: config.in,v 1.93 1999/05/14 22:36:58 cort Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
comment 'Platform support'
define_bool CONFIG_PPC y
choice 'Processor type' \
- "6xx/7xx CONFIG_6xx \
- 860/821 CONFIG_8xx" 6xx/7xx
+ "6xx/7xx CONFIG_6xx \
+ 630/Power3(64-Bit) CONFIG_PPC64 \
+ 860/821 CONFIG_8xx" 6xx/7xx
choice 'Machine Type' \
"PowerMac CONFIG_PMAC \
if [ "$CONFIG_ALL_PPC" != "y" ];then
define_bool CONFIG_MACH_SPECIFIC y
fi
+
+if [ "$CONFIG_PPC64" != "y" ];then
+ define_bool CONFIG_6xx y
+fi
endmenu
if [ "$CONFIG_MBX" = "y" ];then
#
CONFIG_PPC=y
CONFIG_6xx=y
+# CONFIG_PPC64 is not set
# CONFIG_8xx is not set
-# CONFIG_PMAC is not set
+CONFIG_PMAC=y
# CONFIG_PREP is not set
# CONFIG_CHRP is not set
-CONFIG_ALL_PPC=y
+# CONFIG_ALL_PPC is not set
# CONFIG_APUS is not set
# CONFIG_MBX is not set
# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_6xx=y
#
# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_MODULES=y
-# CONFIG_MODVERSIONS is not set
+CONFIG_MODVERSIONS=y
CONFIG_KMOD=y
CONFIG_PCI=y
# CONFIG_PCI_QUIRKS is not set
# CONFIG_BSD_PROCESS_ACCT is not set
CONFIG_BINFMT_ELF=y
CONFIG_KERNEL_ELF=y
-CONFIG_BINFMT_MISC=m
+# CONFIG_BINFMT_MISC is not set
# CONFIG_BINFMT_JAVA is not set
# CONFIG_PARPORT is not set
CONFIG_VGA_CONSOLE=y
# CONFIG_TOTALMP is not set
CONFIG_BOOTX_TEXT=y
# CONFIG_MOTOROLA_HOTSWAP is not set
-# CONFIG_CMDLINE_BOOL is not set
#
# Plug and Play support
#
# CONFIG_BLK_DEV_HD_IDE is not set
CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
CONFIG_BLK_DEV_IDECD=y
+# CONFIG_IDECD_SLOTS is not set
# CONFIG_BLK_DEV_IDETAPE is not set
CONFIG_BLK_DEV_IDEFLOPPY=y
# CONFIG_BLK_DEV_IDESCSI is not set
# CONFIG_BLK_DEV_IDEPCI is not set
# CONFIG_BLK_DEV_SL82C105 is not set
CONFIG_BLK_DEV_IDE_PMAC=y
-# CONFIG_BLK_DEV_IDEDMA_PMAC is not set
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_PMAC_IDEDMA_AUTO=y
# CONFIG_IDE_CHIPSETS is not set
#
# Additional Block Devices
#
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_MD is not set
CONFIG_BLK_DEV_RAM=y
# CONFIG_BLK_DEV_XD is not set
CONFIG_PARIDE_PARPORT=y
# CONFIG_PARIDE is not set
+CONFIG_BLK_DEV_IDE_MODES=y
# CONFIG_BLK_DEV_HD is not set
#
CONFIG_CHR_DEV_ST=y
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
-CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SG is not set
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
-CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_MULTI_LUN is not set
CONFIG_SCSI_CONSTANTS=y
# CONFIG_SCSI_LOGGING is not set
# CONFIG_SCSI_NCR53C406A is not set
# CONFIG_SCSI_SYM53C416 is not set
# CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
+# CONFIG_SCSI_NCR53C8XX is not set
# CONFIG_SCSI_SYM53C8XX is not set
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
# CONFIG_SCSI_PAS16 is not set
# CONFIG_SCSI_PCI2000 is not set
# CONFIG_SCSI_PCI2220I is not set
# CONFIG_ACENIC is not set
# CONFIG_NET_ISA is not set
CONFIG_NET_EISA=y
-CONFIG_PCNET32=y
+# CONFIG_PCNET32 is not set
# CONFIG_AC3200 is not set
# CONFIG_APRICOT is not set
# CONFIG_CS89x0 is not set
CONFIG_FB_CONTROL=y
CONFIG_FB_PLATINUM=y
CONFIG_FB_VALKYRIE=y
-# CONFIG_FB_ATY is not set
+CONFIG_FB_ATY=y
CONFIG_FB_IMSTT=y
CONFIG_FB_CT65550=y
# CONFIG_FB_S3TRIO is not set
-CONFIG_FB_MATROX=y
-CONFIG_FB_MATROX_MILLENIUM=y
-CONFIG_FB_MATROX_MYSTIQUE=y
-CONFIG_FB_MATROX_G100=y
-# CONFIG_FB_MATROX_MULTIHEAD is not set
-# CONFIG_FB_ATY is not set
+# CONFIG_FB_MATROX is not set
+CONFIG_FB_ATY=y
# CONFIG_FB_VIRTUAL is not set
-CONFIG_FBCON_ADVANCED=y
-# CONFIG_FBCON_MFB is not set
-# CONFIG_FBCON_CFB2 is not set
-# CONFIG_FBCON_CFB4 is not set
+# CONFIG_FBCON_ADVANCED is not set
CONFIG_FBCON_CFB8=y
CONFIG_FBCON_CFB16=y
CONFIG_FBCON_CFB24=y
CONFIG_FBCON_CFB32=y
-# CONFIG_FBCON_AFB is not set
-# CONFIG_FBCON_ILBM is not set
-# CONFIG_FBCON_IPLAN2P2 is not set
-# CONFIG_FBCON_IPLAN2P4 is not set
-# CONFIG_FBCON_IPLAN2P8 is not set
-# CONFIG_FBCON_MAC is not set
-# CONFIG_FBCON_VGA is not set
# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
CONFIG_FBCON_FONTS=y
# CONFIG_FONT_8x8 is not set
#
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
-CONFIG_SERIAL=m
+# CONFIG_SERIAL is not set
# CONFIG_SERIAL_EXTENDED is not set
# CONFIG_SERIAL_NONSTANDARD is not set
CONFIG_UNIX98_PTYS=y
CONFIG_UNIX98_PTY_COUNT=256
-CONFIG_MOUSE=y
-
-#
-# Mice
-#
-# CONFIG_ATIXL_BUSMOUSE is not set
-# CONFIG_BUSMOUSE is not set
-# CONFIG_MS_BUSMOUSE is not set
-CONFIG_PSMOUSE=y
-# CONFIG_82C710_MOUSE is not set
-# CONFIG_PC110_PAD is not set
+# CONFIG_MOUSE is not set
# CONFIG_QIC02_TAPE is not set
# CONFIG_WATCHDOG is not set
-# CONFIG_NVRAM is not set
+CONFIG_NVRAM=y
# CONFIG_RTC is not set
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
CONFIG_HFS_FS=y
-CONFIG_FAT_FS=m
-CONFIG_MSDOS_FS=m
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
# CONFIG_UMSDOS_FS is not set
-CONFIG_VFAT_FS=m
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
CONFIG_ISO9660_FS=y
# CONFIG_JOLIET is not set
# CONFIG_MINIX_FS is not set
#
CONFIG_MAGIC_SYSRQ=y
# CONFIG_KGDB is not set
-# CONFIG_XMON is not set
+CONFIG_XMON=y
$(HOSTCC) -o find_name find_name.c
checks: checks.c
- $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c
+ $(HOSTCC) ${CFLAGS} -D__KERNEL__ -o checks checks.c
./checks
include $(TOPDIR)/Rules.make
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/openpic.h>
+#include <linux/ide.h>
#include <asm/io.h>
#include <asm/pgtable.h>
if ( !strncmp("MOT", get_property(find_path_device("/"),
"model", NULL),3) )
*memory_start_p = pmac_find_bridges(*memory_start_p, *memory_end_p);
- /*
- * The f50 has a lot of IO space - we need to map some in that
- * isn't covered by the BAT mappings in MMU_init() -- Cort
- */
- if ( !strncmp("F5", get_property(find_path_device("/"),
- "ibm,model-class", NULL),2) )
- {
-#if 0
- /*
- * This ugly hack allows us to force ioremap() to
- * create a 1-to-1 mapping for us, even though
- * the address is < ioremap_base. This is necessary
- * since we want our PCI IO space to have contiguous
- * virtual addresses and I think it's worse to have
- * calls to map_page() here.
- * -- Cort
- */
- unsigned long hold = ioremap_base;
- ioremap_base = 0;
- __ioremap(0x90000000, 0x10000000, _PAGE_NO_CACHE);
- ioremap_base = hold;
-#endif
- }
}
void
/*
* arch/ppc/kernel/head.S
*
- * $Id: head.S,v 1.130 1999/05/09 19:16:43 cort Exp $
+ * $Id: head.S,v 1.131 1999/05/14 22:37:21 cort Exp $
*
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
bdnz 0b
#endif
+#ifdef CONFIG_PPC64
+#define LOAD_BAT(n, offset, reg, RA, RB) \
+ ld RA,offset+0(reg); \
+ ld RB,offset+8(reg); \
+ mtspr IBAT##n##U,RA; \
+ mtspr IBAT##n##L,RB; \
+ ld RA,offset+16(reg); \
+ ld RB,offset+24(reg); \
+ mtspr DBAT##n##U,RA; \
+ mtspr DBAT##n##L,RB; \
+
+#else /* CONFIG_PPC64 */
+
/* 601 only have IBAT cr0.eq is set on 601 when using this macro */
#define LOAD_BAT(n, offset, reg, RA, RB) \
- lwz RA,offset+0(reg); \
+ lwz RA,offset+0(reg); \
lwz RB,offset+4(reg); \
- mtspr IBAT##n##U,RA; \
- mtspr IBAT##n##L,RB; \
- beq 1f; \
+ mtspr IBAT##n##U,RA; \
+ mtspr IBAT##n##L,RB; \
+ beq 1f; \
lwz RA,offset+8(reg); \
lwz RB,offset+12(reg); \
- mtspr DBAT##n##U,RA; \
- mtspr DBAT##n##L,RB; \
-1:
+ mtspr DBAT##n##U,RA; \
+ mtspr DBAT##n##L,RB; \
+1:
+#endif /* CONFIG_PPC64 */
#ifndef CONFIG_APUS
#define tophys(rd,rs,rt) addis rd,rs,-KERNELBASE@h
.globl __start
__start:
+#ifdef CONFIG_PPC64
+/*
+ * Go into 32-bit mode to boot. OF should do this for
+ * us already but just in case...
+ * -- Cort
+ */
+ mfmsr r10
+ clrldi r10,r10,3
+ mtmsr r10
+#endif
/*
* We have to do any OF calls before we map ourselves to KERNELBASE,
* because OF may have I/O devices mapped in in that area
* of RAM to KERNELBASE. From this point on we can't safely
* call OF any more.
*/
+ lis r11,KERNELBASE@h
+#ifndef CONFIG_PPC64
mfspr r9,PVR
rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */
cmpi 0,r9,1
- lis r11,KERNELBASE@h
bne 4f
ori r11,r11,4 /* set up BAT registers for 601 */
li r8,0x7f /* valid, block length = 8MB */
mtspr IBAT1U,r9
mtspr IBAT1L,r10
b 5f
+#endif /* CONFIG_PPC64 */
4:
#ifdef CONFIG_APUS
ori r11,r11,BL_8M<<2|0x2 /* set up an 8MB mapping */
lwz r8,0(r8)
addis r8,r8,KERNELBASE@h
addi r8,r8,2
-#else
+#else
ori r11,r11,BL_256M<<2|0x2 /* set up BAT registers for 604 */
li r8,2 /* R/W access */
+#ifdef CONFIG_PPC64
+ /* clear out the high 32 bits in the BAT */
+ clrldi r11,r11,32
+ clrldi r8,r8,32
+ /* turn off the pagetable mappings just in case */
+ clrldi r16,r16,63
+ mtsdr1 r16
+#else /* CONFIG_PPC64 */
/*
* allow secondary cpus to get at all of ram in early bootup
* since their init_task may be up there -- Cort
mtspr DBAT2U,r21 /* bit in upper BAT register */
mtspr IBAT2L,r28
mtspr IBAT2U,r21
+#endif /* CONFIG_PPC64 */
#endif
mtspr DBAT0L,r8 /* N.B. 6xx (not 601) have valid */
mtspr DBAT0U,r11 /* bit in upper BAT register */
eieio
lis r2,hash_table_lock@h
ori r2,r2,hash_table_lock@l
- tophys(r2,r2,r6)
+ tophys(r2,r2,r6)
lis r6,100000000@h
mtctr r6
lwz r0,PROCESSOR-TSS(r5)
stw r6,0(r2) /* update PTE (accessed/dirty bits) */
/* Convert linux-style PTE to low word of PPC-style PTE */
+#ifdef CONFIG_PPC64
+ /* clear the high 32 bits just in case */
+ clrldi r6,r6,32
+ clrldi r4,r4,32
+#endif /* CONFIG_PPC64 */
rlwinm r4,r6,32-9,31,31 /* _PAGE_HWWRITE -> PP lsb */
rlwimi r6,r6,32-1,31,31 /* _PAGE_USER -> PP (both bits now) */
ori r4,r4,0xe04 /* clear out reserved bits */
/* Construct the high word of the PPC-style PTE */
mfsrin r5,r3 /* get segment reg for segment */
+#ifdef CONFIG_PPC64
+ sldi r5,r5,12
+#else /* CONFIG_PPC64 */
rlwinm r5,r5,7,1,24 /* put VSID in 0x7fffff80 bits */
+#endif /* CONFIG_PPC64 */
+
#ifndef __SMP__ /* do this later for SMP */
+#ifdef CONFIG_PPC64
+ ori r5,r5,1 /* set V (valid) bit */
+#else /* CONFIG_PPC64 */
oris r5,r5,0x8000 /* set V (valid) bit */
+#endif /* CONFIG_PPC64 */
#endif
+
+#ifdef CONFIG_PPC64
+/* XXX: does this insert the api correctly? -- Cort */
+ rlwimi r5,r3,17,21,25 /* put in API (abbrev page index) */
+#else /* CONFIG_PPC64 */
rlwimi r5,r3,10,26,31 /* put in API (abbrev page index) */
-
+#endif /* CONFIG_PPC64 */
/* Get the address of the primary PTE group in the hash table */
.globl hash_page_patch_A
hash_page_patch_A:
lis r4,Hash_base@h /* base address of hash table */
+#ifdef CONFIG_PPC64
+ /* just in case */
+ clrldi r4,r4,32
+#endif
rlwimi r4,r5,32-1,26-Hash_bits,25 /* (VSID & hash_mask) << 6 */
rlwinm r0,r3,32-6,26-Hash_bits,25 /* (PI & hash_mask) << 6 */
xor r4,r4,r0 /* make primary hash */
*/
#ifndef CONFIG_8xx
lis r6,_SDR1@ha
+#ifdef CONFIG_PPC64
+ ld r6,_SDR1@l(r6)
+#else
lwz r6,_SDR1@l(r6)
+#endif
#else
/* The right way to do this would be to track it down through
* init's TSS like the context switch code does, but this is
#endif
#ifndef CONFIG_8xx
mtspr SDR1,r6
+#ifdef CONFIG_PPC64
+ /* clear the v bit in the ASR so we can
+ * behave as if we have segment registers
+ * -- Cort
+ */
+ clrldi r6,r6,63
+ mtasr r6
+#endif /* CONFIG_PPC64 */
li r0,16 /* load up segment register values */
mtctr r0 /* for context 0 */
lis r3,0x2000 /* Ku = 1, VSID = 0 */
lis r3,BATS@ha
addi r3,r3,BATS@l
tophys(r3,r3,r4)
+#ifdef CONFIG_PPC64
+ LOAD_BAT(0,0,r3,r4,r5)
+ LOAD_BAT(1,32,r3,r4,r5)
+ LOAD_BAT(2,64,r3,r4,r5)
+ LOAD_BAT(3,96,r3,r4,r5)
+#else /* CONFIG_PPC64 */
LOAD_BAT(0,0,r3,r4,r5)
LOAD_BAT(1,16,r3,r4,r5)
LOAD_BAT(2,32,r3,r4,r5)
LOAD_BAT(3,48,r3,r4,r5)
+#endif /* CONFIG_PPC64 */
#endif /* CONFIG_8xx */
/* Set up for using our exception vectors */
/* ptr to phys current tss */
/*
- * $Id: mbx_setup.c,v 1.9 1999/04/28 11:54:09 davem Exp $
+ * $Id: mbx_setup.c,v 1.10 1999/05/14 07:24:19 davem Exp $
*
* linux/arch/ppc/kernel/setup.c
*
mtmsr r0 /* Update machine state */
blr
-/*
- * Fetch the current SR register
- * get_SR(int index)
- */
-_GLOBAL(get_SR)
- mfsrin r4,r3
- mr r3,r4
- blr
-
/*
* Create a kernel thread
* __kernel_thread(flags, fn, arg)
#include <linux/blk.h>
#include <linux/vt_kern.h>
#include <linux/console.h>
+#include <linux/ide.h>
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/pgtable.h>
ppc_generic_ide_fix_driveid(id);
}
+#if defined(CONFIG_BLK_DEV_IDE_PMAC)
/* This is declared in drivers/block/ide-pmac.c */
void pmac_ide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq);
-
+#else
/*
* This registers the standard ports for this architecture with the IDE
* driver.
*/
-void
-ide_init_default_hwifs(void)
+void pmac_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
{
}
#endif
+#endif
__initfunc(void
pmac_init(unsigned long r3, unsigned long r4, unsigned long r5,
/*
- * $Id: prep_pci.c,v 1.33 1999/05/09 20:15:54 cort Exp $
+ * $Id: prep_pci.c,v 1.35 1999/05/10 23:31:03 cort Exp $
* PReP pci functions.
* Originally by Gary Thomas
* rewritten and updated by Cort Dougan (cort@cs.nmt.edu)
/* Used for Motorola to store system config register */
static unsigned long *ProcInfo;
+extern void chrp_do_IRQ(struct pt_regs *,int , int);
+
/* Tables for known hardware */
/* Motorola PowerStackII - Utah */
OpenPIC_InitSenses = mvme2600_openpic_initsenses;
OpenPIC_NumInitSenses = sizeof(mvme2600_openpic_initsenses);
+ ppc_md.do_IRQ = chrp_do_IRQ;
+
/* If raven is present on Motorola store the system config register
* for later use.
*/
#include <linux/timex.h>
#include <linux/pci.h>
#include <linux/openpic.h>
+#include <linux/ide.h>
#include <asm/mmu.h>
#include <asm/processor.h>
case _PREP_Motorola:
/* Enable L2. Assume we don't need to flush -- Cort*/
*(unsigned char *)(0x8000081c) |= 3;
- ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
+ ROOT_DEV = to_kdev_t(0x0802); /* sda2 */
break;
case _PREP_Radstone:
ROOT_DEV = to_kdev_t(0x0801); /* sda1 */
ppc_md.get_cpuinfo = prep_get_cpuinfo;
ppc_md.irq_cannonicalize = prep_irq_cannonicalize;
ppc_md.init_IRQ = prep_init_IRQ;
- if ( !OpenPIC )
- ppc_md.do_IRQ = prep_do_IRQ;
- else
- ppc_md.do_IRQ = chrp_do_IRQ;
+ /* this gets changed later on if we have an OpenPIC -- Cort */
+ ppc_md.do_IRQ = prep_do_IRQ;
ppc_md.init = NULL;
ppc_md.restart = prep_restart;
/*
- * $Id: process.c,v 1.83 1999/05/10 04:43:43 cort Exp $
+ * $Id: process.c,v 1.85 1999/05/16 21:27:08 cort Exp $
*
* linux/arch/ppc/kernel/process.c
*
static struct file * init_fd_array[NR_OPEN] = { NULL, };
static struct files_struct init_files = INIT_FILES;
static struct signal_struct init_signals = INIT_SIGNALS;
-struct mm_struct init_mm = INIT_MM;
-union task_union init_task_union = { INIT_TASK };
+struct mm_struct init_mm = INIT_MM(init_mm);
+union task_union init_task_union = { INIT_TASK(init_task_union.task) };
/* only used to get secondary processor up */
struct task_struct *current_set[NR_CPUS] = {&init_task, };
out:
}
-void instruction_dump (unsigned long *pc)
-{
- int i;
-
- if((((unsigned long) pc) & 3))
- return;
-
- printk("Instruction DUMP:");
- for(i = -3; i < 6; i++)
- {
- unsigned long p;
- if (__get_user( p, &pc[i] ))
- break;
- printk("%c%08lx%c",i?' ':'<',p,i?' ':'>');
- }
- printk("\n");
-}
-
void exit_thread(void)
{
if (last_task_used_math == current)
if ((!child->dumpable ||
(current->uid != child->euid) ||
(current->uid != child->uid) ||
+ (current->uid != child->suid) ||
(current->gid != child->egid) ||
- (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE))
+ (current->gid != child->gid) ||
+ (current->gid != child->sgid) ||
+ (!cap_issubset(child->cap_permitted, current->cap_permitted)))
+ && !capable(CAP_SYS_PTRACE))
goto out;
/* the same process cannot be attached many times */
if (child->flags & PF_PTRACED)
/*
- * $Id: residual.c,v 1.14 1998/10/11 17:38:10 cort Exp $
+ * $Id: residual.c,v 1.15 1999/05/14 07:24:27 davem Exp $
*
* Code to deal with the PReP residual data.
*
/*
- * $Id: setup.c,v 1.132 1999/03/24 00:32:19 cort Exp $
+ * $Id: setup.c,v 1.133 1999/05/14 07:24:30 davem Exp $
* Common prep/pmac/chrp boot and setup code.
*/
/* Eventually we may need a look-up table, but this works for now.
*/
+#define LFS 48
#define LFD 50
#define LFDU 51
#define STFD 54
else
regs->gpr[idxreg] = (uint)ea;
break;
+ case LFS:
+ sdisp = (instword & 0xffff);
+ ea = (uint *)(regs->gpr[idxreg] + sdisp);
+ if (copy_from_user(ip, ea, sizeof(float)))
+ retval = EFAULT;
+ break;
case STFD:
/* this is a 16 bit quantity that is sign extended
* so use a signed short here -- Cort
lock_kernel();
if (!(flags & MAP_ANONYMOUS)) {
- file = fget(fd);
- if (!file)
+ if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
}
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
ret = do_mmap(file, addr, len, prot, flags, offset);
- if (file)
- fput(file);
out:
unlock_kernel();
return ret;
debugger(regs);
#endif
print_backtrace((unsigned long *)regs->gpr[1]);
- instruction_dump((unsigned long *)regs->nip);
panic("Exception in kernel pc %lx signal %d",regs->nip,signr);
}
force_sig(signr, current);
debugger(regs);
#endif
print_backtrace((unsigned long *)regs->gpr[1]);
- instruction_dump((unsigned long *)regs->nip);
panic("machine check");
}
_exception(SIGSEGV, regs);
#endif
show_regs(regs);
print_backtrace((unsigned long *)regs->gpr[1]);
- instruction_dump((unsigned long *)regs->nip);
panic("kernel stack overflow");
}
printk("page fault in interrupt handler, addr=%lx\n",
address);
show_regs(regs);
- instruction_dump((unsigned long *)regs->nip);
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
if (debugger_kernel_faults)
debugger(regs);
/* kernel has accessed a bad area */
show_regs(regs);
print_backtrace( (unsigned long *)regs->gpr[1] );
- instruction_dump((unsigned long *)regs->nip);
#if defined(CONFIG_XMON) || defined(CONFIG_KGDB)
if (debugger_kernel_faults)
debugger(regs);
/*
- * $Id: init.c,v 1.164 1999/05/05 17:33:55 cort Exp $
+ * $Id: init.c,v 1.165 1999/05/14 22:37:29 cort Exp $
*
* PowerPC version
* Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
for (h = 256<<10; h < ramsize / 256 && h < 4<<20; h *= 2, Hash_mask++)
;
Hash_size = h;
- Hash_mask << 10; /* so setting _SDR1 works the same -- Cort */
+ Hash_mask <<= 10; /* so setting _SDR1 works the same -- Cort */
#else
for (h = 64<<10; h < ramsize / 256 && h < 2<<20; h *= 2)
;
--- /dev/null
+# Makefile for xmon
+
+O_TARGET = x.o
+O_OBJS = start.o xmon.o ppc-dis.o ppc-opc.o subr_prf.o setjmp.o
+
+include $(TOPDIR)/Rules.make
--- /dev/null
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include "nonstdio.h"
+#include "privinst.h"
+
+#define scanhex xmon_scanhex
+#define skipbl xmon_skipbl
+
+#define ADB_B (*(volatile unsigned char *)0xf3016000)
+#define ADB_SR (*(volatile unsigned char *)0xf3017400)
+#define ADB_ACR (*(volatile unsigned char *)0xf3017600)
+#define ADB_IFR (*(volatile unsigned char *)0xf3017a00)
+
+static inline void eieio(void) { asm volatile ("eieio" : :); }
+
+#define N_ADB_LOG 1000
+struct adb_log {
+ unsigned char b;
+ unsigned char ifr;
+ unsigned char acr;
+ unsigned int time;
+} adb_log[N_ADB_LOG];
+int n_adb_log;
+
+void
+init_adb_log(void)
+{
+ adb_log[0].b = ADB_B;
+ adb_log[0].ifr = ADB_IFR;
+ adb_log[0].acr = ADB_ACR;
+ adb_log[0].time = get_dec();
+ n_adb_log = 0;
+}
+
+void
+dump_adb_log(void)
+{
+ unsigned t, t0;
+ struct adb_log *ap;
+ int i;
+
+ ap = adb_log;
+ t0 = ap->time;
+ for (i = 0; i <= n_adb_log; ++i, ++ap) {
+ t = t0 - ap->time;
+ printf("b=%x ifr=%x acr=%x at %d.%.7d\n", ap->b, ap->ifr, ap->acr,
+ t / 1000000000, (t % 1000000000) / 100);
+ }
+}
+
+void
+adb_chklog(void)
+{
+ struct adb_log *ap = &adb_log[n_adb_log + 1];
+
+ ap->b = ADB_B;
+ ap->ifr = ADB_IFR;
+ ap->acr = ADB_ACR;
+ if (ap->b != ap[-1].b || (ap->ifr & 4) != (ap[-1].ifr & 4)
+ || ap->acr != ap[-1].acr) {
+ ap->time = get_dec();
+ ++n_adb_log;
+ }
+}
+
+int
+adb_bitwait(int bmask, int bval, int fmask, int fval)
+{
+ int i;
+ struct adb_log *ap;
+
+ for (i = 10000; i > 0; --i) {
+ adb_chklog();
+ ap = &adb_log[n_adb_log];
+ if ((ap->b & bmask) == bval && (ap->ifr & fmask) == fval)
+ return 0;
+ }
+ return -1;
+}
+
+int
+adb_wait(void)
+{
+ if (adb_bitwait(0, 0, 4, 4) < 0) {
+ printf("adb: ready wait timeout\n");
+ return -1;
+ }
+ return 0;
+}
+
+void
+adb_readin(void)
+{
+ int i, j;
+ unsigned char d[64];
+
+ if (ADB_B & 8) {
+ printf("ADB_B: %x\n", ADB_B);
+ return;
+ }
+ i = 0;
+ adb_wait();
+ j = ADB_SR;
+ eieio();
+ ADB_B &= ~0x20;
+ eieio();
+ for (;;) {
+ if (adb_wait() < 0)
+ break;
+ d[i++] = ADB_SR;
+ eieio();
+ if (ADB_B & 8)
+ break;
+ ADB_B ^= 0x10;
+ eieio();
+ }
+ ADB_B |= 0x30;
+ if (adb_wait() == 0)
+ j = ADB_SR;
+ for (j = 0; j < i; ++j)
+ printf("%.2x ", d[j]);
+ printf("\n");
+}
+
+int
+adb_write(unsigned char *d, int i)
+{
+ int j;
+ unsigned x;
+
+ if ((ADB_B & 8) == 0) {
+ printf("r: ");
+ adb_readin();
+ }
+ for (;;) {
+ ADB_ACR = 0x1c;
+ eieio();
+ ADB_SR = d[0];
+ eieio();
+ ADB_B &= ~0x20;
+ eieio();
+ if (ADB_B & 8)
+ break;
+ ADB_ACR = 0xc;
+ eieio();
+ ADB_B |= 0x20;
+ eieio();
+ adb_readin();
+ }
+ adb_wait();
+ for (j = 1; j < i; ++j) {
+ ADB_SR = d[j];
+ eieio();
+ ADB_B ^= 0x10;
+ eieio();
+ if (adb_wait() < 0)
+ break;
+ }
+ ADB_ACR = 0xc;
+ eieio();
+ x = ADB_SR;
+ eieio();
+ ADB_B |= 0x30;
+ return j;
+}
+
+void
+adbcmds(void)
+{
+ char cmd;
+ unsigned rtcu, rtcl, dec, pdec, x;
+ int i, j;
+ unsigned char d[64];
+
+ cmd = skipbl();
+ switch (cmd) {
+ case 't':
+ for (;;) {
+ rtcl = get_rtcl();
+ rtcu = get_rtcu();
+ dec = get_dec();
+ printf("rtc u=%u l=%u dec=%x (%d = %d.%.7d)\n",
+ rtcu, rtcl, dec, pdec - dec, (pdec - dec) / 1000000000,
+ ((pdec - dec) % 1000000000) / 100);
+ pdec = dec;
+ if (cmd == 'x')
+ break;
+ while (xmon_read(stdin, &cmd, 1) != 1)
+ ;
+ }
+ break;
+ case 'r':
+ init_adb_log();
+ while (adb_bitwait(8, 0, 0, 0) == 0)
+ adb_readin();
+ break;
+ case 'w':
+ i = 0;
+ while (scanhex(&x))
+ d[i++] = x;
+ init_adb_log();
+ j = adb_write(d, i);
+ printf("sent %d bytes\n", j);
+ while (adb_bitwait(8, 0, 0, 0) == 0)
+ adb_readin();
+ break;
+ case 'l':
+ dump_adb_log();
+ break;
+ }
+}
--- /dev/null
+/* ANSI and traditional C compatability macros
+ Copyright 1991, 1992 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* ANSI and traditional C compatibility macros
+
+ ANSI C is assumed if __STDC__ is #defined.
+
+ Macro ANSI C definition Traditional C definition
+ ----- ---- - ---------- ----------- - ----------
+ PTR `void *' `char *'
+ LONG_DOUBLE `long double' `double'
+ VOLATILE `volatile' `'
+ SIGNED `signed' `'
+ PTRCONST `void *const' `char *'
+ ANSI_PROTOTYPES 1 not defined
+
+ CONST is also defined, but is obsolete. Just use const.
+
+ DEFUN (name, arglist, args)
+
+ Defines function NAME.
+
+ ARGLIST lists the arguments, separated by commas and enclosed in
+ parentheses. ARGLIST becomes the argument list in traditional C.
+
+ ARGS list the arguments with their types. It becomes a prototype in
+ ANSI C, and the type declarations in traditional C. Arguments should
+ be separated with `AND'. For functions with a variable number of
+ arguments, the last thing listed should be `DOTS'.
+
+ DEFUN_VOID (name)
+
+ Defines a function NAME, which takes no arguments.
+
+ obsolete -- EXFUN (name, (prototype)) -- obsolete.
+
+ Replaced by PARAMS. Do not use; will disappear someday soon.
+ Was used in external function declarations.
+ In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in
+ parentheses). In traditional C it is `NAME()'.
+ For a function that takes no arguments, PROTOTYPE should be `(void)'.
+
+ PARAMS ((args))
+
+ We could use the EXFUN macro to handle prototype declarations, but
+ the name is misleading and the result is ugly. So we just define a
+ simple macro to handle the parameter lists, as in:
+
+ static int foo PARAMS ((int, char));
+
+ This produces: `static int foo();' or `static int foo (int, char);'
+
+ EXFUN would have done it like this:
+
+ static int EXFUN (foo, (int, char));
+
+ but the function is not external...and it's hard to visually parse
+ the function name out of the mess. EXFUN should be considered
+ obsolete; new code should be written to use PARAMS.
+
+ For example:
+ extern int printf PARAMS ((CONST char *format DOTS));
+ int DEFUN(fprintf, (stream, format),
+ FILE *stream AND CONST char *format DOTS) { ... }
+ void DEFUN_VOID(abort) { ... }
+*/
+
+#ifndef _ANSIDECL_H
+
+#define _ANSIDECL_H 1
+
+
+/* Every source file includes this file,
+ so they will all get the switch for lint. */
+/* LINTLIBRARY */
+
+
+#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32)
+/* All known AIX compilers implement these things (but don't always
+ define __STDC__). The RISC/OS MIPS compiler defines these things
+ in SVR4 mode, but does not define __STDC__. */
+
+#define PTR void *
+#define PTRCONST void *CONST
+#define LONG_DOUBLE long double
+
+#define AND ,
+#define NOARGS void
+#define CONST const
+#define VOLATILE volatile
+#define SIGNED signed
+#define DOTS , ...
+
+#define EXFUN(name, proto) name proto
+#define DEFUN(name, arglist, args) name(args)
+#define DEFUN_VOID(name) name(void)
+
+#define PROTO(type, name, arglist) type name arglist
+#define PARAMS(paramlist) paramlist
+#define ANSI_PROTOTYPES 1
+
+#else /* Not ANSI C. */
+
+#define PTR char *
+#define PTRCONST PTR
+#define LONG_DOUBLE double
+
+#define AND ;
+#define NOARGS
+#define CONST
+#ifndef const /* some systems define it in header files for non-ansi mode */
+#define const
+#endif
+#define VOLATILE
+#define SIGNED
+#define DOTS
+
+#define EXFUN(name, proto) name()
+#define DEFUN(name, arglist, args) name arglist args;
+#define DEFUN_VOID(name) name()
+#define PROTO(type, name, arglist) type name ()
+#define PARAMS(paramlist) ()
+
+#endif /* ANSI C. */
+
+#endif /* ansidecl.h */
--- /dev/null
+typedef int FILE;
+extern FILE *xmon_stdin, *xmon_stdout;
+#define EOF (-1)
+#define stdin xmon_stdin
+#define stdout xmon_stdout
+#define printf xmon_printf
+#define fprintf xmon_fprintf
+#define fputs xmon_fputs
+#define fgets xmon_fgets
+#define putchar xmon_putchar
+#define getchar xmon_getchar
+#define putc xmon_putc
+#define getc xmon_getc
+#define fopen(n, m) NULL
+#define fflush(f) do {} while (0)
+#define fclose(f) do {} while (0)
+extern char *fgets(char *, int, void *);
+extern void xmon_printf(const char *, ...);
+extern void xmon_fprintf(void *, const char *, ...);
+extern void xmon_sprintf(char *, const char *, ...);
+
+#define perror(s) printf("%s: no files!\n", (s))
--- /dev/null
+/* ppc-dis.c -- Disassemble PowerPC instructions
+ Copyright 1994 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "nonstdio.h"
+#include "ansidecl.h"
+#include "ppc.h"
+
+static int print_insn_powerpc PARAMS ((FILE *, unsigned long insn,
+ unsigned memaddr, int dialect));
+
+extern void print_address PARAMS((unsigned memaddr));
+
+/* Print a big endian PowerPC instruction. For convenience, also
+ disassemble instructions supported by the Motorola PowerPC 601. */
+
+int
+print_insn_big_powerpc (FILE *out, unsigned long insn, unsigned memaddr)
+{
+ return print_insn_powerpc (out, insn, memaddr,
+ PPC_OPCODE_PPC | PPC_OPCODE_601);
+}
+
+/* Print a PowerPC or POWER instruction. */
+
+static int
+print_insn_powerpc (FILE *out, unsigned long insn, unsigned memaddr,
+ int dialect)
+{
+ const struct powerpc_opcode *opcode;
+ const struct powerpc_opcode *opcode_end;
+ unsigned long op;
+
+ /* Get the major opcode of the instruction. */
+ op = PPC_OP (insn);
+
+ /* Find the first match in the opcode table. We could speed this up
+ a bit by doing a binary search on the major opcode. */
+ opcode_end = powerpc_opcodes + powerpc_num_opcodes;
+ for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
+ {
+ unsigned long table_op;
+ const unsigned char *opindex;
+ const struct powerpc_operand *operand;
+ int invalid;
+ int need_comma;
+ int need_paren;
+
+ table_op = PPC_OP (opcode->opcode);
+ if (op < table_op)
+ break;
+ if (op > table_op)
+ continue;
+
+ if ((insn & opcode->mask) != opcode->opcode
+ || (opcode->flags & dialect) == 0)
+ continue;
+
+ /* Make two passes over the operands. First see if any of them
+ have extraction functions, and, if they do, make sure the
+ instruction is valid. */
+ invalid = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ operand = powerpc_operands + *opindex;
+ if (operand->extract)
+ (*operand->extract) (insn, &invalid);
+ }
+ if (invalid)
+ continue;
+
+ /* The instruction is valid. */
+ fprintf(out, "%s", opcode->name);
+ if (opcode->operands[0] != 0)
+ fprintf(out, "\t");
+
+ /* Now extract and print the operands. */
+ need_comma = 0;
+ need_paren = 0;
+ for (opindex = opcode->operands; *opindex != 0; opindex++)
+ {
+ long value;
+
+ operand = powerpc_operands + *opindex;
+
+ /* Operands that are marked FAKE are simply ignored. We
+ already made sure that the extract function considered
+ the instruction to be valid. */
+ if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+ continue;
+
+ /* Extract the value from the instruction. */
+ if (operand->extract)
+ value = (*operand->extract) (insn, (int *) 0);
+ else
+ {
+ value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0
+ && (value & (1 << (operand->bits - 1))) != 0)
+ value -= 1 << operand->bits;
+ }
+
+ /* If the operand is optional, and the value is zero, don't
+ print anything. */
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+ && (operand->flags & PPC_OPERAND_NEXT) == 0
+ && value == 0)
+ continue;
+
+ if (need_comma)
+ {
+ fprintf(out, ",");
+ need_comma = 0;
+ }
+
+ /* Print the operand as directed by the flags. */
+ if ((operand->flags & PPC_OPERAND_GPR) != 0)
+ fprintf(out, "r%ld", value);
+ else if ((operand->flags & PPC_OPERAND_FPR) != 0)
+ fprintf(out, "f%ld", value);
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
+ print_address (memaddr + value);
+ else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+ print_address (value & 0xffffffff);
+ else if ((operand->flags & PPC_OPERAND_CR) == 0
+ || (dialect & PPC_OPCODE_PPC) == 0)
+ fprintf(out, "%ld", value);
+ else
+ {
+ if (operand->bits == 3)
+ fprintf(out, "cr%d", value);
+ else
+ {
+ static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
+ int cr;
+ int cc;
+
+ cr = value >> 2;
+ if (cr != 0)
+ fprintf(out, "4*cr%d", cr);
+ cc = value & 3;
+ if (cc != 0)
+ {
+ if (cr != 0)
+ fprintf(out, "+");
+ fprintf(out, "%s", cbnames[cc]);
+ }
+ }
+ }
+
+ if (need_paren)
+ {
+ fprintf(out, ")");
+ need_paren = 0;
+ }
+
+ if ((operand->flags & PPC_OPERAND_PARENS) == 0)
+ need_comma = 1;
+ else
+ {
+ fprintf(out, "(");
+ need_paren = 1;
+ }
+ }
+
+ /* We have found and printed an instruction; return. */
+ return 4;
+ }
+
+ /* We could not find a match. */
+ fprintf(out, ".long 0x%lx", insn);
+
+ return 4;
+}
--- /dev/null
+/* ppc-opc.c -- PowerPC opcode list
+ Copyright 1994 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <linux/posix_types.h>
+#include "ansidecl.h"
+#include "ppc.h"
+
+/* This file holds the PowerPC opcode table. The opcode table
+ includes almost all of the extended instruction mnemonics. This
+ permits the disassembler to use them, and simplifies the assembler
+ logic, at the cost of increasing the table size. The table is
+ strictly constant data, so the compiler should be able to put it in
+ the .text section.
+
+ This file also holds the operand table. All knowledge about
+ inserting operands into instructions and vice-versa is kept in this
+ file. */
+\f
+/* Local insertion and extraction functions. */
+
+static unsigned long insert_bat PARAMS ((unsigned long, long, const char **));
+static long extract_bat PARAMS ((unsigned long, int *));
+static unsigned long insert_bba PARAMS ((unsigned long, long, const char **));
+static long extract_bba PARAMS ((unsigned long, int *));
+static unsigned long insert_bd PARAMS ((unsigned long, long, const char **));
+static long extract_bd PARAMS ((unsigned long, int *));
+static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **));
+static long extract_bdm PARAMS ((unsigned long, int *));
+static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **));
+static long extract_bdp PARAMS ((unsigned long, int *));
+static unsigned long insert_bo PARAMS ((unsigned long, long, const char **));
+static long extract_bo PARAMS ((unsigned long, int *));
+static unsigned long insert_boe PARAMS ((unsigned long, long, const char **));
+static long extract_boe PARAMS ((unsigned long, int *));
+static unsigned long insert_ds PARAMS ((unsigned long, long, const char **));
+static long extract_ds PARAMS ((unsigned long, int *));
+static unsigned long insert_li PARAMS ((unsigned long, long, const char **));
+static long extract_li PARAMS ((unsigned long, int *));
+static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **));
+static long extract_mbe PARAMS ((unsigned long, int *));
+static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **));
+static long extract_mb6 PARAMS ((unsigned long, int *));
+static unsigned long insert_nb PARAMS ((unsigned long, long, const char **));
+static long extract_nb PARAMS ((unsigned long, int *));
+static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **));
+static long extract_nsi PARAMS ((unsigned long, int *));
+static unsigned long insert_ral PARAMS ((unsigned long, long, const char **));
+static unsigned long insert_ram PARAMS ((unsigned long, long, const char **));
+static unsigned long insert_ras PARAMS ((unsigned long, long, const char **));
+static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **));
+static long extract_rbs PARAMS ((unsigned long, int *));
+static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **));
+static long extract_sh6 PARAMS ((unsigned long, int *));
+static unsigned long insert_spr PARAMS ((unsigned long, long, const char **));
+static long extract_spr PARAMS ((unsigned long, int *));
+static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **));
+static long extract_tbr PARAMS ((unsigned long, int *));
+\f
+/* The operands table.
+
+ The fields are bits, shift, signed, insert, extract, flags. */
+
+const struct powerpc_operand powerpc_operands[] =
+{
+ /* The zero index is used to indicate the end of the list of
+ operands. */
+#define UNUSED (0)
+ { 0, 0, 0, 0, 0 },
+
+ /* The BA field in an XL form instruction. */
+#define BA (1)
+#define BA_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+ /* The BA field in an XL form instruction when it must be the same
+ as the BT field in the same instruction. */
+#define BAT (2)
+ { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE },
+
+ /* The BB field in an XL form instruction. */
+#define BB (3)
+#define BB_MASK (0x1f << 11)
+ { 5, 11, 0, 0, PPC_OPERAND_CR },
+
+ /* The BB field in an XL form instruction when it must be the same
+ as the BA field in the same instruction. */
+#define BBA (4)
+ { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE },
+
+ /* The BD field in a B form instruction. The lower two bits are
+ forced to zero. */
+#define BD (5)
+ { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when absolute addressing is
+ used. */
+#define BDA (6)
+ { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the - modifier is used.
+ This sets the y bit of the BO field appropriately. */
+#define BDM (7)
+ { 16, 0, insert_bdm, extract_bdm,
+ PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the - modifier is used
+ and absolute address is used. */
+#define BDMA (8)
+ { 16, 0, insert_bdm, extract_bdm,
+ PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the + modifier is used.
+ This sets the y bit of the BO field appropriately. */
+#define BDP (9)
+ { 16, 0, insert_bdp, extract_bdp,
+ PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The BD field in a B form instruction when the + modifier is used
+ and absolute addressing is used. */
+#define BDPA (10)
+ { 16, 0, insert_bdp, extract_bdp,
+ PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The BF field in an X or XL form instruction. */
+#define BF (11)
+ { 3, 23, 0, 0, PPC_OPERAND_CR },
+
+ /* An optional BF field. This is used for comparison instructions,
+ in which an omitted BF field is taken as zero. */
+#define OBF (12)
+ { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+ /* The BFA field in an X or XL form instruction. */
+#define BFA (13)
+ { 3, 18, 0, 0, PPC_OPERAND_CR },
+
+ /* The BI field in a B form or XL form instruction. */
+#define BI (14)
+#define BI_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+ /* The BO field in a B form instruction. Certain values are
+ illegal. */
+#define BO (15)
+#define BO_MASK (0x1f << 21)
+ { 5, 21, insert_bo, extract_bo, 0 },
+
+ /* The BO field in a B form instruction when the + or - modifier is
+ used. This is like the BO field, but it must be even. */
+#define BOE (16)
+ { 5, 21, insert_boe, extract_boe, 0 },
+
+ /* The BT field in an X or XL form instruction. */
+#define BT (17)
+ { 5, 21, 0, 0, PPC_OPERAND_CR },
+
+ /* The condition register number portion of the BI field in a B form
+ or XL form instruction. This is used for the extended
+ conditional branch mnemonics, which set the lower two bits of the
+ BI field. This field is optional. */
+#define CR (18)
+ { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+ /* The D field in a D form instruction. This is a displacement off
+ a register, and implies that the next operand is a register in
+ parentheses. */
+#define D (19)
+ { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+ /* The DS field in a DS form instruction. This is like D, but the
+ lower two bits are forced to zero. */
+#define DS (20)
+ { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+ /* The FL1 field in a POWER SC form instruction. */
+#define FL1 (21)
+ { 4, 12, 0, 0, 0 },
+
+ /* The FL2 field in a POWER SC form instruction. */
+#define FL2 (22)
+ { 3, 2, 0, 0, 0 },
+
+ /* The FLM field in an XFL form instruction. */
+#define FLM (23)
+ { 8, 17, 0, 0, 0 },
+
+ /* The FRA field in an X or A form instruction. */
+#define FRA (24)
+#define FRA_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FRB field in an X or A form instruction. */
+#define FRB (25)
+#define FRB_MASK (0x1f << 11)
+ { 5, 11, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FRC field in an A form instruction. */
+#define FRC (26)
+#define FRC_MASK (0x1f << 6)
+ { 5, 6, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FRS field in an X form instruction or the FRT field in a D, X
+ or A form instruction. */
+#define FRS (27)
+#define FRT (FRS)
+ { 5, 21, 0, 0, PPC_OPERAND_FPR },
+
+ /* The FXM field in an XFX instruction. */
+#define FXM (28)
+#define FXM_MASK (0xff << 12)
+ { 8, 12, 0, 0, 0 },
+
+ /* The L field in a D or X form instruction. */
+#define L (29)
+ { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL },
+
+ /* The LEV field in a POWER SC form instruction. */
+#define LEV (30)
+ { 7, 5, 0, 0, 0 },
+
+ /* The LI field in an I form instruction. The lower two bits are
+ forced to zero. */
+#define LI (31)
+ { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+ /* The LI field in an I form instruction when used as an absolute
+ address. */
+#define LIA (32)
+ { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+ /* The MB field in an M form instruction. */
+#define MB (33)
+#define MB_MASK (0x1f << 6)
+ { 5, 6, 0, 0, 0 },
+
+ /* The ME field in an M form instruction. */
+#define ME (34)
+#define ME_MASK (0x1f << 1)
+ { 5, 1, 0, 0, 0 },
+
+ /* The MB and ME fields in an M form instruction expressed a single
+ operand which is a bitmask indicating which bits to select. This
+ is a two operand form using PPC_OPERAND_NEXT. See the
+ description in opcode/ppc.h for what this means. */
+#define MBE (35)
+ { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT },
+ { 32, 0, insert_mbe, extract_mbe, 0 },
+
+ /* The MB or ME field in an MD or MDS form instruction. The high
+ bit is wrapped to the low end. */
+#define MB6 (37)
+#define ME6 (MB6)
+#define MB6_MASK (0x3f << 5)
+ { 6, 5, insert_mb6, extract_mb6, 0 },
+
+ /* The NB field in an X form instruction. The value 32 is stored as
+ 0. */
+#define NB (38)
+ { 6, 11, insert_nb, extract_nb, 0 },
+
+ /* The NSI field in a D form instruction. This is the same as the
+ SI field, only negated. */
+#define NSI (39)
+ { 16, 0, insert_nsi, extract_nsi,
+ PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED },
+
+ /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */
+#define RA (40)
+#define RA_MASK (0x1f << 16)
+ { 5, 16, 0, 0, PPC_OPERAND_GPR },
+
+ /* The RA field in a D or X form instruction which is an updating
+ load, which means that the RA field may not be zero and may not
+ equal the RT field. */
+#define RAL (41)
+ { 5, 16, insert_ral, 0, PPC_OPERAND_GPR },
+
+ /* The RA field in an lmw instruction, which has special value
+ restrictions. */
+#define RAM (42)
+ { 5, 16, insert_ram, 0, PPC_OPERAND_GPR },
+
+ /* The RA field in a D or X form instruction which is an updating
+ store or an updating floating point load, which means that the RA
+ field may not be zero. */
+#define RAS (43)
+ { 5, 16, insert_ras, 0, PPC_OPERAND_GPR },
+
+ /* The RB field in an X, XO, M, or MDS form instruction. */
+#define RB (44)
+#define RB_MASK (0x1f << 11)
+ { 5, 11, 0, 0, PPC_OPERAND_GPR },
+
+ /* The RB field in an X form instruction when it must be the same as
+ the RS field in the instruction. This is used for extended
+ mnemonics like mr. */
+#define RBS (45)
+ { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE },
+
+ /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form
+ instruction or the RT field in a D, DS, X, XFX or XO form
+ instruction. */
+#define RS (46)
+#define RT (RS)
+#define RT_MASK (0x1f << 21)
+ { 5, 21, 0, 0, PPC_OPERAND_GPR },
+
+ /* The SH field in an X or M form instruction. */
+#define SH (47)
+#define SH_MASK (0x1f << 11)
+ { 5, 11, 0, 0, 0 },
+
+ /* The SH field in an MD form instruction. This is split. */
+#define SH6 (48)
+#define SH6_MASK ((0x1f << 11) | (1 << 1))
+ { 6, 1, insert_sh6, extract_sh6, 0 },
+
+ /* The SI field in a D form instruction. */
+#define SI (49)
+ { 16, 0, 0, 0, PPC_OPERAND_SIGNED },
+
+ /* The SI field in a D form instruction when we accept a wide range
+ of positive values. */
+#define SISIGNOPT (50)
+ { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT },
+
+ /* The SPR field in an XFX form instruction. This is flipped--the
+ lower 5 bits are stored in the upper 5 and vice- versa. */
+#define SPR (51)
+#define SPR_MASK (0x3ff << 11)
+ { 10, 11, insert_spr, extract_spr, 0 },
+
+ /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */
+#define SPRBAT (52)
+#define SPRBAT_MASK (0x3 << 17)
+ { 2, 17, 0, 0, 0 },
+
+ /* The SPRG register number in an XFX form m[ft]sprg instruction. */
+#define SPRG (53)
+#define SPRG_MASK (0x3 << 16)
+ { 2, 16, 0, 0, 0 },
+
+ /* The SR field in an X form instruction. */
+#define SR (54)
+ { 4, 16, 0, 0, 0 },
+
+ /* The SV field in a POWER SC form instruction. */
+#define SV (55)
+ { 14, 2, 0, 0, 0 },
+
+ /* The TBR field in an XFX form instruction. This is like the SPR
+ field, but it is optional. */
+#define TBR (56)
+ { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL },
+
+ /* The TO field in a D or X form instruction. */
+#define TO (57)
+#define TO_MASK (0x1f << 21)
+ { 5, 21, 0, 0, 0 },
+
+ /* The U field in an X form instruction. */
+#define U (58)
+ { 4, 12, 0, 0, 0 },
+
+ /* The UI field in a D form instruction. */
+#define UI (59)
+ { 16, 0, 0, 0, 0 },
+};
+
+/* The functions used to insert and extract complicated operands. */
+
+/* The BA field in an XL form instruction when it must be the same as
+ the BT field in the same instruction. This operand is marked FAKE.
+ The insertion function just copies the BT field into the BA field,
+ and the extraction function just checks that the fields are the
+ same. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bat (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | (((insn >> 21) & 0x1f) << 16);
+}
+
+static long
+extract_bat (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The BB field in an XL form instruction when it must be the same as
+ the BA field in the same instruction. This operand is marked FAKE.
+ The insertion function just copies the BA field into the BB field,
+ and the extraction function just checks that the fields are the
+ same. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bba (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | (((insn >> 16) & 0x1f) << 11);
+}
+
+static long
+extract_bba (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The BD field in a B form instruction. The lower two bits are
+ forced to zero. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bd (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_bd (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the - modifier is used.
+ This modifier means that the branch is not expected to be taken.
+ We must set the y bit of the BO field to 1 if the offset is
+ negative. When extracting, we require that the y bit be 1 and that
+ the offset be positive, since if the y bit is 0 we just want to
+ print the normal form of the instruction. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdm (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if ((value & 0x8000) != 0)
+ insn |= 1 << 21;
+ return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdm (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn & (1 << 21)) == 0
+ || (insn & (1 << 15)) == 0))
+ *invalid = 1;
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the + modifier is used.
+ This is like BDM, above, except that the branch is expected to be
+ taken. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdp (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if ((value & 0x8000) == 0)
+ insn |= 1 << 21;
+ return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdp (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn & (1 << 21)) == 0
+ || (insn & (1 << 15)) != 0))
+ *invalid = 1;
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* Check for legal values of a BO field. */
+
+static int
+valid_bo (value)
+ long value;
+{
+ /* Certain encodings have bits that are required to be zero. These
+ are (z must be zero, y may be anything):
+ 001zy
+ 011zy
+ 1z00y
+ 1z01y
+ 1z1zz
+ */
+ switch (value & 0x14)
+ {
+ default:
+ case 0:
+ return 1;
+ case 0x4:
+ return (value & 0x2) == 0;
+ case 0x10:
+ return (value & 0x8) == 0;
+ case 0x14:
+ return value == 0x14;
+ }
+}
+
+/* The BO field in a B form instruction. Warn about attempts to set
+ the field to an illegal value. */
+
+static unsigned long
+insert_bo (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if (errmsg != (const char **) NULL
+ && ! valid_bo (value))
+ *errmsg = "invalid conditional option";
+ return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_bo (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ long value;
+
+ value = (insn >> 21) & 0x1f;
+ if (invalid != (int *) NULL
+ && ! valid_bo (value))
+ *invalid = 1;
+ return value;
+}
+
+/* The BO field in a B form instruction when the + or - modifier is
+ used. This is like the BO field, but it must be even. When
+ extracting it, we force it to be even. */
+
+static unsigned long
+insert_boe (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if (errmsg != (const char **) NULL)
+ {
+ if (! valid_bo (value))
+ *errmsg = "invalid conditional option";
+ else if ((value & 1) != 0)
+ *errmsg = "attempt to set y bit when using + or - modifier";
+ }
+ return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_boe (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ long value;
+
+ value = (insn >> 21) & 0x1f;
+ if (invalid != (int *) NULL
+ && ! valid_bo (value))
+ *invalid = 1;
+ return value & 0x1e;
+}
+
+/* The DS field in a DS form instruction. This is like D, but the
+ lower two bits are forced to zero. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_ds (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_ds (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if ((insn & 0x8000) != 0)
+ return (insn & 0xfffc) - 0x10000;
+ else
+ return insn & 0xfffc;
+}
+
+/* The LI field in an I form instruction. The lower two bits are
+ forced to zero. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_li (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | (value & 0x3fffffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_li (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if ((insn & 0x2000000) != 0)
+ return (insn & 0x3fffffc) - 0x4000000;
+ else
+ return insn & 0x3fffffc;
+}
+
+/* The MB and ME fields in an M form instruction expressed as a single
+ operand which is itself a bitmask. The extraction function always
+ marks it as invalid, since we never want to recognize an
+ instruction which uses a field of this type. */
+
+static unsigned long
+insert_mbe (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ unsigned long uval;
+ int mb, me;
+
+ uval = value;
+
+ if (uval == 0)
+ {
+ if (errmsg != (const char **) NULL)
+ *errmsg = "illegal bitmask";
+ return insn;
+ }
+
+ me = 31;
+ while ((uval & 1) == 0)
+ {
+ uval >>= 1;
+ --me;
+ }
+
+ mb = me;
+ uval >>= 1;
+ while ((uval & 1) != 0)
+ {
+ uval >>= 1;
+ --mb;
+ }
+
+ if (uval != 0)
+ {
+ if (errmsg != (const char **) NULL)
+ *errmsg = "illegal bitmask";
+ }
+
+ return insn | (mb << 6) | (me << 1);
+}
+
+static long
+extract_mbe (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ long ret;
+ int mb, me;
+ int i;
+
+ if (invalid != (int *) NULL)
+ *invalid = 1;
+
+ ret = 0;
+ mb = (insn >> 6) & 0x1f;
+ me = (insn >> 1) & 0x1f;
+ for (i = mb; i < me; i++)
+ ret |= 1 << (31 - i);
+ return ret;
+}
+
+/* The MB or ME field in an MD or MDS form instruction. The high bit
+ is wrapped to the low end. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_mb6 (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | ((value & 0x1f) << 6) | (value & 0x20);
+}
+
+/*ARGSUSED*/
+static long
+extract_mb6 (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ return ((insn >> 6) & 0x1f) | (insn & 0x20);
+}
+
+/* The NB field in an X form instruction. The value 32 is stored as
+ 0. */
+
+static unsigned long
+insert_nb (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if (value < 0 || value > 32)
+ *errmsg = "value out of range";
+ if (value == 32)
+ value = 0;
+ return insn | ((value & 0x1f) << 11);
+}
+
+/*ARGSUSED*/
+static long
+extract_nb (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ long ret;
+
+ ret = (insn >> 11) & 0x1f;
+ if (ret == 0)
+ ret = 32;
+ return ret;
+}
+
+/* The NSI field in a D form instruction. This is the same as the SI
+ field, only negated. The extraction function always marks it as
+ invalid, since we never want to recognize an instruction which uses
+ a field of this type. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_nsi (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | ((- value) & 0xffff);
+}
+
+static long
+extract_nsi (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL)
+ *invalid = 1;
+ if ((insn & 0x8000) != 0)
+ return - ((insn & 0xffff) - 0x10000);
+ else
+ return - (insn & 0xffff);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+ load, which means that the RA field may not be zero and may not
+ equal the RT field. */
+
+static unsigned long
+insert_ral (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if (value == 0
+ || value == ((insn >> 21) & 0x1f))
+ *errmsg = "invalid register operand when updating";
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in an lmw instruction, which has special value
+ restrictions. */
+
+static unsigned long
+insert_ram (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if (value >= ((insn >> 21) & 0x1f))
+ *errmsg = "index register in load range";
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+ store or an updating floating point load, which means that the RA
+ field may not be zero. */
+
+static unsigned long
+insert_ras (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if (value == 0)
+ *errmsg = "invalid register operand when updating";
+ return insn | ((value & 0x1f) << 16);
+}
+
+/* The RB field in an X form instruction when it must be the same as
+ the RS field in the instruction. This is used for extended
+ mnemonics like mr. This operand is marked FAKE. The insertion
+ function just copies the BT field into the BA field, and the
+ extraction function just checks that the fields are the same. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_rbs (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | (((insn >> 21) & 0x1f) << 11);
+}
+
+static long
+extract_rbs (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ if (invalid != (int *) NULL
+ && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f))
+ *invalid = 1;
+ return 0;
+}
+
+/* The SH field in an MD form instruction. This is split. */
+
+/*ARGSUSED*/
+static unsigned long
+insert_sh6 (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4);
+}
+
+/*ARGSUSED*/
+static long
+extract_sh6 (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20);
+}
+
+/* The SPR field in an XFX form instruction. This is flipped--the
+ lower 5 bits are stored in the upper 5 and vice- versa. */
+
+static unsigned long
+insert_spr (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_spr (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+}
+
+/* The TBR field in an XFX instruction. This is just like SPR, but it
+ is optional. When TBR is omitted, it must be inserted as 268 (the
+ magic number of the TB register). These functions treat 0
+ (indicating an omitted optional operand) as 268. This means that
+ ``mftb 4,0'' is not handled correctly. This does not matter very
+ much, since the architecture manual does not define mftb as
+ accepting any values other than 268 or 269. */
+
+#define TB (268)
+
+static unsigned long
+insert_tbr (insn, value, errmsg)
+ unsigned long insn;
+ long value;
+ const char **errmsg;
+{
+ if (value == 0)
+ value = TB;
+ return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_tbr (insn, invalid)
+ unsigned long insn;
+ int *invalid;
+{
+ long ret;
+
+ ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+ if (ret == TB)
+ ret = 0;
+ return ret;
+}
+\f
+/* Macros used to form opcodes. */
+
+/* The main opcode. */
+#define OP(x) (((x) & 0x3f) << 26)
+#define OP_MASK OP (0x3f)
+
+/* The main opcode combined with a trap code in the TO field of a D
+ form instruction. Used for extended mnemonics for the trap
+ instructions. */
+#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21))
+#define OPTO_MASK (OP_MASK | TO_MASK)
+
+/* The main opcode combined with a comparison size bit in the L field
+ of a D form or X form instruction. Used for extended mnemonics for
+ the comparison instructions. */
+#define OPL(x,l) (OP (x) | (((l) & 1) << 21))
+#define OPL_MASK OPL (0x3f,1)
+
+/* An A form instruction. */
+#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1))
+#define A_MASK A (0x3f, 0x1f, 1)
+
+/* An A_MASK with the FRB field fixed. */
+#define AFRB_MASK (A_MASK | FRB_MASK)
+
+/* An A_MASK with the FRC field fixed. */
+#define AFRC_MASK (A_MASK | FRC_MASK)
+
+/* An A_MASK with the FRA and FRC fields fixed. */
+#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK)
+
+/* A B form instruction. */
+#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1))
+#define B_MASK B (0x3f, 1, 1)
+
+/* A B form instruction setting the BO field. */
+#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21))
+#define BBO_MASK BBO (0x3f, 0x1f, 1, 1)
+
+/* A BBO_MASK with the y bit of the BO field removed. This permits
+ matching a conditional branch regardless of the setting of the y
+ bit. */
+#define Y_MASK (1 << 21)
+#define BBOY_MASK (BBO_MASK &~ Y_MASK)
+
+/* A B form instruction setting the BO field and the condition bits of
+ the BI field. */
+#define BBOCB(op, bo, cb, aa, lk) \
+ (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16))
+#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1)
+
+/* A BBOCB_MASK with the y bit of the BO field removed. */
+#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK)
+
+/* A BBOYCB_MASK in which the BI field is fixed. */
+#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK)
+
+/* The main opcode mask with the RA field clear. */
+#define DRA_MASK (OP_MASK | RA_MASK)
+
+/* A DS form instruction. */
+#define DSO(op, xop) (OP (op) | ((xop) & 0x3))
+#define DS_MASK DSO (0x3f, 3)
+
+/* An M form instruction. */
+#define M(op, rc) (OP (op) | ((rc) & 1))
+#define M_MASK M (0x3f, 1)
+
+/* An M form instruction with the ME field specified. */
+#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1))
+
+/* An M_MASK with the MB and ME fields fixed. */
+#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK)
+
+/* An M_MASK with the SH and ME fields fixed. */
+#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK)
+
+/* An MD form instruction. */
+#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1))
+#define MD_MASK MD (0x3f, 0x7, 1)
+
+/* An MD_MASK with the MB field fixed. */
+#define MDMB_MASK (MD_MASK | MB6_MASK)
+
+/* An MD_MASK with the SH field fixed. */
+#define MDSH_MASK (MD_MASK | SH6_MASK)
+
+/* An MDS form instruction. */
+#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1))
+#define MDS_MASK MDS (0x3f, 0xf, 1)
+
+/* An MDS_MASK with the MB field fixed. */
+#define MDSMB_MASK (MDS_MASK | MB6_MASK)
+
+/* An SC form instruction. */
+#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1))
+#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1)
+
+/* An X form instruction. */
+#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An X form instruction with the RC bit specified. */
+#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1))
+
+/* The mask for an X form instruction. */
+#define X_MASK XRC (0x3f, 0x3ff, 1)
+
+/* An X_MASK with the RA field fixed. */
+#define XRA_MASK (X_MASK | RA_MASK)
+
+/* An X_MASK with the RB field fixed. */
+#define XRB_MASK (X_MASK | RB_MASK)
+
+/* An X_MASK with the RT field fixed. */
+#define XRT_MASK (X_MASK | RT_MASK)
+
+/* An X_MASK with the RA and RB fields fixed. */
+#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK)
+
+/* An X_MASK with the RT and RA fields fixed. */
+#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK)
+
+/* An X form comparison instruction. */
+#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21))
+
+/* The mask for an X form comparison instruction. */
+#define XCMP_MASK (X_MASK | (1 << 22))
+
+/* The mask for an X form comparison instruction with the L field
+ fixed. */
+#define XCMPL_MASK (XCMP_MASK | (1 << 21))
+
+/* An X form trap instruction with the TO field specified. */
+#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21))
+#define XTO_MASK (X_MASK | TO_MASK)
+
+/* An XFL form instruction. */
+#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1))
+#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16))
+
+/* An XL form instruction with the LK field set to 0. */
+#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An XL form instruction which uses the LK field. */
+#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1))
+
+/* The mask for an XL form instruction. */
+#define XL_MASK XLLK (0x3f, 0x3ff, 1)
+
+/* An XL form instruction which explicitly sets the BO field. */
+#define XLO(op, bo, xop, lk) \
+ (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21))
+#define XLO_MASK (XL_MASK | BO_MASK)
+
+/* An XL form instruction which explicitly sets the y bit of the BO
+ field. */
+#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21))
+#define XLYLK_MASK (XL_MASK | Y_MASK)
+
+/* An XL form instruction which sets the BO field and the condition
+ bits of the BI field. */
+#define XLOCB(op, bo, cb, xop, lk) \
+ (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16))
+#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1)
+
+/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */
+#define XLBB_MASK (XL_MASK | BB_MASK)
+#define XLYBB_MASK (XLYLK_MASK | BB_MASK)
+#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK)
+
+/* An XL_MASK with the BO and BB fields fixed. */
+#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK)
+
+/* An XL_MASK with the BO, BI and BB fields fixed. */
+#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK)
+
+/* An XO form instruction. */
+#define XO(op, xop, oe, rc) \
+ (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1))
+#define XO_MASK XO (0x3f, 0x1ff, 1, 1)
+
+/* An XO_MASK with the RB field fixed. */
+#define XORB_MASK (XO_MASK | RB_MASK)
+
+/* An XS form instruction. */
+#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1))
+#define XS_MASK XS (0x3f, 0x1ff, 1)
+
+/* A mask for the FXM version of an XFX form instruction. */
+#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11))
+
+/* An XFX form instruction with the FXM field filled in. */
+#define XFXM(op, xop, fxm) \
+ (X ((op), (xop)) | (((fxm) & 0xff) << 12))
+
+/* An XFX form instruction with the SPR field filled in. */
+#define XSPR(op, xop, spr) \
+ (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6))
+#define XSPR_MASK (X_MASK | SPR_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+ SPRBAT field. */
+#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+ SPRG field. */
+#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK)
+
+/* The BO encodings used in extended conditional branch mnemonics. */
+#define BODNZF (0x0)
+#define BODNZFP (0x1)
+#define BODZF (0x2)
+#define BODZFP (0x3)
+#define BOF (0x4)
+#define BOFP (0x5)
+#define BODNZT (0x8)
+#define BODNZTP (0x9)
+#define BODZT (0xa)
+#define BODZTP (0xb)
+#define BOT (0xc)
+#define BOTP (0xd)
+#define BODNZ (0x10)
+#define BODNZP (0x11)
+#define BODZ (0x12)
+#define BODZP (0x13)
+#define BOU (0x14)
+
+/* The BI condition bit encodings used in extended conditional branch
+ mnemonics. */
+#define CBLT (0)
+#define CBGT (1)
+#define CBEQ (2)
+#define CBSO (3)
+
+/* The TO encodings used in extended trap mnemonics. */
+#define TOLGT (0x1)
+#define TOLLT (0x2)
+#define TOEQ (0x4)
+#define TOLGE (0x5)
+#define TOLNL (0x5)
+#define TOLLE (0x6)
+#define TOLNG (0x6)
+#define TOGT (0x8)
+#define TOGE (0xc)
+#define TONL (0xc)
+#define TOLT (0x10)
+#define TOLE (0x14)
+#define TONG (0x14)
+#define TONE (0x18)
+#define TOU (0x1f)
+\f
+/* Smaller names for the flags so each entry in the opcodes table will
+ fit on a single line. */
+#undef PPC
+#define PPC PPC_OPCODE_PPC
+#define POWER PPC_OPCODE_POWER
+#define POWER2 PPC_OPCODE_POWER2
+#define B32 PPC_OPCODE_32
+#define B64 PPC_OPCODE_64
+#define M601 PPC_OPCODE_601
+\f
+/* The opcode table.
+
+ The format of the opcode table is:
+
+ NAME OPCODE MASK FLAGS { OPERANDS }
+
+ NAME is the name of the instruction.
+ OPCODE is the instruction opcode.
+ MASK is the opcode mask; this is used to tell the disassembler
+ which bits in the actual opcode must match OPCODE.
+ FLAGS are flags indicated what processors support the instruction.
+ OPERANDS is the list of operands.
+
+ The disassembler reads the table in order and prints the first
+ instruction which matches, so this table is sorted to put more
+ specific instructions before more general instructions. It is also
+ sorted by major opcode. */
+
+const struct powerpc_opcode powerpc_opcodes[] = {
+{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC|B64, { RA, SI } },
+{ "tdi", OP(2), OP_MASK, PPC|B64, { TO, RA, SI } },
+
+{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPC, { RA, SI } },
+{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, POWER, { RA, SI } },
+{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPC, { RA, SI } },
+{ "tllti", OPTO(3,TOLLT), OPTO_MASK, POWER, { RA, SI } },
+{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPC, { RA, SI } },
+{ "teqi", OPTO(3,TOEQ), OPTO_MASK, POWER, { RA, SI } },
+{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPC, { RA, SI } },
+{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, POWER, { RA, SI } },
+{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPC, { RA, SI } },
+{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, POWER, { RA, SI } },
+{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPC, { RA, SI } },
+{ "tllei", OPTO(3,TOLLE), OPTO_MASK, POWER, { RA, SI } },
+{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPC, { RA, SI } },
+{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, POWER, { RA, SI } },
+{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPC, { RA, SI } },
+{ "tgti", OPTO(3,TOGT), OPTO_MASK, POWER, { RA, SI } },
+{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPC, { RA, SI } },
+{ "tgei", OPTO(3,TOGE), OPTO_MASK, POWER, { RA, SI } },
+{ "twnli", OPTO(3,TONL), OPTO_MASK, PPC, { RA, SI } },
+{ "tnli", OPTO(3,TONL), OPTO_MASK, POWER, { RA, SI } },
+{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPC, { RA, SI } },
+{ "tlti", OPTO(3,TOLT), OPTO_MASK, POWER, { RA, SI } },
+{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPC, { RA, SI } },
+{ "tlei", OPTO(3,TOLE), OPTO_MASK, POWER, { RA, SI } },
+{ "twngi", OPTO(3,TONG), OPTO_MASK, PPC, { RA, SI } },
+{ "tngi", OPTO(3,TONG), OPTO_MASK, POWER, { RA, SI } },
+{ "twnei", OPTO(3,TONE), OPTO_MASK, PPC, { RA, SI } },
+{ "tnei", OPTO(3,TONE), OPTO_MASK, POWER, { RA, SI } },
+{ "twi", OP(3), OP_MASK, PPC, { TO, RA, SI } },
+{ "ti", OP(3), OP_MASK, POWER, { TO, RA, SI } },
+
+{ "mulli", OP(7), OP_MASK, PPC, { RT, RA, SI } },
+{ "muli", OP(7), OP_MASK, POWER, { RT, RA, SI } },
+
+{ "subfic", OP(8), OP_MASK, PPC, { RT, RA, SI } },
+{ "sfi", OP(8), OP_MASK, POWER, { RT, RA, SI } },
+
+{ "dozi", OP(9), OP_MASK, POWER|M601, { RT, RA, SI } },
+
+{ "cmplwi", OPL(10,0), OPL_MASK, PPC, { OBF, RA, UI } },
+{ "cmpldi", OPL(10,1), OPL_MASK, PPC|B64, { OBF, RA, UI } },
+{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } },
+{ "cmpli", OP(10), OP_MASK, POWER, { BF, RA, UI } },
+
+{ "cmpwi", OPL(11,0), OPL_MASK, PPC, { OBF, RA, SI } },
+{ "cmpdi", OPL(11,1), OPL_MASK, PPC|B64, { OBF, RA, SI } },
+{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } },
+{ "cmpi", OP(11), OP_MASK, POWER, { BF, RA, SI } },
+
+{ "addic", OP(12), OP_MASK, PPC, { RT, RA, SI } },
+{ "ai", OP(12), OP_MASK, POWER, { RT, RA, SI } },
+{ "subic", OP(12), OP_MASK, PPC, { RT, RA, NSI } },
+
+{ "addic.", OP(13), OP_MASK, PPC, { RT, RA, SI } },
+{ "ai.", OP(13), OP_MASK, POWER, { RT, RA, SI } },
+{ "subic.", OP(13), OP_MASK, PPC, { RT, RA, NSI } },
+
+{ "li", OP(14), DRA_MASK, PPC, { RT, SI } },
+{ "lil", OP(14), DRA_MASK, POWER, { RT, SI } },
+{ "addi", OP(14), OP_MASK, PPC, { RT, RA, SI } },
+{ "cal", OP(14), OP_MASK, POWER, { RT, D, RA } },
+{ "subi", OP(14), OP_MASK, PPC, { RT, RA, NSI } },
+{ "la", OP(14), OP_MASK, PPC, { RT, D, RA } },
+
+{ "lis", OP(15), DRA_MASK, PPC, { RT, SISIGNOPT } },
+{ "liu", OP(15), DRA_MASK, POWER, { RT, SISIGNOPT } },
+{ "addis", OP(15), OP_MASK, PPC, { RT,RA,SISIGNOPT } },
+{ "cau", OP(15), OP_MASK, POWER, { RT,RA,SISIGNOPT } },
+{ "subis", OP(15), OP_MASK, PPC, { RT, RA, NSI } },
+
+{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDM } },
+{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDP } },
+{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BD } },
+{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER, { BD } },
+{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDM } },
+{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDP } },
+{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BD } },
+{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER, { BD } },
+{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDA } },
+{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER, { BDA } },
+{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDA } },
+{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER, { BDA } },
+{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDM } },
+{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDP } },
+{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER, { BD } },
+{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDM } },
+{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDP } },
+{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER, { BD } },
+{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER, { BDA } },
+{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDMA } },
+{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDPA } },
+{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER, { BDA } },
+{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } },
+{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } },
+{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } },
+{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } },
+{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } },
+{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } },
+{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } },
+{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } },
+{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } },
+{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, POWER, { BI, BD } },
+{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, POWER, { BI, BD } },
+{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, POWER, { BI, BDA } },
+{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, POWER, { BI, BDA } },
+{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, POWER, { BI, BD } },
+{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, POWER, { BI, BD } },
+{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, POWER, { BI, BDA } },
+{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, POWER, { BI, BDA } },
+{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BD } },
+{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDM } },
+{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDP } },
+{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BD } },
+{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDA } },
+{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDMA } },
+{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDPA } },
+{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDA } },
+{ "bc-", B(16,0,0), B_MASK, PPC, { BOE, BI, BDM } },
+{ "bc+", B(16,0,0), B_MASK, PPC, { BOE, BI, BDP } },
+{ "bc", B(16,0,0), B_MASK, PPC|POWER, { BO, BI, BD } },
+{ "bcl-", B(16,0,1), B_MASK, PPC, { BOE, BI, BDM } },
+{ "bcl+", B(16,0,1), B_MASK, PPC, { BOE, BI, BDP } },
+{ "bcl", B(16,0,1), B_MASK, PPC|POWER, { BO, BI, BD } },
+{ "bca-", B(16,1,0), B_MASK, PPC, { BOE, BI, BDMA } },
+{ "bca+", B(16,1,0), B_MASK, PPC, { BOE, BI, BDPA } },
+{ "bca", B(16,1,0), B_MASK, PPC|POWER, { BO, BI, BDA } },
+{ "bcla-", B(16,1,1), B_MASK, PPC, { BOE, BI, BDMA } },
+{ "bcla+", B(16,1,1), B_MASK, PPC, { BOE, BI, BDPA } },
+{ "bcla", B(16,1,1), B_MASK, PPC|POWER, { BO, BI, BDA } },
+
+{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } },
+{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } },
+{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } },
+{ "svca", SC(17,1,0), SC_MASK, POWER, { SV } },
+{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } },
+
+{ "b", B(18,0,0), B_MASK, PPC|POWER, { LI } },
+{ "bl", B(18,0,1), B_MASK, PPC|POWER, { LI } },
+{ "ba", B(18,1,0), B_MASK, PPC|POWER, { LIA } },
+{ "bla", B(18,1,1), B_MASK, PPC|POWER, { LIA } },
+
+{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER, { 0 } },
+{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER, { 0 } },
+{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC, { 0 } },
+{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, POWER, { BI } },
+{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, POWER, { BI } },
+{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, POWER, { BI } },
+{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, POWER, { BI } },
+{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC, { BI } },
+{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPC, { BO, BI } },
+{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPC, { BO, BI } },
+{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcr", XLLK(19,16,0), XLBB_MASK, POWER, { BO, BI } },
+{ "bcrl", XLLK(19,16,1), XLBB_MASK, POWER, { BO, BI } },
+
+{ "crnot", XL(19,33), XL_MASK, PPC, { BT, BA, BBA } },
+{ "crnor", XL(19,33), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "rfi", XL(19,50), 0xffffffff, PPC|POWER, { 0 } },
+{ "rfci", XL(19,51), 0xffffffff, PPC, { 0 } },
+
+{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } },
+
+{ "crandc", XL(19,129), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "isync", XL(19,150), 0xffffffff, PPC, { 0 } },
+{ "ics", XL(19,150), 0xffffffff, POWER, { 0 } },
+
+{ "crclr", XL(19,193), XL_MASK, PPC, { BT, BAT, BBA } },
+{ "crxor", XL(19,193), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crnand", XL(19,225), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crand", XL(19,257), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crset", XL(19,289), XL_MASK, PPC, { BT, BAT, BBA } },
+{ "creqv", XL(19,289), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crorc", XL(19,417), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "crmove", XL(19,449), XL_MASK, PPC, { BT, BA, BBA } },
+{ "cror", XL(19,449), XL_MASK, PPC|POWER, { BT, BA, BB } },
+
+{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPC, { BI } },
+{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC, { BI } },
+{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPC, { BO, BI } },
+{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPC, { BO, BI } },
+{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC, { BOE, BI } },
+{ "bcc", XLLK(19,528,0), XLBB_MASK, POWER, { BO, BI } },
+{ "bccl", XLLK(19,528,1), XLBB_MASK, POWER, { BO, BI } },
+
+{ "rlwimi", M(20,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlimi", M(20,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+
+{ "rlwimi.", M(20,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlimi.", M(20,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+
+{ "rotlwi", MME(21,31,0), MMBME_MASK, PPC, { RA, RS, SH } },
+{ "clrlwi", MME(21,31,0), MSHME_MASK, PPC, { RA, RS, MB } },
+{ "rlwinm", M(21,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlinm", M(21,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPC, { RA,RS,SH } },
+{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPC, { RA, RS, MB } },
+{ "rlwinm.", M(21,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } },
+{ "rlinm.", M(21,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } },
+
+{ "rlmi", M(22,0), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } },
+{ "rlmi.", M(22,1), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } },
+
+{ "rotlw", MME(23,31,0), MMBME_MASK, PPC, { RA, RS, RB } },
+{ "rlwnm", M(23,0), M_MASK, PPC, { RA,RS,RB,MBE,ME } },
+{ "rlnm", M(23,0), M_MASK, POWER, { RA,RS,RB,MBE,ME } },
+{ "rotlw.", MME(23,31,1), MMBME_MASK, PPC, { RA, RS, RB } },
+{ "rlwnm.", M(23,1), M_MASK, PPC, { RA,RS,RB,MBE,ME } },
+{ "rlnm.", M(23,1), M_MASK, POWER, { RA,RS,RB,MBE,ME } },
+
+{ "nop", OP(24), 0xffffffff, PPC, { 0 } },
+{ "ori", OP(24), OP_MASK, PPC, { RA, RS, UI } },
+{ "oril", OP(24), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "oris", OP(25), OP_MASK, PPC, { RA, RS, UI } },
+{ "oriu", OP(25), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "xori", OP(26), OP_MASK, PPC, { RA, RS, UI } },
+{ "xoril", OP(26), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "xoris", OP(27), OP_MASK, PPC, { RA, RS, UI } },
+{ "xoriu", OP(27), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "andi.", OP(28), OP_MASK, PPC, { RA, RS, UI } },
+{ "andil.", OP(28), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "andis.", OP(29), OP_MASK, PPC, { RA, RS, UI } },
+{ "andiu.", OP(29), OP_MASK, POWER, { RA, RS, UI } },
+
+{ "rotldi", MD(30,0,0), MDMB_MASK, PPC|B64, { RA, RS, SH6 } },
+{ "clrldi", MD(30,0,0), MDSH_MASK, PPC|B64, { RA, RS, MB6 } },
+{ "rldicl", MD(30,0,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC|B64, { RA, RS, SH6 } },
+{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC|B64, { RA, RS, MB6 } },
+{ "rldicl.", MD(30,0,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+
+{ "rldicr", MD(30,1,0), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } },
+{ "rldicr.", MD(30,1,1), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } },
+
+{ "rldic", MD(30,2,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+{ "rldic.", MD(30,2,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+
+{ "rldimi", MD(30,3,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+{ "rldimi.", MD(30,3,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } },
+
+{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC|B64, { RA, RS, RB } },
+{ "rldcl", MDS(30,8,0), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } },
+{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC|B64, { RA, RS, RB } },
+{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } },
+
+{ "rldcr", MDS(30,9,0), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } },
+{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } },
+
+{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPC, { OBF, RA, RB } },
+{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } },
+{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } },
+{ "cmp", X(31,0), XCMPL_MASK, POWER, { BF, RA, RB } },
+
+{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPC, { RA, RB } },
+{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, POWER, { RA, RB } },
+{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPC, { RA, RB } },
+{ "tllt", XTO(31,4,TOLLT), XTO_MASK, POWER, { RA, RB } },
+{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPC, { RA, RB } },
+{ "teq", XTO(31,4,TOEQ), XTO_MASK, POWER, { RA, RB } },
+{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPC, { RA, RB } },
+{ "tlge", XTO(31,4,TOLGE), XTO_MASK, POWER, { RA, RB } },
+{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPC, { RA, RB } },
+{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, POWER, { RA, RB } },
+{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPC, { RA, RB } },
+{ "tlle", XTO(31,4,TOLLE), XTO_MASK, POWER, { RA, RB } },
+{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPC, { RA, RB } },
+{ "tlng", XTO(31,4,TOLNG), XTO_MASK, POWER, { RA, RB } },
+{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPC, { RA, RB } },
+{ "tgt", XTO(31,4,TOGT), XTO_MASK, POWER, { RA, RB } },
+{ "twge", XTO(31,4,TOGE), XTO_MASK, PPC, { RA, RB } },
+{ "tge", XTO(31,4,TOGE), XTO_MASK, POWER, { RA, RB } },
+{ "twnl", XTO(31,4,TONL), XTO_MASK, PPC, { RA, RB } },
+{ "tnl", XTO(31,4,TONL), XTO_MASK, POWER, { RA, RB } },
+{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPC, { RA, RB } },
+{ "tlt", XTO(31,4,TOLT), XTO_MASK, POWER, { RA, RB } },
+{ "twle", XTO(31,4,TOLE), XTO_MASK, PPC, { RA, RB } },
+{ "tle", XTO(31,4,TOLE), XTO_MASK, POWER, { RA, RB } },
+{ "twng", XTO(31,4,TONG), XTO_MASK, PPC, { RA, RB } },
+{ "tng", XTO(31,4,TONG), XTO_MASK, POWER, { RA, RB } },
+{ "twne", XTO(31,4,TONE), XTO_MASK, PPC, { RA, RB } },
+{ "tne", XTO(31,4,TONE), XTO_MASK, POWER, { RA, RB } },
+{ "trap", XTO(31,4,TOU), 0xffffffff, PPC, { 0 } },
+{ "tw", X(31,4), X_MASK, PPC, { TO, RA, RB } },
+{ "t", X(31,4), X_MASK, POWER, { TO, RA, RB } },
+
+{ "subfc", XO(31,8,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sf", XO(31,8,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sf.", XO(31,8,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "subc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfco", XO(31,8,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfo", XO(31,8,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfo.", XO(31,8,1,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } },
+
+{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "addc", XO(31,10,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "a", XO(31,10,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addc.", XO(31,10,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "a.", XO(31,10,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "addco", XO(31,10,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "ao", XO(31,10,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addco.", XO(31,10,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "ao.", XO(31,10,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "mfcr", X(31,19), XRARB_MASK, POWER|PPC, { RT } },
+
+{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } },
+
+{ "ldx", X(31,21), X_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "lwzx", X(31,23), X_MASK, PPC, { RT, RA, RB } },
+{ "lx", X(31,23), X_MASK, POWER, { RT, RA, RB } },
+
+{ "slw", XRC(31,24,0), X_MASK, PPC, { RA, RS, RB } },
+{ "sl", XRC(31,24,0), X_MASK, POWER, { RA, RS, RB } },
+{ "slw.", XRC(31,24,1), X_MASK, PPC, { RA, RS, RB } },
+{ "sl.", XRC(31,24,1), X_MASK, POWER, { RA, RS, RB } },
+
+{ "cntlzw", XRC(31,26,0), XRB_MASK, PPC, { RA, RS } },
+{ "cntlz", XRC(31,26,0), XRB_MASK, POWER, { RA, RS } },
+{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPC, { RA, RS } },
+{ "cntlz.", XRC(31,26,1), XRB_MASK, POWER, { RA, RS } },
+
+{ "sld", XRC(31,27,0), X_MASK, PPC|B64, { RA, RS, RB } },
+{ "sld.", XRC(31,27,1), X_MASK, PPC|B64, { RA, RS, RB } },
+
+{ "and", XRC(31,28,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "and.", XRC(31,28,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "maskg", XRC(31,29,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "maskg.", XRC(31,29,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPC, { OBF, RA, RB } },
+{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } },
+{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } },
+{ "cmpl", X(31,32), XCMPL_MASK, POWER, { BF, RA, RB } },
+
+{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } },
+{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } },
+
+{ "ldux", X(31,53), X_MASK, PPC|B64, { RT, RAL, RB } },
+
+{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } },
+
+{ "lwzux", X(31,55), X_MASK, PPC, { RT, RAL, RB } },
+{ "lux", X(31,55), X_MASK, POWER, { RT, RA, RB } },
+
+{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC|B64, { RA, RS } },
+{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC|B64, { RA, RS } },
+
+{ "andc", XRC(31,60,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "andc.", XRC(31,60,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC|B64, { RA, RB } },
+{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC|B64, { RA, RB } },
+{ "td", X(31,68), X_MASK, PPC|B64, { TO, RA, RB } },
+
+{ "mulhd", XO(31,73,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "mfmsr", X(31,83), XRARB_MASK, PPC|POWER, { RT } },
+
+{ "ldarx", X(31,84), X_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } },
+
+{ "lbzx", X(31,87), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "neg", XO(31,104,0,0), XORB_MASK, PPC|POWER, { RT, RA } },
+{ "neg.", XO(31,104,0,1), XORB_MASK, PPC|POWER, { RT, RA } },
+{ "nego", XO(31,104,1,0), XORB_MASK, PPC|POWER, { RT, RA } },
+{ "nego.", XO(31,104,1,1), XORB_MASK, PPC|POWER, { RT, RA } },
+
+{ "mul", XO(31,107,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "mul.", XO(31,107,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "mulo", XO(31,107,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "mulo.", XO(31,107,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } },
+
+{ "lbzux", X(31,119), X_MASK, PPC|POWER, { RT, RAL, RB } },
+
+{ "not", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "nor", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "not.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "nor.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "subfe", XO(31,136,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfe", XO(31,136,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subfe.", XO(31,136,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfe.", XO(31,136,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "subfeo", XO(31,136,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfeo", XO(31,136,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "subfeo.", XO(31,136,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "sfeo.", XO(31,136,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "adde", XO(31,138,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "ae", XO(31,138,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "adde.", XO(31,138,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "ae.", XO(31,138,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "addeo", XO(31,138,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "aeo", XO(31,138,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addeo.", XO(31,138,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "aeo.", XO(31,138,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }},
+{ "mtcrf", X(31,144), XFXFXM_MASK, PPC|POWER, { FXM, RS } },
+
+{ "mtmsr", X(31,146), XRARB_MASK, PPC|POWER, { RS } },
+
+{ "stdx", X(31,149), X_MASK, PPC|B64, { RS, RA, RB } },
+
+{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } },
+
+{ "stwx", X(31,151), X_MASK, PPC, { RS, RA, RB } },
+{ "stx", X(31,151), X_MASK, POWER, { RS, RA, RB } },
+
+{ "slq", XRC(31,152,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "slq.", XRC(31,152,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sle", XRC(31,153,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sle.", XRC(31,153,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "stdux", X(31,181), X_MASK, PPC|B64, { RS, RAS, RB } },
+
+{ "stwux", X(31,183), X_MASK, PPC, { RS, RAS, RB } },
+{ "stux", X(31,183), X_MASK, POWER, { RS, RA, RB } },
+
+{ "sliq", XRC(31,184,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "sliq.", XRC(31,184,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "subfze", XO(31,200,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfze", XO(31,200,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfze.", XO(31,200,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfzeo", XO(31,200,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfzeo.", XO(31,200,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "addze", XO(31,202,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "aze", XO(31,202,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "addze.", XO(31,202,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "aze.", XO(31,202,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "addzeo", XO(31,202,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "azeo", XO(31,202,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "azeo.", XO(31,202,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "mtsr", X(31,210), XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } },
+
+{ "stdcx.", XRC(31,214,1), X_MASK, PPC|B64, { RS, RA, RB } },
+
+{ "stbx", X(31,215), X_MASK, PPC|POWER, { RS, RA, RB } },
+
+{ "sllq", XRC(31,216,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sllq.", XRC(31,216,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sleq", XRC(31,217,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sleq.", XRC(31,217,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "subfme", XO(31,232,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfme", XO(31,232,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfme.", XO(31,232,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "sfmeo", XO(31,232,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "sfmeo.", XO(31,232,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "mulld", XO(31,233,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulld.", XO(31,233,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulldo", XO(31,233,1,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "addme", XO(31,234,0,0), XORB_MASK, PPC, { RT, RA } },
+{ "ame", XO(31,234,0,0), XORB_MASK, POWER, { RT, RA } },
+{ "addme.", XO(31,234,0,1), XORB_MASK, PPC, { RT, RA } },
+{ "ame.", XO(31,234,0,1), XORB_MASK, POWER, { RT, RA } },
+{ "addmeo", XO(31,234,1,0), XORB_MASK, PPC, { RT, RA } },
+{ "ameo", XO(31,234,1,0), XORB_MASK, POWER, { RT, RA } },
+{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC, { RT, RA } },
+{ "ameo.", XO(31,234,1,1), XORB_MASK, POWER, { RT, RA } },
+
+{ "mullw", XO(31,235,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "muls", XO(31,235,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "mullw.", XO(31,235,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "muls.", XO(31,235,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "mullwo", XO(31,235,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulso", XO(31,235,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "mullwo.", XO(31,235,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "mulso.", XO(31,235,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "mtsrin", X(31,242), XRA_MASK, PPC|B32, { RS, RB } },
+{ "mtsri", X(31,242), XRA_MASK, POWER|B32, { RS, RB } },
+
+{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } },
+
+{ "stbux", X(31,247), X_MASK, PPC|POWER, { RS, RAS, RB } },
+
+{ "slliq", XRC(31,248,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "slliq.", XRC(31,248,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "doz", XO(31,264,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "doz.", XO(31,264,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "dozo", XO(31,264,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "dozo.", XO(31,264,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "add", XO(31,266,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "cax", XO(31,266,0,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "add.", XO(31,266,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "cax.", XO(31,266,0,1), XO_MASK, POWER, { RT, RA, RB } },
+{ "addo", XO(31,266,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "caxo", XO(31,266,1,0), XO_MASK, POWER, { RT, RA, RB } },
+{ "addo.", XO(31,266,1,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "caxo.", XO(31,266,1,1), XO_MASK, POWER, { RT, RA, RB } },
+
+{ "lscbx", XRC(31,277,0), X_MASK, POWER|M601, { RT, RA, RB } },
+{ "lscbx.", XRC(31,277,1), X_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } },
+
+{ "lhzx", X(31,279), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "icbt", X(31,262), XRT_MASK, PPC, { RA, RB } },
+
+{ "eqv", XRC(31,284,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "eqv.", XRC(31,284,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } },
+{ "tlbi", X(31,306), XRTRA_MASK, POWER, { RB } },
+
+{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } },
+
+{ "lhzux", X(31,311), X_MASK, PPC|POWER, { RT, RAL, RB } },
+
+{ "xor", XRC(31,316,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "xor.", XRC(31,316,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "mfdcr", X(31,323), X_MASK, PPC, { RT, SPR } },
+
+{ "div", XO(31,331,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "div.", XO(31,331,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divo", XO(31,331,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divo.", XO(31,331,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "mfmq", XSPR(31,339,0), XSPR_MASK, POWER|M601, { RT } },
+{ "mfxer", XSPR(31,339,1), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfdec", XSPR(31,339,6), XSPR_MASK, POWER|M601, { RT } },
+{ "mflr", XSPR(31,339,8), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfctr", XSPR(31,339,9), XSPR_MASK, PPC|POWER, { RT } },
+{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } },
+{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfdar", XSPR(31,339,19), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfdec", XSPR(31,339,22), XSPR_MASK, PPC, { RT } },
+{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } },
+{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, PPC|POWER, { RT } },
+{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } },
+{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC|B64, { RT } },
+{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } },
+{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } },
+{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } },
+{ "mfspr", X(31,339), X_MASK, PPC|POWER, { RT, SPR } },
+
+{ "lwax", X(31,341), X_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "lhax", X(31,343), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "dccci", X(31,454), XRT_MASK, PPC, { RA, RB } },
+
+{ "abs", XO(31,360,0,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "abs.", XO(31,360,0,1), XORB_MASK, POWER|M601, { RT, RA } },
+{ "abso", XO(31,360,1,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "abso.", XO(31,360,1,1), XORB_MASK, POWER|M601, { RT, RA } },
+
+{ "divs", XO(31,363,0,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divs.", XO(31,363,0,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divso", XO(31,363,1,0), XO_MASK, POWER|M601, { RT, RA, RB } },
+{ "divso.", XO(31,363,1,1), XO_MASK, POWER|M601, { RT, RA, RB } },
+
+{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } },
+
+{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } },
+{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } },
+
+{ "lwaux", X(31,373), X_MASK, PPC|B64, { RT, RAL, RB } },
+
+{ "lhaux", X(31,375), X_MASK, PPC|POWER, { RT, RAL, RB } },
+
+{ "sthx", X(31,407), X_MASK, PPC|POWER, { RS, RA, RB } },
+
+{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } },
+
+{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } },
+
+{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } },
+
+{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } },
+
+{ "orc", XRC(31,412,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "orc.", XRC(31,412,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "sradi", XS(31,413,0), XS_MASK, PPC|B64, { RA, RS, SH6 } },
+{ "sradi.", XS(31,413,1), XS_MASK, PPC|B64, { RA, RS, SH6 } },
+
+{ "slbie", X(31,434), XRTRA_MASK, PPC|B64, { RB } },
+
+{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } },
+
+{ "sthux", X(31,439), X_MASK, PPC|POWER, { RS, RAS, RB } },
+
+{ "mr", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "or", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "mr.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RBS } },
+{ "or.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "mtdcr", X(31,451), X_MASK, PPC, { SPR, RS } },
+
+{ "divdu", XO(31,457,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divdu.", XO(31,457,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divduo", XO(31,457,1,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divduo.", XO(31,457,1,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "mtmq", XSPR(31,467,0), XSPR_MASK, POWER|M601, { RS } },
+{ "mtxer", XSPR(31,467,1), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtlr", XSPR(31,467,8), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtctr", XSPR(31,467,9), XSPR_MASK, PPC|POWER, { RS } },
+{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } },
+{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtdar", XSPR(31,467,19), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtdec", XSPR(31,467,22), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } },
+{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, PPC|POWER, { RS } },
+{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } },
+{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC|B64, { RS } },
+{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } },
+{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } },
+{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } },
+{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } },
+{ "mtspr", X(31,467), X_MASK, PPC|POWER, { SPR, RS } },
+
+{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } },
+
+{ "nand", XRC(31,476,0), X_MASK, PPC|POWER, { RA, RS, RB } },
+{ "nand.", XRC(31,476,1), X_MASK, PPC|POWER, { RA, RS, RB } },
+
+{ "nabs", XO(31,488,0,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "nabs.", XO(31,488,0,1), XORB_MASK, POWER|M601, { RT, RA } },
+{ "nabso", XO(31,488,1,0), XORB_MASK, POWER|M601, { RT, RA } },
+{ "nabso.", XO(31,488,1,1), XORB_MASK, POWER|M601, { RT, RA } },
+
+{ "divd", XO(31,489,0,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divd.", XO(31,489,0,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divdo", XO(31,489,1,0), XO_MASK, PPC|B64, { RT, RA, RB } },
+{ "divdo.", XO(31,489,1,1), XO_MASK, PPC|B64, { RT, RA, RB } },
+
+{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } },
+{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } },
+
+{ "slbia", X(31,498), 0xffffffff, PPC|B64, { 0 } },
+
+{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } },
+
+{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), PPC|POWER, { BF } },
+
+{ "clcs", X(31,531), XRB_MASK, POWER|M601, { RT, RA } },
+
+{ "lswx", X(31,533), X_MASK, PPC, { RT, RA, RB } },
+{ "lsx", X(31,533), X_MASK, POWER, { RT, RA, RB } },
+
+{ "lwbrx", X(31,534), X_MASK, PPC, { RT, RA, RB } },
+{ "lbrx", X(31,534), X_MASK, POWER, { RT, RA, RB } },
+
+{ "lfsx", X(31,535), X_MASK, PPC|POWER, { FRT, RA, RB } },
+
+{ "srw", XRC(31,536,0), X_MASK, PPC, { RA, RS, RB } },
+{ "sr", XRC(31,536,0), X_MASK, POWER, { RA, RS, RB } },
+{ "srw.", XRC(31,536,1), X_MASK, PPC, { RA, RS, RB } },
+{ "sr.", XRC(31,536,1), X_MASK, POWER, { RA, RS, RB } },
+
+{ "rrib", XRC(31,537,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "rrib.", XRC(31,537,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "srd", XRC(31,539,0), X_MASK, PPC|B64, { RA, RS, RB } },
+{ "srd.", XRC(31,539,1), X_MASK, PPC|B64, { RA, RS, RB } },
+
+{ "maskir", XRC(31,541,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "maskir.", XRC(31,541,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } },
+
+{ "lfsux", X(31,567), X_MASK, PPC|POWER, { FRT, RAS, RB } },
+
+{ "mfsr", X(31,595), XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } },
+
+{ "lswi", X(31,597), X_MASK, PPC, { RT, RA, NB } },
+{ "lsi", X(31,597), X_MASK, POWER, { RT, RA, NB } },
+
+{ "sync", X(31,598), 0xffffffff, PPC, { 0 } },
+{ "dcs", X(31,598), 0xffffffff, POWER, { 0 } },
+
+{ "lfdx", X(31,599), X_MASK, PPC|POWER, { FRT, RA, RB } },
+
+{ "mfsri", X(31,627), X_MASK, POWER, { RT, RA, RB } },
+
+{ "dclst", X(31,630), XRB_MASK, POWER, { RS, RA } },
+
+{ "lfdux", X(31,631), X_MASK, PPC|POWER, { FRT, RAS, RB } },
+
+{ "mfsrin", X(31,659), XRA_MASK, PPC|B32, { RT, RB } },
+
+{ "stswx", X(31,661), X_MASK, PPC, { RS, RA, RB } },
+{ "stsx", X(31,661), X_MASK, POWER, { RS, RA, RB } },
+
+{ "stwbrx", X(31,662), X_MASK, PPC, { RS, RA, RB } },
+{ "stbrx", X(31,662), X_MASK, POWER, { RS, RA, RB } },
+
+{ "stfsx", X(31,663), X_MASK, PPC|POWER, { FRS, RA, RB } },
+
+{ "srq", XRC(31,664,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "srq.", XRC(31,664,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sre", XRC(31,665,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sre.", XRC(31,665,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "stfsux", X(31,695), X_MASK, PPC|POWER, { FRS, RAS, RB } },
+
+{ "sriq", XRC(31,696,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "sriq.", XRC(31,696,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "stswi", X(31,725), X_MASK, PPC, { RS, RA, NB } },
+{ "stsi", X(31,725), X_MASK, POWER, { RS, RA, NB } },
+
+{ "stfdx", X(31,727), X_MASK, PPC|POWER, { FRS, RA, RB } },
+
+{ "srlq", XRC(31,728,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "srlq.", XRC(31,728,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "sreq", XRC(31,729,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sreq.", XRC(31,729,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "stfdux", X(31,759), X_MASK, PPC|POWER, { FRS, RAS, RB } },
+
+{ "srliq", XRC(31,760,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "srliq.", XRC(31,760,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "lhbrx", X(31,790), X_MASK, PPC|POWER, { RT, RA, RB } },
+
+{ "sraw", XRC(31,792,0), X_MASK, PPC, { RA, RS, RB } },
+{ "sra", XRC(31,792,0), X_MASK, POWER, { RA, RS, RB } },
+{ "sraw.", XRC(31,792,1), X_MASK, PPC, { RA, RS, RB } },
+{ "sra.", XRC(31,792,1), X_MASK, POWER, { RA, RS, RB } },
+
+{ "srad", XRC(31,794,0), X_MASK, PPC|B64, { RA, RS, RB } },
+{ "srad.", XRC(31,794,1), X_MASK, PPC|B64, { RA, RS, RB } },
+
+{ "rac", X(31,818), X_MASK, POWER, { RT, RA, RB } },
+
+{ "srawi", XRC(31,824,0), X_MASK, PPC, { RA, RS, SH } },
+{ "srai", XRC(31,824,0), X_MASK, POWER, { RA, RS, SH } },
+{ "srawi.", XRC(31,824,1), X_MASK, PPC, { RA, RS, SH } },
+{ "srai.", XRC(31,824,1), X_MASK, POWER, { RA, RS, SH } },
+
+{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } },
+
+{ "sthbrx", X(31,918), X_MASK, PPC|POWER, { RS, RA, RB } },
+
+{ "sraq", XRC(31,920,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "sraq.", XRC(31,920,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "srea", XRC(31,921,0), X_MASK, POWER|M601, { RA, RS, RB } },
+{ "srea.", XRC(31,921,1), X_MASK, POWER|M601, { RA, RS, RB } },
+
+{ "extsh", XRC(31,922,0), XRB_MASK, PPC, { RA, RS } },
+{ "exts", XRC(31,922,0), XRB_MASK, POWER, { RA, RS } },
+{ "extsh.", XRC(31,922,1), XRB_MASK, PPC, { RA, RS } },
+{ "exts.", XRC(31,922,1), XRB_MASK, POWER, { RA, RS } },
+
+{ "sraiq", XRC(31,952,0), X_MASK, POWER|M601, { RA, RS, SH } },
+{ "sraiq.", XRC(31,952,1), X_MASK, POWER|M601, { RA, RS, SH } },
+
+{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} },
+{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} },
+
+{ "iccci", X(31,966), XRT_MASK, PPC, { RA, RB } },
+
+{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } },
+
+{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } },
+
+{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } },
+{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } },
+
+{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } },
+{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } },
+
+{ "lwz", OP(32), OP_MASK, PPC, { RT, D, RA } },
+{ "l", OP(32), OP_MASK, POWER, { RT, D, RA } },
+
+{ "lwzu", OP(33), OP_MASK, PPC, { RT, D, RAL } },
+{ "lu", OP(33), OP_MASK, POWER, { RT, D, RA } },
+
+{ "lbz", OP(34), OP_MASK, PPC|POWER, { RT, D, RA } },
+
+{ "lbzu", OP(35), OP_MASK, PPC|POWER, { RT, D, RAL } },
+
+{ "stw", OP(36), OP_MASK, PPC, { RS, D, RA } },
+{ "st", OP(36), OP_MASK, POWER, { RS, D, RA } },
+
+{ "stwu", OP(37), OP_MASK, PPC, { RS, D, RAS } },
+{ "stu", OP(37), OP_MASK, POWER, { RS, D, RA } },
+
+{ "stb", OP(38), OP_MASK, PPC|POWER, { RS, D, RA } },
+
+{ "stbu", OP(39), OP_MASK, PPC|POWER, { RS, D, RAS } },
+
+{ "lhz", OP(40), OP_MASK, PPC|POWER, { RT, D, RA } },
+
+{ "lhzu", OP(41), OP_MASK, PPC|POWER, { RT, D, RAL } },
+
+{ "lha", OP(42), OP_MASK, PPC|POWER, { RT, D, RA } },
+
+{ "lhau", OP(43), OP_MASK, PPC|POWER, { RT, D, RAL } },
+
+{ "sth", OP(44), OP_MASK, PPC|POWER, { RS, D, RA } },
+
+{ "sthu", OP(45), OP_MASK, PPC|POWER, { RS, D, RAS } },
+
+{ "lmw", OP(46), OP_MASK, PPC, { RT, D, RAM } },
+{ "lm", OP(46), OP_MASK, POWER, { RT, D, RA } },
+
+{ "stmw", OP(47), OP_MASK, PPC, { RS, D, RA } },
+{ "stm", OP(47), OP_MASK, POWER, { RS, D, RA } },
+
+{ "lfs", OP(48), OP_MASK, PPC|POWER, { FRT, D, RA } },
+
+{ "lfsu", OP(49), OP_MASK, PPC|POWER, { FRT, D, RAS } },
+
+{ "lfd", OP(50), OP_MASK, PPC|POWER, { FRT, D, RA } },
+
+{ "lfdu", OP(51), OP_MASK, PPC|POWER, { FRT, D, RAS } },
+
+{ "stfs", OP(52), OP_MASK, PPC|POWER, { FRS, D, RA } },
+
+{ "stfsu", OP(53), OP_MASK, PPC|POWER, { FRS, D, RAS } },
+
+{ "stfd", OP(54), OP_MASK, PPC|POWER, { FRS, D, RA } },
+
+{ "stfdu", OP(55), OP_MASK, PPC|POWER, { FRS, D, RAS } },
+
+{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } },
+
+{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } },
+
+{ "ld", DSO(58,0), DS_MASK, PPC|B64, { RT, DS, RA } },
+
+{ "ldu", DSO(58,1), DS_MASK, PPC|B64, { RT, DS, RAL } },
+
+{ "lwa", DSO(58,2), DS_MASK, PPC|B64, { RT, DS, RA } },
+
+{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+
+{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } },
+{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } },
+
+{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } },
+{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } },
+
+{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+
+{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } },
+
+{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } },
+
+{ "std", DSO(62,0), DS_MASK, PPC|B64, { RS, DS, RA } },
+
+{ "stdu", DSO(62,1), DS_MASK, PPC|B64, { RS, DS, RAS } },
+
+{ "fcmpu", X(63,0), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } },
+
+{ "frsp", XRC(63,12,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "frsp.", XRC(63,12,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "fctiw", XRC(63,14,0), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } },
+{ "fctiw.", XRC(63,14,1), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } },
+
+{ "fctiwz", XRC(63,15,0), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } },
+{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPC, { FRT, FRB } },
+{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } },
+
+{ "fdiv", A(63,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fd", A(63,18,0), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+{ "fdiv.", A(63,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fd.", A(63,18,1), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+
+{ "fsub", A(63,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fs", A(63,20,0), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+{ "fsub.", A(63,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fs.", A(63,20,1), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+
+{ "fadd", A(63,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fa", A(63,21,0), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+{ "fadd.", A(63,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } },
+{ "fa.", A(63,21,1), AFRC_MASK, POWER, { FRT, FRA, FRB } },
+
+{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } },
+{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } },
+
+{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+
+{ "fmul", A(63,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+{ "fm", A(63,25,0), AFRB_MASK, POWER, { FRT, FRA, FRC } },
+{ "fmul.", A(63,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } },
+{ "fm.", A(63,25,1), AFRB_MASK, POWER, { FRT, FRA, FRC } },
+
+{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } },
+{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } },
+
+{ "fmsub", A(63,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fms", A(63,28,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fmsub.", A(63,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fms.", A(63,28,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fmadd", A(63,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fma", A(63,29,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fmadd.", A(63,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fma.", A(63,29,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fnmsub", A(63,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnms", A(63,30,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fnmsub.", A(63,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnms.", A(63,30,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fnmadd", A(63,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnma", A(63,31,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+{ "fnmadd.", A(63,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } },
+{ "fnma.", A(63,31,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } },
+
+{ "fcmpo", X(63,30), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } },
+
+{ "mtfsb1", XRC(63,38,0), XRARB_MASK, PPC|POWER, { BT } },
+{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, PPC|POWER, { BT } },
+
+{ "fneg", XRC(63,40,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fneg.", XRC(63,40,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "mtfsb0", XRC(63,70,0), XRARB_MASK, PPC|POWER, { BT } },
+{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, PPC|POWER, { BT } },
+
+{ "fmr", XRC(63,72,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fmr.", XRC(63,72,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+
+{ "fnabs", XRC(63,136,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fnabs.", XRC(63,136,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "fabs", XRC(63,264,0), XRA_MASK, PPC|POWER, { FRT, FRB } },
+{ "fabs.", XRC(63,264,1), XRA_MASK, PPC|POWER, { FRT, FRB } },
+
+{ "mffs", XRC(63,583,0), XRARB_MASK, PPC|POWER, { FRT } },
+{ "mffs.", XRC(63,583,1), XRARB_MASK, PPC|POWER, { FRT } },
+
+{ "mtfsf", XFL(63,711,0), XFL_MASK, PPC|POWER, { FLM, FRB } },
+{ "mtfsf.", XFL(63,711,1), XFL_MASK, PPC|POWER, { FLM, FRB } },
+
+{ "fctid", XRC(63,814,0), XRA_MASK, PPC|B64, { FRT, FRB } },
+{ "fctid.", XRC(63,814,1), XRA_MASK, PPC|B64, { FRT, FRB } },
+
+{ "fctidz", XRC(63,815,0), XRA_MASK, PPC|B64, { FRT, FRB } },
+{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC|B64, { FRT, FRB } },
+
+{ "fcfid", XRC(63,846,0), XRA_MASK, PPC|B64, { FRT, FRB } },
+{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC|B64, { FRT, FRB } },
+
+};
+
+const int powerpc_num_opcodes =
+ sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
+\f
+/* The macro table. This is only used by the assembler. */
+
+const struct powerpc_macro powerpc_macros[] = {
+{ "extldi", 4, PPC|B64, "rldicr %0,%1,%3,(%2)-1" },
+{ "extldi.", 4, PPC|B64, "rldicr. %0,%1,%3,(%2)-1" },
+{ "extrdi", 4, PPC|B64, "rldicl %0,%1,(%2)+(%3),64-(%2)" },
+{ "extrdi.", 4, PPC|B64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" },
+{ "insrdi", 4, PPC|B64, "rldimi %0,%1,64-((%2)+(%3)),%3" },
+{ "insrdi.", 4, PPC|B64, "rldimi. %0,%1,64-((%2)+(%3)),%3" },
+{ "rotrdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),0" },
+{ "rotrdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),0" },
+{ "sldi", 3, PPC|B64, "rldicr %0,%1,%2,63-(%2)" },
+{ "sldi.", 3, PPC|B64, "rldicr. %0,%1,%2,63-(%2)" },
+{ "srdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),%2" },
+{ "srdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),%2" },
+{ "clrrdi", 3, PPC|B64, "rldicr %0,%1,0,63-(%2)" },
+{ "clrrdi.", 3, PPC|B64, "rldicr. %0,%1,0,63-(%2)" },
+{ "clrlsldi",4, PPC|B64, "rldic %0,%1,%3,(%2)-(%3)" },
+{ "clrlsldi.",4, PPC|B64, "rldic. %0,%1,%3,(%2)-(%3)" },
+
+{ "extlwi", 4, PPC, "rlwinm %0,%1,%3,0,(%2)-1" },
+{ "extlwi.", 4, PPC, "rlwinm. %0,%1,%3,0,(%2)-1" },
+{ "extrwi", 4, PPC, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" },
+{ "extrwi.", 4, PPC, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" },
+{ "inslwi", 4, PPC, "rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "inslwi.", 4, PPC, "rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "insrwi", 4, PPC, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" },
+{ "insrwi.", 4, PPC, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"},
+{ "rotrwi", 3, PPC, "rlwinm %0,%1,32-(%2),0,31" },
+{ "rotrwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),0,31" },
+{ "slwi", 3, PPC, "rlwinm %0,%1,%2,0,31-(%2)" },
+{ "sli", 3, POWER, "rlinm %0,%1,%2,0,31-(%2)" },
+{ "slwi.", 3, PPC, "rlwinm. %0,%1,%2,0,31-(%2)" },
+{ "sli.", 3, POWER, "rlinm. %0,%1,%2,0,31-(%2)" },
+{ "srwi", 3, PPC, "rlwinm %0,%1,32-(%2),%2,31" },
+{ "sri", 3, POWER, "rlinm %0,%1,32-(%2),%2,31" },
+{ "srwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),%2,31" },
+{ "sri.", 3, POWER, "rlinm. %0,%1,32-(%2),%2,31" },
+{ "clrrwi", 3, PPC, "rlwinm %0,%1,0,0,31-(%2)" },
+{ "clrrwi.", 3, PPC, "rlwinm. %0,%1,0,0,31-(%2)" },
+{ "clrlslwi",4, PPC, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" },
+{ "clrlslwi.",4, PPC, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" },
+
+};
+
+const int powerpc_num_macros =
+ sizeof (powerpc_macros) / sizeof (powerpc_macros[0]);
--- /dev/null
+/* ppc.h -- Header file for PowerPC opcode table
+ Copyright 1994 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef PPC_H
+#define PPC_H
+
+/* The opcode table is an array of struct powerpc_opcode. */
+
+struct powerpc_opcode
+{
+ /* The opcode name. */
+ const char *name;
+
+ /* The opcode itself. Those bits which will be filled in with
+ operands are zeroes. */
+ unsigned long opcode;
+
+ /* The opcode mask. This is used by the disassembler. This is a
+ mask containing ones indicating those bits which must match the
+ opcode field, and zeroes indicating those bits which need not
+ match (and are presumably filled in by operands). */
+ unsigned long mask;
+
+ /* One bit flags for the opcode. These are used to indicate which
+ specific processors support the instructions. The defined values
+ are listed below. */
+ unsigned long flags;
+
+ /* An array of operand codes. Each code is an index into the
+ operand table. They appear in the order which the operands must
+ appear in assembly code, and are terminated by a zero. */
+ unsigned char operands[8];
+};
+
+/* The table itself is sorted by major opcode number, and is otherwise
+ in the order in which the disassembler should consider
+ instructions. */
+extern const struct powerpc_opcode powerpc_opcodes[];
+extern const int powerpc_num_opcodes;
+
+/* Values defined for the flags field of a struct powerpc_opcode. */
+
+/* Opcode is defined for the PowerPC architecture. */
+#define PPC_OPCODE_PPC (01)
+
+/* Opcode is defined for the POWER (RS/6000) architecture. */
+#define PPC_OPCODE_POWER (02)
+
+/* Opcode is defined for the POWER2 (Rios 2) architecture. */
+#define PPC_OPCODE_POWER2 (04)
+
+/* Opcode is only defined on 32 bit architectures. */
+#define PPC_OPCODE_32 (010)
+
+/* Opcode is only defined on 64 bit architectures. */
+#define PPC_OPCODE_64 (020)
+
+/* Opcode is supported by the Motorola PowerPC 601 processor. The 601
+ is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions,
+ but it also supports many additional POWER instructions. */
+#define PPC_OPCODE_601 (040)
+
+/* A macro to extract the major opcode from an instruction. */
+#define PPC_OP(i) (((i) >> 26) & 0x3f)
+\f
+/* The operands table is an array of struct powerpc_operand. */
+
+struct powerpc_operand
+{
+ /* The number of bits in the operand. */
+ int bits;
+
+ /* How far the operand is left shifted in the instruction. */
+ int shift;
+
+ /* Insertion function. This is used by the assembler. To insert an
+ operand value into an instruction, check this field.
+
+ If it is NULL, execute
+ i |= (op & ((1 << o->bits) - 1)) << o->shift;
+ (i is the instruction which we are filling in, o is a pointer to
+ this structure, and op is the opcode value; this assumes twos
+ complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction and the operand value. It will return the new value
+ of the instruction. If the ERRMSG argument is not NULL, then if
+ the operand value is illegal, *ERRMSG will be set to a warning
+ string (the operand will be inserted in any case). If the
+ operand value is legal, *ERRMSG will be unchanged (most operands
+ can accept any value). */
+ unsigned long (*insert) PARAMS ((unsigned long instruction, long op,
+ const char **errmsg));
+
+ /* Extraction function. This is used by the disassembler. To
+ extract this operand type from an instruction, check this field.
+
+ If it is NULL, compute
+ op = ((i) >> o->shift) & ((1 << o->bits) - 1);
+ if ((o->flags & PPC_OPERAND_SIGNED) != 0
+ && (op & (1 << (o->bits - 1))) != 0)
+ op -= 1 << o->bits;
+ (i is the instruction, o is a pointer to this structure, and op
+ is the result; this assumes twos complement arithmetic).
+
+ If this field is not NULL, then simply call it with the
+ instruction value. It will return the value of the operand. If
+ the INVALID argument is not NULL, *INVALID will be set to
+ non-zero if this operand type can not actually be extracted from
+ this operand (i.e., the instruction does not match). If the
+ operand is valid, *INVALID will not be changed. */
+ long (*extract) PARAMS ((unsigned long instruction, int *invalid));
+
+ /* One bit syntax flags. */
+ unsigned long flags;
+};
+
+/* Elements in the table are retrieved by indexing with values from
+ the operands field of the powerpc_opcodes table. */
+
+extern const struct powerpc_operand powerpc_operands[];
+
+/* Values defined for the flags field of a struct powerpc_operand. */
+
+/* This operand takes signed values. */
+#define PPC_OPERAND_SIGNED (01)
+
+/* This operand takes signed values, but also accepts a full positive
+ range of values when running in 32 bit mode. That is, if bits is
+ 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode,
+ this flag is ignored. */
+#define PPC_OPERAND_SIGNOPT (02)
+
+/* This operand does not actually exist in the assembler input. This
+ is used to support extended mnemonics such as mr, for which two
+ operands fields are identical. The assembler should call the
+ insert function with any op value. The disassembler should call
+ the extract function, ignore the return value, and check the value
+ placed in the valid argument. */
+#define PPC_OPERAND_FAKE (04)
+
+/* The next operand should be wrapped in parentheses rather than
+ separated from this one by a comma. This is used for the load and
+ store instructions which want their operands to look like
+ reg,displacement(reg)
+ */
+#define PPC_OPERAND_PARENS (010)
+
+/* This operand may use the symbolic names for the CR fields, which
+ are
+ lt 0 gt 1 eq 2 so 3 un 3
+ cr0 0 cr1 1 cr2 2 cr3 3
+ cr4 4 cr5 5 cr6 6 cr7 7
+ These may be combined arithmetically, as in cr2*4+gt. These are
+ only supported on the PowerPC, not the POWER. */
+#define PPC_OPERAND_CR (020)
+
+/* This operand names a register. The disassembler uses this to print
+ register names with a leading 'r'. */
+#define PPC_OPERAND_GPR (040)
+
+/* This operand names a floating point register. The disassembler
+ prints these with a leading 'f'. */
+#define PPC_OPERAND_FPR (0100)
+
+/* This operand is a relative branch displacement. The disassembler
+ prints these symbolically if possible. */
+#define PPC_OPERAND_RELATIVE (0200)
+
+/* This operand is an absolute branch address. The disassembler
+ prints these symbolically if possible. */
+#define PPC_OPERAND_ABSOLUTE (0400)
+
+/* This operand is optional, and is zero if omitted. This is used for
+ the optional BF and L fields in the comparison instructions. The
+ assembler must count the number of operands remaining on the line,
+ and the number of operands remaining for the opcode, and decide
+ whether this operand is present or not. The disassembler should
+ print this operand out only if it is not zero. */
+#define PPC_OPERAND_OPTIONAL (01000)
+
+/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand
+ is omitted, then for the next operand use this operand value plus
+ 1, ignoring the next operand field for the opcode. This wretched
+ hack is needed because the Power rotate instructions can take
+ either 4 or 5 operands. The disassembler should print this operand
+ out regardless of the PPC_OPERAND_OPTIONAL field. */
+#define PPC_OPERAND_NEXT (02000)
+
+/* This operand should be regarded as a negative number for the
+ purposes of overflow checking (i.e., the normal most negative
+ number is disallowed and one more than the normal most positive
+ number is allowed). This flag will only be set for a signed
+ operand. */
+#define PPC_OPERAND_NEGATIVE (04000)
+\f
+/* The POWER and PowerPC assemblers use a few macros. We keep them
+ with the operands table for simplicity. The macro table is an
+ array of struct powerpc_macro. */
+
+struct powerpc_macro
+{
+ /* The macro name. */
+ const char *name;
+
+ /* The number of operands the macro takes. */
+ unsigned int operands;
+
+ /* One bit flags for the opcode. These are used to indicate which
+ specific processors support the instructions. The values are the
+ same as those for the struct powerpc_opcode flags field. */
+ unsigned long flags;
+
+ /* A format string to turn the macro into a normal instruction.
+ Each %N in the string is replaced with operand number N (zero
+ based). */
+ const char *format;
+};
+
+extern const struct powerpc_macro powerpc_macros[];
+extern const int powerpc_num_macros;
+
+#endif /* PPC_H */
--- /dev/null
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+
+#define GETREG(reg) \
+ static inline int get_ ## reg (void) \
+ { int ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; }
+
+#define SETREG(reg) \
+ static inline void set_ ## reg (int val) \
+ { asm volatile ("mt" #reg " %0" : : "r" (val)); }
+
+GETREG(msr)
+SETREG(msr)
+GETREG(cr)
+
+#define GSETSPR(n, name) \
+ static inline int get_ ## name (void) \
+ { int ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \
+ static inline void set_ ## name (int val) \
+ { asm volatile ("mtspr " #n ",%0" : : "r" (val)); }
+
+GSETSPR(0, mq)
+GSETSPR(1, xer)
+GSETSPR(4, rtcu)
+GSETSPR(5, rtcl)
+GSETSPR(8, lr)
+GSETSPR(9, ctr)
+GSETSPR(18, dsisr)
+GSETSPR(19, dar)
+GSETSPR(22, dec)
+GSETSPR(25, sdr1)
+GSETSPR(26, srr0)
+GSETSPR(27, srr1)
+GSETSPR(272, sprg0)
+GSETSPR(273, sprg1)
+GSETSPR(274, sprg2)
+GSETSPR(275, sprg3)
+GSETSPR(282, ear)
+GSETSPR(287, pvr)
+GSETSPR(528, bat0u)
+GSETSPR(529, bat0l)
+GSETSPR(530, bat1u)
+GSETSPR(531, bat1l)
+GSETSPR(532, bat2u)
+GSETSPR(533, bat2l)
+GSETSPR(534, bat3u)
+GSETSPR(535, bat3l)
+GSETSPR(1008, hid0)
+GSETSPR(1009, hid1)
+GSETSPR(1010, iabr)
+GSETSPR(1013, dabr)
+GSETSPR(1023, pir)
+
+static inline int get_sr(int n)
+{
+ int ret;
+
+ asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28));
+ return ret;
+}
+
+static inline void set_sr(int n, int val)
+{
+ asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28));
+}
+
+static inline void store_inst(void *p)
+{
+ asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p));
+}
+
+
--- /dev/null
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * NB this file must be compiled with -O2.
+ */
+
+int
+xmon_setjmp(long *buf)
+{
+ asm ("mflr 0; stw 0,0(%0);"
+ "stw 1,4(%0); stw 2,8(%0);"
+ "mfcr 0; stw 0,12(%0);"
+ "stmw 13,16(%0)"
+ : : "r" (buf));
+ /* XXX should save fp regs as well */
+ return 0;
+}
+
+void
+xmon_longjmp(long *buf, int val)
+{
+ if (val == 0)
+ val = 1;
+ asm ("lmw 13,16(%0);"
+ "lwz 0,12(%0); mtcrf 0x38,0;"
+ "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);"
+ "mtlr 0; mr 3,%1"
+ : : "r" (buf), "r" (val));
+}
--- /dev/null
+/*
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/string.h>
+#include <linux/config.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/adb.h>
+#include <asm/prom.h>
+
+static volatile unsigned char *sccc, *sccd;
+unsigned long TXRDY, RXRDY;
+extern void xmon_printf(const char *fmt, ...);
+
+static int console = 0;
+
+void buf_access(void)
+{
+ if ( _machine == _MACH_chrp )
+ sccd[3] &= ~0x80; /* reset DLAB */
+}
+
+void
+xmon_map_scc(void)
+{
+ volatile unsigned char *base;
+
+ if ( _machine == _MACH_Pmac )
+ {
+ struct device_node *np;
+#ifdef CHRP_ESCC
+ unsigned long addr = 0xc1013020;
+#else
+ unsigned long addr = 0xf3013030;
+#endif
+ TXRDY = 4;
+ RXRDY = 1;
+
+ np = find_devices("mac-io");
+ if (np && np->n_addrs) {
+ addr = np->addrs[0].address + 0x13000;
+ /* use the B channel on the iMac, A channel on others */
+ if (addr >= 0xf0000000)
+ addr += 0x20; /* use A channel */
+ }
+ base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
+ sccc = base + (addr & ~PAGE_MASK);
+#ifdef CHRP_ESCC
+ sccd = sccc + (0xc1013030 - 0xc1013020);
+#else
+ sccd = sccc + (0xf3013030 - 0xf3013020);
+#endif
+ }
+ else
+ {
+ /* should already be mapped by the kernel boot */
+ sccc = (volatile unsigned char *) (isa_io_base + 0x3fd);
+ sccd = (volatile unsigned char *) (isa_io_base + 0x3f8);
+ TXRDY = 0x20;
+ RXRDY = 1;
+ }
+}
+
+static int scc_initialized = 0;
+
+void xmon_init_scc(void);
+extern void pmu_poll(void);
+
+int
+xmon_write(void *handle, void *ptr, int nb)
+{
+ char *p = ptr;
+ int i, ct;
+
+ if (!scc_initialized)
+ xmon_init_scc();
+ for (i = 0; i < nb; ++i) {
+ while ((*sccc & TXRDY) == 0)
+ if (adb_hardware == ADB_VIAPMU)
+ pmu_poll();
+ buf_access();
+ if ( console && (*p != '\r'))
+ printk("%c", *p);
+ ct = 0;
+ if ( *p == '\n')
+ ct = 1;
+ *sccd = *p++;
+ if ( ct )
+ xmon_write(handle, "\r", 1);
+ }
+ return i;
+}
+
+int
+xmon_read(void *handle, void *ptr, int nb)
+{
+ char *p = ptr;
+ int i;
+
+ if (!scc_initialized)
+ xmon_init_scc();
+ for (i = 0; i < nb; ++i) {
+ while ((*sccc & RXRDY) == 0)
+ if (adb_hardware == ADB_VIAPMU)
+ pmu_poll();
+ buf_access();
+#if 0
+ if ( 0/*console*/ )
+ *p++ = ppc_md.kbd_getkeycode();
+ else
+#endif
+ *p++ = *sccd;
+ }
+ return i;
+}
+
+static unsigned char scc_inittab[] = {
+ 13, 0, /* set baud rate divisor */
+ 12, 1,
+ 14, 1, /* baud rate gen enable, src=rtxc */
+ 11, 0x50, /* clocks = br gen */
+ 5, 0x6a, /* tx 8 bits, assert RTS */
+ 4, 0x44, /* x16 clock, 1 stop */
+ 3, 0xc1, /* rx enable, 8 bits */
+};
+
+void
+xmon_init_scc()
+{
+ if ( _machine == _MACH_chrp )
+ {
+ sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */
+ sccd[0] = 3; eieio(); /* DLL = 38400 baud */
+ sccd[1] = 0; eieio();
+ sccd[2] = 0; eieio(); /* FCR = 0 */
+ sccd[3] = 3; eieio(); /* LCR = 8N1 */
+ sccd[1] = 0; eieio(); /* IER = 0 */
+ }
+ else
+ {
+ int i, x;
+
+ for (i = 20000; i != 0; --i) {
+ x = *sccc; eieio();
+ }
+ *sccc = 9; eieio(); /* reset A or B side */
+ *sccc = ((unsigned long)sccc & 0x20)? 0x80: 0x40; eieio();
+ for (i = 0; i < sizeof(scc_inittab); ++i) {
+ *sccc = scc_inittab[i];
+ eieio();
+ }
+ }
+ scc_initialized = 1;
+}
+
+#if 0
+extern int (*prom_entry)(void *);
+
+int
+xmon_exit(void)
+{
+ struct prom_args {
+ char *service;
+ } args;
+
+ for (;;) {
+ args.service = "exit";
+ (*prom_entry)(&args);
+ }
+}
+#endif
+
+void *xmon_stdin;
+void *xmon_stdout;
+void *xmon_stderr;
+
+void
+xmon_init(void)
+{
+}
+
+int
+xmon_putc(int c, void *f)
+{
+ char ch = c;
+
+ if (c == '\n')
+ xmon_putc('\r', f);
+ return xmon_write(f, &ch, 1) == 1? c: -1;
+}
+
+int
+xmon_putchar(int c)
+{
+ return xmon_putc(c, xmon_stdout);
+}
+
+int
+xmon_fputs(char *str, void *f)
+{
+ int n = strlen(str);
+
+ return xmon_write(f, str, n) == n? 0: -1;
+}
+
+int
+xmon_readchar(void)
+{
+ char ch;
+
+ for (;;) {
+ switch (xmon_read(xmon_stdin, &ch, 1)) {
+ case 1:
+ return ch;
+ case -1:
+ xmon_printf("read(stdin) returned -1\r\n", 0, 0);
+ return -1;
+ }
+ }
+}
+
+static char line[256];
+static char *lineptr;
+static int lineleft;
+
+int
+xmon_getchar(void)
+{
+ int c;
+
+ if (lineleft == 0) {
+ lineptr = line;
+ for (;;) {
+ c = xmon_readchar();
+ if (c == -1 || c == 4)
+ break;
+ if (c == '\r' || c == '\n') {
+ *lineptr++ = '\n';
+ xmon_putchar('\n');
+ break;
+ }
+ switch (c) {
+ case 0177:
+ case '\b':
+ if (lineptr > line) {
+ xmon_putchar('\b');
+ xmon_putchar(' ');
+ xmon_putchar('\b');
+ --lineptr;
+ }
+ break;
+ case 'U' & 0x1F:
+ while (lineptr > line) {
+ xmon_putchar('\b');
+ xmon_putchar(' ');
+ xmon_putchar('\b');
+ --lineptr;
+ }
+ break;
+ default:
+ if (lineptr >= &line[sizeof(line) - 1])
+ xmon_putchar('\a');
+ else {
+ xmon_putchar(c);
+ *lineptr++ = c;
+ }
+ }
+ }
+ lineleft = lineptr - line;
+ lineptr = line;
+ }
+ if (lineleft == 0)
+ return -1;
+ --lineleft;
+ return *lineptr++;
+}
+
+char *
+xmon_fgets(char *str, int nb, void *f)
+{
+ char *p;
+ int c;
+
+ for (p = str; p < str + nb - 1; ) {
+ c = xmon_getchar();
+ if (c == -1) {
+ if (p == str)
+ return 0;
+ break;
+ }
+ *p++ = c;
+ if (c == '\n')
+ break;
+ }
+ *p = 0;
+ return str;
+}
--- /dev/null
+/*
+ * Written by Cort Dougan to replace the version written by
+ * Paul Mackerras that had copyright conflicts with Linux.
+ *
+ * This file makes liberal use of the standard linux utility
+ * routines to reduce the size of the binary. We assume we can
+ * trust some parts of Linux inside the debugger.
+ * -- Cort (cort@cs.nmt.edu)
+ *
+ * Copyright (C) 1999 Cort Dougan.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <stdarg.h>
+#include "nonstdio.h"
+
+extern int xmon_write(void *, void *, int);
+
+void
+xmon_vfprintf(void *f, const char *fmt, va_list ap)
+{
+ char buf[2048];
+ vsprintf( buf, fmt, ap );
+ xmon_write( f, buf, strlen(buf) );
+}
+
+void
+xmon_printf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xmon_vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+void
+xmon_fprintf(void *f, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ xmon_vfprintf(f, fmt, ap);
+ va_end(ap);
+}
+
--- /dev/null
+/*
+ * Routines providing a simple monitor for use on the PowerMac.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/ptrace.h>
+#include <asm/string.h>
+#include "nonstdio.h"
+#include "privinst.h"
+
+#define scanhex xmon_scanhex
+#define skipbl xmon_skipbl
+
+static unsigned adrs;
+static int size = 1;
+static unsigned ndump = 64;
+static unsigned nidump = 16;
+static int termch;
+
+static u_int bus_error_jmp[100];
+#define setjmp xmon_setjmp
+#define longjmp xmon_longjmp
+
+/* Breakpoint stuff */
+struct bpt {
+ unsigned address;
+ unsigned instr;
+ unsigned count;
+ unsigned char enabled;
+};
+
+#define NBPTS 16
+static struct bpt bpts[NBPTS];
+static struct bpt dabr;
+static struct bpt iabr;
+static unsigned bpinstr = 0x7fe00008; /* trap */
+
+/* Prototypes */
+extern void (*debugger_fault_handler)(struct pt_regs *);
+static int cmds(struct pt_regs *);
+static int mread(unsigned, void *, int);
+static int mwrite(unsigned, void *, int);
+static void handle_fault(struct pt_regs *);
+static void byterev(unsigned char *, int);
+static void memex(void);
+static int bsesc(void);
+static void dump(void);
+static void prdump(unsigned, int);
+#ifdef __MWERKS__
+static void prndump(unsigned, int);
+static int nvreadb(unsigned);
+#endif
+static int ppc_inst_dump(unsigned, int);
+void print_address(unsigned);
+static int getsp(void);
+static void dump_hash_table(void);
+static void backtrace(struct pt_regs *);
+static void excprint(struct pt_regs *);
+static void prregs(struct pt_regs *);
+static void memops(int);
+static void memlocate(void);
+static void memzcan(void);
+static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned);
+int skipbl(void);
+int scanhex(unsigned *valp);
+static void scannl(void);
+static int hexdigit(int);
+void getstring(char *, int);
+static void flush_input(void);
+static int inchar(void);
+static void take_input(char *);
+/* static void openforth(void); */
+static unsigned read_spr(int);
+static void write_spr(int, unsigned);
+static void super_regs(void);
+static void remove_bpts(void);
+static void insert_bpts(void);
+static struct bpt *at_breakpoint(unsigned pc);
+static void bpt_cmds(void);
+
+extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned);
+extern void printf(const char *fmt, ...);
+extern int putchar(int ch);
+extern int setjmp(u_int *);
+extern void longjmp(u_int *, int);
+
+#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
+
+static char *help_string = "\
+Commands:\n\
+ d dump bytes\n\
+ dc dump characters\n\
+ di dump instructions\n\
+ df dump float values\n\
+ dd dump double values\n\
+ e print exception information\n\
+ h dump hash table\n\
+ m examine/change memory\n\
+ mm move a block of memory\n\
+ ms set a block of memory\n\
+ md compare two blocks of memory\n\
+ r print registers\n\
+ S print special registers\n\
+ t print backtrace\n\
+ x exit monitor\n\
+";
+
+static int xmon_trace;
+#define SSTEP 1 /* stepping because of 's' command */
+#define BRSTEP 2 /* stepping over breakpoint */
+
+void
+xmon(struct pt_regs *excp)
+{
+ struct pt_regs regs;
+ int msr, cmd;
+
+ printk("Entering xmon kernel debugger.\n");
+
+ if (excp == NULL) {
+ asm volatile ("stw 0,0(%0)\n\
+ lwz 0,0(1)\n\
+ stw 0,4(%0)\n\
+ stmw 2,8(%0)" : : "r" (®s));
+ regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[1];
+ regs.msr = get_msr();
+ regs.ctr = get_ctr();
+ regs.xer = get_xer();
+ regs.ccr = get_cr();
+ regs.trap = 0;
+ excp = ®s;
+ }
+
+ msr = get_msr();
+ set_msr(msr & ~0x8000); /* disable interrupts */
+ remove_bpts();
+ excprint(excp);
+ cmd = cmds(excp);
+ if (cmd == 's') {
+ xmon_trace = SSTEP;
+ excp->msr |= 0x400;
+ } else if (at_breakpoint(excp->nip)) {
+ xmon_trace = BRSTEP;
+ excp->msr |= 0x400;
+ } else {
+ xmon_trace = 0;
+ insert_bpts();
+ }
+ set_msr(msr); /* restore interrupt enable */
+}
+
+void
+xmon_irq(int irq, void *d, struct pt_regs *regs)
+{
+ printf("Keyboard interrupt\n");
+ xmon(regs);
+}
+
+int
+xmon_bpt(struct pt_regs *regs)
+{
+ struct bpt *bp;
+
+ bp = at_breakpoint(regs->nip);
+ if (!bp)
+ return 0;
+ if (bp->count) {
+ --bp->count;
+ remove_bpts();
+ excprint(regs);
+ xmon_trace = BRSTEP;
+ regs->msr |= 0x400;
+ } else {
+ xmon(regs);
+ }
+ return 1;
+}
+
+int
+xmon_sstep(struct pt_regs *regs)
+{
+ if (!xmon_trace)
+ return 0;
+ if (xmon_trace == BRSTEP) {
+ xmon_trace = 0;
+ insert_bpts();
+ } else {
+ xmon(regs);
+ }
+ return 1;
+}
+
+int
+xmon_dabr_match(struct pt_regs *regs)
+{
+ if (dabr.enabled && dabr.count) {
+ --dabr.count;
+ remove_bpts();
+ excprint(regs);
+ xmon_trace = BRSTEP;
+ regs->msr |= 0x400;
+ } else {
+ dabr.instr = regs->nip;
+ xmon(regs);
+ }
+ return 1;
+}
+
+int
+xmon_iabr_match(struct pt_regs *regs)
+{
+ if (iabr.enabled && iabr.count) {
+ --iabr.count;
+ remove_bpts();
+ excprint(regs);
+ xmon_trace = BRSTEP;
+ regs->msr |= 0x400;
+ } else {
+ xmon(regs);
+ }
+ return 1;
+}
+
+static struct bpt *
+at_breakpoint(unsigned pc)
+{
+ int i;
+ struct bpt *bp;
+
+ if (dabr.enabled && pc == dabr.instr)
+ return &dabr;
+ if (iabr.enabled && pc == iabr.address)
+ return &iabr;
+ bp = bpts;
+ for (i = 0; i < NBPTS; ++i, ++bp)
+ if (bp->enabled && pc == bp->address)
+ return bp;
+ return 0;
+}
+
+static void
+insert_bpts()
+{
+ int i;
+ struct bpt *bp;
+
+ bp = bpts;
+ for (i = 0; i < NBPTS; ++i, ++bp) {
+ if (!bp->enabled)
+ continue;
+ if (mread(bp->address, &bp->instr, 4) != 4
+ || mwrite(bp->address, &bpinstr, 4) != 4) {
+ printf("Couldn't insert breakpoint at %x, disabling\n",
+ bp->address);
+ bp->enabled = 0;
+ }
+ }
+ if (dabr.enabled)
+ set_dabr(dabr.address);
+ if (iabr.enabled)
+ set_iabr(iabr.address);
+}
+
+static void
+remove_bpts()
+{
+ int i;
+ struct bpt *bp;
+ unsigned instr;
+
+ set_dabr(0);
+ set_iabr(0);
+ bp = bpts;
+ for (i = 0; i < NBPTS; ++i, ++bp) {
+ if (!bp->enabled)
+ continue;
+ if (mread(bp->address, &instr, 4) == 4
+ && instr == bpinstr
+ && mwrite(bp->address, &bp->instr, 4) != 4)
+ printf("Couldn't remove breakpoint at %x\n",
+ bp->address);
+ }
+}
+
+static char *last_cmd;
+
+/* Command interpreting routine */
+static int
+cmds(struct pt_regs *excp)
+{
+ int cmd;
+
+ last_cmd = NULL;
+ for(;;) {
+ printf("mon> ");
+ fflush(stdout);
+ flush_input();
+ termch = 0;
+ cmd = skipbl();
+ if( cmd == '\n' ) {
+ if (last_cmd == NULL)
+ continue;
+ take_input(last_cmd);
+ last_cmd = NULL;
+ cmd = inchar();
+ }
+ switch (cmd) {
+ case 'm':
+ cmd = inchar();
+ switch (cmd) {
+ case 'm':
+ case 's':
+ case 'd':
+ memops(cmd);
+ break;
+ case 'l':
+ memlocate();
+ break;
+ case 'z':
+ memzcan();
+ break;
+ default:
+ termch = cmd;
+ memex();
+ }
+ break;
+ case 'd':
+ dump();
+ break;
+ case 'r':
+ if (excp != NULL)
+ prregs(excp); /* print regs */
+ break;
+ case 'e':
+ if (excp == NULL)
+ printf("No exception information\n");
+ else
+ excprint(excp);
+ break;
+ case 'S':
+ super_regs();
+ break;
+ case 't':
+ backtrace(excp);
+ break;
+#if 0
+ case 'f':
+ openforth();
+ break;
+#endif
+ case 'h':
+ dump_hash_table();
+ break;
+ case 's':
+ case 'x':
+ case EOF:
+ return cmd;
+ case '?':
+ printf(help_string);
+ break;
+ default:
+ printf("Unrecognized command: ");
+ if( ' ' < cmd && cmd <= '~' )
+ putchar(cmd);
+ else
+ printf("\\x%x", cmd);
+ printf(" (type ? for help)\n");
+ break;
+ case 'b':
+ bpt_cmds();
+ break;
+ }
+ }
+}
+
+static void
+bpt_cmds(void)
+{
+ int cmd;
+ unsigned a;
+ int mode, i;
+ struct bpt *bp;
+
+ cmd = inchar();
+ switch (cmd) {
+ case 'd':
+ mode = 7;
+ cmd = inchar();
+ if (cmd == 'r')
+ mode = 5;
+ else if (cmd == 'w')
+ mode = 6;
+ else
+ termch = cmd;
+ dabr.address = 0;
+ dabr.count = 0;
+ dabr.enabled = scanhex(&dabr.address);
+ scanhex(&dabr.count);
+ if (dabr.enabled)
+ dabr.address = (dabr.address & ~7) | mode;
+ break;
+ case 'i':
+ iabr.address = 0;
+ iabr.count = 0;
+ iabr.enabled = scanhex(&iabr.address);
+ if (iabr.enabled)
+ iabr.address |= 3;
+ scanhex(&iabr.count);
+ break;
+ case 'c':
+ if (!scanhex(&a)) {
+ /* clear all breakpoints */
+ for (i = 0; i < NBPTS; ++i)
+ bpts[i].enabled = 0;
+ iabr.enabled = 0;
+ dabr.enabled = 0;
+ printf("All breakpoints cleared\n");
+ } else {
+ bp = at_breakpoint(a);
+ if (bp == 0) {
+ printf("No breakpoint at %x\n", a);
+ } else {
+ bp->enabled = 0;
+ }
+ }
+ break;
+ default:
+ termch = cmd;
+ if (!scanhex(&a)) {
+ /* print all breakpoints */
+ printf("type address count\n");
+ if (dabr.enabled) {
+ printf("data %.8x %8x [", dabr.address & ~7,
+ dabr.count);
+ if (dabr.address & 1)
+ printf("r");
+ if (dabr.address & 2)
+ printf("w");
+ printf("]\n");
+ }
+ if (iabr.enabled)
+ printf("inst %.8x %8x\n", iabr.address & ~3,
+ iabr.count);
+ for (bp = bpts; bp < &bpts[NBPTS]; ++bp)
+ if (bp->enabled)
+ printf("trap %.8x %8x\n", bp->address,
+ bp->count);
+ break;
+ }
+ bp = at_breakpoint(a);
+ if (bp == 0) {
+ for (bp = bpts; bp < &bpts[NBPTS]; ++bp)
+ if (!bp->enabled)
+ break;
+ if (bp >= &bpts[NBPTS]) {
+ printf("Sorry, no free breakpoints\n");
+ break;
+ }
+ }
+ bp->enabled = 1;
+ bp->address = a;
+ bp->count = 0;
+ scanhex(&bp->count);
+ break;
+ }
+}
+
+static void
+backtrace(struct pt_regs *excp)
+{
+ unsigned sp;
+ unsigned stack[2];
+ struct pt_regs regs;
+ extern char int_return, syscall_ret_1, syscall_ret_2;
+ extern char lost_irq_ret, do_bottom_half_ret, do_signal_ret;
+
+ if (excp != NULL)
+ sp = excp->gpr[1];
+ else
+ sp = getsp();
+ scanhex(&sp);
+ scannl();
+ for (; sp != 0; sp = stack[0]) {
+ if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
+ break;
+ printf("%x ", stack[1]);
+ if (stack[1] == (unsigned) &int_return
+ || stack[1] == (unsigned) &syscall_ret_1
+ || stack[1] == (unsigned) &syscall_ret_2
+ || stack[1] == (unsigned) &lost_irq_ret
+ || stack[1] == (unsigned) &do_bottom_half_ret
+ || stack[1] == (unsigned) &do_signal_ret) {
+ if (mread(sp+16, ®s, sizeof(regs)) != sizeof(regs))
+ break;
+ printf("\nexception:%x [%x] %x ", regs.trap, sp+16,
+ regs.nip);
+ sp = regs.gpr[1];
+ if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
+ break;
+ }
+ }
+ printf("\n");
+}
+
+int
+getsp()
+{
+ int x;
+
+ asm("mr %0,1" : "=r" (x) :);
+ return x;
+}
+
+void
+excprint(struct pt_regs *fp)
+{
+ printf("vector: %x at pc = %x, msr = %x, sp = %x [%x]\n",
+ fp->trap, fp->nip, fp->msr, fp->gpr[1], fp);
+ if ((fp->trap == 0x300) || (fp->trap == 0x600) || (fp->trap == 0x200))
+ printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr);
+ if (current)
+ printf("current = %x, pid = %d, comm = %s\n",
+ current, current->pid, current->comm);
+}
+
+void
+prregs(struct pt_regs *fp)
+{
+ int n;
+ unsigned base;
+
+ if (scanhex(&base))
+ fp = (struct pt_regs *) base;
+ for (n = 0; n < 32; ++n)
+ printf("R%.2d = %.8x%s", n, fp->gpr[n],
+ (n & 3) == 3? "\n": " ");
+ printf("pc = %.8x msr = %.8x lr = %.8x cr = %.8x\n",
+ fp->nip, fp->msr, fp->link, fp->ccr);
+ printf("ctr = %.8x xer = %.8x trap = %4x\n",
+ fp->ctr, fp->xer, fp->trap);
+}
+
+unsigned int
+read_spr(int n)
+{
+ unsigned int instrs[2];
+ int (*code)(void);
+
+ instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
+ instrs[1] = 0x4e800020;
+ store_inst(instrs);
+ store_inst(instrs+1);
+ code = (int (*)(void)) instrs;
+ return code();
+}
+
+void
+write_spr(int n, unsigned int val)
+{
+ unsigned int instrs[2];
+ int (*code)(unsigned int);
+
+ instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
+ instrs[1] = 0x4e800020;
+ store_inst(instrs);
+ store_inst(instrs+1);
+ code = (int (*)(unsigned int)) instrs;
+ code(val);
+}
+
+static unsigned int regno;
+extern char exc_prolog;
+extern char dec_exc;
+
+void
+super_regs()
+{
+ int i, cmd;
+ unsigned val;
+
+ cmd = skipbl();
+ if (cmd == '\n') {
+ printf("msr = %x, pvr = %x\n", get_msr(), get_pvr());
+ printf("sprg0-3 = %x %x %x %x\n", get_sprg0(), get_sprg1(),
+ get_sprg2(), get_sprg3());
+ printf("srr0 = %x, srr1 = %x\n", get_srr0(), get_srr1());
+ printf("sr0-15 =");
+ for (i = 0; i < 16; ++i)
+ printf(" %x", get_sr(i));
+ printf("\n");
+ asm("mr %0,1" : "=r" (i) :);
+ printf("sp = %x ", i);
+ asm("mr %0,2" : "=r" (i) :);
+ printf("toc = %x\n", i);
+ return;
+ }
+
+ scanhex(®no);
+ switch (cmd) {
+ case 'w':
+ val = read_spr(regno);
+ scanhex(&val);
+ write_spr(regno, val);
+ /* fall through */
+ case 'r':
+ printf("spr %x = %x\n", regno, read_spr(regno));
+ break;
+ case 's':
+ val = get_sr(regno);
+ scanhex(&val);
+ set_sr(regno, val);
+ break;
+ case 'm':
+ val = get_msr();
+ scanhex(&val);
+ set_msr(val);
+ break;
+ }
+ scannl();
+}
+
+#if 0
+static void
+openforth()
+{
+ int c;
+ char *p;
+ char cmd[1024];
+ int args[5];
+ extern int (*prom_entry)(int *);
+
+ p = cmd;
+ c = skipbl();
+ while (c != '\n') {
+ *p++ = c;
+ c = inchar();
+ }
+ *p = 0;
+ args[0] = (int) "interpret";
+ args[1] = 1;
+ args[2] = 1;
+ args[3] = (int) cmd;
+ (*prom_entry)(args);
+ printf("\n");
+ if (args[4] != 0)
+ printf("error %x\n", args[4]);
+}
+#endif
+
+static void
+dump_hash_table_seg(unsigned seg, unsigned start, unsigned end)
+{
+ extern void *Hash;
+ extern unsigned long Hash_size;
+ unsigned *htab = Hash;
+ unsigned hsize = Hash_size;
+ unsigned v, hmask, va, last_va;
+ int found, last_found, i;
+ unsigned *hg, w1, last_w2, last_va0;
+
+ last_found = 0;
+ hmask = hsize / 64 - 1;
+ va = start;
+ start = (start >> 12) & 0xffff;
+ end = (end >> 12) & 0xffff;
+ for (v = start; v < end; ++v) {
+ found = 0;
+ hg = htab + (((v ^ seg) & hmask) * 16);
+ w1 = 0x80000000 | (seg << 7) | (v >> 10);
+ for (i = 0; i < 8; ++i, hg += 2) {
+ if (*hg == w1) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ w1 ^= 0x40;
+ hg = htab + ((~(v ^ seg) & hmask) * 16);
+ for (i = 0; i < 8; ++i, hg += 2) {
+ if (*hg == w1) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!(last_found && found && (hg[1] & ~0x180) == last_w2 + 4096)) {
+ if (last_found) {
+ if (last_va != last_va0)
+ printf(" ... %x", last_va);
+ printf("\n");
+ }
+ if (found) {
+ printf("%x to %x", va, hg[1]);
+ last_va0 = va;
+ }
+ last_found = found;
+ }
+ if (found) {
+ last_w2 = hg[1] & ~0x180;
+ last_va = va;
+ }
+ va += 4096;
+ }
+ if (last_found)
+ printf(" ... %x\n", last_va);
+}
+static unsigned hash_ctx;
+static unsigned hash_start;
+static unsigned hash_end;
+
+static void
+dump_hash_table()
+{
+ int seg;
+ unsigned seg_start, seg_end;
+
+ hash_ctx = 0;
+ hash_start = 0;
+ hash_end = 0xfffff000;
+ scanhex(&hash_ctx);
+ scanhex(&hash_start);
+ scanhex(&hash_end);
+ printf("Mappings for context %x\n", hash_ctx);
+ seg_start = hash_start;
+ for (seg = hash_start >> 28; seg <= hash_end >> 28; ++seg) {
+ seg_end = (seg << 28) | 0x0ffff000;
+ if (seg_end > hash_end)
+ seg_end = hash_end;
+ dump_hash_table_seg((hash_ctx << 4) + seg, seg_start, seg_end);
+ seg_start = seg_end + 0x1000;
+ }
+}
+
+/*
+ * Stuff for reading and writing memory safely
+ */
+extern inline void sync(void)
+{
+ asm volatile("sync; isync");
+}
+
+extern inline void __delay(unsigned int loops)
+{
+ if (loops != 0)
+ __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : :
+ "r" (loops) : "ctr");
+}
+
+int
+mread(unsigned adrs, void *buf, int size)
+{
+ volatile int n;
+ char *p, *q;
+
+ n = 0;
+ if( setjmp(bus_error_jmp) == 0 ){
+ debugger_fault_handler = handle_fault;
+ sync();
+ p = (char *) adrs;
+ q = (char *) buf;
+ switch (size) {
+ case 2: *(short *)q = *(short *)p; break;
+ case 4: *(int *)q = *(int *)p; break;
+ default:
+ for( ; n < size; ++n ) {
+ *q++ = *p++;
+ sync();
+ }
+ }
+ sync();
+ /* wait a little while to see if we get a machine check */
+ __delay(200);
+ n = size;
+ }
+ debugger_fault_handler = 0;
+ return n;
+}
+
+int
+mwrite(unsigned adrs, void *buf, int size)
+{
+ volatile int n;
+ char *p, *q;
+
+ n = 0;
+ if( setjmp(bus_error_jmp) == 0 ){
+ debugger_fault_handler = handle_fault;
+ sync();
+ p = (char *) adrs;
+ q = (char *) buf;
+ switch (size) {
+ case 2: *(short *)p = *(short *)q; break;
+ case 4: *(int *)p = *(int *)q; break;
+ default:
+ for( ; n < size; ++n ) {
+ *p++ = *q++;
+ sync();
+ }
+ }
+ sync();
+ n = size;
+ } else {
+ printf("*** Error writing address %x\n", adrs + n);
+ }
+ debugger_fault_handler = 0;
+ return n;
+}
+
+static int fault_type;
+static char *fault_chars[] = { "--", "**", "##" };
+
+static void
+handle_fault(struct pt_regs *regs)
+{
+ fault_type = regs->trap == 0x200? 0: regs->trap == 0x300? 1: 2;
+ longjmp(bus_error_jmp, 1);
+}
+
+#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t))
+
+void
+byterev(unsigned char *val, int size)
+{
+ int t;
+
+ switch (size) {
+ case 2:
+ SWAP(val[0], val[1], t);
+ break;
+ case 4:
+ SWAP(val[0], val[3], t);
+ SWAP(val[1], val[2], t);
+ break;
+ }
+}
+
+static int brev;
+static int mnoread;
+
+void
+memex()
+{
+ int cmd, inc, i, nslash;
+ unsigned n;
+ unsigned char val[4];
+
+ last_cmd = "m\n";
+ scanhex(&adrs);
+ while ((cmd = skipbl()) != '\n') {
+ switch( cmd ){
+ case 'b': size = 1; break;
+ case 'w': size = 2; break;
+ case 'l': size = 4; break;
+ case 'r': brev = !brev; break;
+ case 'n': mnoread = 1; break;
+ case '.': mnoread = 0; break;
+ }
+ }
+ if( size <= 0 )
+ size = 1;
+ else if( size > 4 )
+ size = 4;
+ for(;;){
+ if (!mnoread)
+ n = mread(adrs, val, size);
+ printf("%.8x%c", adrs, brev? 'r': ' ');
+ if (!mnoread) {
+ if (brev)
+ byterev(val, size);
+ putchar(' ');
+ for (i = 0; i < n; ++i)
+ printf("%.2x", val[i]);
+ for (; i < size; ++i)
+ printf("%s", fault_chars[fault_type]);
+ }
+ putchar(' ');
+ inc = size;
+ nslash = 0;
+ for(;;){
+ if( scanhex(&n) ){
+ for (i = 0; i < size; ++i)
+ val[i] = n >> (i * 8);
+ if (!brev)
+ byterev(val, size);
+ mwrite(adrs, val, size);
+ inc = size;
+ }
+ cmd = skipbl();
+ if (cmd == '\n')
+ break;
+ inc = 0;
+ switch (cmd) {
+ case '\'':
+ for(;;){
+ n = inchar();
+ if( n == '\\' )
+ n = bsesc();
+ else if( n == '\'' )
+ break;
+ for (i = 0; i < size; ++i)
+ val[i] = n >> (i * 8);
+ if (!brev)
+ byterev(val, size);
+ mwrite(adrs, val, size);
+ adrs += size;
+ }
+ adrs -= size;
+ inc = size;
+ break;
+ case ',':
+ adrs += size;
+ break;
+ case '.':
+ mnoread = 0;
+ break;
+ case ';':
+ break;
+ case 'x':
+ case EOF:
+ scannl();
+ return;
+ case 'b':
+ case 'v':
+ size = 1;
+ break;
+ case 'w':
+ size = 2;
+ break;
+ case 'l':
+ size = 4;
+ break;
+ case '^':
+ adrs -= size;
+ break;
+ break;
+ case '/':
+ if (nslash > 0)
+ adrs -= 1 << nslash;
+ else
+ nslash = 0;
+ nslash += 4;
+ adrs += 1 << nslash;
+ break;
+ case '\\':
+ if (nslash < 0)
+ adrs += 1 << -nslash;
+ else
+ nslash = 0;
+ nslash -= 4;
+ adrs -= 1 << -nslash;
+ break;
+ case 'm':
+ scanhex(&adrs);
+ break;
+ case 'n':
+ mnoread = 1;
+ break;
+ case 'r':
+ brev = !brev;
+ break;
+ case '<':
+ n = size;
+ scanhex(&n);
+ adrs -= n;
+ break;
+ case '>':
+ n = size;
+ scanhex(&n);
+ adrs += n;
+ break;
+ }
+ }
+ adrs += inc;
+ }
+}
+
+int
+bsesc()
+{
+ int c;
+
+ c = inchar();
+ switch( c ){
+ case 'n': c = '\n'; break;
+ case 'r': c = '\r'; break;
+ case 'b': c = '\b'; break;
+ case 't': c = '\t'; break;
+ }
+ return c;
+}
+
+#define isxdigit(c) ('0' <= (c) && (c) <= '9' || 'a' <= (c) && (c) <= 'f' \
+ || 'A' <= (c) && (c) <= 'F')
+void
+dump()
+{
+ int c;
+
+ c = inchar();
+ if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n')
+ termch = c;
+ scanhex(&adrs);
+ if( termch != '\n')
+ termch = 0;
+ if( c == 'i' ){
+ scanhex(&nidump);
+ if( nidump == 0 )
+ nidump = 16;
+ adrs += ppc_inst_dump(adrs, nidump);
+ last_cmd = "di\n";
+ } else {
+ scanhex(&ndump);
+ if( ndump == 0 )
+ ndump = 64;
+ prdump(adrs, ndump);
+ adrs += ndump;
+ last_cmd = "d\n";
+ }
+}
+
+void
+prdump(unsigned adrs, int ndump)
+{
+ register int n, m, c, r, nr;
+ unsigned char temp[16];
+
+ for( n = ndump; n > 0; ){
+ printf("%.8x", adrs);
+ putchar(' ');
+ r = n < 16? n: 16;
+ nr = mread(adrs, temp, r);
+ adrs += nr;
+ for( m = 0; m < r; ++m ){
+ putchar((m & 3) == 0 && m > 0? '.': ' ');
+ if( m < nr )
+ printf("%.2x", temp[m]);
+ else
+ printf("%s", fault_chars[fault_type]);
+ }
+ for(; m < 16; ++m )
+ printf(" ");
+ printf(" |");
+ for( m = 0; m < r; ++m ){
+ if( m < nr ){
+ c = temp[m];
+ putchar(' ' <= c && c <= '~'? c: '.');
+ } else
+ putchar(' ');
+ }
+ n -= r;
+ for(; m < 16; ++m )
+ putchar(' ');
+ printf("|\n");
+ if( nr < r )
+ break;
+ }
+}
+
+int
+ppc_inst_dump(unsigned adr, int count)
+{
+ int nr, dotted;
+ unsigned first_adr;
+ unsigned long inst, last_inst;
+ unsigned char val[4];
+
+ dotted = 0;
+ for (first_adr = adr; count > 0; --count, adr += 4){
+ nr = mread(adr, val, 4);
+ if( nr == 0 ){
+ const char *x = fault_chars[fault_type];
+ printf("%.8x %s%s%s%s\n", adr, x, x, x, x);
+ break;
+ }
+ inst = GETWORD(val);
+ if (adr > first_adr && inst == last_inst) {
+ if (!dotted) {
+ printf(" ...\n");
+ dotted = 1;
+ }
+ continue;
+ }
+ dotted = 0;
+ last_inst = inst;
+ printf("%.8x ", adr);
+ printf("%.8x\t", inst);
+ print_insn_big_powerpc(stdout, inst, adr); /* always returns 4 */
+ printf("\n");
+ }
+ return adr - first_adr;
+}
+
+void
+print_address(addr)
+unsigned addr;
+{
+ printf("0x%x", addr);
+}
+
+/*
+ * Memory operations - move, set, print differences
+ */
+static unsigned mdest; /* destination address */
+static unsigned msrc; /* source address */
+static unsigned mval; /* byte value to set memory to */
+static unsigned mcount; /* # bytes to affect */
+static unsigned mdiffs; /* max # differences to print */
+
+void
+memops(int cmd)
+{
+ scanhex(&mdest);
+ if( termch != '\n' )
+ termch = 0;
+ scanhex(cmd == 's'? &mval: &msrc);
+ if( termch != '\n' )
+ termch = 0;
+ scanhex(&mcount);
+ switch( cmd ){
+ case 'm':
+ memmove((void *)mdest, (void *)msrc, mcount);
+ break;
+ case 's':
+ memset((void *)mdest, mval, mcount);
+ break;
+ case 'd':
+ if( termch != '\n' )
+ termch = 0;
+ scanhex(&mdiffs);
+ memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs);
+ break;
+ }
+}
+
+void
+memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr)
+{
+ unsigned n, prt;
+
+ prt = 0;
+ for( n = nb; n > 0; --n )
+ if( *p1++ != *p2++ )
+ if( ++prt <= maxpr )
+ printf("%.8x %.2x # %.8x %.2x\n", (unsigned)p1 - 1,
+ p1[-1], (unsigned)p2 - 1, p2[-1]);
+ if( prt > maxpr )
+ printf("Total of %d differences\n", prt);
+}
+
+static unsigned mend;
+static unsigned mask;
+
+void
+memlocate()
+{
+ unsigned a, n;
+ unsigned char val[4];
+
+ last_cmd = "ml";
+ scanhex(&mdest);
+ if (termch != '\n') {
+ termch = 0;
+ scanhex(&mend);
+ if (termch != '\n') {
+ termch = 0;
+ scanhex(&mval);
+ mask = ~0;
+ if (termch != '\n') termch = 0;
+ scanhex(&mask);
+ }
+ }
+ n = 0;
+ for (a = mdest; a < mend; a += 4) {
+ if (mread(a, val, 4) == 4
+ && ((GETWORD(val) ^ mval) & mask) == 0) {
+ printf("%.8x: %.8x\n", a, GETWORD(val));
+ if (++n >= 10)
+ break;
+ }
+ }
+}
+
+static unsigned mskip = 0x1000;
+static unsigned mlim = 0xffffffff;
+
+void
+memzcan()
+{
+ unsigned char v;
+ unsigned a;
+ int ok, ook;
+
+ scanhex(&mdest);
+ if (termch != '\n') termch = 0;
+ scanhex(&mskip);
+ if (termch != '\n') termch = 0;
+ scanhex(&mlim);
+ ook = 0;
+ for (a = mdest; a < mlim; a += mskip) {
+ ok = mread(a, &v, 1);
+ if (ok && !ook) {
+ printf("%.8x .. ", a);
+ fflush(stdout);
+ } else if (!ok && ook)
+ printf("%.8x\n", a - mskip);
+ ook = ok;
+ if (a + mskip < a)
+ break;
+ }
+ if (ook)
+ printf("%.8x\n", a - mskip);
+}
+
+/* Input scanning routines */
+int
+skipbl()
+{
+ int c;
+
+ if( termch != 0 ){
+ c = termch;
+ termch = 0;
+ } else
+ c = inchar();
+ while( c == ' ' || c == '\t' )
+ c = inchar();
+ return c;
+}
+
+int
+scanhex(vp)
+unsigned *vp;
+{
+ int c, d;
+ unsigned v;
+
+ c = skipbl();
+ d = hexdigit(c);
+ if( d == EOF ){
+ termch = c;
+ return 0;
+ }
+ v = 0;
+ do {
+ v = (v << 4) + d;
+ c = inchar();
+ d = hexdigit(c);
+ } while( d != EOF );
+ termch = c;
+ *vp = v;
+ return 1;
+}
+
+void
+scannl()
+{
+ int c;
+
+ c = termch;
+ termch = 0;
+ while( c != '\n' )
+ c = inchar();
+}
+
+int
+hexdigit(c)
+{
+ if( '0' <= c && c <= '9' )
+ return c - '0';
+ if( 'A' <= c && c <= 'F' )
+ return c - ('A' - 10);
+ if( 'a' <= c && c <= 'f' )
+ return c - ('a' - 10);
+ return EOF;
+}
+
+void
+getstring(char *s, int size)
+{
+ int c;
+
+ c = skipbl();
+ do {
+ if( size > 1 ){
+ *s++ = c;
+ --size;
+ }
+ c = inchar();
+ } while( c != ' ' && c != '\t' && c != '\n' );
+ termch = c;
+ *s = 0;
+}
+
+static char line[256];
+static char *lineptr;
+
+void
+flush_input()
+{
+ lineptr = NULL;
+}
+
+int
+inchar()
+{
+ if (lineptr == NULL || *lineptr == 0) {
+ if (fgets(line, sizeof(line), stdin) == NULL) {
+ lineptr = NULL;
+ return EOF;
+ }
+ lineptr = line;
+ }
+ return *lineptr++;
+}
+
+void
+take_input(str)
+char *str;
+{
+ lineptr = str;
+}
if((!child->dumpable ||
(current->uid != child->euid) ||
(current->uid != child->uid) ||
+ (current->uid != child->suid) ||
(current->gid != child->egid) ||
(current->gid != child->sgid) ||
(!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
-/* $Id: sys_sunos.c,v 1.94 1998/10/12 06:15:04 jj Exp $
+/* $Id: sys_sunos.c,v 1.97 1999/05/24 19:40:39 davem Exp $
* sys_sunos.c: SunOS specific syscall compatibility support.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
lock_kernel();
ret = check_nonblock(sys_readv(fd,vector,count),fd);
- lock_kernel();
+ unlock_kernel();
return ret;
}
if((!child->dumpable ||
(current->uid != child->euid) ||
(current->uid != child->uid) ||
+ (current->uid != child->suid) ||
(current->gid != child->egid) ||
(current->gid != child->sgid) ||
(!cap_issubset(child->cap_permitted, current->cap_permitted)) ||
-/* $Id: sys_sunos32.c,v 1.22 1998/10/26 20:01:13 davem Exp $
+/* $Id: sys_sunos32.c,v 1.25 1999/05/24 19:40:44 davem Exp $
* sys_sunos32.c: SunOS binary compatability layer on sparc64.
*
* Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
lock_kernel();
ret = check_nonblock(sys32_readv(fd, vector, count), fd);
- lock_kernel();
+ unlock_kernel();
return ret;
}
struct device *d;
int i = 0;
+ read_lock_bh(&dev_base_lock);
for (d = dev_base; d; d = d->next) i++;
+ read_unlock_bh(&dev_base_lock);
+
if (put_user (i, (int *)A(arg)))
return -EFAULT;
return 0;
(void) pci_write_config_byte(dev, 0x58, 0x3f);
(void) pci_write_config_byte(dev, 0x5b, 0x3f);
- if (class_rev == 0x01) {
- hwif->dmaproc = &cmd646_1_dmaproc;
- } else {
- hwif->dmaproc = &cmd646_dmaproc;
+ if (hwif->dma_base) {
+ if (class_rev == 0x01) {
+ hwif->dmaproc = &cmd646_1_dmaproc;
+ } else {
+ hwif->dmaproc = &cmd646_dmaproc;
+ }
}
}
drive->media = ide_disk;
drive->select.all = (unit<<4)|0xa0;
drive->hwif = hwif;
- init_waitqueue_head(&drive->wqueue);
drive->ctl = 0x08;
drive->ready_stat = READY_STAT;
drive->bad_wstat = BAD_W_STAT;
int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
{
byte stat;
+ int i;
unsigned long flags;
udelay(1); /* spec allows drive 400ns to assert "BUSY" */
}
__restore_flags(flags); /* local CPU only */
}
- udelay(1); /* allow status to settle, then read it again */
- if (OK_STAT((stat = GET_STAT()), good, bad))
- return 0;
+ /*
+ * Allow status to settle, then read it again.
+ * A few rare drives vastly violate the 400ns spec here,
+ * so we'll wait up to 10usec for a "good" status
+ * rather than expensively fail things immediately.
+ * This fix courtesy of Matthew Faupel & Niccolo Rigacci.
+ */
+ for (i = 0; i < 10; i++) {
+ udelay(1);
+ if (OK_STAT((stat = GET_STAT()), good, bad))
+ return 0;
+ }
ide_error(drive, "status error", stat);
return 1;
}
if [ "$CONFIG_RADIO_ZOLTRIX" = "y" ]; then
hex ' ZOLTRIX I/O port (0x20c or 0x30c)' CONFIG_RADIO_ZOLTRIX_PORT 20c
fi
+ dep_tristate 'IIC on parallel port' CONFIG_I2C_PARPORT $CONFIG_PARPORT
fi
endmenu
endif
endif
+ifeq ($(CONFIG_I2C_PARPORT),y)
+L_OBJS += i2c-parport.o
+L_I2C = y
+else
+ ifeq ($(CONFIG_I2C_PARPORT),m)
+ M_OBJS += i2c-parport.o
+ M_I2C = y
+ endif
+endif
+
ifeq ($(CONFIG_VIDEO_BWQCAM),y)
L_OBJS += bw-qcam.o
else
#define BLOCKMOVE
#define Z_WAKE
static char rcsid[] =
-"$Revision: 2.2.2.1 $$Date: 1999/04/08 16:17:43 $";
+"$Revision: 2.2.2.2 $$Date: 1999/05/21 17:18:15 $";
/*
* linux/drivers/char/cyclades.c
* extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
* and then fixed as suggested by Michael K. Johnson 12/12/92.
*
- * This version does not support shared irq's.
+ * This version supports shared IRQ's (only for PCI boards).
*
* This module exports the following rs232 io functions:
* int cy_init(void);
* void cleanup_module(void);
*
* $Log: cyclades.c,v $
+ * Revision 2.2.2.2 1999/05/14 17:18:15 ivan
+ * /proc entry location changed to /proc/tty/driver/cyclades;
+ * Added support to shared IRQ's (only for PCI boards);
+ * Added support for Cobalt Qube2 systems;
+ * IRQ [de]allocation scheme revisited;
+ * BREAK implementation changed in order to make use of the 'break_ctl'
+ * TTY facility;
+ * Fixed typo in TTY structure field 'driver_name';
+ * Included a PCI bridge reset and EEPROM reload in the board
+ * initialization code (for both Y and Z series).
+ *
* Revision 2.2.2.1 1999/04/08 16:17:43 ivan
* Fixed a bug in cy_wait_until_sent that was preventing the port to be
* closed properly after a SIGINT;
#undef CY_16Y_HACK
#undef CY_ENABLE_MONITORING
#undef CY_PCI_DEBUG
-#define CY_PROC
+#undef CY_PROC
#if 0
#define PAUSE __asm__("nop");
#include <linux/stat.h>
#include <linux/proc_fs.h>
+#ifdef CONFIG_COBALT_27
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#define CACHED_TO_UNCACHED(x) (((unsigned long)(x) & \
+ (unsigned long)0x1fffffff) + KSEG1)
+#endif
+
#define cy_put_user put_user
static unsigned long cy_get_user(unsigned long *addr)
static struct tty_driver cy_serial_driver, cy_callout_driver;
static int serial_refcount;
+#ifndef CONFIG_COBALT_27
static volatile int cy_irq_triggered;
static volatile int cy_triggered;
static int cy_wild_int_mask;
};
#define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*))
+#endif /* CONFIG_COBALT_27 */
+
/* This is the per-card data structure containing address, irq, number of
channels, etc. This driver supports a maximum of NR_CARDS cards.
*/
static struct termios *serial_termios[NR_PORTS];
static struct termios *serial_termios_locked[NR_PORTS];
-/* This is the per-irq data structure,
- it maps an irq to the corresponding card */
-
-static struct cyclades_card *IRQ_cards[NR_IRQS];
-
/*
* tmp_buf is used as a temporary buffer by serial_write. We need to
* lock it in case the copy_from_user blocks while swapping in a page,
static void cy_start(struct tty_struct *);
static void set_line_char(struct cyclades_port *);
+#ifndef CONFIG_COBALT_27
static void cy_probe(int, void *, struct pt_regs *);
+#endif /* CONFIG_COBALT_27 */
static void cyz_poll(unsigned long);
#ifdef CYCLOM_SHOW_STATUS
static void show_status(int);
return(0);
} /* cyy_issue_cmd */
+#ifndef CONFIG_COBALT_27 /* ISA interrupt detection code */
+
static int probe_ready;
/*
return;
} /* cy_probe */
+#endif /* CONFIG_COBALT_27 */
+
/* The real interrupt service routine is called
whenever the card wants its hand held--chars
received, out buffer empty, modem change, etc.
int mdm_change;
int mdm_status;
- if((cinfo = IRQ_cards[irq]) == 0){
+ if((cinfo = (struct cyclades_card *)dev_id) == 0){
#ifdef CY_DEBUG_INTERRUPTS
-printk("cy_interrupt: spurious interrupt %d\n\r", irq);
+ printk("cy_interrupt: spurious interrupt %d\n\r", irq);
#endif
return; /* spurious interrupt */
}
}
if (status & CySRReceive) { /* reception interrupt */
#ifdef CY_DEBUG_INTERRUPTS
-printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
+ printk("cy_interrupt: rcvd intr, chip %d\n\r", chip);
#endif
/* determine the channel & change to that context */
save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
is empty, we know we can always stuff a dozen
characters. */
#ifdef CY_DEBUG_INTERRUPTS
-printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
+ printk("cy_interrupt: xmit intr, chip %d\n\r", chip);
#endif
/* determine the channel & change to that context */
info->x_char = 0;
}
- if (info->x_break){
- /* The Cirrus chip requires the "Embedded
- Transmit Commands" of start break, delay,
- and end break sequences to be sent. The
- duration of the break is given in TICs,
- which runs at HZ (typically 100) and the
- PPR runs at 200 Hz, so the delay is
- duration * 200/HZ, and thus a break can
- run from 1/100 sec to about 5/4 sec.
- For CD1400 J or later, replace the 200 Hz
- by 500 Hz.
- */
- /* start break */
- cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
- cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
- /* delay a bit */
- cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
- cy_writeb((u_long)base_addr + (CyTDR<<index), 0x82);
- if (info->chip_rev >= CD1400_REV_J ) {
- /* It is a CD1400 rev. J or later */
- cy_writeb((u_long)base_addr + (CyTDR<<index),
- info->x_break*500/HZ);
- } else {
- cy_writeb((u_long)base_addr + (CyTDR<<index),
- info->x_break*200/HZ);
+ if (info->breakon || info->breakoff) {
+ if (info->breakon) {
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x81);
+ info->breakon = 0;
+ char_count -= 2;
+ }
+ if (info->breakoff) {
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
+ cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
+ info->breakoff = 0;
+ char_count -= 2;
}
- /* finish break */
- cy_writeb((u_long)base_addr + (CyTDR<<index), 0);
- cy_writeb((u_long)base_addr + (CyTDR<<index), 0x83);
- char_count -= 7;
- info->x_break = 0;
}
while (char_count-- > 0){
info->last_active = jiffies;
info->jiffies[2] = jiffies;
}
- if (info->x_break){
- printk("cyc cyz_poll shouldn't see x_break\n");
- info->x_break = 0;
- info->last_active = jiffies;
- info->jiffies[2] = jiffies;
- }
#ifdef BLOCKMOVE
while(0 < (small_count
= cy_min((tx_bufsize - tx_put),
startup(struct cyclades_port * info)
{
unsigned long flags;
+ int retval = 0;
unsigned char *base_addr;
int card,chip,channel,index;
+ unsigned long page;
+
+ page = get_free_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ save_flags(flags); cli();
if (info->flags & ASYNC_INITIALIZED){
- return 0;
+ free_page(page);
+ goto errout;
}
if (!info->type){
if (info->tty){
set_bit(TTY_IO_ERROR, &info->tty->flags);
}
- return 0;
- }
- if (!info->xmit_buf){
- info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
- if (!info->xmit_buf){
- return -ENOMEM;
- }
+ free_page(page);
+ goto errout;
}
+ if (info->xmit_buf)
+ free_page(page);
+ else
+ info->xmit_buf = (unsigned char *) page;
+
set_line_char(info);
card = info->card;
card, chip, channel, (long)base_addr);/**/
#endif
- save_flags(flags); cli();
- cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+ cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
- cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
- ? info->default_timeout
- : 0x02)); /* 10ms rx timeout */
+ cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
+ ? info->default_timeout : 0x02)); /* 10ms rx timeout */
- cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
+ cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR,index);
- cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
- cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
- cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
+ cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
+ cy_writeb((ulong)base_addr+(CyMSVR1<<index), CyRTS);
+ cy_writeb((ulong)base_addr+(CyMSVR2<<index), CyDTR);
#ifdef CY_DEBUG_DTR
- printk("cyc:startup raising DTR\n");
- printk(" status: 0x%x, 0x%x\n",
- cy_readb(base_addr+(CyMSVR1<<index)),
- cy_readb(base_addr+(CyMSVR2<<index)));
+ printk("cyc:startup raising DTR\n");
+ printk(" status: 0x%x, 0x%x\n",
+ cy_readb(base_addr+(CyMSVR1<<index)),
+ cy_readb(base_addr+(CyMSVR2<<index)));
#endif
- cy_writeb((u_long)base_addr+(CySRER<<index),
- cy_readb(base_addr+(CySRER<<index)) | CyRxData);
- info->flags |= ASYNC_INITIALIZED;
+ cy_writeb((u_long)base_addr+(CySRER<<index),
+ cy_readb(base_addr+(CySRER<<index)) | CyRxData);
+ info->flags |= ASYNC_INITIALIZED;
+
+ if (info->tty){
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ }
+ info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+ info->breakon = info->breakoff = 0;
+ memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
+ info->idle_stats.in_use =
+ info->idle_stats.recv_idle =
+ info->idle_stats.xmit_idle = jiffies;
- if (info->tty){
- clear_bit(TTY_IO_ERROR, &info->tty->flags);
- }
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
- info->idle_stats.in_use =
- info->idle_stats.recv_idle =
- info->idle_stats.xmit_idle = jiffies;
restore_flags(flags);
+
} else {
struct FIRM_ID *firm_id;
struct ZFW_CTRL *zfw_ctrl;
struct CH_CTRL *ch_ctrl;
int retval;
+ restore_flags(flags);
+
base_addr = (unsigned char*) (cy_card[card].base_addr);
firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
clear_bit(TTY_IO_ERROR, &info->tty->flags);
}
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-
+ info->breakon = info->breakoff = 0;
memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats));
info->idle_stats.in_use =
info->idle_stats.recv_idle =
printk(" cyc startup done\n");
#endif
return 0;
+
+errout:
+ restore_flags(flags);
+ return retval;
} /* startup */
return 0;
} /* set_modem_info */
+/*
+ * cy_break() --- routine which turns the break handling on or off
+ */
static void
-send_break( struct cyclades_port * info, int duration)
+cy_break(struct tty_struct *tty, int break_state)
{
+ struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (serial_paranoia_check(info, tty->device, "cy_break"))
+ return;
+ save_flags(flags); cli();
if (!IS_CYC_Z(cy_card[info->card])) {
/* Let the transmit ISR take care of this (since it
requires stuffing characters into the output stream).
*/
- info->x_break = duration;
- if (!info->xmit_cnt ) {
- start_xmit(info);
+ if (break_state == -1) {
+ if (!info->breakon) {
+ info->breakon = 1;
+ if (!info->xmit_cnt ) {
+ start_xmit(info);
+ }
+ }
+ } else {
+ if (!info->breakoff) {
+ info->breakoff = 1;
+ if (!info->xmit_cnt ) {
+ start_xmit(info);
+ }
+ }
}
} else {
- /* For the moment we ignore the duration parameter!!!
- A better implementation will use C_CM_SET_BREAK
- and C_CM_CLR_BREAK with the appropriate delay.
- */
-#if 1
-// this appears to wedge the output data stream
-int retval;
- retval = cyz_issue_cmd(&cy_card[info->card],
+ int retval;
+
+ if (break_state == -1) {
+ retval = cyz_issue_cmd(&cy_card[info->card],
(info->line) - (cy_card[info->card].first_line),
- C_CM_SENDBRK, 0L);
- if (retval != 0){
- printk("cyc:send_break retval at %d was %x\n",
- __LINE__, retval);
+ C_CM_SET_BREAK, 0L);
+ if (retval != 0) {
+ printk("cyc:cy_break (set) retval at %d was %x\n",
+ __LINE__, retval);
+ }
+ } else {
+ retval = cyz_issue_cmd(&cy_card[info->card],
+ (info->line) - (cy_card[info->card].first_line),
+ C_CM_CLR_BREAK, 0L);
+ if (retval != 0) {
+ printk("cyc:cy_break (clr) retval at %d was %x\n",
+ __LINE__, retval);
+ }
}
-#endif
}
-} /* send_break */
+ restore_flags(flags);
+
+} /* cy_break */
static int
get_mon_info(struct cyclades_port * info, struct cyclades_monitor * mon)
case CYGETWAIT:
ret_val = info->closing_wait / (HZ/100);
break;
- case TCSBRK: /* SVID version: non-zero arg --> no break */
- ret_val = tty_check_change(tty);
- if (ret_val)
- return ret_val;
- tty_wait_until_sent(tty,0);
- if (!arg)
- send_break(info, HZ/4); /* 1/4 second */
- break;
- case TCSBRKP: /* support for POSIX tcsendbreak() */
- ret_val = tty_check_change(tty);
- if (ret_val)
- return ret_val;
- tty_wait_until_sent(tty,0);
- send_break(info, arg ? arg*(HZ/10) : HZ/4);
- break;
case TIOCMGET:
ret_val = get_modem_info(info, (unsigned int *) arg);
break;
tty->stopped = 0;
cy_start(tty);
}
-#ifdef tytso_patch_94Nov25_1726
+#if 0
+ /*
+ * No need to wake up processes in open wait, since they
+ * sample the CLOCAL flag once, and don't recheck it.
+ * XXX It's not clear whether the current behavior is correct
+ * or not. Hence, this may change.....
+ */
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&info->open_wait);
return;
} /* cy_set_termios */
-
-/*
- * void (*set_ldisc)(struct tty_struct *tty);
- *
- * This routine allows the tty driver to be notified when the
- * device's termios settings have changed.
- *
- */
-
-
/* This routine is called by the upper-layer tty layer to signal
that incoming characters should be throttled because the input
buffers are close to full.
return chip_number;
} /* cyy_init_card */
+#ifndef CONFIG_COBALT_27
/*
* ---------------------------------------------------------------------
* cy_detect_isa() - Probe for Cyclom-Y/ISA boards.
/* allocate IRQ */
if(request_irq(cy_isa_irq, cyy_interrupt,
- SA_INTERRUPT, "cyclomY", NULL))
+ SA_INTERRUPT, "Cyclom-Y", &cy_card[j]))
{
printk("Cyclom-Y/ISA found at 0x%lx ",
(unsigned long) cy_isa_address);
cy_card[j].bus_index = 0;
cy_card[j].first_line = cy_next_channel;
cy_card[j].num_chips = cy_isa_nchan/4;
- IRQ_cards[cy_isa_irq] = &cy_card[j];
nboard++;
/* print message */
return(nboard);
} /* cy_detect_isa */
+#endif /* CONFIG_COBALT_27 */
+
+static void plx_init(uclong addr, uclong initctl)
+{
+ /* Reset PLX */
+ cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x40000000);
+ udelay(100L);
+ cy_writel(addr + initctl, cy_readl(addr + initctl) & ~0x40000000);
+
+ /* Reload Config. Registers from EEPROM */
+ cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x20000000);
+ udelay(100L);
+ cy_writel(addr + initctl, cy_readl(addr + initctl) & ~0x20000000);
+}
/*
* ---------------------------------------------------------------------
cy_pci_addr0 &= PCI_BASE_ADDRESS_MEM_MASK;
cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK;
+ if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) {
+ printk(" Warning: PCI I/O bit incorrectly set. "
+ "Ignoring it...\n");
+ cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK;
+ }
+
#if defined(__alpha__)
if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
/* allocate IRQ */
if(request_irq(cy_pci_irq, cyy_interrupt,
- SA_INTERRUPT, "cyclomY", NULL))
+ SA_SHIRQ, "Cyclom-Y", &cy_card[j]))
{
printk("Cyclom-Y/PCI found at 0x%lx ",
(ulong) cy_pci_addr2);
cy_card[j].bus_index = 1;
cy_card[j].first_line = cy_next_channel;
cy_card[j].num_chips = cy_pci_nchan/4;
- IRQ_cards[cy_pci_irq] = &cy_card[j];
/* enable interrupts in the PCI interface */
plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
switch (plx_ver) {
case PLX_9050:
+ plx_init(cy_pci_addr0, 0x50);
+
cy_writew(cy_pci_addr0+0x4c,
cy_readw(cy_pci_addr0+0x4c)|0x0040);
break;
case PLX_9080:
default: /* Old boards, use PLX_9060 */
+ plx_init(cy_pci_addr0, 0x6c);
+
cy_writew(cy_pci_addr0+0x68,
cy_readw(cy_pci_addr0+0x68)|0x0900);
break;
#if !defined(__alpha__)
cy_pci_addr0 = (ulong)ioremap(cy_pci_addr0, CyPCI_Zctl);
#endif
+
+ plx_init(cy_pci_addr0, 0x6c);
+
mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *)
cy_pci_addr0)->mail_box_0);
cy_pci_addr2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ if (cy_pci_addr2 & ~PCI_BASE_ADDRESS_IO_MASK) {
+ printk(" Warning: PCI I/O bit incorrectly set. "
+ "Ignoring it...\n");
+ cy_pci_addr2 &= PCI_BASE_ADDRESS_IO_MASK;
+ }
if (mailbox == ZE_V1) {
#if !defined(__alpha__)
cy_pci_addr2 = (ulong)ioremap(cy_pci_addr2, CyPCI_Ze_win);
/* allocate IRQ only if board has an IRQ */
if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
if(request_irq(cy_pci_irq,cyz_interrupt,
- SA_INTERRUPT,"cyclomZ",NULL))
+ SA_SHIRQ,"Cyclades-Z",&cy_card[j]))
{
printk("Could not allocate IRQ%d ",
cy_pci_irq);
cy_card[j].bus_index = 1;
cy_card[j].first_line = cy_next_channel;
cy_card[j].num_chips = -1;
- IRQ_cards[cy_pci_irq] = &cy_card[j];
/* print message */
/* don't report IRQ if board is no IRQ */
/* allocate IRQ only if board has an IRQ */
if( (1 < cy_pci_irq) && (cy_pci_irq < 15) ) {
if(request_irq(cy_pci_irq,cyz_interrupt,
- SA_INTERRUPT,"cyclomZ",NULL))
+ SA_SHIRQ,"Cyclades-Z",&cy_card[j]))
{
printk("Could not allocate IRQ%d ",
cy_pci_irq);
cy_card[j].bus_index = 1;
cy_card[j].first_line = cy_next_channel;
cy_card[j].num_chips = -1;
- IRQ_cards[cy_pci_irq] = &cy_card[j];
/* print message */
/* don't report IRQ if board is no IRQ */
__DATE__, __TIME__);
} /* show_version */
-#ifdef CY_PROC
static int
cyclades_get_proc_info(char *buf, char **start, off_t offset, int length,
int *eof, void *data)
len = 0;
return len;
}
-#endif
/* The serial driver boot-time initialization code!
Hardware I/O ports are mapped to character special devices on a
struct proc_dir_entry *ent;
#endif
+ init_bh(CYCLADES_BH, do_cyclades_bh);
+
show_version();
/* Initialize the tty_driver structure */
memset(&cy_serial_driver, 0, sizeof(struct tty_driver));
cy_serial_driver.magic = TTY_DRIVER_MAGIC;
- cy_serial_driver.name = "cyclades";
+ cy_serial_driver.driver_name = "cyclades";
cy_serial_driver.name = "ttyC";
cy_serial_driver.major = CYCLADES_MAJOR;
cy_serial_driver.minor_start = 0;
cy_serial_driver.table = serial_table;
cy_serial_driver.termios = serial_termios;
cy_serial_driver.termios_locked = serial_termios_locked;
+
cy_serial_driver.open = cy_open;
cy_serial_driver.close = cy_close;
cy_serial_driver.write = cy_write;
cy_serial_driver.stop = cy_stop;
cy_serial_driver.start = cy_start;
cy_serial_driver.hangup = cy_hangup;
+ cy_serial_driver.break_ctl = cy_break;
cy_serial_driver.wait_until_sent = cy_wait_until_sent;
+ cy_serial_driver.read_proc = cyclades_get_proc_info;
/*
* The callout device is just like normal device except for
if (tty_register_driver(&cy_callout_driver))
panic("Couldn't register Cyclades callout driver\n");
- init_bh(CYCLADES_BH, do_cyclades_bh);
-
- for (i = 0; i < NR_IRQS; i++) {
- IRQ_cards[i] = 0;
- }
-
for (i = 0; i < NR_CARDS; i++) {
/* base_addr=0 indicates board not found */
cy_card[i].base_addr = 0;
availability of cy_card and cy_port data structures and updating
the cy_next_channel. */
+#ifndef CONFIG_COBALT_27
/* look for isa boards */
cy_isa_nboard = cy_detect_isa();
-
+#endif /* CONFIG_COBALT_27 */
+
/* look for pci boards */
cy_pci_nboard = cy_detect_pci();
cy_callout_driver.init_termios;
info->normal_termios =
cy_serial_driver.init_termios;
- init_waitqueue(&info->open_wait);
- init_waitqueue(&info->close_wait);
- init_waitqueue(&info->shutdown_wait);
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
+ init_waitqueue_head(&info->shutdown_wait);
/* info->session */
/* info->pgrp */
info->read_status_mask =
cleanup_module(void)
{
int i;
+ int e1, e2;
unsigned long flags;
if (cyz_timeron){
save_flags(flags); cli();
remove_bh(CYCLADES_BH);
- free_page((unsigned long)tmp_buf);
- if (tty_unregister_driver(&cy_callout_driver))
- printk("Couldn't unregister Cyclades callout driver\n");
- if (tty_unregister_driver(&cy_serial_driver))
- printk("Couldn't unregister Cyclades serial driver\n");
+ if ((e1 = tty_unregister_driver(&cy_serial_driver)))
+ printk("cyc: failed to unregister Cyclades serial driver(%d)\n",
+ e1);
+ if ((e2 = tty_unregister_driver(&cy_callout_driver)))
+ printk("cyc: failed to unregister Cyclades callout driver (%d)\n",
+ e2);
restore_flags(flags);
if (cy_card[i].base_addr != 0
&& cy_card[i].irq)
{
- free_irq(cy_card[i].irq,NULL);
+ free_irq(cy_card[i].irq, &cy_card[i]);
}
}
+ if (tmp_buf) {
+ free_page((unsigned long) tmp_buf);
+ tmp_buf = NULL;
+ }
#ifdef CY_PROC
remove_proc_entry("cyclades", 0);
#endif
void
cy_setup(char *str, int *ints)
{
+#ifndef CONFIG_COBALT_27
int i, j;
for (i = 0 ; i < NR_ISA_ADDRS ; i++) {
cy_isa_addresses[i++] = (unsigned char *)(ints[j]);
}
}
+#endif /* CONFIG_COBALT_27 */
} /* cy_setup */
#endif
--- /dev/null
+/*
+ * I2C driver for parallel port
+ *
+ * Author: Phil Blundell <philb@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * This driver implements a simple I2C protocol by bit-twiddling some
+ * signals on the parallel port. Since the outputs on the parallel port
+ * aren't open collector, three lines rather than two are used:
+ *
+ * D0 clock out
+ * D1 data out
+ * BUSY data in
+ */
+
+#include <linux/parport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/spinlock.h>
+
+#define I2C_DELAY 10
+
+static int debug = 0;
+
+struct parport_i2c_bus
+{
+ struct i2c_bus i2c;
+ struct parport_i2c_bus *next;
+};
+
+static struct parport_i2c_bus *bus_list;
+
+#ifdef __SMP__
+static spinlock_t bus_list_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/* software I2C functions */
+
+static void i2c_setlines(struct i2c_bus *bus, int clk, int data)
+{
+ struct parport *p = bus->data;
+ parport_write_data(p, (clk?1:0) | (data?2:0));
+ udelay(I2C_DELAY);
+}
+
+static int i2c_getdataline(struct i2c_bus *bus)
+{
+ struct parport *p = bus->data;
+ return (parport_read_status(p) & PARPORT_STATUS_BUSY) ? 0 : 1;
+}
+
+static struct i2c_bus parport_i2c_bus_template =
+{
+ "...",
+ I2C_BUSID_PARPORT,
+ NULL,
+
+ SPIN_LOCK_UNLOCKED,
+
+ NULL,
+ NULL,
+
+ i2c_setlines,
+ i2c_getdataline,
+ NULL,
+ NULL,
+};
+
+static void i2c_parport_attach(struct parport *port)
+{
+ struct parport_i2c_bus *b = kmalloc(sizeof(struct parport_i2c_bus),
+ GFP_KERNEL);
+ b->i2c = parport_i2c_bus_template;
+ b->i2c.data = port;
+ strncpy(b->i2c.name, port->name, 32);
+ spin_lock(&bus_list_lock);
+ b->next = bus_list;
+ bus_list = b;
+ spin_unlock(&bus_list_lock);
+ i2c_register_bus(&b->i2c);
+ if (debug)
+ printk(KERN_DEBUG "i2c: attached to %s\n", port->name);
+}
+
+static void i2c_parport_detach(struct parport *port)
+{
+ struct parport_i2c_bus *b, *old_b = NULL;
+ spin_lock(&bus_list_lock);
+ b = bus_list;
+ while (b)
+ {
+ if (b->i2c.data == port)
+ {
+ if (old_b)
+ old_b->next = b->next;
+ else
+ bus_list = b->next;
+ i2c_unregister_bus(&b->i2c);
+ kfree(b);
+ break;
+ }
+ old_b = b;
+ b = b->next;
+ }
+ spin_unlock(&bus_list_lock);
+ if (debug)
+ printk(KERN_DEBUG "i2c: detached from %s\n", port->name);
+}
+
+static struct parport_driver parport_i2c_driver =
+{
+ "i2c",
+ i2c_parport_attach,
+ i2c_parport_detach
+};
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init i2c_parport_init(void)
+#endif
+{
+ printk("I2C: driver for parallel port v0.1 philb@gnu.org\n");
+ parport_register_driver(&parport_i2c_driver);
+ return 0;
+}
+
+#ifdef MODULE
+MODULE_PARM(debug, "i");
+
+void cleanup_module(void)
+{
+ struct parport_i2c_bus *b = bus_list;
+ while (b)
+ {
+ struct parport_i2c_bus *next = b->next;
+ i2c_unregister_bus(&b->i2c);
+ kfree(b);
+ b = next;
+ }
+ parport_unregister_driver(&parport_i2c_driver);
+}
+#endif
sysrq_pressed = !up_flag;
return;
} else if (sysrq_pressed) {
- if (!up_flag)
+ if (!up_flag) {
handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty);
- return;
+ return;
+ }
}
#endif
static void boot_it(void)
{
- if (kbd->slockstate & ~shift_state) return;
ctrl_alt_del();
}
if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
bool 'HiSax Support for EURO/DSS1' CONFIG_HISAX_EURO
if [ "$CONFIG_HISAX_EURO" != "n" ]; then
- bool 'Support for german tarifinfo' CONFIG_DE_AOC
- bool 'Support for australian Microlink service (not for std. EURO)' CONFIG_HISAX_ML
+ bool 'Support for german chargeinfo' CONFIG_DE_AOC
+ bool 'Disable sending complete' CONFIG_HISAX_NO_SENDCOMPLETE
+ bool 'Disable sending low layer compatibility' CONFIG_HISAX_NO_LLC
fi
bool 'HiSax Support for german 1TR6' CONFIG_HISAX_1TR6
bool 'HiSax Support for Teles 16.0/8.0' CONFIG_HISAX_16_0
bool 'HiSax Support for Teles 16.3 or PNP or PCMCIA' CONFIG_HISAX_16_3
bool 'HiSax Support for Teles 16.3c' CONFIG_HISAX_TELES3C
+ bool 'HiSax Support for Teles PCI' CONFIG_HISAX_TELESPCI
+ bool 'HiSax Support for Teles S0Box' CONFIG_HISAX_S0BOX
bool 'HiSax Support for AVM A1 (Fritz)' CONFIG_HISAX_AVM_A1
+ bool 'HiSax Support for AVM PnP/PCI (Fritz!PnP/PCI)' CONFIG_HISAX_FRITZPCI
+ bool 'HiSax Support for AVM A1 PCMCIA (Fritz)' CONFIG_HISAX_AVM_A1_PCMCIA
bool 'HiSax Support for Elsa cards' CONFIG_HISAX_ELSA
bool 'HiSax Support for ITK ix1-micro Revision 2' CONFIG_HISAX_IX1MICROR2
bool 'HiSax Support for Eicon.Diehl Diva cards' CONFIG_HISAX_DIEHLDIVA
bool 'HiSax Support for ASUSCOM cards' CONFIG_HISAX_ASUSCOM
bool 'HiSax Support for TELEINT cards' CONFIG_HISAX_TELEINT
- bool 'HiSax Support for Sedlbauer speed card/win/star' CONFIG_HISAX_SEDLBAUER
+ bool 'HiSax Support for Sedlbauer speed card/win/star/fax' CONFIG_HISAX_SEDLBAUER
bool 'HiSax Support for USR Sportster internal TA' CONFIG_HISAX_SPORTSTER
bool 'HiSax Support for MIC card' CONFIG_HISAX_MIC
bool 'HiSax Support for NETjet card' CONFIG_HISAX_NETJET
bool 'HiSax Support for Niccy PnP/PCI card' CONFIG_HISAX_NICCY
if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
- bool 'HiSax Support for SPARC Am7930' CONFIG_HISAX_AMD7930
- bool 'HiSax Support for SPARC DBRI' CONFIG_HISAX_DBRI
+ bool 'HiSax Support for Am7930' CONFIG_HISAX_AMD7930
fi
fi
fi
if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then
dep_tristate 'Spellcaster support (EXPERIMENTAL)' CONFIG_ISDN_DRV_SC $CONFIG_ISDN
dep_tristate 'IBM Active 2000 support (EXPERIMENTAL)' CONFIG_ISDN_DRV_ACT2000 $CONFIG_ISDN
+ dep_tristate 'Eicon.Diehl active card support (EXPERIMENTAL)' CONFIG_ISDN_DRV_EICON $CONFIG_ISDN
fi
dep_tristate 'AVM-B1 with CAPI2.0 support' CONFIG_ISDN_DRV_AVMB1 $CONFIG_ISDN
if [ "$CONFIG_ISDN_DRV_AVMB1" != "n" ]; then
SUB_DIRS :=
MOD_SUB_DIRS :=
-ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000
+ALL_SUB_DIRS := icn pcbit hisax avmb1 act2000 eicon
L_OBJS :=
LX_OBJS :=
OX_OBJS += isdn_common.o
ifdef CONFIG_ISDN_PPP
O_OBJS += isdn_ppp.o
+ M_OBJS += isdn_bsdcomp.o
endif
ifdef CONFIG_ISDN_X25
O_OBJS += isdn_x25iface.o
endif
endif
+ifeq ($(CONFIG_ISDN_DRV_EICON),y)
+ L_OBJS += eicon/eicon.o
+ SUB_DIRS += eicon
+ MOD_SUB_DIRS += eicon
+else
+ ifeq ($(CONFIG_ISDN_DRV_EICON),m)
+ MOD_SUB_DIRS += eicon
+ endif
+endif
+
include $(TOPDIR)/Rules.make
-/* $Id: act2000.h,v 1.5 1997/10/09 22:22:59 fritz Exp $
+/* $Id: act2000.h,v 1.7 1999/04/12 13:13:54 fritz Exp $
*
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
*
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Thanks to Friedemann Baitinger and IBM Germany
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: act2000.h,v $
+ * Revision 1.7 1999/04/12 13:13:54 fritz
+ * Made cards pointer static to avoid name-clash.
+ *
+ * Revision 1.6 1998/11/05 22:12:38 fritz
+ * Changed mail-address.
+ *
* Revision 1.5 1997/10/09 22:22:59 fritz
* New HL<->LL interface:
* New BSENT callback with nr. of bytes included.
char regname[35]; /* Name used for request_region */
} act2000_card;
-extern act2000_card *actcards;
-
extern __inline__ void act2000_schedule_tx(act2000_card *card)
{
queue_task(&card->snd_tq, &tq_immediate);
-/* $Id: act2000_isa.c,v 1.5 1998/02/12 23:06:47 keil Exp $
+/* $Id: act2000_isa.c,v 1.8 1999/01/05 18:29:25 he Exp $
*
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
*
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Thanks to Friedemann Baitinger and IBM Germany
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: act2000_isa.c,v $
+ * Revision 1.8 1999/01/05 18:29:25 he
+ * merged remaining schedule_timeout() changes from 2.1.127
+ *
+ * Revision 1.7 1998/11/05 22:12:41 fritz
+ * Changed mail-address.
+ *
+ * Revision 1.6 1998/06/17 19:51:09 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
* Revision 1.5 1998/02/12 23:06:47 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
-/* $Id: act2000_isa.h,v 1.1 1997/09/23 18:00:07 fritz Exp $
+/* $Id: act2000_isa.h,v 1.2 1998/11/05 22:12:43 fritz Exp $
*
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
*
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Thanks to Friedemann Baitinger and IBM Germany
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: act2000_isa.h,v $
+ * Revision 1.2 1998/11/05 22:12:43 fritz
+ * Changed mail-address.
+ *
* Revision 1.1 1997/09/23 18:00:07 fritz
* New driver for IBM Active 2000.
*
-/* $Id: capi.c,v 1.7 1998/02/23 23:35:41 fritz Exp $
+/* $Id: capi.c,v 1.8 1998/11/05 22:12:46 fritz Exp $
*
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
* CAPI encoder/decoder
*
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Thanks to Friedemann Baitinger and IBM Germany
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: capi.c,v $
+ * Revision 1.8 1998/11/05 22:12:46 fritz
+ * Changed mail-address.
+ *
* Revision 1.7 1998/02/23 23:35:41 fritz
* Eliminated some compiler warnings.
*
-/* $Id: capi.h,v 1.4 1997/10/01 09:21:04 fritz Exp $
+/* $Id: capi.h,v 1.5 1998/11/05 22:12:48 fritz Exp $
*
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
*
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Thanks to Friedemann Baitinger and IBM Germany
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: capi.h,v $
+ * Revision 1.5 1998/11/05 22:12:48 fritz
+ * Changed mail-address.
+ *
* Revision 1.4 1997/10/01 09:21:04 fritz
* Removed old compatibility stuff for 2.0.X kernels.
* From now on, this code is for 2.1.X ONLY!
-/* $Id: module.c,v 1.7 1998/02/12 23:06:52 keil Exp $
+/* $Id: module.c,v 1.9 1999/04/12 13:13:56 fritz Exp $
*
* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
*
- * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de)
* Thanks to Friedemann Baitinger and IBM Germany
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: module.c,v $
+ * Revision 1.9 1999/04/12 13:13:56 fritz
+ * Made cards pointer static to avoid name-clash.
+ *
+ * Revision 1.8 1998/11/05 22:12:51 fritz
+ * Changed mail-address.
+ *
* Revision 1.7 1998/02/12 23:06:52 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
};
#define ISA_NRPORTS (sizeof(isa_ports)/sizeof(unsigned short))
-act2000_card *actcards = (act2000_card *) NULL;
+static act2000_card *cards = (act2000_card *) NULL;
/* Parameters to be set by insmod */
static int act_bus = 0;
static inline act2000_card *
act2000_findcard(int driverid)
{
- act2000_card *p = actcards;
+ act2000_card *p = cards;
while (p) {
if (p->myid == driverid)
card->bus = bus;
card->port = port;
card->irq = irq;
- card->next = actcards;
- actcards = card;
+ card->next = cards;
+ cards = card;
}
/*
bus);
}
}
- if (!actcards)
+ if (!cards)
return 1;
- p = actcards;
+ p = cards;
while (p) {
initialized = 0;
if (!p->interface.statcallb) {
kfree(p);
p = q->next;
} else {
- actcards = p->next;
+ cards = p->next;
kfree(p);
- p = actcards;
+ p = cards;
}
failed++;
}
act2000_init(void)
{
printk(KERN_INFO "%s\n", DRIVERNAME);
- if (!actcards)
+ if (!cards)
act2000_addcard(act_bus, act_port, act_irq, act_id);
- if (!actcards)
+ if (!cards)
printk(KERN_INFO "act2000: No cards defined yet\n");
/* No symbols to export, hide all symbols */
EXPORT_NO_SYMBOLS;
void
cleanup_module(void)
{
- act2000_card *card = actcards;
+ act2000_card *card = cards;
act2000_card *last;
while (card) {
unregister_card(card);
del_timer(&card->ptimer);
card = card->next;
}
- card = actcards;
+ card = cards;
while (card) {
last = card;
card = card->next;
/*
- * $Id: b1capi.c,v 1.10 1998/02/13 07:09:10 calle Exp $
+ * $Id: b1capi.c,v 1.14 1999/04/15 19:49:29 calle Exp $
*
* CAPI 2.0 Module for AVM B1-card.
*
* (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: b1capi.c,v $
+ * Revision 1.14 1999/04/15 19:49:29 calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.13 1999/01/05 18:29:31 he
+ * merged remaining schedule_timeout() changes from 2.1.127
+ *
+ * Revision 1.12 1998/10/25 14:38:58 fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.11 1998/03/29 16:05:58 calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.4.2.18 1998/03/20 20:34:37 calle
+ * port valid check now only for T1, because of the PCI and PCMCIA cards.
+ *
+ * Revision 1.4.2.17 1998/03/20 14:38:17 calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.4.2.16 1998/03/20 09:01:08 calle
+ * Changes capi_register handling to get full support for 30 bchannels.
+ *
+ * Revision 1.4.2.15 1998/03/18 17:43:26 calle
+ * T1 with fastlink, bugfix for multicontroller support in capidrv.c
+ *
+ * Revision 1.4.2.14 1998/03/04 17:33:47 calle
+ * Changes for T1.
+ *
+ * Revision 1.4.2.13 1998/02/27 15:40:41 calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
+ * Revision 1.4.2.12 1998/02/24 17:58:25 calle
+ * changes for T1.
+ *
* Revision 1.10 1998/02/13 07:09:10 calle
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#include "capicmd.h"
#include "capiutil.h"
-static char *revision = "$Revision: 1.10 $";
+static char *revision = "$Revision: 1.14 $";
/* ------------------------------------------------------------- */
int loaddebug = 0;
MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
-MODULE_PARM(showcapimsgs, "0-3i");
+MODULE_PARM(showcapimsgs, "0-5i");
MODULE_PARM(loaddebug, "0-1i");
/* ------------------------------------------------------------- */
{
switch (cardtype) {
default:
- case AVM_CARDTYPE_B1: return "B1";
- case AVM_CARDTYPE_M1: return "M1";
- case AVM_CARDTYPE_M2: return "M2";
- case AVM_CARDTYPE_T1: return "T1";
+ case AVM_CARDTYPE_B1: return "B1-ISA";
+ case AVM_CARDTYPE_B1PCI: return "B1-PCI";
+ case AVM_CARDTYPE_M1: return "M1";
+ case AVM_CARDTYPE_M2: return "M2";
+ case AVM_CARDTYPE_T1: return "T1";
}
}
}
}
APPL(appl)->releasing--;
- if (APPL(appl)->releasing == 0) {
+ if (APPL(appl)->releasing <= 0) {
APPL(appl)->signal = 0;
APPL_MARK_FREE(appl);
printk(KERN_INFO "b1capi: appl %d down\n", appl);
/* -------- card ready callback ------------------------------- */
+
void avmb1_card_ready(avmb1_card * card)
{
struct capi_profile *profp =
__u16 appl;
char *cardname, cname[20];
__u32 flag;
+ int nbchan = profp->nbchannel;
card->cversion.majorversion = 2;
card->cversion.minorversion = 0;
for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
if (VALID_APPLID(appl) && !APPL(appl)->releasing) {
+ int nconn, want = APPL(appl)->rparam.level3cnt;
+
+ if (want > 0) nconn = want;
+ else nconn = nbchan * -want;
+ if (nconn == 0) nconn = nbchan;
+
B1_send_register(card->port, appl,
- 1024 * (APPL(appl)->rparam.level3cnt+1),
- APPL(appl)->rparam.level3cnt,
+ 1024 * (nconn+1), nconn,
APPL(appl)->rparam.datablkcnt,
APPL(appl)->rparam.datablklen);
}
SA_SHIRQ, card->name, card)) != 0) {
printk(KERN_ERR "b1capi: unable to get IRQ %d (irqval=%d).\n",
irq, irqval);
- release_region((unsigned short) port, AVMB1_PORTLEN);
- return -EIO;
+ release_region(port, AVMB1_PORTLEN);
+ return -EBUSY;
}
card->cardstate = CARD_DETECTED;
if (!B1_valid_irq(irq, cardtype)) {
printk(KERN_WARNING "b1capi: irq %d not valid for %s-card.\n",
irq, cardtype2str(cardtype));
- return -EIO;
+ return -EINVAL;
+ }
+ if (!B1_valid_port(port, cardtype)) {
+ printk(KERN_WARNING "b1capi: port 0x%x not valid for %s-card.\n",
+ port, cardtype2str(cardtype));
+ return -EINVAL;
}
+ B1_reset(port);
if ((rc = B1_detect(port, cardtype)) != 0) {
printk(KERN_NOTICE "b1capi: NO %s-card at 0x%x (%d)\n",
cardtype2str(cardtype), port, rc);
case AVM_CARDTYPE_M1:
case AVM_CARDTYPE_M2:
case AVM_CARDTYPE_B1:
+ case AVM_CARDTYPE_B1PCI:
printk(KERN_NOTICE "b1capi: AVM-%s-Controller detected at 0x%x\n", cardtype2str(cardtype), port);
break;
case AVM_CARDTYPE_T1:
- printk(KERN_NOTICE "b1capi: AVM-%s-Controller may be at 0x%x\n", cardtype2str(cardtype), port);
break;
}
int avmb1_probecard(int port, int irq, int cardtype)
{
- if (check_region((unsigned short) port, AVMB1_PORTLEN)) {
+ if (check_region(port, AVMB1_PORTLEN)) {
printk(KERN_WARNING
"b1capi: ports 0x%03x-0x%03x in use.\n",
port, port + AVMB1_PORTLEN);
- return -EIO;
+ return -EBUSY;
}
return avmb1_detectcard(port, irq, cardtype);
}
if (!VALID_CARD(cnr))
return -ESRCH;
card = CARD(cnr);
+
if (card->cardstate == CARD_FREE)
return -ESRCH;
if (card->cardstate == CARD_RUNNING)
avmb1_card_down(card, freeio);
+ if (card->cardstate != CARD_FREE)
+ if (card->cardtype == AVM_CARDTYPE_T1)
+ T1_reset(card->port);
+
free_irq(card->irq, card);
if (freeio)
release_region(card->port, AVMB1_PORTLEN);
static __u16 capi_register(capi_register_params * rparam, __u16 * applidp)
{
+ int nconn, want = rparam->level3cnt;
int i;
int appl;
memcpy(&APPL(appl)->rparam, rparam, sizeof(capi_register_params));
for (i = 0; i < CAPI_MAXCONTR; i++) {
+ struct capi_profile *profp =
+ (struct capi_profile *)cards[i].version[VER_PROFILE];
+
if (cards[i].cardstate != CARD_RUNNING)
continue;
+
+ if (want > 0) nconn = want;
+ else nconn = profp->nbchannel * -want;
+ if (nconn == 0) nconn = profp->nbchannel;
+
B1_send_register(cards[i].port, appl,
- 1024 * (APPL(appl)->rparam.level3cnt + 1),
- APPL(appl)->rparam.level3cnt,
- APPL(appl)->rparam.datablkcnt,
- APPL(appl)->rparam.datablklen);
+ 1024 * (nconn+1), nconn,
+ APPL(appl)->rparam.datablkcnt,
+ APPL(appl)->rparam.datablklen);
}
*applidp = appl;
printk(KERN_INFO "b1capi: appl %d up\n", appl);
struct sk_buff *skb;
int i;
- if (ncards == 0)
- return CAPI_REGNOTINSTALLED;
if (!VALID_APPLID(applid) || APPL(applid)->releasing)
return CAPI_ILLAPPNR;
while ((skb = skb_dequeue(&APPL(applid)->recv_queue)) != 0)
APPL(applid)->releasing++;
B1_send_release(cards[i].port, applid);
}
- if (APPL(applid)->releasing == 0) {
+ if (APPL(applid)->releasing <= 0) {
APPL(applid)->signal = 0;
APPL_MARK_FREE(applid);
printk(KERN_INFO "b1capi: appl %d down\n", applid);
if ((rc = avmb1_probecard(cdef.port, cdef.irq, cdef.cardtype)) != 0)
return rc;
- return avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype);
+ if (cdef.cardtype == AVM_CARDTYPE_T1) {
+ int i;
+ for (i=0; i < CAPI_MAXCONTR; i++) {
+ if ( cards[i].cardstate != CARD_FREE
+ && cards[i].cardtype == AVM_CARDTYPE_T1
+ && cards[i].cardnr == cdef.cardnr) {
+ printk(KERN_ERR
+ "b1capi: T1-HEMA-card-%d already at 0x%x\n",
+ cdef.cardnr, cards[i].port);
+ return -EBUSY;
+ }
+ }
+ rc = T1_detectandinit(cdef.port,cdef.irq,cdef.cardnr);
+ if (rc) {
+ printk(KERN_NOTICE "b1capi: NO T1-HEMA-card-%d at 0x%x (%d)\n",
+ cdef.cardnr, cdef.port, rc);
+ return -EIO;
+ }
+ printk(KERN_NOTICE "b1capi: T1-HEMA-card-%d at 0x%x\n",
+ cdef.cardnr, cdef.port);
+ }
+
+ rc = avmb1_addcard(cdef.port, cdef.irq, cdef.cardtype);
+ if (rc < 0)
+ return rc;
+ /* don't want to change interface t
+ addcard/probecard/registercard */
+ if (cdef.cardtype == AVM_CARDTYPE_T1) {
+ int i;
+ for (i=0; i < CAPI_MAXCONTR; i++) {
+ if (cards[i].cnr == rc) {
+ cards[i].cardnr = cdef.cardnr;
+ break;
+ }
+ }
+ }
+ return rc;
case AVMB1_LOAD:
case AVMB1_LOAD_AND_CONFIG:
return -ESRCH;
if (ldef.t4file.len <= 0) {
- if (loaddebug)
- printk(KERN_DEBUG "b1capi: load: invalid parameter length of t4file is %d ?\n", ldef.t4file.len);
+ printk(KERN_DEBUG "b1capi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
return -EINVAL;
}
}
B1_reset(card->port);
+
+ if (loaddebug) {
+ printk(KERN_DEBUG "b1capi: loading contr %d\n",
+ ldef.contr);
+ }
+
if ((rc = B1_load_t4file(card->port, &ldef.t4file))) {
B1_reset(card->port);
printk(KERN_ERR "b1capi: failed to load t4file!!\n");
card->cardstate = CARD_DETECTED;
return rc;
}
+
B1_disable_irq(card->port);
if (ldef.t4config.len > 0) { /* load config */
card->cardstate = CARD_INITSTATE;
save_flags(flags);
cli();
- B1_assign_irq(card->port, card->irq, card->cardtype);
- B1_enable_irq(card->port);
+ B1_setinterrupt(card->port, card->irq, card->cardtype);
restore_flags(flags);
if (loaddebug) {
/*
* init card
*/
- B1_send_init(card->port, AVM_NAPPS, AVM_NNCCI, card->cnr - 1);
+ if (card->cardtype == AVM_CARDTYPE_T1)
+ B1_send_init(card->port, AVM_NAPPS,
+ AVM_NNCCI_PER_CHANNEL*30,
+ card->cnr - 1);
+ else
+ B1_send_init(card->port, AVM_NAPPS,
+ AVM_NNCCI_PER_CHANNEL*2,
+ card->cnr - 1);
if (loaddebug) {
printk(KERN_DEBUG "b1capi: load: waiting for init reply contr %d\n",
return rc;
return 0;
+ case AVMB1_REMOVECARD:
+ if ((rc = copy_from_user((void *) &rdef, data,
+ sizeof(avmb1_resetdef))))
+ return rc;
+ if (!VALID_CARD(rdef.contr))
+ return -ESRCH;
+
+ card = CARD(rdef.contr);
+
+ if (card->cardstate != CARD_DETECTED)
+ return -EBUSY;
+
+ return avmb1_unregistercard(rdef.contr, 1);
}
return -EINVAL;
}
userp->next = capi_users;
capi_users = userp;
MOD_INC_USE_COUNT;
+ printk(KERN_NOTICE "b1capi: %s attached\n", userp->name);
return &avmb1_interface;
}
*pp = userp->next;
userp->next = 0;
MOD_DEC_USE_COUNT;
+ printk(KERN_NOTICE "b1capi: %s detached\n", userp->name);
return 0;
}
}
/*
- * $Id: b1lli.c,v 1.6 1998/02/13 07:09:11 calle Exp $
+ * $Id: b1lli.c,v 1.10 1999/04/15 19:49:31 calle Exp $
*
* ISDN lowlevel-module for AVM B1-card.
*
* (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: b1lli.c,v $
+ * Revision 1.10 1999/04/15 19:49:31 calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.9 1999/01/05 18:33:23 he
+ * merged remaining 2.2pre{1,2} changes (jiffies and Config)
+ *
+ * Revision 1.8 1998/10/25 14:39:00 fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.7 1998/03/29 16:06:00 calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.1.2.10 1998/03/20 20:34:41 calle
+ * port valid check now only for T1, because of the PCI and PCMCIA cards.
+ *
+ * Revision 1.1.2.9 1998/03/20 14:38:20 calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.1.2.8 1998/03/18 17:43:29 calle
+ * T1 with fastlink, bugfix for multicontroller support in capidrv.c
+ *
+ * Revision 1.1.2.7 1998/03/04 17:33:50 calle
+ * Changes for T1.
+ *
+ * Revision 1.1.2.6 1998/02/27 15:40:44 calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
+ * Revision 1.1.2.5 1998/02/13 16:28:28 calle
+ * first step for T1
+ *
* Revision 1.6 1998/02/13 07:09:11 calle
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
*
*
*/
+/* #define FASTLINK_DEBUG */
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include "capicmd.h"
#include "capiutil.h"
+extern int showcapimsgs;
+
/*
* LLI Messages to the ISDN-ControllerISDN Controller
*/
#define SEND_CONFIG 0x21 /*
*/
+#define SEND_POLLACK 0x73 /* T1 Watchdog */
+
/*
* LLI Messages from the ISDN-ControllerISDN Controller
*/
#define RECEIVE_RELEASE 0x26 /*
* int32 AppllID int32 0xffffffff
*/
+#define RECEIVE_TASK_READY 0x31 /*
+ * int32 tasknr
+ * int32 Length Taskname ...
+ */
#define WRITE_REGISTER 0x00
#define READ_REGISTER 0x01
#define B1_OUTSTAT 0x03
#define B1_RESET 0x10
#define B1_ANALYSE 0x04
-#define B1_IDENT 0x17 /* Hema card T1 */
-#define B1_IRQ_MASTER 0x12 /* Hema card T1 */
+
+/* Hema card T1 */
+
+#define T1_FASTLINK 0x00
+#define T1_SLOWLINK 0x08
+
+#define T1_READ B1_READ
+#define T1_WRITE B1_WRITE
+#define T1_INSTAT B1_INSTAT
+#define T1_OUTSTAT B1_OUTSTAT
+#define T1_IRQENABLE 0x05
+#define T1_FIFOSTAT 0x06
+#define T1_RESETLINK 0x10
+#define T1_ANALYSE 0x11
+#define T1_IRQMASTER 0x12
+#define T1_IDENT 0x17
+#define T1_RESETBOARD 0x1f
+
+#define T1F_IREADY 0x01
+#define T1F_IHALF 0x02
+#define T1F_IFULL 0x04
+#define T1F_IEMPTY 0x08
+#define T1F_IFLAGS 0xF0
+
+#define T1F_OREADY 0x10
+#define T1F_OHALF 0x20
+#define T1F_OEMPTY 0x40
+#define T1F_OFULL 0x80
+#define T1F_OFLAGS 0xF0
+
+/* there are HEMA cards with 1k and 4k FIFO out */
+#define FIFO_OUTBSIZE 256
+#define FIFO_INPBSIZE 512
+
+#define HEMA_VERSION_ID 0
+#define HEMA_PAL_ID 0
#define B1_STAT0(cardtype) ((cardtype) == AVM_CARDTYPE_M1 ? 0x81200000l : 0x80A00000l)
#define B1_STAT1(cardtype) (0x80E00000l)
-static inline unsigned char b1outp(unsigned short base,
+static inline unsigned char b1outp(unsigned int base,
unsigned short offset,
unsigned char value)
{
return inb(base + B1_ANALYSE);
}
-static inline int B1_rx_full(unsigned short base)
+static inline void t1outp(unsigned int base,
+ unsigned short offset,
+ unsigned char value)
+{
+ outb(value, base + offset);
+}
+
+static inline unsigned char t1inp(unsigned int base,
+ unsigned short offset)
+{
+ return inb(base + offset);
+}
+
+static inline int B1_isfastlink(unsigned int base)
+{
+ return (inb(base + T1_IDENT) & ~0x82) == 1;
+}
+static inline unsigned char B1_fifostatus(unsigned int base)
+{
+ return inb(base + T1_FIFOSTAT);
+}
+
+static inline int B1_rx_full(unsigned int base)
{
return inb(base + B1_INSTAT) & 0x1;
}
-static inline unsigned char B1_get_byte(unsigned short base)
+static inline unsigned char B1_get_byte(unsigned int base)
{
- unsigned long i = jiffies + 5 * HZ; /* maximum wait time 5 sec */
+ unsigned long i = jiffies + 1 * HZ; /* maximum wait time 1 sec */
while (!B1_rx_full(base) && time_before(jiffies, i));
if (B1_rx_full(base))
return inb(base + B1_READ);
- printk(KERN_CRIT "b1lli: rx not full after 5 second\n");
+ printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
return 0;
}
-static inline unsigned int B1_get_word(unsigned short base)
+static inline unsigned int B1_get_word(unsigned int base)
{
unsigned int val = 0;
val |= B1_get_byte(base);
return val;
}
-static inline int B1_tx_empty(unsigned short base)
+static inline int B1_tx_empty(unsigned int base)
{
return inb(base + B1_OUTSTAT) & 0x1;
}
-static inline void B1_put_byte(unsigned short base, unsigned char val)
+static inline void B1_put_byte(unsigned int base, unsigned char val)
{
while (!B1_tx_empty(base));
b1outp(base, B1_WRITE, val);
}
-static inline void B1_put_word(unsigned short base, unsigned int val)
+static inline void B1_put_word(unsigned int base, unsigned int val)
{
B1_put_byte(base, val & 0xff);
B1_put_byte(base, (val >> 8) & 0xff);
B1_put_byte(base, (val >> 24) & 0xff);
}
-static inline unsigned int B1_get_slice(unsigned short base,
+static inline unsigned int B1_get_slice(unsigned int base,
unsigned char *dp)
{
unsigned int len, i;
+#ifdef FASTLINK_DEBUG
+ unsigned wcnt = 0, bcnt = 0;
+#endif
len = i = B1_get_word(base);
- while (i-- > 0)
- *dp++ = B1_get_byte(base);
+ if (B1_isfastlink(base)) {
+ int status;
+ while (i > 0) {
+ status = B1_fifostatus(base) & (T1F_IREADY|T1F_IHALF);
+ if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
+
+ switch (status) {
+ case T1F_IREADY|T1F_IHALF|T1F_IFULL:
+ insb(base+B1_READ, dp, FIFO_INPBSIZE);
+ dp += FIFO_INPBSIZE;
+ i -= FIFO_INPBSIZE;
+#ifdef FASTLINK_DEBUG
+ wcnt += FIFO_INPBSIZE;
+#endif
+ break;
+ case T1F_IREADY|T1F_IHALF:
+ insb(base+B1_READ,dp, i);
+#ifdef FASTLINK_DEBUG
+ wcnt += i;
+#endif
+ dp += i;
+ i = 0;
+ if (i == 0)
+ break;
+ /* fall through */
+ default:
+ *dp++ = B1_get_byte(base);
+ i--;
+#ifdef FASTLINK_DEBUG
+ bcnt++;
+#endif
+ break;
+ }
+ }
+#ifdef FASTLINK_DEBUG
+ if (wcnt)
+ printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
+ base, len, wcnt, bcnt);
+#endif
+ } else {
+ while (i-- > 0)
+ *dp++ = B1_get_byte(base);
+ }
return len;
}
-static inline void B1_put_slice(unsigned short base,
+static inline void B1_put_slice(unsigned int base,
unsigned char *dp, unsigned int len)
{
- B1_put_word(base, len);
- while (len-- > 0)
- B1_put_byte(base, *dp++);
+ unsigned i = len;
+ B1_put_word(base, i);
+ if (B1_isfastlink(base)) {
+ int status;
+ while (i > 0) {
+ status = B1_fifostatus(base) & (T1F_OREADY|T1F_OHALF);
+ if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
+ switch (status) {
+ case T1F_OREADY|T1F_OHALF|T1F_OEMPTY:
+ outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE);
+ dp += FIFO_OUTBSIZE;
+ i -= FIFO_OUTBSIZE;
+ break;
+ case T1F_OREADY|T1F_OHALF:
+ outsb(base+B1_WRITE, dp, i);
+ dp += i;
+ i = 0;
+ break;
+ default:
+ B1_put_byte(base, *dp++);
+ i--;
+ break;
+ }
+ }
+ } else {
+ while (i-- > 0)
+ B1_put_byte(base, *dp++);
+ }
}
-static void b1_wr_reg(unsigned short base,
+static void b1_wr_reg(unsigned int base,
unsigned int reg,
unsigned int value)
{
B1_put_word(base, value);
}
-static inline unsigned int b1_rd_reg(unsigned short base,
+static inline unsigned int b1_rd_reg(unsigned int base,
unsigned int reg)
{
B1_put_byte(base, READ_REGISTER);
}
-static inline void b1_set_test_bit(unsigned short base,
+static inline void b1_set_test_bit(unsigned int base,
int cardtype,
int onoff)
{
b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
}
-static inline int b1_get_test_bit(unsigned short base,
+static inline int b1_get_test_bit(unsigned int base,
int cardtype)
{
return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
112, /* irq 15 */
};
+static int hema_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 0x80, /* irq 3 */
+ 0,
+ 0x90, /* irq 5 */
+ 0,
+ 0xA0, /* irq 7 */
+ 0,
+ 0xB0, /* irq 9 */
+ 0xC0, /* irq 10 */
+ 0xD0, /* irq 11 */
+ 0xE0, /* irq 12 */
+ 0,
+ 0,
+ 0xF0, /* irq 15 */
+};
+
+
int B1_valid_irq(unsigned irq, int cardtype)
{
switch (cardtype) {
case AVM_CARDTYPE_M1:
case AVM_CARDTYPE_M2:
case AVM_CARDTYPE_B1:
- return irq_table[irq] != 0;
+ return irq_table[irq & 0xf] != 0;
case AVM_CARDTYPE_T1:
- return irq == 5;
+ return hema_irq_table[irq & 0xf] != 0;
+ case AVM_CARDTYPE_B1PCI:
+ return 1;
}
}
-unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype)
+int B1_valid_port(unsigned port, int cardtype)
+{
+ switch (cardtype) {
+ default:
+ case AVM_CARDTYPE_M1:
+ case AVM_CARDTYPE_M2:
+ case AVM_CARDTYPE_B1:
+#if 0 /* problem with PCMCIA and PCI cards */
+ switch (port) {
+ case 0x150:
+ case 0x250:
+ case 0x300:
+ case 0x340:
+ return 1;
+ }
+ return 0;
+#else
+ return 1;
+#endif
+ case AVM_CARDTYPE_B1PCI:
+ return 1;
+ case AVM_CARDTYPE_T1:
+ return ((port & 0x7) == 0) && ((port & 0x30) != 0x30);
+ }
+}
+
+void B1_setinterrupt(unsigned int base,
+ unsigned irq, int cardtype)
{
switch (cardtype) {
case AVM_CARDTYPE_T1:
- return b1outp(base, B1_IRQ_MASTER, 0x08);
+ t1outp(base, B1_INSTAT, 0x00);
+ t1outp(base, B1_INSTAT, 0x02);
+ t1outp(base, T1_IRQMASTER, 0x08);
+ break;
default:
case AVM_CARDTYPE_M1:
case AVM_CARDTYPE_M2:
case AVM_CARDTYPE_B1:
- return b1outp(base, B1_RESET, irq_table[irq]);
+ b1outp(base, B1_INSTAT, 0x00);
+ b1outp(base, B1_RESET, irq_table[irq]);
+ b1outp(base, B1_INSTAT, 0x02);
+ break;
+ case AVM_CARDTYPE_B1PCI:
+ b1outp(base, B1_INSTAT, 0x00);
+ b1outp(base, B1_RESET, 0xf0);
+ b1outp(base, B1_INSTAT, 0x02);
+ break;
}
}
-unsigned char B1_enable_irq(unsigned short base)
+unsigned char B1_disable_irq(unsigned int base)
{
- return b1outp(base, B1_INSTAT, 0x02);
+ return b1outp(base, B1_INSTAT, 0x00);
}
-unsigned char B1_disable_irq(unsigned short base)
+void T1_disable_irq(unsigned int base)
{
- return b1outp(base, B1_INSTAT, 0x00);
+ t1outp(base, T1_IRQMASTER, 0x00);
}
-void B1_reset(unsigned short base)
+void B1_reset(unsigned int base)
{
b1outp(base, B1_RESET, 0);
udelay(55 * 2 * 1000); /* 2 TIC's */
udelay(55 * 2 * 1000); /* 2 TIC's */
}
-int B1_detect(unsigned short base, int cardtype)
+void T1_reset(unsigned int base)
+{
+ /* reset T1 Controller */
+ B1_reset(base);
+ /* disable irq on HEMA */
+ t1outp(base, B1_INSTAT, 0x00);
+ t1outp(base, B1_OUTSTAT, 0x00);
+ t1outp(base, T1_IRQMASTER, 0x00);
+ /* reset HEMA board configuration */
+ t1outp(base, T1_RESETBOARD, 0xf);
+}
+
+int B1_detect(unsigned int base, int cardtype)
{
int onoff, i;
return 0;
}
+int T1_detectandinit(unsigned int base, unsigned irq, int cardnr)
+{
+ unsigned char cregs[8];
+ unsigned char reverse_cardnr;
+ unsigned long flags;
+ unsigned char dummy;
+ int i;
+
+ reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
+ | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
+ cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
+ cregs[1] = 0x00; /* fast & slow link connected to CON1 */
+ cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
+ cregs[3] = 0;
+ cregs[4] = 0x11; /* zero wait state */
+ cregs[5] = hema_irq_table[irq & 0xf];
+ cregs[6] = 0;
+ cregs[7] = 0;
+
+ save_flags(flags);
+ cli();
+ /* board reset */
+ t1outp(base, T1_RESETBOARD, 0xf);
+ udelay(100 * 1000);
+ dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */
+
+ /* write config */
+ dummy = (base >> 4) & 0xff;
+ for (i=1;i<=0xf;i++) t1outp(base, i, dummy);
+ t1outp(base, HEMA_PAL_ID & 0xf, dummy);
+ t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
+ for(i=1;i<7;i++) t1outp(base, 0, cregs[i]);
+ t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
+ restore_flags(flags);
+
+ udelay(100 * 1000);
+ t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+ t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+ udelay(10 * 1000);
+ t1outp(base, T1_FASTLINK+T1_RESETLINK, 1);
+ t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1);
+ udelay(100 * 1000);
+ t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+ t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+ udelay(10 * 1000);
+ t1outp(base, T1_FASTLINK+T1_ANALYSE, 0);
+ udelay(5 * 1000);
+ t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0);
+
+ if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+ return 1;
+ if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */
+ return 2;
+ if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0)
+ return 3;
+ if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70)
+ return 4;
+ if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0)
+ return 5;
+ if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1)
+ return 6;
+ if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+ return 7;
+ if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0)
+ return 8;
+ if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0)
+ return 9;
+ return 0;
+}
extern int loaddebug;
-int B1_load_t4file(unsigned short base, avmb1_t4file * t4file)
+int B1_load_t4file(unsigned int base, avmb1_t4file * t4file)
{
/*
* Data is in user space !!!
return 0;
}
-int B1_load_config(unsigned short base, avmb1_t4file * config)
+int B1_load_config(unsigned int base, avmb1_t4file * config)
{
/*
* Data is in user space !!!
return 0;
}
-int B1_loaded(unsigned short base)
+int B1_loaded(unsigned int base)
{
int i;
unsigned char ans;
break;
}
if (!B1_tx_empty(base)) {
- printk(KERN_ERR "b1lli: B1_loaded: timeout tx\n");
+ printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout tx\n", base);
return 0;
}
B1_put_byte(base, SEND_POLL);
printk(KERN_DEBUG "b1capi: loaded: ok\n");
return 1;
}
- printk(KERN_ERR "b1lli: B1_loaded: got 0x%x ???\n", ans);
+ printk(KERN_ERR "b1lli(0x%x): B1_loaded: got 0x%x ???\n",
+ base, ans);
return 0;
}
}
- printk(KERN_ERR "b1lli: B1_loaded: timeout rx\n");
+ printk(KERN_ERR "b1lli(0x%x): B1_loaded: timeout rx\n", base);
return 0;
}
* -------------------------------------------------------------------
*/
-void B1_send_init(unsigned short port,
+void B1_send_init(unsigned int port,
unsigned int napps, unsigned int nncci, unsigned int cardnr)
{
unsigned long flags;
restore_flags(flags);
}
-void B1_send_register(unsigned short port,
+void B1_send_register(unsigned int port,
__u16 appid, __u32 nmsg,
__u32 nb3conn, __u32 nb3blocks, __u32 b3bsize)
{
restore_flags(flags);
}
-void B1_send_release(unsigned short port,
+void B1_send_release(unsigned int port,
__u16 appid)
{
unsigned long flags;
restore_flags(flags);
}
-extern int showcapimsgs;
-
-void B1_send_message(unsigned short port, struct sk_buff *skb)
+void B1_send_message(unsigned int port, struct sk_buff *skb)
{
unsigned long flags;
__u16 len = CAPIMSG_LEN(skb->data);
unsigned NCCI;
unsigned WindowSize;
+t1retry:
if (!B1_rx_full(card->port))
return;
WindowSize = B1_get_word(card->port);
if (showcapimsgs)
- printk(KERN_DEBUG "b1lli: NEW_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+ printk(KERN_DEBUG "b1lli(0x%x): NEW_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI);
avmb1_handle_new_ncci(card, ApplId, NCCI, WindowSize);
NCCI = B1_get_word(card->port);
if (showcapimsgs)
- printk(KERN_DEBUG "b1lli: FREE_NCCI app %u ncci 0x%x\n", ApplId, NCCI);
+ printk(KERN_DEBUG "b1lli(0x%x): FREE_NCCI app %u ncci 0x%x\n", card->port, ApplId, NCCI);
avmb1_handle_free_ncci(card, ApplId, NCCI);
break;
case RECEIVE_START:
+ if (card->cardtype == AVM_CARDTYPE_T1) {
+ B1_put_byte(card->port, SEND_POLLACK);
+ /* printk(KERN_DEBUG "b1lli: T1 watchdog\n"); */
+ }
if (card->blocked)
- printk(KERN_DEBUG "b1lli: RESTART\n");
+ printk(KERN_DEBUG "b1lli(0x%x): RESTART\n", card->port);
card->blocked = 0;
break;
case RECEIVE_STOP:
- printk(KERN_DEBUG "b1lli: STOP\n");
+ printk(KERN_DEBUG "b1lli(0x%x): STOP\n", card->port);
card->blocked = 1;
break;
card->versionlen = B1_get_slice(card->port, card->versionbuf);
card->cardstate = CARD_ACTIVE;
parse_version(card);
- printk(KERN_INFO "b1lli: %s-card (%s) now active\n",
+ printk(KERN_INFO "b1lli(0x%x): %s-card (%s) now active\n",
+ card->port,
card->version[VER_CARDTYPE],
card->version[VER_DRIVER]);
avmb1_card_ready(card);
break;
+ case RECEIVE_TASK_READY:
+ ApplId = (unsigned) B1_get_word(card->port);
+ MsgLen = B1_get_slice(card->port, card->msgbuf);
+ card->msgbuf[MsgLen] = 0;
+ printk(KERN_INFO "b1lli(0x%x): Task %d \"%s\" ready.\n",
+ card->port, ApplId, card->msgbuf);
+ break;
default:
- printk(KERN_ERR "b1lli: B1_handle_interrupt: 0x%x ???\n", b1cmd);
+ printk(KERN_ERR "b1lli(0x%x): B1_handle_interrupt: 0x%x ???\n",
+ card->port, b1cmd);
break;
}
+ if (card->cardtype == AVM_CARDTYPE_T1)
+ goto t1retry;
}
/*
- * $Id: b1pci.c,v 1.5 1998/01/31 11:14:43 calle Exp $
+ * $Id: b1pci.c,v 1.9 1999/04/15 19:49:32 calle Exp $
*
* Module for AVM B1 PCI-card.
*
* (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: b1pci.c,v $
+ * Revision 1.9 1999/04/15 19:49:32 calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.8 1998/06/17 19:51:16 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.7 1998/03/29 16:06:02 calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.2.2.2 1998/01/23 16:49:30 calle
+ * added functions for pcmcia cards,
+ * avmb1_addcard returns now the controller number.
+ *
+ * Revision 1.6 1998/02/25 09:15:36 fritz
+ * apply Martin's pci driver patch to isdn drivers (vgerCVS)
+ *
* Revision 1.5 1998/01/31 11:14:43 calle
* merged changes to 2.0 tree, prepare 2.1.82 to work.
*
#define PCI_DEVICE_ID_AVM_B1 0x700
#endif
-static char *revision = "$Revision: 1.5 $";
+static char *revision = "$Revision: 1.9 $";
/* ------------------------------------------------------------- */
printk(KERN_INFO
"b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
ioaddr, irq);
- if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1)) != 0) {
+ if ((rc = avmb1_probecard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) != 0) {
printk(KERN_ERR
"b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
ioaddr, irq);
return rc;
}
- if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1)) < 0)
+ if ((rc = avmb1_addcard(ioaddr, irq, AVM_CARDTYPE_B1PCI)) < 0)
return rc;
}
return 0;
/*
- * $Id: capi.c,v 1.10 1998/02/13 07:09:13 calle Exp $
+ * $Id: capi.c,v 1.13 1998/08/28 04:32:25 calle Exp $
*
* CAPI 2.0 Interface for Linux
*
* Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: capi.c,v $
+ * Revision 1.13 1998/08/28 04:32:25 calle
+ * Added patch send by Michael.Mueller4@post.rwth-aachen.de, to get AVM B1
+ * driver running with 2.1.118.
+ *
+ * Revision 1.12 1998/05/26 22:39:34 he
+ * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
+ * concap typo
+ * cleared dev.tbusy in isdn_net BCONN status callback
+ *
+ * Revision 1.11 1998/03/09 17:46:37 he
+ * merged in 2.1.89 changes
+ *
* Revision 1.10 1998/02/13 07:09:13 calle
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
return POLLERR;
cdev = &capidevs[minor];
+#if (LINUX_VERSION_CODE < 0x020159) /* 2.1.89 */
+#define poll_wait(f,wq,w) poll_wait((wq),(w))
+#endif
poll_wait(file, &(cdev->recv_wait), wait);
mask = POLLOUT | POLLWRNORM;
if (!skb_queue_empty(&cdev->recv_queue))
capi_ioctl,
NULL, /* capi_mmap */
capi_open,
- NULL, /* flush */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118)
+ NULL, /* capi_flush */
+#endif
capi_release,
NULL, /* capi_fsync */
NULL, /* capi_fasync */
int capi_init(void)
{
+#if LINUX_VERSION_CODE >= 131841
+ int j;
+#endif
+
memset(capidevs, 0, sizeof(capidevs));
+#if LINUX_VERSION_CODE >= 131841
+ for ( j = 0; j < CAPI_MAXMINOR+1; j++ ) {
+ init_waitqueue_head(&capidevs[j].recv_wait);
+ }
+#endif
if (register_chrdev(capi_major, "capi20", &capi_fops)) {
printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
unregister_chrdev(capi_major, "capi20");
return -EIO;
}
+
return 0;
}
int is_registered;
__u16 applid;
struct sk_buff_head recv_queue;
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue *recv_wait;
+#else
wait_queue_head_t recv_wait;
+#endif
__u16 errcode;
};
/*
- * $Id: capidrv.c,v 1.11 1998/02/13 07:09:15 calle Exp $
+ * $Id: capidrv.c,v 1.13 1998/06/26 15:12:55 fritz Exp $
*
* ISDN4Linux Driver, using capi20 interface (kernelcapi)
*
* Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: capidrv.c,v $
+ * Revision 1.13 1998/06/26 15:12:55 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.12 1998/03/29 16:06:03 calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.3.2.10 1998/03/20 14:38:24 calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
+ * Revision 1.3.2.9 1998/03/20 09:01:12 calle
+ * Changes capi_register handling to get full support for 30 bchannels.
+ *
+ * Revision 1.3.2.8 1998/03/18 17:51:28 calle
+ * added controller number to error messages
+ *
+ * Revision 1.3.2.7 1998/02/27 15:40:47 calle
+ * T1 running with slow link. bugfix in capi_release.
+ *
* Revision 1.11 1998/02/13 07:09:15 calle
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#include "capicmd.h"
#include "capidrv.h"
-static char *revision = "$Revision: 1.11 $";
+static char *revision = "$Revision: 1.13 $";
int debugmode = 0;
MODULE_AUTHOR("Carsten Paeth <calle@calle.in-berlin.de>");
return;
}
}
- printk(KERN_ERR "capidrv: free_plci %p (0x%x) not found, Huh?\n",
- plcip, plcip->plci);
+ printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n",
+ card->contrnr, plcip, plcip->plci);
}
/* -------- ncci management ------------------------------------------ */
static struct listenstatechange listentable[] =
{
- {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
- {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
- {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
- {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
- {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
- {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
- {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
- {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
- {},
+ {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
+ {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+ {},
};
static void listen_change_state(capidrv_contr * card, int event)
while (p->event) {
if (card->state == p->actstate && p->event == event) {
if (debugmode)
- printk(KERN_DEBUG "capidrv: listen_change_state %d -> %d\n",
- card->state, p->nextstate);
+ printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n",
+ card->contrnr, card->state, p->nextstate);
card->state = p->nextstate;
return;
}
p++;
}
- printk(KERN_ERR "capidrv: listen_change_state state=%d event=%d ????\n",
- card->state, event);
+ printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n",
+ card->contrnr, card->state, event);
}
static struct plcistatechange plcitable[] =
{
/* P-0 */
- {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0},
- {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0},
- {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0},
+ {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, 0},
+ {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, 0},
+ {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, 0},
+ {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, 0},
/* P-0.1 */
- {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
- {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0},
- {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
- {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+ {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
+ {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, 0},
+ {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_OUTGOING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
/* P-1 */
- {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
- {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
-{ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
- {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
/* P-ACT */
- {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
- {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
- {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, 0},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, 0},
/* P-2 */
- {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
- {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0},
- {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0},
- {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
- {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
- {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, 0},
/* P-3 */
-{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
-{ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0},
-{ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
- {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
- {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, 0},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
/* P-4 */
- {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
- {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
-{ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
- {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, 0},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, 0},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, 0},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
/* P-5 */
-{ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
+ {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, 0},
/* P-6 */
- {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
- {},
+ {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
+ /* P-0.Res */
+ {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0},
+ {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, 0},
+ /* P-RES */
+ {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, 0},
+ /* P-HELD */
+ {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, 0},
+ {},
};
static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event)
while (p->event) {
if (plci->state == p->actstate && p->event == event) {
if (debugmode)
- printk(KERN_DEBUG "capidrv: plci_change_state:0x%x %d -> %d\n",
- plci->plci, plci->state, p->nextstate);
+ printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n",
+ card->contrnr, plci->plci, plci->state, p->nextstate);
plci->state = p->nextstate;
if (p->changefunc)
p->changefunc(card, plci);
}
p++;
}
- printk(KERN_ERR "capidrv: plci_change_state:0x%x state=%d event=%d ????\n",
- plci->plci, plci->state, event);
+ printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n",
+ card->contrnr, plci->plci, plci->state, event);
}
/* ------------------------------------------------------------------ */
ncci->plcip->plci,
0, /* BChannelinformation */
0, /* Keypadfacility */
- 0, /* Useruserdata */
+ 0, /* Useruserdata */ /* $$$$ */
0 /* Facilitydataarray */
);
send_message(card, &cmsg);
static struct nccistatechange nccitable[] =
{
/* N-0 */
- {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0},
- {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0},
+ {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, 0},
+ {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, 0},
/* N-0.1 */
- {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0},
- {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, 0},
+ {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, 0},
+ {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0},
/* N-1 */
- {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0},
- {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0},
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, 0},
+ {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, 0},
{ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
- {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
/* N-2 */
- {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0},
- {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
-{ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+ {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, 0},
+ {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+ {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
/* N-ACT */
- {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0},
- {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
- {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+ {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
+ {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, 0},
+ {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+ {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
/* N-3 */
- {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
+ {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, 0},
{ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
- {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
+ {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, 0},
/* N-4 */
- {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
- {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR, 0},
+ {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, 0},
+ {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,0},
/* N-5 */
- {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
- {},
+ {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
+ {},
};
static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event)
while (p->event) {
if (ncci->state == p->actstate && p->event == event) {
if (debugmode)
- printk(KERN_DEBUG "capidrv: ncci_change_state:0x%x %d -> %d\n",
- ncci->ncci, ncci->state, p->nextstate);
+ printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n",
+ card->contrnr, ncci->ncci, ncci->state, p->nextstate);
if (p->nextstate == ST_NCCI_PREVIOUS) {
ncci->state = ncci->oldstate;
ncci->oldstate = p->actstate;
}
p++;
}
- printk(KERN_ERR "capidrv: ncci_change_state:0x%x state=%d event=%d ????\n",
- ncci->ncci, ncci->state, event);
+ printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n",
+ card->contrnr, ncci->ncci, ncci->state, event);
}
/* ------------------------------------------------------------------- */
case CAPI_LISTEN_CONF: /* Controller */
if (debugmode)
- printk(KERN_DEBUG "capidrv: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
- cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
+ printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
+ card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
if (cmsg->Info) {
listen_change_state(card, EV_LISTEN_CONF_ERROR);
} else if (card->cipmask == 0) {
handle_dtrace_data(card, direction, 0, data, len);
break;
}
- printk(KERN_INFO "capidrv: %s from controller 0x%x layer 0x%x, ignored\n",
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrController, layer);
break;
default: s = "unkown error"; break;
}
if (s)
- printk(KERN_INFO "capidrv: %s from controller 0x%x function %d: %s\n",
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrController,
cmsg->Function, s);
goto ignored;
default:
- printk(KERN_ERR "capidrv: got %s from controller 0x%x ???",
+ printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrController);
}
return;
ignored:
- printk(KERN_INFO "capidrv: %s from controller 0x%x ignored\n",
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrController);
}
int chan;
if ((chan = new_bchan(card)) == -1) {
- printk(KERN_ERR "capidrv: incoming call on not existing bchan ?\n");
+ printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr);
return;
}
bchan = &card->bchans[chan];
if ((plcip = new_plci(card, chan)) == 0) {
- printk(KERN_ERR "capidrv: incoming call: no memory, sorry.\n");
+ printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr);
return;
}
bchan->incoming = 1;
cmd.parm.setup.plan = cmsg->CallingPartyNumber[1];
cmd.parm.setup.screen = cmsg->CallingPartyNumber[2];
- printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s\n",
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n",
+ card->contrnr,
cmd.parm.setup.phone,
cmd.parm.setup.si1,
cmd.parm.setup.si2,
switch (card->interface.statcallb(&cmd)) {
case 0:
+ case 3:
/* No device matching this call.
* and isdn_common.c has send a HANGUP command
* which is ignored in state ST_PLCI_INCOMING,
cmsg->Reject = 1; /* ignore */
send_message(card, cmsg);
plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
- printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s ignored\n",
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n",
+ card->contrnr,
cmd.parm.setup.phone,
cmd.parm.setup.si1,
cmd.parm.setup.si2,
* and CONNECT_RESP already sent.
*/
if (plcip->state == ST_PLCI_INCOMING) {
- printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s tty alerting\n",
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n",
+ card->contrnr,
cmd.parm.setup.phone,
cmd.parm.setup.si1,
cmd.parm.setup.si2,
plcip->msgid = cmsg->Messagenumber;
send_message(card, cmsg);
} else {
- printk(KERN_INFO "capidrv: incoming call %s,%d,%d,%s on netdev\n",
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n",
+ card->contrnr,
cmd.parm.setup.phone,
cmd.parm.setup.si1,
cmd.parm.setup.si2,
case CAPI_DISCONNECT_IND: /* plci */
if (cmsg->Reason) {
- printk(KERN_INFO "capidrv: %s reason 0x%x (%s) for plci 0x%x\n",
+ printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI);
}
case CAPI_DISCONNECT_CONF: /* plci */
if (cmsg->Info) {
- printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->Info, capi_info2str(cmsg->Info),
cmsg->adr.adrPLCI);
case CAPI_ALERT_CONF: /* plci */
if (cmsg->Info) {
- printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->Info, capi_info2str(cmsg->Info),
cmsg->adr.adrPLCI);
case CAPI_CONNECT_CONF: /* plci */
if (cmsg->Info) {
- printk(KERN_INFO "capidrv: %s info 0x%x (%s) for plci 0x%x\n",
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->Info, capi_info2str(cmsg->Info),
cmsg->adr.adrPLCI);
nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI);
if (!nccip) {
- printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+ printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
break; /* $$$$ */
}
capi_fill_CONNECT_B3_REQ(cmsg,
break;
}
}
- printk(KERN_ERR "capidrv: %s\n", capi_cmsg2str(cmsg));
+ printk(KERN_ERR "capidrv-%d: %s\n",
+ card->contrnr, capi_cmsg2str(cmsg));
break;
case CAPI_CONNECT_ACTIVE_CONF: /* plci */
goto ignored;
default:
- printk(KERN_ERR "capidrv: got %s for plci 0x%x ???",
+ printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrPLCI);
}
return;
ignored:
- printk(KERN_INFO "capidrv: %s for plci 0x%x ignored\n",
+ printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrPLCI);
return;
notfound:
- printk(KERN_ERR "capidrv: %s: plci 0x%x not found\n",
+ printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrPLCI);
return;
cmd.arg = nccip->chan;
card->interface.statcallb(&cmd);
- printk(KERN_INFO "capidrv: chan %d up with ncci 0x%x\n",
- nccip->chan, nccip->ncci);
+ printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n",
+ card->contrnr, nccip->chan, nccip->ncci);
break;
case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */
ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP);
break;
}
- printk(KERN_ERR "capidrv: no mem for ncci, sorry\n");
+ printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
} else {
- printk(KERN_ERR "capidrv: %s: plci for ncci 0x%x not found\n",
+ printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrNCCI);
}
nccip->ncci = cmsg->adr.adrNCCI;
if (cmsg->Info) {
- printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n",
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->Info, capi_info2str(cmsg->Info),
cmsg->adr.adrNCCI);
if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
goto notfound;
if (cmsg->Info) {
- printk(KERN_INFO "capidrv: %s info 0x%x (%s) for ncci 0x%x\n",
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->Info, capi_info2str(cmsg->Info),
cmsg->adr.adrNCCI);
break;
case CAPI_RESET_B3_IND: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+ ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND);
capi_cmsg_answer(cmsg);
send_message(card, cmsg);
break;
goto ignored;
default:
- printk(KERN_ERR "capidrv: got %s for ncci 0x%x ???",
+ printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrNCCI);
}
return;
ignored:
- printk(KERN_INFO "capidrv: %s for ncci 0x%x ignored\n",
+ printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrNCCI);
return;
notfound:
- printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+ printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrNCCI);
}
return;
}
if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) {
- printk(KERN_ERR "capidrv: %s: ncci 0x%x not found\n",
+ printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+ card->contrnr,
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
cmsg->adr.adrNCCI);
kfree_skb(skb);
while ((*capifuncs->capi_get_message) (global.appid, &skb) == CAPI_NOERROR) {
capi_message2cmsg(&s_cmsg, skb->data);
if (debugmode > 1)
- printk(KERN_DEBUG "capidrv_signal: %s\n", capi_cmsg2str(&s_cmsg));
+ printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n",
+ applid, capi_cmsg2str(&s_cmsg));
if (s_cmsg.Command == CAPI_DATA_B3
&& s_cmsg.Subcommand == CAPI_IND) {
isdn_ctrl cmd;
if (!len) {
- printk(KERN_DEBUG "avmb1_q931_data: len == %d\n", len);
+ printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
+ card->contrnr, len);
return;
}
{
switch (c->arg) {
default:
- printk(KERN_DEBUG "capidrv: capidrv_ioctl(%ld) called ??\n", c->arg);
+ printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n",
+ card->contrnr, c->arg);
return -EINVAL;
}
return -EINVAL;
__u8 called[ISDN_MSNLEN + 2];
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+ card->contrnr,
c->arg,
c->parm.setup.phone,
c->parm.setup.si1,
bchan = &card->bchans[c->arg % card->nbchan];
if (bchan->plcip) {
- printk(KERN_ERR "capidrv: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+ printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+ card->contrnr,
c->arg,
c->parm.setup.phone,
c->parm.setup.si1,
case ISDN_CMD_ACCEPTD:
- if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTD(ch=%ld)\n",
- c->arg);
bchan = &card->bchans[c->arg % card->nbchan];
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n",
+ card->contrnr,
+ c->arg, bchan->l2, bchan->l3);
capi_fill_CONNECT_RESP(&cmdcmsg,
global.appid,
case ISDN_CMD_ACCEPTB:
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+ card->contrnr,
c->arg);
return -ENOSYS;
case ISDN_CMD_HANGUP:
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_HANGUP(ch=%ld)\n",
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n",
+ card->contrnr,
c->arg);
bchan = &card->bchans[c->arg % card->nbchan];
if (bchan->disconnecting) {
if (debugmode)
- printk(KERN_DEBUG "capidrv: chan %ld already disconnecting ...\n",
+ printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n",
+ card->contrnr,
c->arg);
return 0;
}
case ISDN_CMD_SETL2:
if (debugmode)
- printk(KERN_DEBUG "capidrv: set L2 on chan %ld to %ld\n",
+ printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n",
+ card->contrnr,
(c->arg & 0xff), (c->arg >> 8));
- bchan = &card->bchans[c->arg % card->nbchan];
+ bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
bchan->l2 = (c->arg >> 8);
return 0;
case ISDN_CMD_SETL3:
if (debugmode)
- printk(KERN_DEBUG "capidrv: set L3 on chan %ld to %ld\n",
+ printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n",
+ card->contrnr,
(c->arg & 0xff), (c->arg >> 8));
- bchan = &card->bchans[c->arg % card->nbchan];
+ bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
bchan->l3 = (c->arg >> 8);
return 0;
case ISDN_CMD_SETEAZ:
if (debugmode)
- printk(KERN_DEBUG "capidrv: set EAZ \"%s\" on chan %ld\n",
+ printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n",
+ card->contrnr,
c->parm.num, c->arg);
bchan = &card->bchans[c->arg % card->nbchan];
strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN);
case ISDN_CMD_CLREAZ:
if (debugmode)
- printk(KERN_DEBUG "capidrv: clearing EAZ on chan %ld\n", c->arg);
+ printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n",
+ card->contrnr, c->arg);
bchan = &card->bchans[c->arg % card->nbchan];
bchan->msn[0] = 0;
return 0;
case ISDN_CMD_LOCK:
if (debugmode > 1)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_LOCK (%ld)\n", c->arg);
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_LOCK (%ld)\n", card->contrnr, c->arg);
MOD_INC_USE_COUNT;
break;
case ISDN_CMD_UNLOCK:
if (debugmode > 1)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_UNLOCK (%ld)\n", c->arg);
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_UNLOCK (%ld)\n",
+ card->contrnr, c->arg);
MOD_DEC_USE_COUNT;
break;
/* never called */
case ISDN_CMD_GETL2:
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL2\n");
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL2\n",
+ card->contrnr);
return -ENODEV;
case ISDN_CMD_GETL3:
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_GETL3\n");
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETL3\n",
+ card->contrnr);
return -ENODEV;
case ISDN_CMD_GETEAZ:
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_GETEAZ\n");
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETEAZ\n",
+ card->contrnr);
return -ENODEV;
case ISDN_CMD_SETSIL:
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_SETSIL\n");
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_SETSIL\n",
+ card->contrnr);
return -ENODEV;
case ISDN_CMD_GETSIL:
if (debugmode)
- printk(KERN_DEBUG "capidrv: ISDN_CMD_GETSIL\n");
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_GETSIL\n",
+ card->contrnr);
return -ENODEV;
default:
- printk(KERN_ERR "capidrv: ISDN_CMD_%d, Huh?\n", c->command);
+ printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n",
+ card->contrnr, c->command);
return -EINVAL;
}
return 0;
return capidrv_command(c, card);
printk(KERN_ERR
- "capidrv: if_command %d called with invalid driverId %d!\n",
- c->command, c->driver);
+ "capidrv-%d: if_command %d called with invalid driverId %d!\n",
+ card->contrnr, c->command, c->driver);
return -ENODEV;
}
__u16 datahandle;
if (!card) {
- printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
- id);
+ printk(KERN_ERR "capidrv-%d: if_sendbuf called with invalid driverId %d!\n",
+ card->contrnr, id);
return 0;
}
bchan = &card->bchans[channel % card->nbchan];
nccip = bchan->nccip;
if (!nccip || nccip->state != ST_NCCI_ACTIVE) {
- printk(KERN_ERR "capidrv: if_sendbuf: %s:%d: chan not up!\n",
- card->name, channel);
+ printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n",
+ card->contrnr, card->name, channel);
return 0;
}
datahandle = nccip->datahandle;
if (skb_headroom(skb) < msglen) {
struct sk_buff *nskb = dev_alloc_skb(msglen + skb->len);
if (!nskb) {
- printk(KERN_ERR "capidrv: if_sendbuf: no memory\n");
+ printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n",
+ card->contrnr);
(void)capidrv_del_ack(nccip, datahandle);
return 0;
}
#if 0
- printk(KERN_DEBUG "capidrv: only %d bytes headroom\n",
- skb_headroom(skb));
+ printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom\n",
+ card->contrnr, skb_headroom(skb));
#endif
memcpy(skb_put(nskb, msglen), sendcmsg.buf, msglen);
memcpy(skb_put(nskb, skb->len), skb->data, skb->len);
__u8 *p;
if (!card) {
- printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n",
- id);
+ printk(KERN_ERR "capidrv-%d: if_readstat called with invalid driverId %d!\n",
+ card->contrnr, id);
return -ENODEV;
}
avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
avmversion[2] |= version.minormanuversion & 0x0f;
- if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+ if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 6)) {
printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.appid,
card->msgid++,
printk(KERN_INFO "%s: now up (%d B channels)\n",
card->name, card->nbchan);
- enable_dchannel_trace(card);
+ if (card->nbchan == 2) /* no T1 */
+ enable_dchannel_trace(card);
return 0;
}
} else
strcpy(rev, " ??? ");
- rparam.level3cnt = 2;
- rparam.datablkcnt = 8;
+ rparam.level3cnt = -2; /* number of bchannels twice */
+ rparam.datablkcnt = 16;
rparam.datablklen = 2048;
errcode = (*capifuncs->capi_register) (&rparam, &global.appid);
if (errcode) {
/*
- * $Id: capidrv.h,v 1.1 1997/03/04 21:50:33 calle Exp $
+ * $Id: capidrv.h,v 1.2 1998/03/29 16:06:06 calle Exp $
*
* ISDN4Linux Driver, using capi20 interface (kernelcapi)
*
* Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: capidrv.h,v $
+ * Revision 1.2 1998/03/29 16:06:06 calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.1.2.1 1998/03/20 14:38:28 calle
+ * capidrv: prepared state machines for suspend/resume/hold
+ * capidrv: fix bug in state machine if B1/T1 is out of nccis
+ * b1capi: changed some errno returns.
+ * b1capi: detect if you try to add same T1 to different io address.
+ * b1capi: change number of nccis depending on number of channels.
+ * b1lli: cosmetics
+ *
* Revision 1.1 1997/03/04 21:50:33 calle
* Frirst version in isdn4linux
*
#define ST_PLCI_ACCEPTING 6 /* P-4 */
#define ST_PLCI_DISCONNECTING 7 /* P-5 */
#define ST_PLCI_DISCONNECTED 8 /* P-6 */
+#define ST_PLCI_RESUMEING 9 /* P-0.Res */
+#define ST_PLCI_RESUME 10 /* P-Res */
+#define ST_PLCI_HELD 11 /* P-HELD */
-#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1 */
-#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0 */
-#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1 */
-#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1 */
-#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2 */
-#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT */
+#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1
+ */
+#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0
+ */
+#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1
+ */
+#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1
+ */
+#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2
+ */
+#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT
+ */
#define EV_PLCI_CONNECT_REJECT 7 /* P-2 -> P-5
- P-3 -> P-5 */
+ P-3 -> P-5
+ */
#define EV_PLCI_DISCONNECT_REQ 8 /* P-1 -> P-5
P-2 -> P-5
P-3 -> P-5
P-4 -> P-5
- P-ACT -> P-5 */
+ P-ACT -> P-5
+ P-Res -> P-5 (*)
+ P-HELD -> P-5 (*)
+ */
#define EV_PLCI_DISCONNECT_IND 9 /* P-1 -> P-6
P-2 -> P-6
P-3 -> P-6
P-4 -> P-6
P-5 -> P-6
- P-ACT -> P-6 */
+ P-ACT -> P-6
+ P-Res -> P-6 (*)
+ P-HELD -> P-6 (*)
+ */
#define EV_PLCI_FACILITY_IND_DOWN 10 /* P-0.1 -> P-5
P-1 -> P-5
P-ACT -> P-5
P-2 -> P-5
P-3 -> P-5
- P-4 -> P-5 */
-#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0 */
-#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0 */
+ P-4 -> P-5
+ */
+#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0
+ */
+#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0
+ */
+
+#define EV_PLCI_RESUME_REQ 13 /* P-0 -> P-0.Res
+ */
+#define EV_PLCI_RESUME_CONF_OK 14 /* P-0.Res -> P-Res
+ */
+#define EV_PLCI_RESUME_CONF_ERROR 15 /* P-0.Res -> P-0
+ */
+#define EV_PLCI_RESUME_IND 16 /* P-Res -> P-ACT
+ */
+#define EV_PLCI_HOLD_IND 17 /* P-ACT -> P-HELD
+ */
+#define EV_PLCI_RETRIEVE_IND 18 /* P-HELD -> P-ACT
+ */
+#define EV_PLCI_SUSPEND_IND 19 /* P-ACT -> P-5
+ */
+#define EV_PLCI_CD_IND 20 /* P-2 -> P-5
+ */
/*
* per ncci state machine
/*
- * $Id: compat.h,v 1.3 1997/11/04 06:12:15 calle Exp $
+ * $Id: compat.h,v 1.4 1998/10/25 14:39:02 fritz Exp $
*
* Headerfile for Compartibility between different kernel versions
*
* (c) Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: compat.h,v $
+ * Revision 1.4 1998/10/25 14:39:02 fritz
+ * Backported from MIPS (Cobalt).
+ *
* Revision 1.3 1997/11/04 06:12:15 calle
* capi.c: new read/write in file_ops since 2.1.60
* capidrv.c: prepared isdnlog interface for d2-trace in newer firmware.
#define __COMPAT_H__
#include <linux/version.h>
+#include <asm/segment.h>
#include <linux/isdnif.h>
#ifndef LinuxVersionCode
--- /dev/null
+L_OBJS :=
+M_OBJS :=
+O_OBJS := eicon_mod.o eicon_isa.o eicon_pci.o eicon_idi.o eicon_io.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_EICON),y)
+ O_TARGET += eicon.o
+else
+ O_TARGET += eicon.o
+ M_OBJS = eicon.o
+endif
+
+include $(TOPDIR)/Rules.make
--- /dev/null
+/* $Id: eicon.h,v 1.5 1999/03/29 11:19:41 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon.h,v $
+ * Revision 1.5 1999/03/29 11:19:41 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.4 1999/03/02 12:37:42 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.3 1999/01/24 20:14:07 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.2 1999/01/10 18:46:04 armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.1 1999/01/01 18:09:41 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+
+#ifndef eicon_h
+#define eicon_h
+
+#define EICON_IOCTL_SETMMIO 0
+#define EICON_IOCTL_GETMMIO 1
+#define EICON_IOCTL_SETIRQ 2
+#define EICON_IOCTL_GETIRQ 3
+#define EICON_IOCTL_LOADBOOT 4
+#define EICON_IOCTL_ADDCARD 5
+#define EICON_IOCTL_GETTYPE 6
+#define EICON_IOCTL_LOADPCI 7
+#define EICON_IOCTL_LOADISA 8
+#define EICON_IOCTL_GETVER 9
+
+#define EICON_IOCTL_MANIF 90
+
+#define EICON_IOCTL_FREEIT 97
+#define EICON_IOCTL_TEST 98
+#define EICON_IOCTL_DEBUGVAR 99
+
+/* Bus types */
+#define EICON_BUS_ISA 1
+#define EICON_BUS_MCA 2
+#define EICON_BUS_PCI 3
+
+/* Constants for describing Card-Type */
+#define EICON_CTYPE_S 0
+#define EICON_CTYPE_SX 1
+#define EICON_CTYPE_SCOM 2
+#define EICON_CTYPE_QUADRO 3
+#define EICON_CTYPE_S2M 4
+#define EICON_CTYPE_MAESTRA 5
+#define EICON_CTYPE_MAESTRAQ 6
+#define EICON_CTYPE_MAESTRAQ_U 7
+#define EICON_CTYPE_MAESTRAP 8
+#define EICON_CTYPE_ISABRI 0x10
+#define EICON_CTYPE_ISAPRI 0x20
+#define EICON_CTYPE_MASK 0x0f
+#define EICON_CTYPE_QUADRO_NR(n) (n<<4)
+
+#define MAX_HEADER_LEN 10
+
+/* Struct for adding new cards */
+typedef struct eicon_cdef {
+ int membase;
+ int irq;
+ char id[10];
+} eicon_cdef;
+
+#define EICON_ISA_BOOT_MEMCHK 1
+#define EICON_ISA_BOOT_NORMAL 2
+
+/* Struct for downloading protocol via ioctl for ISA cards */
+typedef struct {
+ /* start-up parameters */
+ unsigned char tei;
+ unsigned char nt2;
+ unsigned char skip1;
+ unsigned char WatchDog;
+ unsigned char Permanent;
+ unsigned char XInterface;
+ unsigned char StableL2;
+ unsigned char NoOrderCheck;
+ unsigned char HandsetType;
+ unsigned char skip2;
+ unsigned char LowChannel;
+ unsigned char ProtVersion;
+ unsigned char Crc4;
+ unsigned char Loopback;
+ unsigned char oad[32];
+ unsigned char osa[32];
+ unsigned char spid[32];
+ unsigned char boot_opt;
+ unsigned long bootstrap_len;
+ unsigned long firmware_len;
+ unsigned char code[1]; /* Rest (bootstrap- and firmware code) will be allocated */
+} eicon_isa_codebuf;
+
+/* Struct for downloading protocol via ioctl for PCI cards */
+typedef struct {
+ /* start-up parameters */
+ unsigned char tei;
+ unsigned char nt2;
+ unsigned char WatchDog;
+ unsigned char Permanent;
+ unsigned char XInterface;
+ unsigned char StableL2;
+ unsigned char NoOrderCheck;
+ unsigned char HandsetType;
+ unsigned char LowChannel;
+ unsigned char ProtVersion;
+ unsigned char Crc4;
+ unsigned char NoHscx30Mode; /* switch PRI into No HSCX30 test mode */
+ unsigned char Loopback; /* switch card into Loopback mode */
+ struct q931_link_s
+ {
+ unsigned char oad[32];
+ unsigned char osa[32];
+ unsigned char spid[32];
+ } l[2];
+ unsigned long protocol_len;
+ unsigned int dsp_code_num;
+ unsigned long dsp_code_len[9];
+ unsigned char code[1]; /* Rest (protocol- and dsp code) will be allocated */
+} eicon_pci_codebuf;
+
+/* Data for downloading protocol via ioctl */
+typedef union {
+ eicon_isa_codebuf isa;
+ eicon_pci_codebuf pci;
+} eicon_codebuf;
+
+/* Data for Management interface */
+typedef struct {
+ int count;
+ int pos;
+ int length[50];
+ unsigned char data[700];
+} eicon_manifbuf;
+
+
+#ifdef __KERNEL__
+
+/* Kernel includes */
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <linux/isdnif.h>
+
+typedef struct {
+ __u16 length __attribute__ ((packed)); /* length of data/parameter field */
+ __u8 P[1]; /* data/parameter field */
+} eicon_PBUFFER;
+
+#include "eicon_isa.h"
+
+/* Macro for delay via schedule() */
+#define SLEEP(j) { \
+ current->state = TASK_INTERRUPTIBLE; \
+ schedule_timeout(j); \
+}
+
+#endif /* KERNEL */
+
+
+#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_COMBIFILE_FORMAT_VERSION_BCD 0x0100
+
+#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_FILE_FORMAT_VERSION_BCD 0x0100
+
+typedef struct tag_dsp_combifile_header
+{
+ char format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE] __attribute__ ((packed));
+ __u16 format_version_bcd __attribute__ ((packed));
+ __u16 header_size __attribute__ ((packed));
+ __u16 combifile_description_size __attribute__ ((packed));
+ __u16 directory_entries __attribute__ ((packed));
+ __u16 directory_size __attribute__ ((packed));
+ __u16 download_count __attribute__ ((packed));
+ __u16 usage_mask_size __attribute__ ((packed));
+} t_dsp_combifile_header;
+
+typedef struct tag_dsp_combifile_directory_entry
+{
+ __u16 card_type_number __attribute__ ((packed));
+ __u16 file_set_number __attribute__ ((packed));
+} t_dsp_combifile_directory_entry;
+
+typedef struct tag_dsp_file_header
+{
+ char format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE] __attribute__ ((packed));
+ __u16 format_version_bcd __attribute__ ((packed));
+ __u16 download_id __attribute__ ((packed));
+ __u16 download_flags __attribute__ ((packed));
+ __u16 required_processing_power __attribute__ ((packed));
+ __u16 interface_channel_count __attribute__ ((packed));
+ __u16 header_size __attribute__ ((packed));
+ __u16 download_description_size __attribute__ ((packed));
+ __u16 memory_block_table_size __attribute__ ((packed));
+ __u16 memory_block_count __attribute__ ((packed));
+ __u16 segment_table_size __attribute__ ((packed));
+ __u16 segment_count __attribute__ ((packed));
+ __u16 symbol_table_size __attribute__ ((packed));
+ __u16 symbol_count __attribute__ ((packed));
+ __u16 total_data_size_dm __attribute__ ((packed));
+ __u16 data_block_count_dm __attribute__ ((packed));
+ __u16 total_data_size_pm __attribute__ ((packed));
+ __u16 data_block_count_pm __attribute__ ((packed));
+} t_dsp_file_header;
+
+typedef struct tag_dsp_memory_block_desc
+{
+ __u16 alias_memory_block;
+ __u16 memory_type;
+ __u16 address;
+ __u16 size; /* DSP words */
+} t_dsp_memory_block_desc;
+
+typedef struct tag_dsp_segment_desc
+{
+ __u16 memory_block;
+ __u16 attributes;
+ __u16 base;
+ __u16 size;
+ __u16 alignment; /* ==0 -> no other legal start address than base */
+} t_dsp_segment_desc;
+
+typedef struct tag_dsp_symbol_desc
+{
+ __u16 symbol_id;
+ __u16 segment;
+ __u16 offset;
+ __u16 size; /* DSP words */
+} t_dsp_symbol_desc;
+
+typedef struct tag_dsp_data_block_header
+{
+ __u16 attributes;
+ __u16 segment;
+ __u16 offset;
+ __u16 size; /* DSP words */
+} t_dsp_data_block_header;
+
+typedef struct tag_dsp_download_desc /* be sure to keep native alignment for MAESTRA's */
+{
+ __u16 download_id;
+ __u16 download_flags;
+ __u16 required_processing_power;
+ __u16 interface_channel_count;
+ __u16 excess_header_size;
+ __u16 memory_block_count;
+ __u16 segment_count;
+ __u16 symbol_count;
+ __u16 data_block_count_dm;
+ __u16 data_block_count_pm;
+ __u8 * p_excess_header_data __attribute__ ((packed));
+ char * p_download_description __attribute__ ((packed));
+ t_dsp_memory_block_desc *p_memory_block_table __attribute__ ((packed));
+ t_dsp_segment_desc *p_segment_table __attribute__ ((packed));
+ t_dsp_symbol_desc *p_symbol_table __attribute__ ((packed));
+ __u16 * p_data_blocks_dm __attribute__ ((packed));
+ __u16 * p_data_blocks_pm __attribute__ ((packed));
+} t_dsp_download_desc;
+
+
+#ifdef __KERNEL__
+
+typedef struct {
+ __u8 Req; /* pending request */
+ __u8 Rc; /* return code received */
+ __u8 Ind; /* indication received */
+ __u8 ReqCh; /* channel of current Req */
+ __u8 RcCh; /* channel of current Rc */
+ __u8 IndCh; /* channel of current Ind */
+ __u8 D3Id; /* ID used by this entity */
+ __u8 B2Id; /* ID used by this entity */
+ __u8 GlobalId; /* reserved field */
+ __u8 XNum; /* number of X-buffers */
+ __u8 RNum; /* number of R-buffers */
+ struct sk_buff_head X; /* X-buffer queue */
+ struct sk_buff_head R; /* R-buffer queue */
+ __u8 RNR; /* receive not ready flag */
+ __u8 complete; /* receive complete status */
+ __u8 busy; /* busy flag */
+ __u16 ref; /* saved reference */
+} entity;
+
+
+typedef struct {
+ int No; /* Channel Number */
+ unsigned short callref; /* Call Reference */
+ unsigned short fsm_state; /* Current D-Channel state */
+ unsigned short eazmask; /* EAZ-Mask for this Channel */
+ unsigned int queued; /* User-Data Bytes in TX queue */
+ unsigned int waitq; /* User-Data Bytes in wait queue */
+ unsigned int waitpq; /* User-Data Bytes in packet queue */
+ unsigned short plci;
+ unsigned short ncci;
+ unsigned char l2prot; /* Layer 2 protocol */
+ unsigned char l3prot; /* Layer 3 protocol */
+ entity e; /* Entity */
+ char cpn[32]; /* remember cpn */
+ char oad[32]; /* remember oad */
+ unsigned char cause[2]; /* Last Cause */
+ unsigned char si1;
+ unsigned char si2;
+} eicon_chan;
+
+typedef struct {
+ eicon_chan *ptr;
+} eicon_chan_ptr;
+
+#include "eicon_pci.h"
+
+#define EICON_FLAGS_RUNNING 1 /* Cards driver activated */
+#define EICON_FLAGS_PVALID 2 /* Cards port is valid */
+#define EICON_FLAGS_IVALID 4 /* Cards irq is valid */
+#define EICON_FLAGS_MVALID 8 /* Cards membase is valid */
+#define EICON_FLAGS_LOADED 8 /* Firmware loaded */
+
+#define EICON_BCH 2 /* # of channels per card */
+
+/* D-Channel states */
+#define EICON_STATE_NULL 0
+#define EICON_STATE_ICALL 1
+#define EICON_STATE_OCALL 2
+#define EICON_STATE_IWAIT 3
+#define EICON_STATE_OWAIT 4
+#define EICON_STATE_IBWAIT 5
+#define EICON_STATE_OBWAIT 6
+#define EICON_STATE_BWAIT 7
+#define EICON_STATE_BHWAIT 8
+#define EICON_STATE_BHWAIT2 9
+#define EICON_STATE_DHWAIT 10
+#define EICON_STATE_DHWAIT2 11
+#define EICON_STATE_BSETUP 12
+#define EICON_STATE_ACTIVE 13
+#define EICON_STATE_ICALLW 14
+#define EICON_STATE_LISTEN 15
+#define EICON_STATE_WMCONN 16
+
+#define EICON_MAX_QUEUED 8000 /* 2 * maxbuff */
+
+#define EICON_LOCK_TX 0
+#define EICON_LOCK_RX 1
+
+typedef struct {
+ int dummy;
+} eicon_mca_card;
+
+typedef union {
+ eicon_isa_card isa;
+ eicon_pci_card pci;
+ eicon_mca_card mca;
+} eicon_hwif;
+
+typedef struct {
+ __u8 ret;
+ __u8 id;
+ __u8 ch;
+} eicon_ack;
+
+typedef struct {
+ __u8 code;
+ __u8 id;
+ __u8 ch;
+} eicon_req;
+
+typedef struct {
+ __u8 ret;
+ __u8 id;
+ __u8 ch;
+ __u8 more;
+} eicon_indhdr;
+
+typedef struct msn_entry {
+ char eaz;
+ char msn[16];
+ struct msn_entry * next;
+} msn_entry;
+
+/*
+ * Per card driver data
+ */
+typedef struct eicon_card {
+ eicon_hwif hwif; /* Hardware dependant interface */
+ u_char ptype; /* Protocol type (1TR6 or Euro) */
+ u_char bus; /* Bustype (ISA, MCA, PCI) */
+ u_char type; /* Cardtype (EICON_CTYPE_...) */
+ struct eicon_card *qnext; /* Pointer to next quadro adapter */
+ int Feature; /* Protocol Feature Value */
+ struct eicon_card *next; /* Pointer to next device struct */
+ int myid; /* Driver-Nr. assigned by linklevel */
+ unsigned long flags; /* Statusflags */
+ unsigned long ilock; /* Semaphores for IRQ-Routines */
+ struct sk_buff_head rcvq; /* Receive-Message queue */
+ struct sk_buff_head sndq; /* Send-Message queue */
+ struct sk_buff_head rackq; /* Req-Ack-Message queue */
+ struct sk_buff_head sackq; /* Data-Ack-Message queue */
+ u_char *ack_msg; /* Ptr to User Data in User skb */
+ __u16 need_b3ack; /* Flag: Need ACK for current skb */
+ struct sk_buff *sbuf; /* skb which is currently sent */
+ struct tq_struct snd_tq; /* Task struct for xmit bh */
+ struct tq_struct rcv_tq; /* Task struct for rcv bh */
+ struct tq_struct ack_tq; /* Task struct for ack bh */
+ msn_entry *msn_list;
+ unsigned short msgnum; /* Message number for sending */
+ eicon_chan* IdTable[256]; /* Table to find entity */
+ __u16 ref_in;
+ __u16 ref_out;
+ int nchannels; /* Number of B-Channels */
+ int ReadyInt; /* Ready Interrupt */
+ eicon_chan *bch; /* B-Channel status/control */
+ char status_buf[256]; /* Buffer for status messages */
+ char *status_buf_read;
+ char *status_buf_write;
+ char *status_buf_end;
+ isdn_if interface; /* Interface to upper layer */
+ char regname[35]; /* Name used for request_region */
+} eicon_card;
+
+/* -----------------------------------------------------------**
+** The PROTOCOL_FEATURE_STRING **
+** defines capabilities and **
+** features of the actual protocol code. It's used as a bit **
+** mask. **
+** The following Bits are defined: **
+** -----------------------------------------------------------*/
+#define PROTCAP_TELINDUS 0x0001 /* Telindus Variant of protocol code */
+#define PROTCAP_MANIF 0x0002 /* Management interface implemented */
+#define PROTCAP_V_42 0x0004 /* V42 implemented */
+#define PROTCAP_V90D 0x0008 /* V.90D (implies up to 384k DSP code) */
+#define PROTCAP_EXTD_FAX 0x0010 /* Extended FAX (ECM, 2D, T6, Polling) */
+#define PROTCAP_FREE4 0x0020 /* not used */
+#define PROTCAP_FREE5 0x0040 /* not used */
+#define PROTCAP_FREE6 0x0080 /* not used */
+#define PROTCAP_FREE7 0x0100 /* not used */
+#define PROTCAP_FREE8 0x0200 /* not used */
+#define PROTCAP_FREE9 0x0400 /* not used */
+#define PROTCAP_FREE10 0x0800 /* not used */
+#define PROTCAP_FREE11 0x1000 /* not used */
+#define PROTCAP_FREE12 0x2000 /* not used */
+#define PROTCAP_FREE13 0x4000 /* not used */
+#define PROTCAP_EXTENSION 0x8000 /* used for future extentions */
+
+#include "eicon_idi.h"
+
+extern eicon_card *cards;
+extern char *eicon_ctype_name[];
+
+
+extern __inline__ void eicon_schedule_tx(eicon_card *card)
+{
+ queue_task(&card->snd_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void eicon_schedule_rx(eicon_card *card)
+{
+ queue_task(&card->rcv_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void eicon_schedule_ack(eicon_card *card)
+{
+ queue_task(&card->ack_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+extern char *eicon_find_eaz(eicon_card *, char);
+extern int eicon_addcard(int, int, int, char *);
+extern void eicon_io_transmit(eicon_card *card);
+extern void eicon_irq(int irq, void *dev_id, struct pt_regs *regs);
+extern void eicon_io_rcv_dispatch(eicon_card *ccard);
+extern void eicon_io_ack_dispatch(eicon_card *ccard);
+extern ulong DebugVar;
+
+#endif /* __KERNEL__ */
+
+#endif /* eicon_h */
--- /dev/null
+/* $Id: eicon_dsp.h,v 1.2 1999/03/29 11:19:42 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * DSP definitions
+ *
+ * Copyright 1999 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_dsp.h,v $
+ * Revision 1.2 1999/03/29 11:19:42 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.1 1999/03/02 12:18:54 armin
+ * First checkin of DSP defines for audio features.
+ *
+ *
+ */
+
+#ifndef DSP_H
+#define DSP_H
+
+#define DSP_UDATA_REQUEST_RECONFIGURE 0
+/*
+parameters:
+ <word> reconfigure delay (in 8kHz samples)
+ <word> reconfigure code
+ <byte> reconfigure hdlc preamble flags
+*/
+
+#define DSP_RECONFIGURE_TX_FLAG 0x8000
+#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG 0x4000
+#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG 0x2000
+#define DSP_RECONFIGURE_HDLC_FLAG 0x1000
+#define DSP_RECONFIGURE_SYNC_FLAG 0x0800
+#define DSP_RECONFIGURE_PROTOCOL_MASK 0x00ff
+#define DSP_RECONFIGURE_IDLE 0
+#define DSP_RECONFIGURE_V25 1
+#define DSP_RECONFIGURE_V21_CH2 2
+#define DSP_RECONFIGURE_V27_2400 3
+#define DSP_RECONFIGURE_V27_4800 4
+#define DSP_RECONFIGURE_V29_7200 5
+#define DSP_RECONFIGURE_V29_9600 6
+#define DSP_RECONFIGURE_V33_12000 7
+#define DSP_RECONFIGURE_V33_14400 8
+#define DSP_RECONFIGURE_V17_7200 9
+#define DSP_RECONFIGURE_V17_9600 10
+#define DSP_RECONFIGURE_V17_12000 11
+#define DSP_RECONFIGURE_V17_14400 12
+
+/*
+data indications if transparent framer
+ <byte> data 0
+ <byte> data 1
+ ...
+
+data indications if HDLC framer
+ <byte> data 0
+ <byte> data 1
+ ...
+ <byte> CRC 0
+ <byte> CRC 1
+ <byte> preamble flags
+*/
+
+#define DSP_UDATA_REQUEST_SWITCH_FRAMER 1
+/*
+parameters:
+ <byte> transmit framer type
+ <byte> receive framer type
+*/
+
+#define DSP_REQUEST_SWITCH_FRAMER_HDLC 0
+#define DSP_REQUEST_SWITCH_FRAMER_TRANSPARENT 1
+#define DSP_REQUEST_SWITCH_FRAMER_ASYNC 2
+
+
+#define DSP_UDATA_REQUEST_CLEARDOWN 2
+/*
+parameters:
+ - none -
+*/
+
+
+#define DSP_UDATA_REQUEST_TX_CONFIRMATION_ON 3
+/*
+parameters:
+ - none -
+*/
+
+
+#define DSP_UDATA_REQUEST_TX_CONFIRMATION_OFF 4
+/*
+parameters:
+ - none -
+*/
+
+
+#define DSP_UDATA_INDICATION_SYNC 0
+/*
+returns:
+ <word> time of sync (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_DCD_OFF 1
+/*
+returns:
+ <word> time of DCD off (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_DCD_ON 2
+/*
+returns:
+ <word> time of DCD on (sampled from counter at 8kHz)
+ <byte> connected norm
+ <word> connected options
+ <dword> connected speed (bit/s, max of tx and rx speed)
+ <word> roundtrip delay (ms)
+ <dword> connected speed tx (bit/s)
+ <dword> connected speed rx (bit/s)
+*/
+
+#define DSP_UDATA_INDICATION_CTS_OFF 3
+/*
+returns:
+ <word> time of CTS off (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_CTS_ON 4
+/*
+returns:
+ <word> time of CTS on (sampled from counter at 8kHz)
+ <byte> connected norm
+ <word> connected options
+ <dword> connected speed (bit/s, max of tx and rx speed)
+ <word> roundtrip delay (ms)
+ <dword> connected speed tx (bit/s)
+ <dword> connected speed rx (bit/s)
+*/
+
+typedef struct eicon_dsp_ind {
+ __u16 time __attribute__ ((packed));
+ __u8 norm __attribute__ ((packed));
+ __u16 options __attribute__ ((packed));
+ __u32 speed __attribute__ ((packed));
+ __u16 delay __attribute__ ((packed));
+ __u32 txspeed __attribute__ ((packed));
+ __u32 rxspeed __attribute__ ((packed));
+} eicon_dsp_ind;
+
+#define DSP_CONNECTED_NORM_UNSPECIFIED 0
+#define DSP_CONNECTED_NORM_V21 1
+#define DSP_CONNECTED_NORM_V23 2
+#define DSP_CONNECTED_NORM_V22 3
+#define DSP_CONNECTED_NORM_V22_BIS 4
+#define DSP_CONNECTED_NORM_V32_BIS 5
+#define DSP_CONNECTED_NORM_V34 6
+#define DSP_CONNECTED_NORM_V8 7
+#define DSP_CONNECTED_NORM_BELL_212A 8
+#define DSP_CONNECTED_NORM_BELL_103 9
+#define DSP_CONNECTED_NORM_V29_LEASED_LINE 10
+#define DSP_CONNECTED_NORM_V33_LEASED_LINE 11
+#define DSP_CONNECTED_NORM_V90 12
+#define DSP_CONNECTED_NORM_V21_CH2 13
+#define DSP_CONNECTED_NORM_V27_TER 14
+#define DSP_CONNECTED_NORM_V29 15
+#define DSP_CONNECTED_NORM_V33 16
+#define DSP_CONNECTED_NORM_V17 17
+
+#define DSP_CONNECTED_OPTION_TRELLIS 0x0001
+#define DSP_CONNECTED_OPTION_V42_TRANS 0x0002
+#define DSP_CONNECTED_OPTION_V42_LAPM 0x0004
+#define DSP_CONNECTED_OPTION_SHORT_TRAIN 0x0008
+#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010
+
+
+#define DSP_UDATA_INDICATION_DISCONNECT 5
+/*
+returns:
+ <byte> cause
+*/
+
+#define DSP_DISCONNECT_CAUSE_NONE 0x00
+#define DSP_DISCONNECT_CAUSE_BUSY_TONE 0x01
+#define DSP_DISCONNECT_CAUSE_CONGESTION_TONE 0x02
+#define DSP_DISCONNECT_CAUSE_INCOMPATIBILITY 0x03
+#define DSP_DISCONNECT_CAUSE_CLEARDOWN 0x04
+#define DSP_DISCONNECT_CAUSE_TRAINING_TIMEOUT 0x05
+
+
+#define DSP_UDATA_INDICATION_TX_CONFIRMATION 6
+/*
+returns:
+ <word> confirmation number
+*/
+
+
+#define DSP_UDATA_REQUEST_SEND_DTMF_DIGITS 16
+/*
+parameters:
+ <word> tone duration (ms)
+ <word> gap duration (ms)
+ <byte> digit 0 tone code
+ ...
+ <byte> digit n tone code
+*/
+
+#define DSP_SEND_DTMF_DIGITS_HEADER_LENGTH 5
+
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_697_HZ 0x00
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_770_HZ 0x01
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_852_HZ 0x02
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_941_HZ 0x03
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_MASK 0x03
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ 0x00
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ 0x04
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ 0x08
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ 0x0c
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_MASK 0x0c
+
+#define DSP_DTMF_DIGIT_TONE_CODE_0 0x07
+#define DSP_DTMF_DIGIT_TONE_CODE_1 0x00
+#define DSP_DTMF_DIGIT_TONE_CODE_2 0x04
+#define DSP_DTMF_DIGIT_TONE_CODE_3 0x08
+#define DSP_DTMF_DIGIT_TONE_CODE_4 0x01
+#define DSP_DTMF_DIGIT_TONE_CODE_5 0x05
+#define DSP_DTMF_DIGIT_TONE_CODE_6 0x09
+#define DSP_DTMF_DIGIT_TONE_CODE_7 0x02
+#define DSP_DTMF_DIGIT_TONE_CODE_8 0x06
+#define DSP_DTMF_DIGIT_TONE_CODE_9 0x0a
+#define DSP_DTMF_DIGIT_TONE_CODE_STAR 0x03
+#define DSP_DTMF_DIGIT_TONE_CODE_HASHMARK 0x0b
+#define DSP_DTMF_DIGIT_TONE_CODE_A 0x0c
+#define DSP_DTMF_DIGIT_TONE_CODE_B 0x0d
+#define DSP_DTMF_DIGIT_TONE_CODE_C 0x0e
+#define DSP_DTMF_DIGIT_TONE_CODE_D 0x0f
+
+
+#define DSP_UDATA_INDICATION_DTMF_DIGITS_SENT 16
+/*
+returns:
+ - none -
+ One indication will be sent for every request.
+*/
+
+
+#define DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER 17
+/*
+parameters:
+ <word> tone duration (ms)
+ <word> gap duration (ms)
+*/
+
+#define DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER 18
+/*
+parameters:
+ - none -
+*/
+
+#define DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED 17
+/*
+returns:
+ <byte> digit 0 tone code
+ ...
+ <byte> digit n tone code
+*/
+
+#define DSP_DTMF_DIGITS_RECEIVED_HEADER_LENGTH 1
+
+
+#define DSP_UDATA_INDICATION_MODEM_CALLING_TONE 18
+/*
+returns:
+ - none -
+*/
+
+#define DSP_UDATA_INDICATION_FAX_CALLING_TONE 19
+/*
+returns:
+ - none -
+*/
+
+#define DSP_UDATA_INDICATION_ANSWER_TONE 20
+/*
+returns:
+ - none -
+*/
+
+#endif /* DSP_H */
+
--- /dev/null
+/* $Id: eicon_idi.c,v 1.9 1999/03/29 11:19:42 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * IDI interface
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_idi.c,v $
+ * Revision 1.9 1999/03/29 11:19:42 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.8 1999/03/02 12:37:43 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.7 1999/02/03 18:34:35 armin
+ * Channel selection for outgoing calls w/o CHI.
+ * Added channel # in debug messages.
+ * L2 Transparent should work with 800 byte/packet now.
+ *
+ * Revision 1.6 1999/01/26 07:18:59 armin
+ * Bug with wrong added CPN fixed.
+ *
+ * Revision 1.5 1999/01/24 20:14:11 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.4 1999/01/10 18:46:05 armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.3 1999/01/05 14:49:34 armin
+ * Added experimental usage of full BC and HLC for
+ * speech, 3.1kHz audio, fax gr.2/3
+ *
+ * Revision 1.2 1999/01/04 13:19:29 armin
+ * Channel status with listen-request wrong - fixed.
+ *
+ * Revision 1.1 1999/01/01 18:09:41 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "eicon.h"
+#include "eicon_idi.h"
+#include "eicon_dsp.h"
+
+#undef EICON_FULL_SERVICE_OKTETT
+
+char *eicon_idi_revision = "$Revision: 1.9 $";
+
+eicon_manifbuf *manbuf;
+
+static char BC_Speech[3] = { 0x80, 0x90, 0xa3 };
+static char BC_31khz[3] = { 0x90, 0x90, 0xa3 };
+static char BC_64k[2] = { 0x88, 0x90 };
+static char BC_video[3] = { 0x91, 0x90, 0xa5 };
+
+#ifdef EICON_FULL_SERVICE_OKTETT
+/*
+static char HLC_telephony[2] = { 0x91, 0x81 };
+*/
+static char HLC_faxg3[2] = { 0x91, 0x84 };
+#endif
+
+int eicon_idi_manage_assign(eicon_card *card);
+int eicon_idi_manage_remove(eicon_card *card);
+
+int
+idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan)
+{
+ int l = 0;
+ if (!signet) {
+ /* Signal Layer */
+ reqbuf->XBuffer.P[l++] = CAI;
+ reqbuf->XBuffer.P[l++] = 1;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = KEY;
+ reqbuf->XBuffer.P[l++] = 3;
+ reqbuf->XBuffer.P[l++] = 'I';
+ reqbuf->XBuffer.P[l++] = '4';
+ reqbuf->XBuffer.P[l++] = 'L';
+ reqbuf->XBuffer.P[l++] = SHIFT|6;
+ reqbuf->XBuffer.P[l++] = SIN;
+ reqbuf->XBuffer.P[l++] = 2;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0; /* end */
+ reqbuf->Req = ASSIGN;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 0;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 0; /* Sig Entity */
+ }
+ else {
+ /* Network Layer */
+ reqbuf->XBuffer.P[l++] = CAI;
+ reqbuf->XBuffer.P[l++] = 1;
+ reqbuf->XBuffer.P[l++] = chan->e.D3Id;
+ reqbuf->XBuffer.P[l++] = LLC;
+ reqbuf->XBuffer.P[l++] = 2;
+ switch(chan->l2prot) {
+ case ISDN_PROTO_L2_HDLC:
+ reqbuf->XBuffer.P[l++] = 2;
+ break;
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ reqbuf->XBuffer.P[l++] = 5;
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ case ISDN_PROTO_L2_MODEM:
+ reqbuf->XBuffer.P[l++] = 2;
+ break;
+ default:
+ reqbuf->XBuffer.P[l++] = 1;
+ }
+ switch(chan->l3prot) {
+ case ISDN_PROTO_L3_TRANS:
+ default:
+ reqbuf->XBuffer.P[l++] = 4;
+ }
+ reqbuf->XBuffer.P[l++] = 0; /* end */
+ reqbuf->Req = ASSIGN;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 0x20;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 1; /* Net Entity */
+ }
+ return(0);
+}
+
+int
+idi_put_req(eicon_REQ *reqbuf, int rq, int signet)
+{
+ reqbuf->Req = rq;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.length = 1;
+ reqbuf->XBuffer.P[0] = 0;
+ reqbuf->Reference = signet;
+ return(0);
+}
+
+int
+idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
+{
+ int l = 9;
+ reqbuf->Req = CALL_RES;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.P[0] = CAI;
+ reqbuf->XBuffer.P[1] = 6;
+ reqbuf->XBuffer.P[2] = 9;
+ reqbuf->XBuffer.P[3] = 0;
+ reqbuf->XBuffer.P[4] = 0;
+ reqbuf->XBuffer.P[5] = 0;
+ reqbuf->XBuffer.P[6] = 32;
+ reqbuf->XBuffer.P[7] = 3;
+ switch(chan->l2prot) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ reqbuf->XBuffer.P[1] = 1;
+ reqbuf->XBuffer.P[2] = 0x05;
+ l = 4;
+ break;
+ case ISDN_PROTO_L2_V11096:
+ reqbuf->XBuffer.P[2] = 0x0d;
+ reqbuf->XBuffer.P[3] = 5;
+ reqbuf->XBuffer.P[4] = 0;
+ break;
+ case ISDN_PROTO_L2_V11019:
+ reqbuf->XBuffer.P[2] = 0x0d;
+ reqbuf->XBuffer.P[3] = 6;
+ reqbuf->XBuffer.P[4] = 0;
+ break;
+ case ISDN_PROTO_L2_V11038:
+ reqbuf->XBuffer.P[2] = 0x0d;
+ reqbuf->XBuffer.P[3] = 7;
+ reqbuf->XBuffer.P[4] = 0;
+ break;
+ case ISDN_PROTO_L2_MODEM:
+ reqbuf->XBuffer.P[2] = 0x11;
+ reqbuf->XBuffer.P[3] = 7;
+ reqbuf->XBuffer.P[4] = 0;
+ reqbuf->XBuffer.P[5] = 0;
+ reqbuf->XBuffer.P[6] = 128;
+ reqbuf->XBuffer.P[7] = 0;
+ break;
+ }
+ reqbuf->XBuffer.P[8] = 0;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 0; /* Sig Entity */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Call_Res\n", chan->No);
+ return(0);
+}
+
+int
+idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan_ptr *chan2;
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+ if (DebugVar & 8)
+ printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
+ if (layer) cmd |= 0x700;
+ switch(cmd) {
+ case ASSIGN:
+ case ASSIGN|0x700:
+ idi_assign_req(reqbuf, layer, chan);
+ break;
+ case REMOVE:
+ case REMOVE|0x700:
+ idi_put_req(reqbuf, REMOVE, layer);
+ break;
+ case INDICATE_REQ:
+ idi_put_req(reqbuf, INDICATE_REQ, 0);
+ break;
+ case HANGUP:
+ idi_put_req(reqbuf, HANGUP, 0);
+ break;
+ case REJECT:
+ idi_put_req(reqbuf, REJECT, 0);
+ break;
+ case CALL_ALERT:
+ idi_put_req(reqbuf, CALL_ALERT, 0);
+ break;
+ case CALL_RES:
+ idi_call_res_req(reqbuf, chan);
+ break;
+ case IDI_N_CONNECT|0x700:
+ idi_put_req(reqbuf, IDI_N_CONNECT, 1);
+ break;
+ case IDI_N_CONNECT_ACK|0x700:
+ idi_put_req(reqbuf, IDI_N_CONNECT_ACK, 1);
+ break;
+ case IDI_N_DISC|0x700:
+ idi_put_req(reqbuf, IDI_N_DISC, 1);
+ break;
+ case IDI_N_DISC_ACK|0x700:
+ idi_put_req(reqbuf, IDI_N_DISC_ACK, 1);
+ break;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No);
+ return(-1);
+ }
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+ return(0);
+}
+
+int
+eicon_idi_listen_req(eicon_card *card, eicon_chan *chan)
+{
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask);
+ if (!chan->e.D3Id) {
+ idi_do_req(card, chan, ASSIGN, 0);
+ }
+ if (chan->fsm_state == EICON_STATE_NULL) {
+ idi_do_req(card, chan, INDICATE_REQ, 0);
+ chan->fsm_state = EICON_STATE_LISTEN;
+ }
+ return(0);
+}
+
+unsigned char
+idi_si2bc(int si1, int si2, char *bc, char *hlc)
+{
+ hlc[0] = 0;
+ switch(si1) {
+ case 1:
+ bc[0] = 0x90; /* 3,1 kHz audio */
+ bc[1] = 0x90; /* 64 kbit/s */
+ bc[2] = 0xa3; /* G.711 A-law */
+#ifdef EICON_FULL_SERVICE_OKTETT
+ if (si2 == 1) {
+ bc[0] = 0x80; /* Speech */
+ hlc[0] = 0x02; /* hlc len */
+ hlc[1] = 0x91; /* first hic */
+ hlc[2] = 0x81; /* Telephony */
+ }
+#endif
+ return(3);
+ case 2:
+ bc[0] = 0x90; /* 3,1 kHz audio */
+ bc[1] = 0x90; /* 64 kbit/s */
+ bc[2] = 0xa3; /* G.711 A-law */
+#ifdef EICON_FULL_SERVICE_OKTETT
+ if (si2 == 2) {
+ hlc[0] = 0x02; /* hlc len */
+ hlc[1] = 0x91; /* first hic */
+ hlc[2] = 0x84; /* Fax Gr.2/3 */
+ }
+#endif
+ return(3);
+ case 5:
+ case 7:
+ default:
+ bc[0] = 0x88;
+ bc[1] = 0x90;
+ return(2);
+ }
+ return (0);
+}
+
+int
+idi_hangup(eicon_card *card, eicon_chan *chan)
+{
+ if ((chan->fsm_state == EICON_STATE_ACTIVE) ||
+ (chan->fsm_state == EICON_STATE_WMCONN)) {
+ if (chan->e.B2Id) idi_do_req(card, chan, IDI_N_DISC, 1);
+ }
+ if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1);
+ idi_do_req(card, chan, HANGUP, 0);
+ chan->fsm_state = EICON_STATE_NULL;
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No);
+ return(0);
+}
+
+int
+idi_connect_res(eicon_card *card, eicon_chan *chan)
+{
+ chan->fsm_state = EICON_STATE_IWAIT;
+ idi_do_req(card, chan, CALL_RES, 0);
+ idi_do_req(card, chan, ASSIGN, 1);
+ return(0);
+}
+
+int
+idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
+ char *eazmsn, int si1, int si2)
+{
+ int l = 0;
+ int i;
+ unsigned char tmp;
+ unsigned char bc[5];
+ unsigned char hlc[5];
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan_ptr *chan2;
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+ reqbuf->Req = CALL_REQ;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+
+ reqbuf->XBuffer.P[l++] = CPN;
+ reqbuf->XBuffer.P[l++] = strlen(phone) + 1;
+ reqbuf->XBuffer.P[l++] = 0xc1;
+ for(i=0; i<strlen(phone);i++)
+ reqbuf->XBuffer.P[l++] = phone[i];
+
+ reqbuf->XBuffer.P[l++] = OAD;
+ reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2;
+ reqbuf->XBuffer.P[l++] = 0x01;
+ reqbuf->XBuffer.P[l++] = 0x81;
+ for(i=0; i<strlen(eazmsn);i++)
+ reqbuf->XBuffer.P[l++] = eazmsn[i];
+
+ if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) {
+ reqbuf->XBuffer.P[l++] = BC;
+ reqbuf->XBuffer.P[l++] = tmp;
+ for(i=0; i<tmp;i++)
+ reqbuf->XBuffer.P[l++] = bc[i];
+ if ((tmp=hlc[0])) {
+ reqbuf->XBuffer.P[l++] = HLC;
+ reqbuf->XBuffer.P[l++] = tmp;
+ for(i=1; i<=tmp;i++)
+ reqbuf->XBuffer.P[l++] = hlc[i];
+ }
+ }
+ reqbuf->XBuffer.P[l++] = CAI;
+ reqbuf->XBuffer.P[l++] = 6;
+ reqbuf->XBuffer.P[l++] = 0x09;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 32;
+ reqbuf->XBuffer.P[l++] = 3;
+ switch(chan->l2prot) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ reqbuf->XBuffer.P[l-6] = 5;
+ reqbuf->XBuffer.P[l-7] = 1;
+ l -= 5;
+ break;
+ case ISDN_PROTO_L2_V11096:
+ reqbuf->XBuffer.P[l-7] = 3;
+ reqbuf->XBuffer.P[l-6] = 0x0d;
+ reqbuf->XBuffer.P[l-5] = 5;
+ reqbuf->XBuffer.P[l-4] = 0;
+ l -= 3;
+ break;
+ case ISDN_PROTO_L2_V11019:
+ reqbuf->XBuffer.P[l-7] = 3;
+ reqbuf->XBuffer.P[l-6] = 0x0d;
+ reqbuf->XBuffer.P[l-5] = 6;
+ reqbuf->XBuffer.P[l-4] = 0;
+ l -= 3;
+ break;
+ case ISDN_PROTO_L2_V11038:
+ reqbuf->XBuffer.P[l-7] = 3;
+ reqbuf->XBuffer.P[l-6] = 0x0d;
+ reqbuf->XBuffer.P[l-5] = 7;
+ reqbuf->XBuffer.P[l-4] = 0;
+ l -= 3;
+ break;
+ case ISDN_PROTO_L2_MODEM:
+ reqbuf->XBuffer.P[l-6] = 0x11;
+ reqbuf->XBuffer.P[l-5] = 7;
+ reqbuf->XBuffer.P[l-4] = 0;
+ reqbuf->XBuffer.P[l-3] = 0;
+ reqbuf->XBuffer.P[l-2] = 128;
+ reqbuf->XBuffer.P[l-1] = 0;
+ break;
+ }
+
+ reqbuf->XBuffer.P[l++] = 0; /* end */
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 0; /* Sig Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone);
+ return(0);
+}
+
+
+void
+idi_IndParse(eicon_card *ccard, eicon_chan *chan, idi_ind_message *message, unsigned char *buffer, int len)
+{
+ int i,j;
+ int pos = 0;
+ int codeset = 0;
+ int wlen = 0;
+ int lock = 0;
+ __u8 w;
+ __u16 code;
+ isdn_ctrl cmd;
+
+ memset(message, 0, sizeof(idi_ind_message));
+
+ if ((!len) || (!buffer[pos])) return;
+ while(pos <= len) {
+ w = buffer[pos++];
+ if (!w) return;
+ if (w & 0x80) {
+ wlen = 0;
+ }
+ else {
+ wlen = buffer[pos++];
+ }
+
+ if (pos > len) return;
+
+ if (lock & 0x80) lock &= 0x7f;
+ else codeset = lock;
+
+ if((w&0xf0) == SHIFT) {
+ codeset = w;
+ if(!(codeset & 0x08)) lock = codeset & 7;
+ codeset &= 7;
+ lock |= 0x80;
+ }
+ else {
+ if (w==ESC && wlen >=2) {
+ code = buffer[pos++]|0x800;
+ wlen--;
+ }
+ else code = w;
+ code |= (codeset<<8);
+
+ switch(code) {
+ case OAD:
+ j = 1;
+ if (wlen) {
+ message->plan = buffer[pos++];
+ if (message->plan &0x80)
+ message->screen = 0;
+ else {
+ message->screen = buffer[pos++];
+ j = 2;
+ }
+ }
+ for(i=0; i < wlen-j; i++)
+ message->oad[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No,
+ message->plan, message->screen, message->oad);
+ break;
+ case RDN:
+ j = 1;
+ if (wlen) {
+ if (!(buffer[pos++] & 0x80)) {
+ pos++;
+ j = 2;
+ }
+ }
+ for(i=0; i < wlen-j; i++)
+ message->rdn[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: RDN= %s\n", chan->No,
+ message->rdn);
+ break;
+ case CPN:
+ for(i=0; i < wlen; i++)
+ message->cpn[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No,
+ (__u8)message->cpn[0], message->cpn + 1);
+ break;
+ case DSA:
+ pos++;
+ for(i=0; i < wlen-1; i++)
+ message->dsa[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa);
+ break;
+ case OSA:
+ pos++;
+ for(i=0; i < wlen-1; i++)
+ message->osa[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa);
+ break;
+ case BC:
+ for(i=0; i < wlen; i++)
+ message->bc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No,
+ message->bc[0],message->bc[1],message->bc[2]);
+ break;
+ case 0x800|BC:
+ for(i=0; i < wlen; i++)
+ message->e_bc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]);
+ break;
+ case LLC:
+ for(i=0; i < wlen; i++)
+ message->llc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: LLC=%d %d %d %d\n", chan->No, message->llc[0],
+ message->llc[1],message->llc[2],message->llc[3]);
+ break;
+ case HLC:
+ for(i=0; i < wlen; i++)
+ message->hlc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: HLC=%x %x %x %x %x\n", chan->No,
+ message->hlc[0], message->hlc[1],
+ message->hlc[2], message->hlc[3], message->hlc[4]);
+ break;
+ case DSP:
+ case 0x600|DSP:
+ for(i=0; i < wlen; i++)
+ message->display[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Display: %s\n", chan->No,
+ message->display);
+ break;
+ case 0x600|KEY:
+ for(i=0; i < wlen; i++)
+ message->keypad[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Keypad: %s\n", chan->No,
+ message->keypad);
+ break;
+ case NI:
+ case 0x600|NI:
+ if (wlen) {
+ if (DebugVar & 4) {
+ switch(buffer[pos] & 127) {
+ case 0:
+ printk(KERN_DEBUG"idi_inf: Ch%d: User suspended.\n", chan->No);
+ break;
+ case 1:
+ printk(KERN_DEBUG"idi_inf: Ch%d: User resumed.\n", chan->No);
+ break;
+ case 2:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Bearer service change.\n", chan->No);
+ break;
+ default:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Notification %x.\n",
+ chan->No, buffer[pos] & 127);
+ }
+ }
+ pos += wlen;
+ }
+ break;
+ case PI:
+ case 0x600|PI:
+ if (wlen > 1) {
+ if (DebugVar & 4) {
+ switch(buffer[pos+1] & 127) {
+ case 1:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No);
+ break;
+ case 2:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No);
+ break;
+ case 3:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No);
+ break;
+ case 4:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No);
+ break;
+ case 5:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Interworking has occurred.\n", chan->No);
+ break;
+ case 8:
+ printk(KERN_DEBUG"idi_inf: Ch%d: In-band information available.\n", chan->No);
+ break;
+ default:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Progress %x.\n",
+ chan->No, buffer[pos+1] & 127);
+ }
+ }
+ }
+ pos += wlen;
+ break;
+ case CAU:
+ for(i=0; i < wlen; i++)
+ message->cau[i] = buffer[pos++];
+ memcpy(&chan->cause, &message->cau, 2);
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: CAU=%d %d\n", chan->No,
+ message->cau[0],message->cau[1]);
+ break;
+ case 0x800|CAU:
+ for(i=0; i < wlen; i++)
+ message->e_cau[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: ECAU=%d %d\n", chan->No,
+ message->e_cau[0],message->e_cau[1]);
+ break;
+ case 0x800|CHI:
+ for(i=0; i < wlen; i++)
+ message->e_chi[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: ESC/CHI=%d\n", chan->No,
+ message->e_cau[0]);
+ break;
+ case 0x800|0x7a:
+ pos ++;
+ message->e_mt=buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt);
+ break;
+ case DT:
+ for(i=0; i < wlen; i++)
+ message->dt[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No,
+ message->dt[2], message->dt[1], message->dt[0],
+ message->dt[3], message->dt[4], message->dt[5]);
+ break;
+ case 0x600|SIN:
+ for(i=0; i < wlen; i++)
+ message->sin[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: SIN=%d %d\n", chan->No,
+ message->sin[0],message->sin[1]);
+ break;
+ case 0x600|CPS:
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Called Party Status in ind\n", chan->No);
+ pos += wlen;
+ break;
+ case 0x600|CIF:
+ for (i = 0; i < wlen; i++)
+ if (buffer[pos + i] != '0') break;
+ memcpy(&cmd.parm.num, &buffer[pos + i], wlen - i);
+ cmd.parm.num[wlen - i] = 0;
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num);
+ pos += wlen;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_CINF;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ break;
+ case 0x600|DATE:
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Date in ind\n", chan->No);
+ pos += wlen;
+ break;
+ case 0xe08:
+ case 0xe7a:
+ case 0xe04:
+ case 0xe00:
+ /* *** TODO *** */
+ case CHA:
+ /* Charge advice */
+ case FTY:
+ case 0x600|FTY:
+ case CHI:
+ case 0x800:
+ /* Not yet interested in this */
+ pos += wlen;
+ break;
+ case 0x880:
+ /* Managment Information Element */
+ if (!manbuf) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING"idi_err: manbuf not allocated\n");
+ }
+ else {
+ memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen);
+ manbuf->length[manbuf->count] = wlen;
+ manbuf->count++;
+ manbuf->pos += wlen;
+ }
+ pos += wlen;
+ break;
+ default:
+ pos += wlen;
+ if (DebugVar & 6)
+ printk(KERN_WARNING"idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n",
+ chan->No, code, wlen);
+ }
+ }
+ }
+}
+
+void
+idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned char *si2)
+{
+ si1[0] = 0;
+ si2[0] = 0;
+ if (memcmp(bc, BC_Speech, 3) == 0) { /* Speech */
+ si1[0] = 1;
+#ifdef EICON_FULL_SERVICE_OKTETT
+ si2[0] = 1;
+#endif
+ }
+ if (memcmp(bc, BC_31khz, 3) == 0) { /* 3.1kHz audio */
+ si1[0] = 1;
+#ifdef EICON_FULL_SERVICE_OKTETT
+ si2[0] = 2;
+ if (memcmp(hlc, HLC_faxg3, 2) == 0) { /* Fax Gr.2/3 */
+ si1[0] = 2;
+ }
+#endif
+ }
+ if (memcmp(bc, BC_64k, 2) == 0) { /* unrestricted 64 kbits */
+ si1[0] = 7;
+ }
+ if (memcmp(bc, BC_video, 3) == 0) { /* video */
+ si1[0] = 4;
+ }
+}
+
+void
+idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
+{
+ isdn_ctrl cmd;
+ eicon_dsp_ind *p = (eicon_dsp_ind *) (&buffer[1]);
+ static char *connmsg[] =
+ {"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34",
+ "V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90",
+ "V.21 CH2", "V.27ter", "V.29", "V.33", "V.17"};
+
+ switch (buffer[0]) {
+ case DSP_UDATA_INDICATION_SYNC:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time);
+ break;
+ case DSP_UDATA_INDICATION_DCD_OFF:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time);
+ break;
+ case DSP_UDATA_INDICATION_DCD_ON:
+ if ((chan->l2prot == ISDN_PROTO_L2_MODEM) &&
+ (chan->fsm_state == EICON_STATE_WMCONN)) {
+ chan->fsm_state = EICON_STATE_ACTIVE;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.arg = chan->No;
+ sprintf(cmd.parm.num, "%d/%s", p->speed, connmsg[p->norm]);
+ ccard->interface.statcallb(&cmd);
+ }
+ if (DebugVar & 8) {
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time);
+ printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No,
+ p->norm, p->options, p->speed, p->delay);
+ }
+ break;
+ case DSP_UDATA_INDICATION_CTS_OFF:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time);
+ break;
+ case DSP_UDATA_INDICATION_CTS_ON:
+ if (DebugVar & 8) {
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time);
+ printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No,
+ p->norm, p->options, p->speed, p->delay);
+ }
+ break;
+ case DSP_UDATA_INDICATION_DISCONNECT:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]);
+ break;
+ default:
+ if (DebugVar & 8)
+ printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]);
+ }
+}
+
+void
+idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
+{
+ int tmp;
+ int free_buff;
+ struct sk_buff *skb2;
+ eicon_IND *ind = (eicon_IND *)skb->data;
+ eicon_chan *chan;
+ idi_ind_message message;
+ isdn_ctrl cmd;
+
+ if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if ((DebugVar & 128) ||
+ ((DebugVar & 16) && (ind->Ind != 8))) {
+ printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No,
+ ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
+ }
+
+ free_buff = 1;
+ /* Signal Layer */
+ if (chan->e.D3Id == ind->IndId) {
+ idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
+ switch(ind->Ind) {
+ case HANGUP:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Hangup\n", chan->No);
+ while((skb2 = skb_dequeue(&chan->e.X))) {
+ dev_kfree_skb(skb2);
+ }
+ chan->e.busy = 0;
+ chan->queued = 0;
+ chan->waitq = 0;
+ chan->waitpq = 0;
+ chan->fsm_state = EICON_STATE_NULL;
+ if (message.e_cau[0] & 0x7f) {
+ cmd.driver = ccard->myid;
+ cmd.arg = chan->No;
+ sprintf(cmd.parm.num,"E%02x%02x",
+ chan->cause[0]&0x7f, message.e_cau[0]&0x7f);
+ cmd.command = ISDN_STAT_CAUSE;
+ ccard->interface.statcallb(&cmd);
+ }
+ chan->cause[0] = 0;
+ cmd.driver = ccard->myid;
+ cmd.arg = chan->No;
+ cmd.command = ISDN_STAT_DHUP;
+ ccard->interface.statcallb(&cmd);
+ eicon_idi_listen_req(ccard, chan);
+ break;
+ case INDICATE_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Indicate_Ind\n", chan->No);
+ chan->fsm_state = EICON_STATE_ICALL;
+ idi_bc2si(message.bc, message.hlc, &chan->si1, &chan->si2);
+ strcpy(chan->cpn, message.cpn + 1);
+ if (strlen(message.dsa)) {
+ strcat(chan->cpn, ".");
+ strcat(chan->cpn, message.dsa);
+ }
+ strcpy(chan->oad, message.oad);
+ try_stat_icall_again:
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_ICALL;
+ cmd.arg = chan->No;
+ cmd.parm.setup.si1 = chan->si1;
+ cmd.parm.setup.si2 = chan->si2;
+ strcpy(cmd.parm.setup.eazmsn, chan->cpn);
+ strcpy(cmd.parm.setup.phone, chan->oad);
+ cmd.parm.setup.plan = message.plan;
+ cmd.parm.setup.screen = message.screen;
+ tmp = ccard->interface.statcallb(&cmd);
+ switch(tmp) {
+ case 0: /* no user responding */
+ idi_do_req(ccard, chan, HANGUP, 0);
+ break;
+ case 1: /* alert */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Call Alert\n", chan->No);
+ if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_ICALLW)) {
+ chan->fsm_state = EICON_STATE_ICALL;
+ idi_do_req(ccard, chan, CALL_ALERT, 0);
+ }
+ break;
+ case 2: /* reject */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Call Reject\n", chan->No);
+ idi_do_req(ccard, chan, REJECT, 0);
+ break;
+ case 3: /* incomplete number */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Incomplete Number\n", chan->No);
+ switch(ccard->type) {
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ /* TODO (other protocols) */
+ chan->fsm_state = EICON_STATE_ICALLW;
+ break;
+ default:
+ idi_do_req(ccard, chan, HANGUP, 0);
+ }
+ break;
+ }
+ break;
+ case INFO_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Info_Ind\n", chan->No);
+ if ((chan->fsm_state == EICON_STATE_ICALLW) &&
+ (message.cpn[0])) {
+ strcat(chan->cpn, message.cpn + 1);
+ goto try_stat_icall_again;
+ }
+ break;
+ case CALL_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Call_Ind\n", chan->No);
+ if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_IWAIT)) {
+ chan->fsm_state = EICON_STATE_IBWAIT;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_DCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+ } else
+ idi_hangup(ccard, chan);
+ break;
+ case CALL_CON:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Call_Con\n", chan->No);
+ if (chan->fsm_state == EICON_STATE_OCALL) {
+ chan->fsm_state = EICON_STATE_OBWAIT;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_DCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ idi_do_req(ccard, chan, ASSIGN, 1);
+ idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+ } else
+ idi_hangup(ccard, chan);
+ break;
+ case AOC_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Advice of Charge\n", chan->No);
+ break;
+ default:
+ if (DebugVar & 8)
+ printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind);
+ }
+ }
+ /* Network Layer */
+ else if (chan->e.B2Id == ind->IndId) {
+
+ if (chan->No == ccard->nchannels) {
+ /* Management Indication */
+ idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
+ chan->fsm_state = 1;
+ }
+ else
+ switch(ind->Ind) {
+ case IDI_N_CONNECT_ACK:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect_Ack\n", chan->No);
+ if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
+ chan->fsm_state = EICON_STATE_WMCONN;
+ break;
+ }
+ chan->fsm_state = EICON_STATE_ACTIVE;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ break;
+ case IDI_N_CONNECT:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No);
+ if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1);
+ if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
+ chan->fsm_state = EICON_STATE_WMCONN;
+ break;
+ }
+ chan->fsm_state = EICON_STATE_ACTIVE;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ break;
+ case IDI_N_DISC:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC\n", chan->No);
+ if (chan->e.B2Id) {
+ idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1);
+ idi_do_req(ccard, chan, REMOVE, 1);
+ }
+ chan->queued = 0;
+ chan->waitq = 0;
+ chan->waitpq = 0;
+ if (chan->fsm_state == EICON_STATE_ACTIVE) {
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BHUP;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ }
+ break;
+ case IDI_N_DISC_ACK:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No);
+ break;
+ case IDI_N_DATA_ACK:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_DATA_ACK\n", chan->No);
+ break;
+ case IDI_N_DATA:
+ skb_pull(skb, sizeof(eicon_IND) - 1);
+ if (DebugVar & 128)
+ printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len);
+ ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
+ free_buff = 0;
+ break;
+ case IDI_N_UDATA:
+ idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
+ break;
+ default:
+ if (DebugVar & 8)
+ printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind);
+ }
+ }
+ else {
+ if (DebugVar & 1)
+ printk(KERN_ERR "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No);
+ }
+ if (free_buff) dev_kfree_skb(skb);
+}
+
+void
+idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
+{
+ int j;
+ eicon_RC *ack = (eicon_RC *)skb->data;
+ eicon_chan *chan;
+ isdn_ctrl cmd;
+
+ if ((ack->Rc != ASSIGN_OK) && (ack->Rc != OK)) {
+ if ((chan = ccard->IdTable[ack->RcId]) != NULL) {
+ chan->e.busy = 0;
+ if (DebugVar & 24)
+ printk(KERN_ERR "eicon_ack: Ch%d: Not OK: Rc=%d Id=%d Ch=%d\n", chan->No,
+ ack->Rc, ack->RcId, ack->RcCh);
+ if (chan->No == ccard->nchannels) { /* Management */
+ chan->fsm_state = 2;
+ } else { /* any other channel */
+ /* card reports error: we hangup */
+ idi_hangup(ccard, chan);
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ }
+ }
+ }
+ else {
+ if ((chan = ccard->IdTable[ack->RcId]) != NULL) {
+ if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No,
+ ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id);
+ } else {
+ if (chan->No == ccard->nchannels) { /* Management */
+ if (chan->e.Req == 0x04) chan->fsm_state = 1;
+ }
+ if (chan->e.ReqCh) {
+ switch(chan->e.Req & 0x0f) {
+ case IDI_N_MDATA:
+ case IDI_N_DATA:
+ chan->queued -= chan->waitq;
+ if (chan->queued < 0) chan->queued = 0;
+ if ((chan->e.Req & 0x0f) == IDI_N_DATA) {
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.arg = chan->No;
+ cmd.parm.length = chan->waitpq;
+ chan->waitpq = 0;
+ ccard->interface.statcallb(&cmd);
+ }
+ break;
+ default:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No,
+ ack->RcId, ack->RcCh, ack->Reference);
+ }
+ }
+ else {
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No,
+ ack->RcId, ack->RcCh, ack->Reference);
+ }
+
+ if (chan->e.Req == REMOVE) {
+ if (ack->Reference == chan->e.ref) {
+ ccard->IdTable[ack->RcId] = NULL;
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No,
+ ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
+ if (!chan->e.ReqCh)
+ chan->e.D3Id = 0;
+ else
+ chan->e.B2Id = 0;
+ }
+ else {
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
+ ack->Reference, chan->e.ref);
+ }
+ }
+ chan->e.busy = 0;
+ }
+ }
+ else {
+ for(j = 0; j < ccard->nchannels + 1; j++) {
+ if (ccard->bch[j].e.ref == ack->Reference) {
+ if (!ccard->bch[j].e.ReqCh)
+ ccard->bch[j].e.D3Id = ack->RcId;
+ else
+ ccard->bch[j].e.B2Id = ack->RcId;
+ ccard->IdTable[ack->RcId] = &ccard->bch[j];
+ ccard->bch[j].e.busy = 0;
+ ccard->bch[j].e.ref = 0;
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j,
+ ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
+ break;
+ }
+ }
+ if (j > ccard->nchannels) {
+ if (DebugVar & 24)
+ printk(KERN_DEBUG"idi_ack: Ch??: ref %d not found for Id %d\n",
+ ack->Reference, ack->RcId);
+ }
+ }
+ }
+ dev_kfree_skb(skb);
+ eicon_schedule_tx(ccard);
+}
+
+int
+idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb)
+{
+ struct sk_buff *xmit_skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan_ptr *chan2;
+ int len, plen = 0, offset = 0;
+ unsigned long flags;
+
+ if (chan->fsm_state != EICON_STATE_ACTIVE) {
+ if (DebugVar & 1)
+ printk(KERN_DEBUG"idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state);
+ return -ENODEV;
+ }
+
+ len = skb->len;
+ if (len > 2138) /* too much for the shared memory */
+ return -1;
+ if (!len)
+ return 0;
+ if (chan->queued + len > ((chan->l2prot == ISDN_PROTO_L2_TRANS) ? 4000 : EICON_MAX_QUEUED))
+ return 0;
+ if (DebugVar & 128)
+ printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len);
+ save_flags(flags);
+ cli();
+ while(offset < len) {
+
+ plen = ((len - offset) > 270) ? 270 : len - offset;
+
+ xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ restore_flags(flags);
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ));
+ if (((len - offset) > 270) &&
+ (chan->l2prot != ISDN_PROTO_L2_TRANS)) {
+ reqbuf->Req = IDI_N_MDATA;
+ } else {
+ reqbuf->Req = IDI_N_DATA;
+ if (ack) reqbuf->Req |= N_D_BIT;
+ }
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ memcpy(&reqbuf->XBuffer.P, skb->data + offset, plen);
+ reqbuf->XBuffer.length = plen;
+ reqbuf->Reference = 1; /* Net Entity */
+
+ skb_queue_tail(&chan->e.X, xmit_skb);
+ skb_queue_tail(&card->sndq, skb2);
+
+ offset += plen;
+ }
+ chan->queued += len;
+ restore_flags(flags);
+ eicon_schedule_tx(card);
+ dev_kfree_skb(skb);
+ return len;
+}
+
+
+
+int
+eicon_idi_manage_assign(eicon_card *card)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+
+ chan = &(card->bch[card->nchannels]);
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+ reqbuf->XBuffer.P[0] = 0;
+ reqbuf->Req = ASSIGN;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 0xe0;
+ reqbuf->XBuffer.length = 1;
+ reqbuf->Reference = 2; /* Man Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+ return(0);
+}
+
+
+int
+eicon_idi_manage_remove(eicon_card *card)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+
+ chan = &(card->bch[card->nchannels]);
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+ reqbuf->Req = REMOVE;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.length = 0;
+ reqbuf->Reference = 2; /* Man Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+ return(0);
+}
+
+
+int
+eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
+{
+ int l = 0;
+ int ret = 0;
+ int timeout;
+ int i;
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+
+ chan = &(card->bch[card->nchannels]);
+
+ if (chan->e.D3Id) return -EBUSY;
+ chan->e.D3Id = 1;
+ while((skb2 = skb_dequeue(&chan->e.X)))
+ dev_kfree_skb(skb2);
+ chan->e.busy = 0;
+
+ if ((ret = eicon_idi_manage_assign(card))) {
+ chan->e.D3Id = 0;
+ return(ret);
+ }
+
+ timeout = jiffies + 50;
+ while (timeout > jiffies) {
+ if (chan->e.B2Id) break;
+ SLEEP(10);
+ }
+ if (!chan->e.B2Id) {
+ chan->e.D3Id = 0;
+ return -EIO;
+ }
+
+ chan->fsm_state = 0;
+
+ if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: alloc_manifbuf failed\n");
+ chan->e.D3Id = 0;
+ return -ENOMEM;
+ }
+ if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
+ chan->e.D3Id = 0;
+ return -EFAULT;
+ }
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n");
+ kfree(manbuf);
+ chan->e.D3Id = 0;
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+ reqbuf->XBuffer.P[l++] = ESC;
+ reqbuf->XBuffer.P[l++] = 6;
+ reqbuf->XBuffer.P[l++] = 0x80;
+ for (i = 0; i < manbuf->length[0]; i++)
+ reqbuf->XBuffer.P[l++] = manbuf->data[i];
+ reqbuf->XBuffer.P[1] = manbuf->length[0] + 1;
+
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->Req = (manbuf->count) ? manbuf->count : 0x02; /* Request */
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 2; /* Man Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+
+ manbuf->count = 0;
+ manbuf->pos = 0;
+
+ eicon_schedule_tx(card);
+
+ timeout = jiffies + 50;
+ while (timeout > jiffies) {
+ if (chan->fsm_state) break;
+ SLEEP(10);
+ }
+ if ((!chan->fsm_state) || (chan->fsm_state == 2)) {
+ eicon_idi_manage_remove(card);
+ kfree(manbuf);
+ chan->e.D3Id = 0;
+ return -EIO;
+ }
+
+ if ((ret = eicon_idi_manage_remove(card))) {
+ chan->e.D3Id = 0;
+ return(ret);
+ }
+
+ if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) {
+ chan->e.D3Id = 0;
+ return -EFAULT;
+ }
+
+ kfree(manbuf);
+ chan->e.D3Id = 0;
+ return(0);
+}
--- /dev/null
+/* $Id: eicon_idi.h,v 1.4 1999/03/29 11:19:44 armin Exp $
+ *
+ * ISDN lowlevel-module for the Eicon.Diehl active cards.
+ * IDI-Interface
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_idi.h,v $
+ * Revision 1.4 1999/03/29 11:19:44 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3 1999/03/02 12:37:45 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2 1999/01/24 20:14:18 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1 1999/01/01 18:09:42 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef IDI_H
+#define IDI_H
+
+
+#define ASSIGN 0x01
+#define REMOVE 0xff
+
+#define CALL_REQ 1 /* call request */
+#define CALL_CON 1 /* call confirmation */
+#define CALL_IND 2 /* incoming call connected */
+#define LISTEN_REQ 2 /* listen request */
+#define HANGUP 3 /* hangup request/indication */
+#define SUSPEND 4 /* call suspend request/confirm */
+#define RESUME 5 /* call resume request/confirm */
+#define SUSPEND_REJ 6 /* suspend rejected indication */
+#define USER_DATA 8 /* user data for user to user signaling */
+#define CONGESTION 9 /* network congestion indication */
+#define INDICATE_REQ 10 /* request to indicate an incoming call */
+#define INDICATE_IND 10 /* indicates that there is an incoming call */
+#define CALL_RES 11 /* accept an incoming call */
+#define CALL_ALERT 12 /* send ALERT for incoming call */
+#define INFO_REQ 13 /* INFO request */
+#define INFO_IND 13 /* INFO indication */
+#define REJECT 14 /* reject an incoming call */
+#define RESOURCES 15 /* reserve B-Channel hardware resources */
+#define TEL_CTRL 16 /* Telephone control request/indication */
+#define STATUS_REQ 17 /* Request D-State (returned in INFO_IND) */
+#define FAC_REG_REQ 18 /* connection idependent fac registration */
+#define FAC_REG_ACK 19 /* fac registration acknowledge */
+#define FAC_REG_REJ 20 /* fac registration reject */
+#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call */
+#define AOC_IND 26/* Advice of Charge */
+
+#define IDI_N_MDATA (0x01)
+#define IDI_N_CONNECT (0x02)
+#define IDI_N_CONNECT_ACK (0x03)
+#define IDI_N_DISC (0x04)
+#define IDI_N_DISC_ACK (0x05)
+#define IDI_N_RESET (0x06)
+#define IDI_N_RESET_ACK (0x07)
+#define IDI_N_DATA (0x08)
+#define IDI_N_EDATA (0x09)
+#define IDI_N_UDATA (0x0a)
+#define IDI_N_BDATA (0x0b)
+#define IDI_N_DATA_ACK (0x0c)
+#define IDI_N_EDATA_ACK (0x0d)
+
+#define N_Q_BIT 0x10 /* Q-bit for req/ind */
+#define N_M_BIT 0x20 /* M-bit for req/ind */
+#define N_D_BIT 0x40 /* D-bit for req/ind */
+
+
+#define SHIFT 0x90 /* codeset shift */
+#define MORE 0xa0 /* more data */
+#define CL 0xb0 /* congestion level */
+
+ /* codeset 0 */
+
+#define BC 0x04 /* Bearer Capability */
+#define CAU 0x08 /* cause */
+#define CAD 0x0c /* Connected address */
+#define CAI 0x10 /* call identity */
+#define CHI 0x18 /* channel identification */
+#define LLI 0x19 /* logical link id */
+#define CHA 0x1a /* charge advice */
+#define FTY 0x1c
+#define PI 0x1e /* Progress Indicator */
+#define NI 0x27 /* Notification Indicator */
+#define DT 0x29 /* ETSI date/time */
+#define KEY 0x2c /* keypad information element */
+#define DSP 0x28 /* display */
+#define OAD 0x6c /* origination address */
+#define OSA 0x6d /* origination sub-address */
+#define CPN 0x70 /* called party number */
+#define DSA 0x71 /* destination sub-address */
+#define RDN 0x74 /* redirecting number */
+#define LLC 0x7c /* low layer compatibility */
+#define HLC 0x7d /* high layer compatibility */
+#define UUI 0x7e /* user user information */
+#define ESC 0x7f /* escape extension */
+
+#define DLC 0x20 /* data link layer configuration */
+#define NLC 0x21 /* network layer configuration */
+
+ /* codeset 6 */
+
+#define SIN 0x01 /* service indicator */
+#define CIF 0x02 /* charging information */
+#define DATE 0x03 /* date */
+#define CPS 0x07 /* called party status */
+
+/*------------------------------------------------------------------*/
+/* return code coding */
+/*------------------------------------------------------------------*/
+
+#define UNKNOWN_COMMAND 0x01 /* unknown command */
+#define WRONG_COMMAND 0x02 /* wrong command */
+#define WRONG_ID 0x03 /* unknown task/entity id */
+#define WRONG_CH 0x04 /* wrong task/entity id */
+#define UNKNOWN_IE 0x05 /* unknown information el. */
+#define WRONG_IE 0x06 /* wrong information el. */
+#define OUT_OF_RESOURCES 0x07 /* card out of res. */
+#define N_FLOW_CONTROL 0x10 /* Flow-Control, retry */
+#define ASSIGN_RC 0xe0 /* ASSIGN acknowledgement */
+#define ASSIGN_OK 0xef /* ASSIGN OK */
+#define OK_FC 0xfc /* Flow-Control RC */
+#define READY_INT 0xfd /* Ready interrupt */
+#define TIMER_INT 0xfe /* timer interrupt */
+#define OK 0xff /* command accepted */
+
+/*------------------------------------------------------------------*/
+
+typedef struct {
+ char cpn[32];
+ char oad[32];
+ char dsa[32];
+ char osa[32];
+ __u8 plan;
+ __u8 screen;
+ __u8 sin[4];
+ __u8 chi[4];
+ __u8 e_chi[4];
+ __u8 bc[12];
+ __u8 e_bc[12];
+ __u8 llc[18];
+ __u8 hlc[5];
+ __u8 cau[4];
+ __u8 e_cau[2];
+ __u8 e_mt;
+ __u8 dt[6];
+ char display[83];
+ char keypad[35];
+ char rdn[32];
+} idi_ind_message;
+
+typedef struct {
+ __u16 next __attribute__ ((packed));
+ __u8 Req __attribute__ ((packed));
+ __u8 ReqId __attribute__ ((packed));
+ __u8 ReqCh __attribute__ ((packed));
+ __u8 Reserved1 __attribute__ ((packed));
+ __u16 Reference __attribute__ ((packed));
+ __u8 Reserved[8] __attribute__ ((packed));
+ eicon_PBUFFER XBuffer;
+} eicon_REQ;
+
+typedef struct {
+ __u16 next __attribute__ ((packed));
+ __u8 Rc __attribute__ ((packed));
+ __u8 RcId __attribute__ ((packed));
+ __u8 RcCh __attribute__ ((packed));
+ __u8 Reserved1 __attribute__ ((packed));
+ __u16 Reference __attribute__ ((packed));
+ __u8 Reserved2[8] __attribute__ ((packed));
+} eicon_RC;
+
+typedef struct {
+ __u16 next __attribute__ ((packed));
+ __u8 Ind __attribute__ ((packed));
+ __u8 IndId __attribute__ ((packed));
+ __u8 IndCh __attribute__ ((packed));
+ __u8 MInd __attribute__ ((packed));
+ __u16 MLength __attribute__ ((packed));
+ __u16 Reference __attribute__ ((packed));
+ __u8 RNR __attribute__ ((packed));
+ __u8 Reserved __attribute__ ((packed));
+ __u32 Ack __attribute__ ((packed));
+ eicon_PBUFFER RBuffer;
+} eicon_IND;
+
+typedef struct {
+ __u16 NextReq __attribute__ ((packed)); /* pointer to next Req Buffer */
+ __u16 NextRc __attribute__ ((packed)); /* pointer to next Rc Buffer */
+ __u16 NextInd __attribute__ ((packed)); /* pointer to next Ind Buffer */
+ __u8 ReqInput __attribute__ ((packed)); /* number of Req Buffers sent */
+ __u8 ReqOutput __attribute__ ((packed)); /* number of Req Buffers returned */
+ __u8 ReqReserved __attribute__ ((packed));/*number of Req Buffers reserved */
+ __u8 Int __attribute__ ((packed)); /* ISDN-P interrupt */
+ __u8 XLock __attribute__ ((packed)); /* Lock field for arbitration */
+ __u8 RcOutput __attribute__ ((packed)); /* number of Rc buffers received */
+ __u8 IndOutput __attribute__ ((packed)); /* number of Ind buffers received */
+ __u8 IMask __attribute__ ((packed)); /* Interrupt Mask Flag */
+ __u8 Reserved1[2] __attribute__ ((packed)); /* reserved field, do not use */
+ __u8 ReadyInt __attribute__ ((packed)); /* request field for ready int */
+ __u8 Reserved2[12] __attribute__ ((packed)); /* reserved field, do not use */
+ __u8 InterfaceType __attribute__ ((packed)); /* interface type 1=16K */
+ __u16 Signature __attribute__ ((packed)); /* ISDN-P initialized ind */
+ __u8 B[1]; /* buffer space for Req,Ind and Rc */
+} eicon_pr_ram;
+
+
+extern int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer);
+extern int idi_hangup(eicon_card *card, eicon_chan *chan);
+extern int idi_connect_res(eicon_card *card, eicon_chan *chan);
+extern int eicon_idi_listen_req(eicon_card *card, eicon_chan *chan);
+extern int idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
+ char *eazmsn, int si1, int si2);
+
+extern void idi_handle_ack(eicon_card *card, struct sk_buff *skb);
+extern void idi_handle_ind(eicon_card *card, struct sk_buff *skb);
+extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb);
+extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb);
+
+#endif
--- /dev/null
+/* $Id: eicon_io.c,v 1.1 1999/03/29 11:19:45 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Code for communicating with hardware.
+ *
+ * Copyright 1999 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for
+ * documents, informations and hardware.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_io.c,v $
+ * Revision 1.1 1999/03/29 11:19:45 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ *
+ */
+
+
+#include "eicon.h"
+
+void
+eicon_io_rcv_dispatch(eicon_card *ccard) {
+ struct sk_buff *skb, *skb2, *skb_new;
+ eicon_IND *ind, *ind2, *ind_new;
+ eicon_chan *chan;
+
+ if (!ccard) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon_io_rcv_dispatch: NULL card!\n");
+ return;
+ }
+
+ while((skb = skb_dequeue(&ccard->rcvq))) {
+ ind = (eicon_IND *)skb->data;
+
+ if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
+ if (DebugVar & 1) {
+ switch(ind->Ind) {
+ case IDI_N_DISC_ACK:
+ /* doesn't matter if this happens */
+ break;
+ default:
+ printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%d\n", ind->Ind, ind->IndId);
+ printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+ ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
+ }
+ }
+ dev_kfree_skb(skb);
+ continue;
+ }
+
+ if (chan->e.complete) { /* check for rec-buffer chaining */
+ if (ind->MLength == ind->RBuffer.length) {
+ chan->e.complete = 1;
+ idi_handle_ind(ccard, skb);
+ continue;
+ }
+ else {
+ chan->e.complete = 0;
+ ind->Ind = ind->MInd;
+ skb_queue_tail(&chan->e.R, skb);
+ continue;
+ }
+ }
+ else {
+ if (!(skb2 = skb_dequeue(&chan->e.R))) {
+ chan->e.complete = 1;
+ if (DebugVar & 1)
+ printk(KERN_ERR "eicon: buffer incomplete, but 0 in queue\n");
+ dev_kfree_skb(skb);
+ dev_kfree_skb(skb2);
+ continue;
+ }
+ ind2 = (eicon_IND *)skb2->data;
+ skb_new = alloc_skb(((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length),
+ GFP_ATOMIC);
+ ind_new = (eicon_IND *)skb_put(skb_new,
+ ((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length));
+ ind_new->Ind = ind2->Ind;
+ ind_new->IndId = ind2->IndId;
+ ind_new->IndCh = ind2->IndCh;
+ ind_new->MInd = ind2->MInd;
+ ind_new->MLength = ind2->MLength;
+ ind_new->RBuffer.length = ind2->RBuffer.length + ind->RBuffer.length;
+ memcpy(&ind_new->RBuffer.P, &ind2->RBuffer.P, ind2->RBuffer.length);
+ memcpy((&ind_new->RBuffer.P)+ind2->RBuffer.length, &ind->RBuffer.P, ind->RBuffer.length);
+ dev_kfree_skb(skb);
+ dev_kfree_skb(skb2);
+ if (ind->MLength == ind->RBuffer.length) {
+ chan->e.complete = 2;
+ idi_handle_ind(ccard, skb_new);
+ continue;
+ }
+ else {
+ chan->e.complete = 0;
+ skb_queue_tail(&chan->e.R, skb_new);
+ continue;
+ }
+ }
+ }
+}
+
+void
+eicon_io_ack_dispatch(eicon_card *ccard) {
+ struct sk_buff *skb;
+
+ if (!ccard) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon_io_ack_dispatch: NULL card!\n");
+ return;
+ }
+ while((skb = skb_dequeue(&ccard->rackq))) {
+ idi_handle_ack(ccard, skb);
+ }
+}
+
+
+/*
+ * IO-Functions for different card-types
+ */
+
+u8 ram_inb(eicon_card *card, void *adr) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ return(inb((u16)pcard->PCIreg + M_DATA));
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ return(readb(addr));
+ }
+ return(0);
+}
+
+u16 ram_inw(eicon_card *card, void *adr) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ return(inw((u16)pcard->PCIreg + M_DATA));
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ return(readw(addr));
+ }
+ return(0);
+}
+
+void ram_outb(eicon_card *card, void *adr, u8 data) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ outb((u8)data, (u16)pcard->PCIreg + M_DATA);
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ writeb(data, addr);
+ break;
+ }
+}
+
+void ram_outw(eicon_card *card, void *adr , u16 data) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ outw((u16)data, (u16)pcard->PCIreg + M_DATA);
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ writew(data, addr);
+ break;
+ }
+}
+
+void ram_copyfromcard(eicon_card *card, void *adrto, void *adr, int len) {
+ int i;
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ for(i = 0; i < len; i++) {
+ writeb(ram_inb(card, adr + i), adrto + i);
+ }
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ memcpy(adrto, adr, len);
+ break;
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ memcpy_fromio(adrto, adr, len);
+ break;
+ }
+}
+
+void ram_copytocard(eicon_card *card, void *adrto, void *adr, int len) {
+ int i;
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ for(i = 0; i < len; i++) {
+ ram_outb(card, adrto + i, readb(adr + i));
+ }
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ memcpy(adrto, adr, len);
+ break;
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ memcpy_toio(adrto, adr, len);
+ break;
+ }
+}
+
+/*
+ * Transmit-Function
+ */
+void
+eicon_io_transmit(eicon_card *ccard) {
+ eicon_pci_card *pci_card;
+ eicon_isa_card *isa_card;
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ unsigned long flags;
+ char *ram, *reg, *cfg;
+ eicon_pr_ram *prram = 0;
+ eicon_isa_com *com = 0;
+ eicon_REQ *ReqOut = 0;
+ eicon_REQ *reqbuf = 0;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+ int ReqCount;
+ int scom = 0;
+ int tmp = 0;
+ int quloop = 1;
+
+ pci_card = &ccard->hwif.pci;
+ isa_card = &ccard->hwif.isa;
+
+ if (!ccard) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon_transmit: NULL card!\n");
+ return;
+ }
+
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ scom = 1;
+ com = (eicon_isa_com *)isa_card->shmem;
+ break;
+ case EICON_CTYPE_S2M:
+ scom = 0;
+ prram = (eicon_pr_ram *)isa_card->shmem;
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ prram = (eicon_pr_ram *)ram;
+ break;
+ case EICON_CTYPE_MAESTRA:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ prram = 0;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_transmit: unsupported card-type!\n");
+ return;
+ }
+
+ ReqCount = 0;
+ if (!(skb2 = skb_dequeue(&ccard->sndq)))
+ quloop = 0;
+ while(quloop) {
+ save_flags(flags);
+ cli();
+ if (scom) {
+ if (ram_inb(ccard, &com->Req)) {
+ if (!ccard->ReadyInt) {
+ tmp = ram_inb(ccard, &com->ReadyInt) + 1;
+ ram_outb(ccard, &com->ReadyInt, tmp);
+ ccard->ReadyInt++;
+ }
+ restore_flags(flags);
+ skb_queue_head(&ccard->sndq, skb2);
+ if (DebugVar & 32)
+ printk(KERN_INFO "eicon: transmit: Card not ready\n");
+ return;
+ }
+ } else {
+ if (!(ram_inb(ccard, &prram->ReqOutput) - ram_inb(ccard, &prram->ReqInput))) {
+ restore_flags(flags);
+ skb_queue_head(&ccard->sndq, skb2);
+ if (DebugVar & 32)
+ printk(KERN_INFO "eicon: transmit: Card not ready\n");
+ return;
+ }
+ }
+ restore_flags(flags);
+ chan2 = (eicon_chan_ptr *)skb2->data;
+ chan = chan2->ptr;
+ if (!chan->e.busy) {
+ if((skb = skb_dequeue(&chan->e.X))) {
+ save_flags(flags);
+ cli();
+ reqbuf = (eicon_REQ *)skb->data;
+ if (scom) {
+ ram_outw(ccard, &com->XBuffer.length, reqbuf->XBuffer.length);
+ ram_copytocard(ccard, &com->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
+ ram_outb(ccard, &com->ReqCh, reqbuf->ReqCh);
+
+ } else {
+ /* get address of next available request buffer */
+ ReqOut = (eicon_REQ *)&prram->B[ram_inw(ccard, &prram->NextReq)];
+ ram_outw(ccard, &ReqOut->XBuffer.length, reqbuf->XBuffer.length);
+ ram_copytocard(ccard, &ReqOut->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
+ ram_outb(ccard, &ReqOut->ReqCh, reqbuf->ReqCh);
+ ram_outb(ccard, &ReqOut->Req, reqbuf->Req);
+ }
+
+ if (reqbuf->ReqId &0x1f) { /* if this is no ASSIGN */
+
+ if (!reqbuf->Reference) { /* Signal Layer */
+ if (scom)
+ ram_outb(ccard, &com->ReqId, chan->e.D3Id);
+ else
+ ram_outb(ccard, &ReqOut->ReqId, chan->e.D3Id);
+
+ chan->e.ReqCh = 0;
+ }
+ else { /* Net Layer */
+ if (scom)
+ ram_outb(ccard, &com->ReqId, chan->e.B2Id);
+ else
+ ram_outb(ccard, &ReqOut->ReqId, chan->e.B2Id);
+
+ chan->e.ReqCh = 1;
+ if (((reqbuf->Req & 0x0f) == 0x08) ||
+ ((reqbuf->Req & 0x0f) == 0x01)) { /* Send Data */
+ chan->waitq = reqbuf->XBuffer.length;
+ chan->waitpq += reqbuf->XBuffer.length;
+ }
+ }
+
+ } else { /* It is an ASSIGN */
+
+ if (scom)
+ ram_outb(ccard, &com->ReqId, reqbuf->ReqId);
+ else
+ ram_outb(ccard, &ReqOut->ReqId, reqbuf->ReqId);
+
+ if (!reqbuf->Reference)
+ chan->e.ReqCh = 0;
+ else
+ chan->e.ReqCh = 1;
+ }
+ if (scom)
+ chan->e.ref = ccard->ref_out++;
+ else
+ chan->e.ref = ram_inw(ccard, &ReqOut->Reference);
+
+ chan->e.Req = reqbuf->Req;
+ ReqCount++;
+ if (scom)
+ ram_outb(ccard, &com->Req, reqbuf->Req);
+ else
+ ram_outw(ccard, &prram->NextReq, ram_inw(ccard, &ReqOut->next));
+
+ chan->e.busy = 1;
+ restore_flags(flags);
+ if (DebugVar & 32)
+ printk(KERN_DEBUG "eicon: Req=%x Id=%x Ch=%x Len=%x Ref=%d\n",
+ reqbuf->Req,
+ ram_inb(ccard, &ReqOut->ReqId),
+ reqbuf->ReqCh, reqbuf->XBuffer.length,
+ chan->e.ref);
+ dev_kfree_skb(skb);
+ }
+ dev_kfree_skb(skb2);
+ }
+ else {
+ skb_queue_tail(&ccard->sackq, skb2);
+ if (DebugVar & 32)
+ printk(KERN_INFO "eicon: transmit: busy chan %d\n", chan->No);
+ }
+
+ if (scom)
+ quloop = 0;
+ else
+ if (!(skb2 = skb_dequeue(&ccard->sndq)))
+ quloop = 0;
+
+ }
+ if (!scom)
+ ram_outb(ccard, &prram->ReqInput, (__u8)(ram_inb(ccard, &prram->ReqInput) + ReqCount));
+
+ while((skb = skb_dequeue(&ccard->sackq))) {
+ skb_queue_tail(&ccard->sndq, skb);
+ }
+}
+
+
+/*
+ * IRQ handler
+ */
+void
+eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
+ eicon_card *ccard = (eicon_card *)dev_id;
+ eicon_pci_card *pci_card;
+ eicon_isa_card *isa_card;
+ char *ram = 0;
+ char *reg = 0;
+ char *cfg = 0;
+ eicon_pr_ram *prram = 0;
+ eicon_isa_com *com = 0;
+ eicon_RC *RcIn;
+ eicon_IND *IndIn;
+ struct sk_buff *skb;
+ int Count = 0;
+ int Rc = 0;
+ int Ind = 0;
+ unsigned char *irqprobe = 0;
+ int scom = 0;
+ int tmp = 0;
+
+
+ if (!ccard) {
+ printk(KERN_WARNING "eicon_irq: spurious interrupt %d\n", irq);
+ return;
+ }
+
+ if (ccard->type == EICON_CTYPE_QUADRO) {
+ tmp = 4;
+ while(tmp) {
+ com = (eicon_isa_com *)ccard->hwif.isa.shmem;
+ if ((readb(ccard->hwif.isa.intack))) { /* quadro found */
+ break;
+ }
+ ccard = ccard->qnext;
+ tmp--;
+ }
+ }
+
+ pci_card = &ccard->hwif.pci;
+ isa_card = &ccard->hwif.isa;
+
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ scom = 1;
+ com = (eicon_isa_com *)isa_card->shmem;
+ irqprobe = &isa_card->irqprobe;
+ break;
+ case EICON_CTYPE_S2M:
+ scom = 0;
+ prram = (eicon_pr_ram *)isa_card->shmem;
+ irqprobe = &isa_card->irqprobe;
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ irqprobe = &pci_card->irqprobe;
+ prram = (eicon_pr_ram *)ram;
+ break;
+ case EICON_CTYPE_MAESTRA:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ irqprobe = &pci_card->irqprobe;
+ prram = 0;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_irq: unsupported card-type!\n");
+ return;
+ }
+
+ if (*irqprobe) {
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ if (readb(isa_card->intack)) {
+ writeb(0, &com->Rc);
+ writeb(0, isa_card->intack);
+ }
+ (*irqprobe)++;
+ break;
+ case EICON_CTYPE_S2M:
+ if (readb(isa_card->intack)) {
+ writeb(0, &prram->RcOutput);
+ writeb(0, isa_card->intack);
+ }
+ (*irqprobe)++;
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ if (readb(&ram[0x3fe])) {
+ writeb(0, &prram->RcOutput);
+ writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+ writew(0, &cfg[MP_IRQ_RESET + 2]);
+ writeb(0, &ram[0x3fe]);
+ }
+ *irqprobe = 0;
+ break;
+ case EICON_CTYPE_MAESTRA:
+ outb(0x08, pci_card->PCIreg + M_RESET);
+ *irqprobe = 0;
+ break;
+ }
+ return;
+ }
+
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ case EICON_CTYPE_S2M:
+ if (!(readb(isa_card->intack))) { /* card did not interrupt */
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+ return;
+ }
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+ return;
+ }
+ break;
+ case EICON_CTYPE_MAESTRA:
+ outw(0x3fe, pci_card->PCIreg + M_ADDR);
+ if (!(inb(pci_card->PCIreg + M_DATA))) { /* card did not interrupt */
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+ return;
+ }
+ break;
+ }
+
+ if (scom) {
+
+ /* if a return code is available ... */
+ if ((tmp = ram_inb(ccard, &com->Rc))) {
+ eicon_RC *ack;
+ if (tmp == READY_INT) {
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Rc=READY_INT\n");
+ if (ccard->ReadyInt) {
+ ccard->ReadyInt--;
+ ram_outb(ccard, &com->Rc, 0);
+ }
+ } else {
+ skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
+ ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
+ ack->Rc = tmp;
+ ack->RcId = ram_inb(ccard, &com->RcId);
+ ack->RcCh = ram_inb(ccard, &com->RcCh);
+ ack->Reference = ccard->ref_in++;
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+ tmp,ack->RcId,ack->RcCh,ack->Reference);
+ skb_queue_tail(&ccard->rackq, skb);
+ eicon_schedule_ack(ccard);
+ ram_outb(ccard, &com->Req, 0);
+ ram_outb(ccard, &com->Rc, 0);
+ }
+
+ } else {
+
+ /* if an indication is available ... */
+ if ((tmp = ram_inb(ccard, &com->Ind))) {
+ eicon_IND *ind;
+ int len = ram_inw(ccard, &com->RBuffer.length);
+ skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
+ ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
+ ind->Ind = tmp;
+ ind->IndId = ram_inb(ccard, &com->IndId);
+ ind->IndCh = ram_inb(ccard, &com->IndCh);
+ ind->MInd = ram_inb(ccard, &com->MInd);
+ ind->MLength = ram_inw(ccard, &com->MLength);
+ ind->RBuffer.length = len;
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+ tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
+ ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len);
+ skb_queue_tail(&ccard->rcvq, skb);
+ eicon_schedule_rx(ccard);
+ ram_outb(ccard, &com->Ind, 0);
+ }
+ }
+
+ } else {
+
+ /* if return codes are available ... */
+ if((Count = ram_inb(ccard, &prram->RcOutput))) {
+ eicon_RC *ack;
+ /* get the buffer address of the first return code */
+ RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &prram->NextRc)];
+ /* for all return codes do ... */
+ while(Count--) {
+
+ if((Rc=ram_inb(ccard, &RcIn->Rc))) {
+ skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
+ ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
+ ack->Rc = Rc;
+ ack->RcId = ram_inb(ccard, &RcIn->RcId);
+ ack->RcCh = ram_inb(ccard, &RcIn->RcCh);
+ ack->Reference = ram_inw(ccard, &RcIn->Reference);
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+ Rc,ack->RcId,ack->RcCh,ack->Reference);
+ ram_outb(ccard, &RcIn->Rc, 0);
+ skb_queue_tail(&ccard->rackq, skb);
+ eicon_schedule_ack(ccard);
+ }
+ /* get buffer address of next return code */
+ RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &RcIn->next)];
+ }
+ /* clear all return codes (no chaining!) */
+ ram_outb(ccard, &prram->RcOutput, 0);
+ }
+
+ /* if indications are available ... */
+ if((Count = ram_inb(ccard, &prram->IndOutput))) {
+ eicon_IND *ind;
+ /* get the buffer address of the first indication */
+ IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &prram->NextInd)];
+ /* for all indications do ... */
+ while(Count--) {
+ Ind = ram_inb(ccard, &IndIn->Ind);
+ if(Ind) {
+ int len = ram_inw(ccard, &IndIn->RBuffer.length);
+ skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
+ ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
+ ind->Ind = Ind;
+ ind->IndId = ram_inb(ccard, &IndIn->IndId);
+ ind->IndCh = ram_inb(ccard, &IndIn->IndCh);
+ ind->MInd = ram_inb(ccard, &IndIn->MInd);
+ ind->MLength = ram_inw(ccard, &IndIn->MLength);
+ ind->RBuffer.length = len;
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+ Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
+ ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len);
+ skb_queue_tail(&ccard->rcvq, skb);
+ eicon_schedule_rx(ccard);
+ ram_outb(ccard, &IndIn->Ind, 0);
+ }
+ /* get buffer address of next indication */
+ IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &IndIn->next)];
+ }
+ ram_outb(ccard, &prram->IndOutput, 0);
+ }
+
+ }
+
+ /* clear interrupt */
+ switch(ccard->type) {
+ case EICON_CTYPE_QUADRO:
+ writeb(0, isa_card->intack);
+ writeb(0, &com[0x401]);
+ break;
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_S2M:
+ writeb(0, isa_card->intack);
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+ writew(0, &cfg[MP_IRQ_RESET + 2]);
+ writeb(0, &ram[0x3fe]);
+ break;
+ case EICON_CTYPE_MAESTRA:
+ outb(0x08, pci_card->PCIreg + M_RESET);
+ outw(0x3fe, pci_card->PCIreg + M_ADDR);
+ outb(0, pci_card->PCIreg + M_DATA);
+ break;
+ }
+
+ return;
+}
+
--- /dev/null
+/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Hardware-specific code for old ISA cards.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_isa.c,v $
+ * Revision 1.5 1999/04/01 12:48:33 armin
+ * Changed some log outputs.
+ *
+ * Revision 1.4 1999/03/29 11:19:46 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3 1999/03/02 12:37:45 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2 1999/01/24 20:14:19 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1 1999/01/01 18:09:43 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include "eicon.h"
+#include "eicon_isa.h"
+
+#define check_shmem check_region
+#define release_shmem release_region
+#define request_shmem request_region
+
+char *eicon_isa_revision = "$Revision: 1.5 $";
+
+/* Mask for detecting invalid IRQ parameter */
+static int eicon_isa_valid_irq[] = {
+ 0x1c1c, /* 2, 3, 4, 10, 11, 12 (S)*/
+ 0x1c1c, /* 2, 3, 4, 10, 11, 12 (SX) */
+ 0x1cbc, /* 2, 3, 4, 5, 7, 10, 11, 12 (SCOM) */
+ 0x1cbc, /* 2, 3, 4, 5, 6, 10, 11, 12 (Quadro) */
+ 0x1cbc /* 2, 3, 4, 5, 7, 10, 11, 12 (S2M) */
+};
+
+static void
+eicon_isa_release_shmem(eicon_isa_card *card) {
+ if (card->mvalid)
+ release_shmem((unsigned long)card->shmem, card->ramsize);
+ card->mvalid = 0;
+}
+
+static void
+eicon_isa_release_irq(eicon_isa_card *card) {
+ if (!card->master)
+ return;
+ if (card->ivalid)
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+}
+
+void
+eicon_isa_release(eicon_isa_card *card) {
+ eicon_isa_release_irq(card);
+ eicon_isa_release_shmem(card);
+}
+
+void
+eicon_isa_printpar(eicon_isa_card *card) {
+ switch (card->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ case EICON_CTYPE_S2M:
+ printk(KERN_INFO "Eicon %s at 0x%lx, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned long)card->shmem,
+ card->irq);
+ }
+}
+
+int
+eicon_isa_find_card(int Mem, int Irq, char * Id)
+{
+ int primary = 1;
+
+ if (!strlen(Id))
+ return -1;
+
+ /* Check for valid membase address */
+ if ((Mem < 0x0c0000) ||
+ (Mem > 0x0fc000) ||
+ (Mem & 0xfff)) {
+ printk(KERN_WARNING "eicon_isa: illegal membase 0x%x for %s\n",
+ Mem, Id);
+ return -1;
+ }
+ if (check_shmem(Mem, RAMSIZE)) {
+ printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem);
+ return -1;
+ }
+
+ writew(0x55aa, Mem + 0x402);
+ if (readw(Mem + 0x402) != 0x55aa) primary = 0;
+ writew(0, Mem + 0x402);
+ if (readw(Mem + 0x402) != 0) primary = 0;
+
+ printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id);
+ if (primary) {
+ printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem);
+ writeb(0, Mem + 0x3ffe);
+ return EICON_CTYPE_ISAPRI;
+ } else {
+ printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem);
+ writeb(0, Mem + 0x400);
+ return EICON_CTYPE_ISABRI;
+ }
+ return -1;
+}
+
+int
+eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) {
+ int tmp;
+ int timeout;
+ eicon_isa_codebuf cbuf;
+ unsigned char *code;
+ eicon_isa_boot *boot;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
+ return -EFAULT;
+
+ /* Allocate code-buffer and copy code from userspace */
+ if (cbuf.bootstrap_len > 1024) {
+ printk(KERN_WARNING "eicon_isa_boot: Invalid startup-code size %ld\n",
+ cbuf.bootstrap_len);
+ return -EINVAL;
+ }
+ if (!(code = kmalloc(cbuf.bootstrap_len, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+ if (copy_from_user(code, &cb->code, cbuf.bootstrap_len)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ switch(card->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ case EICON_CTYPE_ISABRI:
+ card->ramsize = RAMSIZE;
+ card->intack = (__u8 *)card->shmem + INTACK;
+ card->startcpu = (__u8 *)card->shmem + STARTCPU;
+ card->stopcpu = (__u8 *)card->shmem + STOPCPU;
+ break;
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_ISAPRI:
+ card->ramsize = RAMSIZE_P;
+ card->intack = (__u8 *)card->shmem + INTACK_P;
+ card->startcpu = (__u8 *)card->shmem + STARTCPU_P;
+ card->stopcpu = (__u8 *)card->shmem + STOPCPU_P;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type);
+ return -EINVAL;
+ }
+
+ /* Register shmem */
+ if (check_shmem((unsigned long)card->shmem, card->ramsize)) {
+ printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n",
+ (unsigned long)card->shmem);
+ kfree(code);
+ return -EBUSY;
+ }
+ request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN");
+ card->mvalid = 1;
+
+ /* clear any pending irq's */
+ readb(card->intack);
+ /* set reset-line active */
+ writeb(0, card->stopcpu);
+ /* clear irq-requests */
+ writeb(0, card->intack);
+ readb(card->intack);
+
+ /* Copy code into card */
+ memcpy_toio(&card->shmem->c, code, cbuf.bootstrap_len);
+
+ /* Check for properly loaded code */
+ if (!check_signature((unsigned long)&card->shmem->c, code, 1020)) {
+ printk(KERN_WARNING "eicon_isa_boot: Could not load startup-code\n");
+ eicon_isa_release_shmem(card);
+ kfree(code);
+ return -EIO;
+ }
+ /* if 16k-ramsize, duplicate the reset-jump-code */
+ if (card->ramsize == RAMSIZE_P)
+ memcpy_toio((__u8 *)card->shmem + 0x3ff0, &code[0x3f0], 12);
+
+ kfree(code);
+ boot = &card->shmem->boot;
+
+ /* Delay 0.2 sec. */
+ SLEEP(20);
+
+ /* Start CPU */
+ writeb(cbuf.boot_opt, &boot->ctrl);
+ writeb(0, card->startcpu);
+
+ /* Delay 0.2 sec. */
+ SLEEP(20);
+
+ timeout = jiffies + (HZ * 22);
+ while (timeout > jiffies) {
+ if (readb(&boot->ctrl) == 0)
+ break;
+ SLEEP(10);
+ }
+ if (readb(&boot->ctrl) != 0) {
+ printk(KERN_WARNING "eicon_isa_boot: CPU test failed\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+
+ /* Check for memory-test errors */
+ if (readw(&boot->ebit)) {
+ printk(KERN_WARNING "eicon_isa_boot: memory test failed (bit 0x%04x at 0x%08x)\n",
+ readw(&boot->ebit), readl(&boot->eloc));
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+
+ /* Check card type and memory size */
+ tmp = readb(&boot->card);
+ if ((tmp < 0) || (tmp > 4)) {
+ printk(KERN_WARNING "eicon_isa_boot: Type detect failed\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+ card->type = tmp;
+ ((eicon_card *)card->card)->type = tmp;
+
+ tmp = readb(&boot->msize);
+ if (tmp != 8 && tmp != 16 && tmp != 24 &&
+ tmp != 32 && tmp != 48 && tmp != 60) {
+ printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+ printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]);
+ if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) {
+ tmp = eicon_addcard(card->type, (unsigned long)card->shmem, card->irq,
+ ((eicon_card *)card->card)->regname);
+ printk(KERN_INFO "Eicon: %d adapters added\n", tmp);
+ }
+ return 0;
+}
+
+int
+eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) {
+ eicon_isa_boot *boot;
+ int tmp;
+ int timeout;
+ int j;
+ eicon_isa_codebuf cbuf;
+ unsigned char *code;
+ unsigned char *p;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
+ return -EFAULT;
+
+ if (!(code = kmalloc(cbuf.firmware_len, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(code, &cb->code, cbuf.firmware_len)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ boot = &card->shmem->boot;
+
+ if ((!card->ivalid) && card->master) {
+ card->irqprobe = 1;
+ /* Check for valid IRQ */
+ if ((card->irq < 0) || (card->irq > 15) ||
+ (!((1 << card->irq) & eicon_isa_valid_irq[card->type & 0x0f]))) {
+ printk(KERN_WARNING "eicon_isa_boot: illegal irq: %d\n", card->irq);
+ eicon_isa_release_shmem(card);
+ kfree(code);
+ return -EINVAL;
+ }
+ /* Register irq */
+ if (!request_irq(card->irq, &eicon_irq, 0, "Eicon ISA ISDN", card))
+ card->ivalid = 1;
+ else {
+ printk(KERN_WARNING "eicon_isa_boot: irq %d already in use.\n",
+ card->irq);
+ eicon_isa_release_shmem(card);
+ kfree(code);
+ return -EBUSY;
+ }
+ }
+
+ tmp = readb(&boot->msize);
+ if (tmp != 8 && tmp != 16 && tmp != 24 &&
+ tmp != 32 && tmp != 48 && tmp != 60) {
+ printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+
+ eicon_isa_printpar(card);
+
+ /* Download firmware */
+ printk(KERN_INFO "%s %dkB, loading firmware ...\n",
+ eicon_ctype_name[card->type],
+ tmp * 16);
+ tmp = cbuf.firmware_len >> 8;
+ p = code;
+ while (tmp--) {
+ memcpy_toio(&boot->b, p, 256);
+ writeb(1, &boot->ctrl);
+ timeout = jiffies + 10;
+ while (timeout > jiffies) {
+ if (readb(&boot->ctrl) == 0)
+ break;
+ SLEEP(2);
+ }
+ if (readb(&boot->ctrl)) {
+ printk(KERN_WARNING "eicon_isa_boot: download timeout at 0x%x\n", p-code);
+ eicon_isa_release(card);
+ kfree(code);
+ return -EIO;
+ }
+ p += 256;
+ }
+ kfree(code);
+
+ /* Initialize firmware parameters */
+ memcpy_toio(&card->shmem->c[8], &cbuf.tei, 14);
+ memcpy_toio(&card->shmem->c[32], &cbuf.oad, 96);
+ memcpy_toio(&card->shmem->c[128], &cbuf.oad, 96);
+
+ /* Start firmware, wait for signature */
+ writeb(2, &boot->ctrl);
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ if (readw(&boot->signature) == 0x4447)
+ break;
+ SLEEP(2);
+ }
+ if (readw(&boot->signature) != 0x4447) {
+ printk(KERN_WARNING "eicon_isa_boot: firmware selftest failed %04x\n",
+ readw(&boot->signature));
+ eicon_isa_release(card);
+ return -EIO;
+ }
+
+ card->channels = readb(&card->shmem->c[0x3f6]);
+
+ /* clear irq-requests, reset irq-count */
+ readb(card->intack);
+ writeb(0, card->intack);
+
+ if (card->master) {
+ card->irqprobe = 1;
+ /* Trigger an interrupt and check if it is delivered */
+ tmp = readb(&card->shmem->com.ReadyInt);
+ tmp ++;
+ writeb(tmp, &card->shmem->com.ReadyInt);
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe > 1)
+ break;
+ SLEEP(2);
+ }
+ if (card->irqprobe == 1) {
+ printk(KERN_WARNING "eicon_isa_boot: IRQ test failed\n");
+ eicon_isa_release(card);
+ return -EIO;
+ }
+ }
+ writeb(card->irq, &card->shmem->com.Int);
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ ((eicon_card *)card->card)->ref_in = 1;
+ ((eicon_card *)card->card)->ref_out = 1;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Supported channels: %d\n", card->channels);
+ printk(KERN_INFO "%s successfully started\n", eicon_ctype_name[card->type]);
+
+ /* Enable normal IRQ processing */
+ card->irqprobe = 0;
+ return 0;
+}
--- /dev/null
+/* $Id: eicon_isa.h,v 1.3 1999/03/29 11:19:47 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_isa.h,v $
+ * Revision 1.3 1999/03/29 11:19:47 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.2 1999/03/02 12:37:46 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.1 1999/01/01 18:09:44 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef eicon_isa_h
+#define eicon_isa_h
+
+#ifdef __KERNEL__
+
+/* Factory defaults for ISA-Cards */
+#define EICON_ISA_MEMBASE 0xd0000
+#define EICON_ISA_IRQ 3
+/* shmem offset for Quadro parts */
+#define EICON_ISA_QOFFSET 0x0800
+
+typedef struct {
+ __u16 length __attribute__ ((packed)); /* length of data/parameter field */
+ __u8 P[270]; /* data/parameter field */
+} eicon_scom_PBUFFER;
+
+/* General communication buffer */
+typedef struct {
+ __u8 Req; /* request register */
+ __u8 ReqId; /* request task/entity identification */
+ __u8 Rc; /* return code register */
+ __u8 RcId; /* return code task/entity identification */
+ __u8 Ind; /* Indication register */
+ __u8 IndId; /* Indication task/entity identification */
+ __u8 IMask; /* Interrupt Mask Flag */
+ __u8 RNR; /* Receiver Not Ready (set by PC) */
+ __u8 XLock; /* XBuffer locked Flag */
+ __u8 Int; /* ISDN interrupt */
+ __u8 ReqCh; /* Channel field for layer-3 Requests */
+ __u8 RcCh; /* Channel field for layer-3 Returncodes */
+ __u8 IndCh; /* Channel field for layer-3 Indications */
+ __u8 MInd; /* more data indication field */
+ __u16 MLength; /* more data total packet length */
+ __u8 ReadyInt; /* request field for ready interrupt */
+ __u8 Reserved[12]; /* reserved space */
+ __u8 IfType; /* 1 = 16k-Interface */
+ __u16 Signature __attribute__ ((packed)); /* ISDN adapter Signature */
+ eicon_scom_PBUFFER XBuffer; /* Transmit Buffer */
+ eicon_scom_PBUFFER RBuffer; /* Receive Buffer */
+} eicon_isa_com;
+
+/* struct for downloading firmware */
+typedef struct {
+ __u8 ctrl;
+ __u8 card;
+ __u8 msize;
+ __u8 fill0;
+ __u16 ebit __attribute__ ((packed));
+ __u32 eloc __attribute__ ((packed));
+ __u8 reserved[20];
+ __u16 signature __attribute__ ((packed));
+ __u8 fill[224];
+ __u8 b[256];
+} eicon_isa_boot;
+
+/* Shared memory */
+typedef union {
+ unsigned char c[0x400];
+ eicon_isa_com com;
+ eicon_isa_boot boot;
+} eicon_isa_shmem;
+
+/*
+ * card's description
+ */
+typedef struct {
+ int ramsize;
+ int irq; /* IRQ */
+ void* card;
+ eicon_isa_shmem* shmem; /* Shared-memory area */
+ unsigned char* intack; /* Int-Acknowledge */
+ unsigned char* stopcpu; /* Writing here stops CPU */
+ unsigned char* startcpu; /* Writing here starts CPU */
+ unsigned char type; /* card type */
+ int channels; /* No. of channels */
+ unsigned char irqprobe; /* Flag: IRQ-probing */
+ unsigned char mvalid; /* Flag: Memory is valid */
+ unsigned char ivalid; /* Flag: IRQ is valid */
+ unsigned char master; /* Flag: Card ist Quadro 1/4 */
+ void* generic; /* Ptr to generic card struct */
+} eicon_isa_card;
+
+/* Offsets for special locations on standard cards */
+#define INTACK 0x03fe
+#define STOPCPU 0x0400
+#define STARTCPU 0x0401
+#define RAMSIZE 0x0400
+/* Offsets for special location on PRI card */
+#define INTACK_P 0x3ffc
+#define STOPCPU_P 0x3ffe
+#define STARTCPU_P 0x3fff
+#define RAMSIZE_P 0x4000
+
+
+extern int eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb);
+extern int eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb);
+extern void eicon_isa_release(eicon_isa_card *card);
+extern void eicon_isa_printpar(eicon_isa_card *card);
+extern void eicon_isa_transmit(eicon_isa_card *card);
+extern int eicon_isa_find_card(int Mem, int Irq, char * Id);
+
+#endif /* __KERNEL__ */
+
+#endif /* eicon_isa_h */
--- /dev/null
+/* $Id: eicon_mod.c,v 1.5 1999/04/01 12:48:35 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for
+ * documents, informations and hardware.
+ *
+ * Deutsche Telekom AG for S2M support.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_mod.c,v $
+ * Revision 1.5 1999/04/01 12:48:35 armin
+ * Changed some log outputs.
+ *
+ * Revision 1.4 1999/03/29 11:19:47 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3 1999/03/02 12:37:47 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2 1999/01/24 20:14:21 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1 1999/01/01 18:09:44 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "eicon.h"
+
+#define INCLUDE_INLINE_FUNCS
+
+static eicon_card *cards = (eicon_card *) NULL;
+
+static char *eicon_revision = "$Revision: 1.5 $";
+
+extern char *eicon_pci_revision;
+extern char *eicon_isa_revision;
+extern char *eicon_idi_revision;
+
+#ifdef MODULE
+#define MOD_USE_COUNT (GET_USE_COUNT (&__this_module))
+#endif
+
+#define EICON_CTRL_VERSION 1
+
+ulong DebugVar;
+
+/* Parameters to be set by insmod */
+static int membase = -1;
+static int irq = -1;
+static char *id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+MODULE_DESCRIPTION( "Driver for Eicon.Diehl active ISDN cards");
+MODULE_AUTHOR( "Armin Schindler");
+MODULE_SUPPORTED_DEVICE( "ISDN subsystem");
+MODULE_PARM_DESC(membase, "Base address of first ISA card");
+MODULE_PARM_DESC(irq, "IRQ of first card");
+MODULE_PARM_DESC(id, "ID-String of first card");
+MODULE_PARM(membase, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(id, "s");
+
+char *eicon_ctype_name[] = {
+ "ISDN-S",
+ "ISDN-SX",
+ "ISDN-SCOM",
+ "ISDN-QUADRO",
+ "ISDN-S2M",
+ "DIVA Server BRI/PCI",
+ "DIVA Server 4BRI/PCI",
+ "DIVA Server 4BRI/PCI",
+ "DIVA Server PRI/PCI"
+};
+
+static int
+getrel(char *p)
+{
+ int v = 0;
+ char *tmp = 0;
+
+ if ((tmp = strchr(p, '.')))
+ p = tmp + 1;
+ while (p[0] >= '0' && p[0] <= '9') {
+ v = ((v < 0) ? 0 : (v * 10)) + (int) (p[0] - '0');
+ p++;
+ }
+ return v;
+
+
+}
+
+static char *
+eicon_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else rev = "?.??";
+ return rev;
+
+}
+
+static eicon_chan *
+find_channel(eicon_card *card, int channel)
+{
+ if ((channel >= 0) && (channel < card->nchannels))
+ return &(card->bch[channel]);
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon: Invalid channel %d\n", channel);
+ return NULL;
+}
+
+/*
+ * Free MSN list
+ */
+static void
+eicon_clear_msn(eicon_card *card)
+{
+ struct msn_entry *p = card->msn_list;
+ struct msn_entry *q;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ card->msn_list = NULL;
+ restore_flags(flags);
+ while (p) {
+ q = p->next;
+ kfree(p);
+ p = q;
+ }
+}
+
+/*
+ * Find an MSN entry in the list.
+ * If ia5 != 0, return IA5-encoded EAZ, else
+ * return a bitmask with corresponding bit set.
+ */
+static __u16
+eicon_find_msn(eicon_card *card, char *msn, int ia5)
+{
+ struct msn_entry *p = card->msn_list;
+ __u8 eaz = '0';
+
+ while (p) {
+ if (!strcmp(p->msn, msn)) {
+ eaz = p->eaz;
+ break;
+ }
+ p = p->next;
+ }
+ if (!ia5)
+ return (1 << (eaz - '0'));
+ else
+ return eaz;
+}
+
+/*
+ * Find an EAZ entry in the list.
+ * return a string with corresponding msn.
+ */
+char *
+eicon_find_eaz(eicon_card *card, char eaz)
+{
+ struct msn_entry *p = card->msn_list;
+
+ while (p) {
+ if (p->eaz == eaz)
+ return(p->msn);
+ p = p->next;
+ }
+ return("\0");
+}
+
+#if 0
+/*
+ * Add or delete an MSN to the MSN list
+ *
+ * First character of msneaz is EAZ, rest is MSN.
+ * If length of eazmsn is 1, delete that entry.
+ */
+static int
+eicon_set_msn(eicon_card *card, char *eazmsn)
+{
+ struct msn_entry *p = card->msn_list;
+ struct msn_entry *q = NULL;
+ unsigned long flags;
+ int i;
+
+ if (!strlen(eazmsn))
+ return 0;
+ if (strlen(eazmsn) > 16)
+ return -EINVAL;
+ for (i = 0; i < strlen(eazmsn); i++)
+ if (!isdigit(eazmsn[i]))
+ return -EINVAL;
+ if (strlen(eazmsn) == 1) {
+ /* Delete a single MSN */
+ while (p) {
+ if (p->eaz == eazmsn[0]) {
+ save_flags(flags);
+ cli();
+ if (q)
+ q->next = p->next;
+ else
+ card->msn_list = p->next;
+ restore_flags(flags);
+ kfree(p);
+ if (DebugVar & 8)
+ printk(KERN_DEBUG
+ "Mapping for EAZ %c deleted\n",
+ eazmsn[0]);
+ return 0;
+ }
+ q = p;
+ p = p->next;
+ }
+ return 0;
+ }
+ /* Add a single MSN */
+ while (p) {
+ /* Found in list, replace MSN */
+ if (p->eaz == eazmsn[0]) {
+ save_flags(flags);
+ cli();
+ strcpy(p->msn, &eazmsn[1]);
+ restore_flags(flags);
+ if (DebugVar & 8)
+ printk(KERN_DEBUG
+ "Mapping for EAZ %c changed to %s\n",
+ eazmsn[0],
+ &eazmsn[1]);
+ return 0;
+ }
+ p = p->next;
+ }
+ /* Not found in list, add new entry */
+ p = kmalloc(sizeof(msn_entry), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->eaz = eazmsn[0];
+ strcpy(p->msn, &eazmsn[1]);
+ p->next = card->msn_list;
+ save_flags(flags);
+ cli();
+ card->msn_list = p;
+ restore_flags(flags);
+ if (DebugVar & 8)
+ printk(KERN_DEBUG
+ "Mapping %c -> %s added\n",
+ eazmsn[0],
+ &eazmsn[1]);
+ return 0;
+}
+#endif
+
+static void
+eicon_rcv_dispatch(struct eicon_card *card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ case EICON_BUS_PCI:
+ eicon_io_rcv_dispatch(card);
+ break;
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static void
+eicon_ack_dispatch(struct eicon_card *card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ case EICON_BUS_PCI:
+ eicon_io_ack_dispatch(card);
+ break;
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static void
+eicon_transmit(struct eicon_card *card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ case EICON_BUS_PCI:
+ eicon_io_transmit(card);
+ break;
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_transmit: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static int
+eicon_command(eicon_card * card, isdn_ctrl * c)
+{
+ ulong a;
+ eicon_chan *chan;
+ eicon_cdef cdef;
+ isdn_ctrl cmd;
+ char tmp[17];
+ int ret = 0;
+ unsigned long flags;
+
+ switch (c->command) {
+ case ISDN_CMD_IOCTL:
+ memcpy(&a, c->parm.num, sizeof(ulong));
+ switch (c->arg) {
+ case EICON_IOCTL_GETVER:
+ return(EICON_CTRL_VERSION);
+ case EICON_IOCTL_GETTYPE:
+ return(card->type);
+ case EICON_IOCTL_GETMMIO:
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ return (int)card->hwif.isa.shmem;
+#if CONFIG_PCI
+ case EICON_BUS_PCI:
+ return card->hwif.pci.PCIram;
+#endif
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_SETMMIO:
+ if (card->flags & EICON_FLAGS_LOADED)
+ return -EBUSY;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ if (eicon_isa_find_card(a,
+ card->hwif.isa.irq,
+ card->regname) < 0)
+ return -EFAULT;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)a;
+ return 0;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_GETIRQ:
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ return card->hwif.isa.irq;
+#if CONFIG_PCI
+ case EICON_BUS_PCI:
+ return card->hwif.pci.irq;
+#endif
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_SETIRQ:
+ if (card->flags & EICON_FLAGS_LOADED)
+ return -EBUSY;
+ if ((a < 2) || (a > 15))
+ return -EFAULT;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ card->hwif.isa.irq = a;
+ return 0;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_LOADBOOT:
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return -EBUSY;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ ret = eicon_isa_bootload(
+ &(card->hwif.isa),
+ &(((eicon_codebuf *)a)->isa));
+ break;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ return ret;
+ case EICON_IOCTL_LOADISA:
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return -EBUSY;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ ret = eicon_isa_load(
+ &(card->hwif.isa),
+ &(((eicon_codebuf *)a)->isa));
+ if (!ret) {
+ card->flags |= EICON_FLAGS_LOADED;
+ card->flags |= EICON_FLAGS_RUNNING;
+ if (card->hwif.isa.channels > 1) {
+ cmd.command = ISDN_STAT_ADDCH;
+ cmd.driver = card->myid;
+ cmd.arg = card->hwif.isa.channels - 1;
+ card->interface.statcallb(&cmd);
+ }
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ break;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ return ret;
+
+ case EICON_IOCTL_MANIF:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!card->Feature & PROTCAP_MANIF)
+ return -ENODEV;
+ ret = eicon_idi_manage(
+ card,
+ (eicon_manifbuf *)a);
+ return ret;
+#if CONFIG_PCI
+ case EICON_IOCTL_LOADPCI:
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return -EBUSY;
+ if (card->bus == EICON_BUS_PCI) {
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ ret = eicon_pci_load_bri(
+ &(card->hwif.pci),
+ &(((eicon_codebuf *)a)->pci));
+ break;
+
+ case EICON_CTYPE_MAESTRAP:
+ ret = eicon_pci_load_pri(
+ &(card->hwif.pci),
+ &(((eicon_codebuf *)a)->pci));
+ break;
+ }
+ if (!ret) {
+ card->flags |= EICON_FLAGS_LOADED;
+ card->flags |= EICON_FLAGS_RUNNING;
+ if (card->hwif.pci.channels > 1) {
+ cmd.command = ISDN_STAT_ADDCH;
+ cmd.driver = card->myid;
+ cmd.arg = card->hwif.pci.channels - 1;
+ card->interface.statcallb(&cmd);
+ }
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ return ret;
+ } else return -ENODEV;
+#endif
+#if 0
+ case EICON_IOCTL_SETMSN:
+ if ((ret = copy_from_user(tmp, (char *)a, sizeof(tmp))))
+ return -EFAULT;
+ if ((ret = eicon_set_msn(card, tmp)))
+ return ret;
+#if 0
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return(eicon_capi_manufacturer_req_msn(card));
+#endif
+ return 0;
+#endif
+ case EICON_IOCTL_ADDCARD:
+ if ((ret = copy_from_user(&cdef, (char *)a, sizeof(cdef))))
+ return -EFAULT;
+ if (!(eicon_addcard(0, cdef.membase, cdef.irq, cdef.id)))
+ return -EIO;
+ return 0;
+ case EICON_IOCTL_DEBUGVAR:
+ DebugVar = a;
+ printk(KERN_DEBUG"Eicon: Debug Value set to %ld\n", DebugVar);
+ return 0;
+#ifdef MODULE
+ case EICON_IOCTL_FREEIT:
+ while (MOD_USE_COUNT > 0) MOD_DEC_USE_COUNT;
+ MOD_INC_USE_COUNT;
+ return 0;
+#endif
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ISDN_CMD_DIAL:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ save_flags(flags);
+ cli();
+ if ((chan->fsm_state != EICON_STATE_NULL) && (chan->fsm_state != EICON_STATE_LISTEN)) {
+ restore_flags(flags);
+ if (DebugVar & 1)
+ printk(KERN_WARNING "Dial on channel %d with state %d\n",
+ chan->No, chan->fsm_state);
+ return -EBUSY;
+ }
+ if (card->ptype == ISDN_PTYPE_EURO)
+ tmp[0] = eicon_find_msn(card, c->parm.setup.eazmsn, 1);
+ else
+ tmp[0] = c->parm.setup.eazmsn[0];
+ chan->fsm_state = EICON_STATE_OCALL;
+ chan->callref = 0xffff;
+ restore_flags(flags);
+
+ ret = idi_connect_req(card, chan, c->parm.setup.phone,
+ c->parm.setup.eazmsn,
+ c->parm.setup.si1,
+ c->parm.setup.si2);
+ if (ret) {
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg &= 0x1f;
+ card->interface.statcallb(&cmd);
+ }
+ return ret;
+ case ISDN_CMD_ACCEPTD:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ if (chan->fsm_state == EICON_STATE_ICALL) {
+ idi_connect_res(card, chan);
+ }
+ return 0;
+ case ISDN_CMD_ACCEPTB:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ return 0;
+ case ISDN_CMD_HANGUP:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ idi_hangup(card, chan);
+ return 0;
+ case ISDN_CMD_SETEAZ:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ if (strlen(c->parm.num)) {
+ if (card->ptype == ISDN_PTYPE_EURO) {
+ chan->eazmask = eicon_find_msn(card, c->parm.num, 0);
+ }
+ if (card->ptype == ISDN_PTYPE_1TR6) {
+ int i;
+ chan->eazmask = 0;
+ for (i = 0; i < strlen(c->parm.num); i++)
+ if (isdigit(c->parm.num[i]))
+ chan->eazmask |= (1 << (c->parm.num[i] - '0'));
+ }
+ } else
+ chan->eazmask = 0x3ff;
+ eicon_idi_listen_req(card, chan);
+ return 0;
+ case ISDN_CMD_CLREAZ:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ chan->eazmask = 0;
+ eicon_idi_listen_req(card, chan);
+ return 0;
+ case ISDN_CMD_SETL2:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ chan->l2prot = (c->arg >> 8);
+ return 0;
+ case ISDN_CMD_GETL2:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ return chan->l2prot;
+ case ISDN_CMD_SETL3:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "L3 protocol unknown\n");
+ return -1;
+ }
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ chan->l3prot = (c->arg >> 8);
+ return 0;
+ case ISDN_CMD_GETL3:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ return chan->l3prot;
+ case ISDN_CMD_GETEAZ:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon CMD_GETEAZ not implemented\n");
+ return 0;
+ case ISDN_CMD_SETSIL:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon CMD_SETSIL not implemented\n");
+ return 0;
+ case ISDN_CMD_GETSIL:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon CMD_GETSIL not implemented\n");
+ return 0;
+ case ISDN_CMD_LOCK:
+ MOD_INC_USE_COUNT;
+ return 0;
+ case ISDN_CMD_UNLOCK:
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline eicon_card *
+eicon_findcard(int driverid)
+{
+ eicon_card *p = cards;
+
+ while (p) {
+ if (p->myid == driverid)
+ return p;
+ p = p->next;
+ }
+ return (eicon_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+ eicon_card *card = eicon_findcard(c->driver);
+
+ if (card)
+ return (eicon_command(card, c));
+ printk(KERN_ERR
+ "eicon: if_command %d called with invalid driverId %d!\n",
+ c->command, c->driver);
+ return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+ eicon_card *card = eicon_findcard(id);
+
+ if (card) {
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ return (len);
+ }
+ printk(KERN_ERR
+ "eicon: if_writecmd called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+#if 0
+ /* Not yet used */
+ eicon_card *card = eicon_findcard(id);
+
+ if (card) {
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ return (eicon_readstatus(buf, len, user, card));
+ }
+ printk(KERN_ERR
+ "eicon: if_readstatus called with invalid driverId!\n");
+#endif
+ return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+ eicon_card *card = eicon_findcard(id);
+ eicon_chan *chan;
+
+ if (card) {
+ if (!card->flags & EICON_FLAGS_RUNNING) {
+ dev_kfree_skb(skb);
+ return -ENODEV;
+ }
+ if (!(chan = find_channel(card, channel))) {
+ dev_kfree_skb(skb);
+ return -ENODEV;
+ }
+ if (chan->fsm_state == EICON_STATE_ACTIVE)
+ return (idi_send_data(card, chan, ack, skb));
+ else {
+ dev_kfree_skb(skb);
+ return -ENODEV;
+ }
+ }
+ printk(KERN_ERR
+ "eicon: if_sendbuf called with invalid driverId!\n");
+ dev_kfree_skb(skb);
+ return -ENODEV;
+}
+
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list.
+ */
+static void
+eicon_alloccard(int Type, int membase, int irq, char *id)
+{
+ int i;
+ int j;
+ int qloop;
+ char qid[5];
+ eicon_card *card;
+#if CONFIG_PCI
+ eicon_pci_card *pcic;
+#endif
+
+ qloop = (Type == EICON_CTYPE_QUADRO)?2:0;
+ for (i = 0; i <= qloop; i++) {
+ if (!(card = (eicon_card *) kmalloc(sizeof(eicon_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "eicon: (%s) Could not allocate card-struct.\n", id);
+ return;
+ }
+ memset((char *) card, 0, sizeof(eicon_card));
+ skb_queue_head_init(&card->sndq);
+ skb_queue_head_init(&card->rcvq);
+ skb_queue_head_init(&card->rackq);
+ skb_queue_head_init(&card->sackq);
+ card->snd_tq.routine = (void *) (void *) eicon_transmit;
+ card->snd_tq.data = card;
+ card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch;
+ card->rcv_tq.data = card;
+ card->ack_tq.routine = (void *) (void *) eicon_ack_dispatch;
+ card->ack_tq.data = card;
+ card->interface.maxbufsize = 4000;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = if_writecmd;
+ card->interface.readstat = if_readstatus;
+ card->interface.features =
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN;
+ card->interface.hl_hdrlen = 20;
+ card->ptype = ISDN_PTYPE_UNKNOWN;
+ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+ card->myid = -1;
+ card->type = Type;
+ switch (Type) {
+ case EICON_CTYPE_QUADRO:
+ if (membase == -1)
+ membase = EICON_ISA_MEMBASE;
+ if (irq == -1)
+ irq = EICON_ISA_IRQ;
+ card->bus = EICON_BUS_ISA;
+ card->hwif.isa.card = (void *)card;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET);
+ card->hwif.isa.master = 0;
+ strcpy(card->interface.id, id);
+ if (id[strlen(id) - 1] == 'a') {
+ card->interface.id[strlen(id) - 1] = 'a' + i + 1;
+ } else {
+ sprintf(qid, "_%c",'2' + i);
+ strcat(card->interface.id, qid);
+ }
+ printk(KERN_INFO "Eicon: Quadro: Driver-Id %s added.\n",
+ card->interface.id);
+ if (i == 0) {
+ eicon_card *p = cards;
+ while(p) {
+ if ((p->hwif.isa.master) && (p->hwif.isa.irq == irq)) {
+ p->qnext = card;
+ break;
+ }
+ p = p->next;
+ }
+ if (!p) {
+ printk(KERN_WARNING "eicon_alloccard: Quadro Master not found.\n");
+ kfree(card);
+ return;
+ }
+ } else {
+ cards->qnext = card;
+ }
+ card->hwif.isa.irq = irq;
+ card->hwif.isa.type = Type;
+ card->nchannels = 2;
+ card->interface.channels = 1;
+ break;
+#if CONFIG_PCI
+ case EICON_CTYPE_MAESTRA:
+ (eicon_pci_card *)pcic = (eicon_pci_card *)membase;
+ card->bus = EICON_BUS_PCI;
+ card->interface.features |=
+ ISDN_FEATURE_L2_V11096 |
+ ISDN_FEATURE_L2_V11019 |
+ ISDN_FEATURE_L2_V11038 |
+ ISDN_FEATURE_L2_MODEM;
+ card->hwif.pci.card = (void *)card;
+ card->hwif.pci.PCIreg = pcic->PCIreg;
+ card->hwif.pci.PCIcfg = pcic->PCIcfg;
+ card->hwif.pci.master = 1;
+ card->hwif.pci.mvalid = pcic->mvalid;
+ card->hwif.pci.ivalid = 0;
+ card->hwif.pci.irq = irq;
+ card->hwif.pci.type = Type;
+ card->flags = 0;
+ card->nchannels = 2;
+ card->interface.channels = 1;
+ break;
+
+ case EICON_CTYPE_MAESTRAP:
+ (eicon_pci_card *)pcic = (eicon_pci_card *)membase;
+ card->bus = EICON_BUS_PCI;
+ card->interface.features |=
+ ISDN_FEATURE_L2_V11096 |
+ ISDN_FEATURE_L2_V11019 |
+ ISDN_FEATURE_L2_V11038 |
+ ISDN_FEATURE_L2_MODEM;
+ card->hwif.pci.card = (void *)card;
+ card->hwif.pci.shmem = (eicon_pci_shmem *)pcic->shmem;
+ card->hwif.pci.PCIreg = pcic->PCIreg;
+ card->hwif.pci.PCIram = pcic->PCIram;
+ card->hwif.pci.PCIcfg = pcic->PCIcfg;
+ card->hwif.pci.master = 1;
+ card->hwif.pci.mvalid = pcic->mvalid;
+ card->hwif.pci.ivalid = 0;
+ card->hwif.pci.irq = irq;
+ card->hwif.pci.type = Type;
+ card->flags = 0;
+ card->nchannels = 30;
+ card->interface.channels = 1;
+ break;
+#endif
+ case EICON_CTYPE_ISABRI:
+ if (membase == -1)
+ membase = EICON_ISA_MEMBASE;
+ if (irq == -1)
+ irq = EICON_ISA_IRQ;
+ card->bus = EICON_BUS_ISA;
+ card->hwif.isa.card = (void *)card;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+ card->hwif.isa.master = 1;
+ card->hwif.isa.irq = irq;
+ card->hwif.isa.type = Type;
+ card->nchannels = 2;
+ card->interface.channels = 1;
+ break;
+ case EICON_CTYPE_ISAPRI:
+ if (membase == -1)
+ membase = EICON_ISA_MEMBASE;
+ if (irq == -1)
+ irq = EICON_ISA_IRQ;
+ card->bus = EICON_BUS_ISA;
+ card->hwif.isa.card = (void *)card;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+ card->hwif.isa.master = 1;
+ card->hwif.isa.irq = irq;
+ card->hwif.isa.type = Type;
+ card->nchannels = 30;
+ card->interface.channels = 1;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type);
+ kfree(card);
+ return;
+ }
+ if (!(card->bch = (eicon_chan *) kmalloc(sizeof(eicon_chan) * (card->nchannels + 1)
+ , GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "eicon: (%s) Could not allocate bch-struct.\n", id);
+ kfree(card);
+ return;
+ }
+ for (j=0; j< (card->nchannels + 1); j++) {
+ memset((char *)&card->bch[j], 0, sizeof(eicon_chan));
+ card->bch[j].plci = 0x8000;
+ card->bch[j].ncci = 0x8000;
+ card->bch[j].l2prot = ISDN_PROTO_L2_X75I;
+ card->bch[j].l3prot = ISDN_PROTO_L3_TRANS;
+ card->bch[j].e.D3Id = 0;
+ card->bch[j].e.B2Id = 0;
+ card->bch[j].e.Req = 0;
+ card->bch[j].No = j;
+ skb_queue_head_init(&card->bch[j].e.X);
+ skb_queue_head_init(&card->bch[j].e.R);
+ }
+ card->next = cards;
+ cards = card;
+ }
+}
+
+/*
+ * register card at linklevel
+ */
+static int
+eicon_registercard(eicon_card * card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ /* TODO something to print */
+ break;
+ case EICON_BUS_PCI:
+#if CONFIG_PCI
+ eicon_pci_printpar(&card->hwif.pci);
+ break;
+#endif
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_registercard: Illegal BUS type %d\n",
+ card->bus);
+ return -1;
+ }
+ if (!register_isdn(&card->interface)) {
+ printk(KERN_WARNING
+ "eicon_registercard: Unable to register %s\n",
+ card->interface.id);
+ return -1;
+ }
+ card->myid = card->interface.channels;
+ sprintf(card->regname, "%s", card->interface.id);
+ return 0;
+}
+
+#ifdef MODULE
+static void
+unregister_card(eicon_card * card)
+{
+ isdn_ctrl cmd;
+
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ eicon_isa_release(&card->hwif.isa);
+ break;
+ case EICON_BUS_PCI:
+#if CONFIG_PCI
+ eicon_pci_release(&card->hwif.pci);
+ break;
+#endif
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Invalid BUS type %d\n",
+ card->bus);
+ break;
+ }
+}
+#endif /* MODULE */
+
+static void
+eicon_freecard(eicon_card *card) {
+ eicon_clear_msn(card);
+ kfree(card->bch);
+ kfree(card);
+}
+
+int
+eicon_addcard(int Type, int membase, int irq, char *id)
+{
+ eicon_card *p;
+ eicon_card *q = NULL;
+ int registered;
+ int added = 0;
+ int failed = 0;
+
+ if (!Type) /* ISA */
+ if ((Type = eicon_isa_find_card(membase, irq, id)) < 0)
+ return 0;
+ eicon_alloccard(Type, membase, irq, id);
+ p = cards;
+ while (p) {
+ registered = 0;
+ if (!p->interface.statcallb) {
+ /* Not yet registered.
+ * Try to register and activate it.
+ */
+ added++;
+ switch (p->bus) {
+ case EICON_BUS_ISA:
+ if (eicon_registercard(p))
+ break;
+ registered = 1;
+ break;
+ case EICON_BUS_PCI:
+#if CONFIG_PCI
+ if (eicon_registercard(p))
+ break;
+ registered = 1;
+ break;
+#endif
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: addcard: Invalid BUS type %d\n",
+ p->bus);
+ }
+ } else
+ /* Card already registered */
+ registered = 1;
+ if (registered) {
+ /* Init OK, next card ... */
+ q = p;
+ p = p->next;
+ } else {
+ /* registering failed, remove card from list, free memory */
+ printk(KERN_WARNING
+ "eicon: Initialization of %s failed\n",
+ p->interface.id);
+ if (q) {
+ q->next = p->next;
+ eicon_freecard(p);
+ p = q->next;
+ } else {
+ cards = p->next;
+ eicon_freecard(p);
+ p = cards;
+ }
+ failed++;
+ }
+ }
+ return (added - failed);
+}
+
+#define DRIVERNAME "Eicon active ISDN driver"
+#define DRIVERRELEASE "1"
+
+#ifdef MODULE
+#define eicon_init init_module
+#endif
+
+__initfunc(int
+eicon_init(void))
+{
+ int tmp = 0;
+ int release = 0;
+ char tmprev[50];
+
+ DebugVar = 1;
+
+ printk(KERN_INFO "%s Rev: ", DRIVERNAME);
+ strcpy(tmprev, eicon_revision);
+ printk("%s/", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ strcpy(tmprev, eicon_pci_revision);
+ printk("%s/", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ strcpy(tmprev, eicon_isa_revision);
+ printk("%s/", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ strcpy(tmprev, eicon_idi_revision);
+ printk("%s\n", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ sprintf(tmprev,"%d", release);
+ printk(KERN_INFO "%s Release: %s.%s\n", DRIVERNAME,
+ DRIVERRELEASE, tmprev);
+
+ tmp = eicon_addcard(0, membase, irq, id);
+#if CONFIG_PCI
+ tmp += eicon_pci_find_card(id);
+#endif
+ if (!cards) {
+#ifdef MODULE
+ printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n");
+#endif
+ return -ENODEV;
+
+ } else
+ printk(KERN_INFO "Eicon: %d card%s added\n", tmp, (tmp>1)?"s":"");
+ /* No symbols to export, hide all symbols */
+ EXPORT_NO_SYMBOLS;
+ return 0;
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ eicon_card *card = cards;
+ eicon_card *last;
+ while (card) {
+ unregister_card(card);
+ card = card->next;
+ }
+ card = cards;
+ while (card) {
+ last = card;
+ card = card->next;
+ eicon_freecard(last);
+ }
+ printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
+}
+
+#else
+__initfunc(void
+eicon_setup(char *str, int *ints))
+{
+ int i, argc;
+
+ argc = ints[0];
+ i = 1;
+ if (argc) {
+ membase = irq = -1;
+ if (argc) {
+ membase = ints[i];
+ i++;
+ argc--;
+ }
+ if (argc) {
+ irq = ints[i];
+ i++;
+ argc--;
+ }
+ if (strlen(str)) {
+ strcpy(id, str);
+ } else {
+ strcpy(id, "eicon");
+ }
+ /* eicon_addcard(0, membase, irq, id); */
+ printk(KERN_INFO "eicon: membase=0x%x irq=%d id=%s\n", membase, irq, id);
+ }
+}
+#endif
--- /dev/null
+/* $Id: eicon_pci.c,v 1.6 1999/04/01 12:48:37 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Hardware-specific code for PCI cards.
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for
+ * documents, informations and hardware.
+ *
+ * Deutsche Telekom AG for S2M support.
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_pci.c,v $
+ * Revision 1.6 1999/04/01 12:48:37 armin
+ * Changed some log outputs.
+ *
+ * Revision 1.5 1999/03/29 11:19:49 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.4 1999/03/02 12:37:48 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.3 1999/01/24 20:14:24 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.2 1999/01/10 18:46:06 armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.1 1999/01/01 18:09:45 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include <linux/pci.h>
+
+#include "eicon.h"
+#include "eicon_pci.h"
+
+
+char *eicon_pci_revision = "$Revision: 1.6 $";
+
+#if CONFIG_PCI /* intire stuff is only for PCI */
+
+#undef EICON_PCI_DEBUG
+
+int eicon_pci_find_card(char *ID)
+{
+ if (pci_present()) {
+ struct pci_dev *pdev = NULL;
+ int pci_nextindex=0, pci_cards=0, pci_akt=0;
+ int pci_type = PCI_MAESTRA;
+ int NoMorePCICards = FALSE;
+ char *ram, *reg, *cfg;
+ unsigned int pram=0, preg=0, pcfg=0;
+ char did[12];
+ eicon_pci_card *aparms;
+
+ if (!(aparms = (eicon_pci_card *) kmalloc(sizeof(eicon_pci_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "eicon_pci: Could not allocate card-struct.\n");
+ return 0;
+ }
+
+ for (pci_cards = 0; pci_cards < 0x0f; pci_cards++)
+ {
+ do {
+ if ((pdev = pci_find_device(PCI_VENDOR_EICON,
+ pci_type,
+ pdev)))
+ {
+ pci_nextindex++;
+ break;
+ }
+ else {
+ pci_nextindex = 0;
+ switch (pci_type) /* switch to next card type */
+ {
+ case PCI_MAESTRA:
+ pci_type = PCI_MAESTRAQ; break;
+ case PCI_MAESTRAQ:
+ pci_type = PCI_MAESTRAQ_U; break;
+ case PCI_MAESTRAQ_U:
+ pci_type = PCI_MAESTRAP; break;
+ default:
+ case PCI_MAESTRAP:
+ NoMorePCICards = TRUE;
+ }
+ }
+ }
+ while (!NoMorePCICards);
+ if (NoMorePCICards)
+ {
+ if (pci_cards < 1) {
+ printk(KERN_INFO "Eicon: No supported PCI cards found.\n");
+ kfree(aparms);
+ return 0;
+ }
+ else
+ {
+ printk(KERN_INFO "Eicon: %d PCI card%s registered.\n",
+ pci_cards, (pci_cards > 1) ? "s":"");
+ kfree(aparms);
+ return (pci_cards);
+ }
+ }
+
+ pci_akt = 0;
+ switch(pci_type)
+ {
+ case PCI_MAESTRA:
+ printk(KERN_INFO "Eicon: DIVA Server BRI/PCI detected !\n");
+ aparms->type = EICON_CTYPE_MAESTRA;
+
+ aparms->irq = pdev->irq;
+ preg = pdev->base_address[2] & 0xfffffffc;
+ pcfg = pdev->base_address[1] & 0xffffff80;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+ printk(KERN_DEBUG "eicon_pci: reg=0x%x\n", preg);
+ printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n", pcfg);
+#endif
+ pci_akt = 1;
+ break;
+
+ case PCI_MAESTRAQ:
+ case PCI_MAESTRAQ_U:
+ printk(KERN_ERR "Eicon: DIVA Server 4BRI/PCI detected but not supported !\n");
+ pci_cards--;
+ pci_akt = 0;
+ break;
+
+ case PCI_MAESTRAP:
+ printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n");
+ aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/
+ aparms->irq = pdev->irq;
+ pram = pdev->base_address[0] & 0xfffff000;
+ preg = pdev->base_address[2] & 0xfffff000;
+ pcfg = pdev->base_address[4] & 0xfffff000;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+ printk(KERN_DEBUG "eicon_pci: ram=0x%x\n",
+ (pram));
+ printk(KERN_DEBUG "eicon_pci: reg=0x%x\n",
+ (preg));
+ printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n",
+ (pcfg));
+#endif
+ pci_akt = 1;
+ break;
+ default:
+ printk(KERN_ERR "eicon_pci: Unknown PCI card detected !\n");
+ pci_cards--;
+ pci_akt = 0;
+ break;
+ }
+
+ if (pci_akt) {
+ /* remapping memory */
+ switch(pci_type)
+ {
+ case PCI_MAESTRA:
+ aparms->PCIreg = (unsigned int) preg;
+ aparms->PCIcfg = (unsigned int) pcfg;
+ if (check_region((aparms->PCIreg), 0x20)) {
+ printk(KERN_WARNING "eicon_pci: reg port already in use !\n");
+ aparms->PCIreg = 0;
+ break;
+ } else {
+ request_region(aparms->PCIreg, 0x20, "eicon reg");
+ }
+ if (check_region((aparms->PCIcfg), 0x100)) {
+ printk(KERN_WARNING "eicon_pci: cfg port already in use !\n");
+ aparms->PCIcfg = 0;
+ break;
+ } else {
+ request_region(aparms->PCIcfg, 0x100, "eicon cfg");
+ }
+ break;
+ case PCI_MAESTRAQ:
+ case PCI_MAESTRAQ_U:
+ case PCI_MAESTRAP:
+ aparms->shmem = (eicon_pci_shmem *) ioremap(pram, 0x10000);
+ ram = (u8 *) ((u32)aparms->shmem + MP_SHARED_RAM_OFFSET);
+ reg = ioremap(preg, 0x4000);
+ cfg = ioremap(pcfg, 0x1000);
+ aparms->PCIram = (unsigned int) ram;
+ aparms->PCIreg = (unsigned int) reg;
+ aparms->PCIcfg = (unsigned int) cfg;
+ break;
+ }
+ if ((!aparms->PCIreg) || (!aparms->PCIcfg)) {
+ printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+ pci_cards--;
+ } else {
+ aparms->mvalid = 1;
+
+ sprintf(did, "%s%d", (strlen(ID) < 1) ? "eicon":ID, pci_cards);
+
+ printk(KERN_INFO "%s: DriverID: '%s'\n",eicon_ctype_name[aparms->type] , did);
+
+ if (!(eicon_addcard(aparms->type, (int) aparms, aparms->irq, did))) {
+ printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+ pci_cards--;
+ }
+ }
+ }
+
+ }
+ } else
+ printk(KERN_ERR "eicon_pci: Kernel compiled with PCI but no PCI-bios found !\n");
+ return 0;
+}
+
+/*
+ * Checks protocol file id for "F#xxxx" string fragment to
+ * extract the features, supported by this protocol version.
+ * binary representation of the feature string value is returned
+ * in *value. The function returns 0 if feature string was not
+ * found or has a wrong format, else 1.
+ */
+static int GetProtFeatureValue(char *sw_id, int *value)
+{
+ __u8 i, offset;
+
+ while (*sw_id)
+ {
+ if ((sw_id[0] == 'F') && (sw_id[1] == '#'))
+ {
+ sw_id = &sw_id[2];
+ for (i=0, *value=0; i<4; i++, sw_id++)
+ {
+ if ((*sw_id >= '0') && (*sw_id <= '9'))
+ {
+ offset = '0';
+ }
+ else if ((*sw_id >= 'A') && (*sw_id <= 'F'))
+ {
+ offset = 'A' + 10;
+ }
+ else if ((*sw_id >= 'a') && (*sw_id <= 'f'))
+ {
+ offset = 'a' + 10;
+ }
+ else
+ {
+ return 0;
+ }
+ *value |= (*sw_id - offset) << (4*(3-i));
+ }
+ return 1;
+ }
+ else
+ {
+ sw_id++;
+ }
+ }
+ return 0;
+}
+
+
+void
+eicon_pci_printpar(eicon_pci_card *card) {
+ switch (card->type) {
+ case EICON_CTYPE_MAESTRA:
+ printk(KERN_INFO "%s at 0x%x / 0x%x, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned int)card->PCIreg,
+ (unsigned int)card->PCIcfg,
+ card->irq);
+ break;
+ case EICON_CTYPE_MAESTRAQ:
+ case EICON_CTYPE_MAESTRAQ_U:
+ case EICON_CTYPE_MAESTRAP:
+ printk(KERN_INFO "%s at 0x%x, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned int)card->shmem,
+ card->irq);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_INFO "eicon_pci: remapped ram= 0x%x\n",(unsigned int)card->PCIram);
+ printk(KERN_INFO "eicon_pci: remapped reg= 0x%x\n",(unsigned int)card->PCIreg);
+ printk(KERN_INFO "eicon_pci: remapped cfg= 0x%x\n",(unsigned int)card->PCIcfg);
+#endif
+ break;
+ }
+}
+
+
+static void
+eicon_pci_release_shmem(eicon_pci_card *card) {
+ if (!card->master)
+ return;
+ if (card->mvalid) {
+ switch (card->type) {
+ case EICON_CTYPE_MAESTRA:
+ /* reset board */
+ outb(0, card->PCIcfg + 0x4c); /* disable interrupts from PLX */
+ outb(0, card->PCIreg + M_RESET);
+ SLEEP(20);
+ outb(0, card->PCIreg + M_ADDRH);
+ outw(0, card->PCIreg + M_ADDR);
+ outw(0, card->PCIreg + M_DATA);
+
+ release_region(card->PCIreg, 0x20);
+ release_region(card->PCIcfg, 0x100);
+ break;
+ case EICON_CTYPE_MAESTRAQ:
+ case EICON_CTYPE_MAESTRAQ_U:
+ case EICON_CTYPE_MAESTRAP:
+ /* reset board */
+ writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+ SLEEP(20);
+ writeb(0, card->PCIreg + MP_RESET);
+ SLEEP(20);
+
+ iounmap((void *)card->shmem);
+ iounmap((void *)card->PCIreg);
+ iounmap((void *)card->PCIcfg);
+ break;
+ }
+ }
+ card->mvalid = 0;
+}
+
+static void
+eicon_pci_release_irq(eicon_pci_card *card) {
+ if (!card->master)
+ return;
+ if (card->ivalid)
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+}
+
+void
+eicon_pci_release(eicon_pci_card *card) {
+ eicon_pci_release_irq(card);
+ eicon_pci_release_shmem(card);
+}
+
+/*
+ * Upload buffer content to adapters shared memory
+ * on verify error, 1 is returned and a message is printed on screen
+ * else 0 is returned
+ * Can serve IO-Type and Memory type adapters
+ */
+int eicon_upload(t_dsp_download_space *p_para,
+ __u16 length, /* byte count */
+ __u8 *buffer,
+ int verify)
+{
+ __u32 i, dwdata = 0, val = 0, timeout;
+ __u16 data;
+ eicon_pci_boot *boot = 0;
+
+ switch (p_para->type) /* actions depend on type of union */
+ {
+ case DL_PARA_IO_TYPE:
+ for (i=0; i<length; i+=2)
+ {
+ outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+ outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR);
+ /* outw (((u16 *)code)[i >> 1], p_para->dat.io.ioDATA); */
+ outw (*(u16 *)&buffer[i], p_para->dat.io.ioDATA);
+ }
+ if (verify) /* check written block */
+ {
+ for (i=0; i<length; i+=2)
+ {
+ outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+ outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR);
+ data = inw(p_para->dat.io.ioDATA);
+ if (data != *(u16 *)&buffer[i])
+ {
+ p_para->dat.io.r3addr += i;
+ p_para->dat.io.BadData = data;
+ p_para->dat.io.GoodData = *(u16 *)&buffer[i];
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case DL_PARA_MEM_TYPE:
+ boot = p_para->dat.mem.boot;
+ writel(p_para->dat.mem.r3addr, &boot->addr);
+ for (i=0; i<length; i+=4)
+ {
+ writel(((u32 *)buffer)[i >> 2], &boot->data[i]);
+ }
+ if (verify) /* check written block */
+ {
+ for (i=0; i<length; i+=4)
+ {
+ dwdata = readl(&boot->data[i]);
+ if (((u32 *)buffer)[i >> 2] != dwdata)
+ {
+ p_para->dat.mem.r3addr += i;
+ p_para->dat.mem.BadData = dwdata;
+ p_para->dat.mem.GoodData = ((u32 *)buffer)[i >> 2];
+ return 1;
+ }
+ }
+ }
+ writel(((length + 3) / 4), &boot->len); /* len in dwords */
+ writel(2, &boot->cmd);
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ val = readl(&boot->cmd);
+ if (!val) break;
+ SLEEP(2);
+ }
+ if (val)
+ {
+ p_para->dat.mem.timeout = 1;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/* show header information of code file */
+static
+int eicon_pci_print_hdr(unsigned char *code, int offset)
+{
+ unsigned char hdr[80];
+ int i, fvalue = 0;
+
+ i = 0;
+ while ((i < (sizeof(hdr) -1))
+ && (code[offset + i] != '\0')
+ && (code[offset + i] != '\r')
+ && (code[offset + i] != '\n'))
+ {
+ hdr[i] = code[offset + i];
+ i++;
+ }
+ hdr[i] = '\0';
+ printk(KERN_DEBUG "Eicon: loading %s\n", hdr);
+ if (GetProtFeatureValue(hdr, &fvalue)) return(fvalue);
+ else return(0);
+}
+
+
+/*
+ * Configure a card, download code into BRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_bri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+ int i,j;
+ int timeout;
+ unsigned int offset, offp=0, size, length;
+ int signature = 0;
+ int FeatureValue = 0;
+ eicon_pci_codebuf cbuf;
+ t_dsp_download_space dl_para;
+ t_dsp_download_desc dsp_download_table;
+ unsigned char *code;
+ unsigned int reg;
+ unsigned int cfg;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+ return -EFAULT;
+
+ reg = card->PCIreg;
+ cfg = card->PCIcfg;
+
+ /* reset board */
+ outb(0, reg + M_RESET);
+ SLEEP(10);
+ outb(0, reg + M_ADDRH);
+ outw(0, reg + M_ADDR);
+ outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: reset card\n");
+#endif
+
+ /* clear shared memory */
+ outb(0xff, reg + M_ADDRH);
+ outw(0, reg + M_ADDR);
+ for(i = 0; i < 0xffff; i++) outw(0, reg + M_DATA);
+ SLEEP(10);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: clear shared memory\n");
+#endif
+
+ /* download protocol and dsp file */
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+ /* Allocate code-buffer */
+ if (!(code = kmalloc(400, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ /* prepare protocol upload */
+ dl_para.type = DL_PARA_IO_TYPE;
+ dl_para.dat.io.ioADDR = reg + M_ADDR;
+ dl_para.dat.io.ioADDRH = reg + M_ADDRH;
+ dl_para.dat.io.ioDATA = reg + M_DATA;
+
+ for (j = 0; j <= cbuf.dsp_code_num; j++)
+ {
+ if (j == 0) size = cbuf.protocol_len;
+ else size = cbuf.dsp_code_len[j];
+
+ offset = 0;
+
+ if (j == 0) dl_para.dat.io.r3addr = 0;
+ if (j == 1) dl_para.dat.io.r3addr = M_DSP_CODE_BASE +
+ ((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+ if (j == 2) dl_para.dat.io.r3addr = M_DSP_CODE_BASE;
+ if (j == 3) dl_para.dat.io.r3addr = M_DSP_CODE_BASE + sizeof(__u32);
+
+ do /* download block of up to 400 bytes */
+ {
+ length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+ if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ if ((offset == 0) && (j < 2)) {
+ FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80);
+#ifdef EICON_PCI_DEBUG
+ if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%04x.\n", FeatureValue);
+#endif
+ if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+ printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+ kfree(code);
+ return -EFAULT;
+ }
+ ((eicon_card *)card->card)->Feature = FeatureValue;
+ }
+
+ if (eicon_upload(&dl_para, length, code, 1))
+ {
+ printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+ kfree(code);
+ return -EIO;
+ }
+ /* move onto next block */
+ offset += length;
+ dl_para.dat.io.r3addr += length;
+ } while (offset < size);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "Eicon: %d bytes loaded.\n", offset);
+#endif
+ offp += size;
+ }
+ kfree(code);
+
+ /* clear signature */
+ outb(0xff, reg + M_ADDRH);
+ outw(0x1e, reg + M_ADDR);
+ outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+ /* copy configuration data into shared memory */
+ outw(8, reg + M_ADDR); outb(cbuf.tei, reg + M_DATA);
+ outw(9, reg + M_ADDR); outb(cbuf.nt2, reg + M_DATA);
+ outw(10,reg + M_ADDR); outb(0, reg + M_DATA);
+ outw(11,reg + M_ADDR); outb(cbuf.WatchDog, reg + M_DATA);
+ outw(12,reg + M_ADDR); outb(cbuf.Permanent, reg + M_DATA);
+ outw(13,reg + M_ADDR); outb(0, reg + M_DATA); /* XInterface */
+ outw(14,reg + M_ADDR); outb(cbuf.StableL2, reg + M_DATA);
+ outw(15,reg + M_ADDR); outb(cbuf.NoOrderCheck, reg + M_DATA);
+ outw(16,reg + M_ADDR); outb(0, reg + M_DATA); /* HandsetType */
+ outw(17,reg + M_ADDR); outb(0, reg + M_DATA); /* SigFlags */
+ outw(18,reg + M_ADDR); outb(cbuf.LowChannel, reg + M_DATA);
+ outw(19,reg + M_ADDR); outb(cbuf.ProtVersion, reg + M_DATA);
+ outw(20,reg + M_ADDR); outb(cbuf.Crc4, reg + M_DATA);
+ outw(21,reg + M_ADDR); outb((cbuf.Loopback) ? 2:0, reg + M_DATA);
+
+ for (i=0;i<32;i++)
+ {
+ outw( 32+i, reg + M_ADDR); outb(cbuf.l[0].oad[i], reg + M_DATA);
+ outw( 64+i, reg + M_ADDR); outb(cbuf.l[0].osa[i], reg + M_DATA);
+ outw( 96+i, reg + M_ADDR); outb(cbuf.l[0].spid[i], reg + M_DATA);
+ outw(128+i, reg + M_ADDR); outb(cbuf.l[1].oad[i], reg + M_DATA);
+ outw(160+i, reg + M_ADDR); outb(cbuf.l[1].osa[i], reg + M_DATA);
+ outw(192+i, reg + M_ADDR); outb(cbuf.l[1].spid[i], reg + M_DATA);
+ }
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: starting CPU...\n");
+#endif
+ /* let the CPU run */
+ outw(0x08, reg + M_RESET);
+
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ outw(0x1e, reg + M_ADDR);
+ signature = inw(reg + M_DATA);
+ if (signature == DIVAS_SIGNATURE) break;
+ SLEEP(2);
+ }
+ if (signature != DIVAS_SIGNATURE)
+ {
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: signature 0x%x expected 0x%x\n",signature,DIVAS_SIGNATURE);
+#endif
+ printk(KERN_ERR "eicon_pci: Timeout, protocol code not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+ /* get serial number and number of channels supported by card */
+ outb(0xff, reg + M_ADDRH);
+ outw(0x3f6, reg + M_ADDR);
+ card->channels = inw(reg + M_DATA);
+ card->serial = (u32)inw(cfg + 0x22) << 16 | (u32)inw(cfg + 0x26);
+ printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+ printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+ /* test interrupt */
+ card->irqprobe = 1;
+
+ if (!card->ivalid) {
+ if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card))
+ {
+ printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+ return -EIO;
+ }
+ }
+ card->ivalid = 1;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+ /* Trigger an interrupt and check if it is delivered */
+ outb(0x41, cfg + 0x4c); /* enable PLX for interrupts */
+ outb(0x89, reg + M_RESET); /* place int request */
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe != 1) break;
+ SLEEP(5);
+ }
+ if (card->irqprobe == 1) {
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+ printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+ return -EIO;
+ }
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+
+/*
+ * Configure a card, download code into PRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_pri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+ eicon_pci_boot *boot;
+ eicon_pr_ram *prram;
+ int i,j;
+ int timeout;
+ int FeatureValue = 0;
+ unsigned int offset, offp=0, size, length;
+ unsigned long int signature = 0;
+ t_dsp_download_space dl_para;
+ t_dsp_download_desc dsp_download_table;
+ eicon_pci_codebuf cbuf;
+ unsigned char *code;
+ unsigned char req_int;
+ char *ram, *reg, *cfg;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+ return -EFAULT;
+
+ boot = &card->shmem->boot;
+ ram = (char *)card->PCIram;
+ reg = (char *)card->PCIreg;
+ cfg = (char *)card->PCIcfg;
+ prram = (eicon_pr_ram *)ram;
+
+ /* reset board */
+ writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+ SLEEP(20);
+ writeb(0, card->PCIreg + MP_RESET);
+ SLEEP(20);
+
+ /* set command count to 0 */
+ writel(0, &boot->reserved);
+
+ /* check if CPU increments the life word */
+ i = readw(&boot->live);
+ SLEEP(20);
+ if (i == readw(&boot->live)) {
+ printk(KERN_ERR "eicon_pci: card is reset, but CPU not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: reset card OK (CPU running)\n");
+#endif
+
+ /* download firmware : DSP and Protocol */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+ /* Allocate code-buffer */
+ if (!(code = kmalloc(400, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ /* prepare protocol upload */
+ dl_para.type = DL_PARA_MEM_TYPE;
+ dl_para.dat.mem.boot = boot;
+
+ for (j = 0; j <= cbuf.dsp_code_num; j++)
+ {
+ if (j==0) size = cbuf.protocol_len;
+ else size = cbuf.dsp_code_len[j];
+
+ if (j==1) writel(MP_DSP_ADDR, &boot->addr); /* DSP code entry point */
+
+ if (j == 0) dl_para.dat.io.r3addr = MP_PROTOCOL_ADDR;
+ if (j == 1) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE +
+ ((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+ if (j == 2) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE;
+ if (j == 3) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE + sizeof(__u32);
+
+ offset = 0;
+ do /* download block of up to 400 bytes */
+ {
+ length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+ if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ if ((offset == 0) && (j < 2)) {
+ FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80);
+#ifdef EICON_PCI_DEBUG
+ if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%x.\n", FeatureValue);
+#endif
+ if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+ printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+ kfree(code);
+ return -EFAULT;
+ }
+ ((eicon_card *)card->card)->Feature = FeatureValue;
+ }
+
+ if (eicon_upload(&dl_para, length, code, 1))
+ {
+ if (dl_para.dat.mem.timeout == 0)
+ printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+ else
+ printk(KERN_ERR "eicon_pci: timeout, no ACK to load !\n");
+ kfree(code);
+ return -EIO;
+ }
+
+ /* move onto next block */
+ offset += length;
+ dl_para.dat.mem.r3addr += length;
+ } while (offset < size);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: %d bytes loaded.\n", offset);
+#endif
+ offp += size;
+ }
+ kfree(code);
+
+ /* initialize the adapter data structure */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+ /* clear out config space */
+ for (i = 0; i < 256; i++) writeb(0, &ram[i]);
+
+ /* copy configuration down to the card */
+ writeb(cbuf.tei, &ram[8]);
+ writeb(cbuf.nt2, &ram[9]);
+ writeb(0, &ram[10]);
+ writeb(cbuf.WatchDog, &ram[11]);
+ writeb(cbuf.Permanent, &ram[12]);
+ writeb(cbuf.XInterface, &ram[13]);
+ writeb(cbuf.StableL2, &ram[14]);
+ writeb(cbuf.NoOrderCheck, &ram[15]);
+ writeb(cbuf.HandsetType, &ram[16]);
+ writeb(0, &ram[17]);
+ writeb(cbuf.LowChannel, &ram[18]);
+ writeb(cbuf.ProtVersion, &ram[19]);
+ writeb(cbuf.Crc4, &ram[20]);
+ for (i = 0; i < 32; i++)
+ {
+ writeb(cbuf.l[0].oad[i], &ram[32 + i]);
+ writeb(cbuf.l[0].osa[i], &ram[64 + i]);
+ writeb(cbuf.l[0].spid[i], &ram[96 + i]);
+ writeb(cbuf.l[1].oad[i], &ram[128 + i]);
+ writeb(cbuf.l[1].osa[i], &ram[160 + i]);
+ writeb(cbuf.l[1].spid[i], &ram[192 + i]);
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: configured card OK\n");
+#endif
+
+ /* start adapter */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: tell card to start...\n");
+#endif
+ writel(MP_PROTOCOL_ADDR, &boot->addr); /* RISC code entry point */
+ writel(3, &boot->cmd); /* DIVAS_START_CMD */
+
+ /* wait till card ACKs */
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ signature = readl(&boot->signature);
+ if ((signature >> 16) == DIVAS_SIGNATURE) break;
+ SLEEP(2);
+ }
+ if ((signature >> 16) != DIVAS_SIGNATURE)
+ {
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: signature 0x%lx expected 0x%x\n",(signature >> 16),DIVAS_SIGNATURE);
+#endif
+ printk(KERN_ERR "eicon_pci: timeout, protocol code not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+ /* get serial number and number of channels supported by card */
+ card->channels = readb(&ram[0x3f6]);
+ card->serial = readl(&ram[0x3f0]);
+ printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+ printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+ /* test interrupt */
+ readb(&ram[0x3fe]);
+ writeb(0, &ram[0x3fe]); /* reset any pending interrupt */
+ readb(&ram[0x3fe]);
+
+ writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+ writew(0, &cfg[MP_IRQ_RESET + 2]);
+
+ card->irqprobe = 1;
+
+ if (!card->ivalid) {
+ if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card))
+ {
+ printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+ return -EIO;
+ }
+ }
+ card->ivalid = 1;
+
+ req_int = readb(&prram->ReadyInt);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+ req_int++;
+ /* Trigger an interrupt and check if it is delivered */
+ writeb(req_int, &prram->ReadyInt);
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe != 1) break;
+ SLEEP(2);
+ }
+ if (card->irqprobe == 1) {
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+ printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+ return -EIO;
+ }
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+#endif /* CONFIG_PCI */
+
--- /dev/null
+/* $Id: eicon_pci.h,v 1.3 1999/03/29 11:19:51 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards (PCI part).
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_pci.h,v $
+ * Revision 1.3 1999/03/29 11:19:51 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.2 1999/03/02 12:37:50 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.1 1999/01/01 18:09:46 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef eicon_pci_h
+#define eicon_pci_h
+
+#ifdef __KERNEL__
+
+
+#define PCI_VENDOR_EICON 0x1133
+#define PCI_DIVA_PRO20 0xe001 /* Not supported */
+#define PCI_DIVA20 0xe002 /* Not supported */
+#define PCI_DIVA_PRO20_U 0xe003 /* Not supported */
+#define PCI_DIVA20_U 0xe004 /* Not supported */
+#define PCI_MAESTRA 0xe010
+#define PCI_MAESTRAQ 0xe012
+#define PCI_MAESTRAQ_U 0xe013
+#define PCI_MAESTRAP 0xe014
+
+#define DIVA_PRO20 1
+#define DIVA20 2
+#define DIVA_PRO20_U 3
+#define DIVA20_U 4
+#define MAESTRA 5
+#define MAESTRAQ 6
+#define MAESTRAQ_U 7
+#define MAESTRAP 8
+
+#define TRUE 1
+#define FALSE 0
+
+#define DIVAS_SIGNATURE 0x4447
+
+
+/* MAESTRA BRI PCI */
+
+#define M_RESET 0x10 /* offset of reset register */
+#define M_DATA 0x00 /* offset of data register */
+#define M_ADDR 0x04 /* offset of address register */
+#define M_ADDRH 0x0c /* offset of high address register */
+
+#define M_DSP_CODE_LEN 0xbf7d0000
+#define M_DSP_CODE 0xbf7d0004 /* max 128K DSP-Code */
+#define M_DSP_CODE_BASE 0xbf7a0000
+#define M_MAX_DSP_CODE_SIZE 0x00050000 /* max 320K DSP-Code (Telindus) */
+
+
+
+/* MAESTRA PRI PCI */
+
+#define MP_SHARED_RAM_OFFSET 0x1000 /* offset of shared RAM base in the DRAM memory bar */
+
+#define MP_IRQ_RESET 0xc18 /* offset of interrupt status register in the CONFIG memory bar */
+#define MP_IRQ_RESET_VAL 0xfe /* value to clear an interrupt */
+
+#define MP_PROTOCOL_ADDR 0xa0011000 /* load address of protocol code */
+#define MP_DSP_ADDR 0xa03c0000 /* load address of DSP code */
+#define MP_MAX_PROTOCOL_CODE_SIZE 0x000a0000 /* max 640K Protocol-Code */
+#define MP_DSP_CODE_BASE 0xa03a0000
+#define MP_MAX_DSP_CODE_SIZE 0x00060000 /* max 384K DSP-Code */
+
+#define MP_RESET 0x20 /* offset of RESET register in the DEVICES memory bar */
+
+/* RESET register bits */
+#define _MP_S2M_RESET 0x10 /* active lo */
+#define _MP_LED2 0x08 /* 1 = on */
+#define _MP_LED1 0x04 /* 1 = on */
+#define _MP_DSP_RESET 0x02 /* active lo */
+#define _MP_RISC_RESET 0x81 /* active hi, bit 7 for compatibility with old boards */
+
+/* boot interface structure */
+typedef struct {
+ __u32 cmd __attribute__ ((packed));
+ __u32 addr __attribute__ ((packed));
+ __u32 len __attribute__ ((packed));
+ __u32 err __attribute__ ((packed));
+ __u32 live __attribute__ ((packed));
+ __u32 reserved[(0x1020>>2)-6] __attribute__ ((packed));
+ __u32 signature __attribute__ ((packed));
+ __u8 data[1]; /* real interface description */
+} eicon_pci_boot;
+
+
+#define DL_PARA_IO_TYPE 0
+#define DL_PARA_MEM_TYPE 1
+
+typedef struct tag_dsp_download_space
+{
+ __u16 type; /* see definitions above to differ union elements */
+ union
+ {
+ struct
+ {
+ __u32 r3addr;
+ __u16 ioADDR;
+ __u16 ioADDRH;
+ __u16 ioDATA;
+ __u16 BadData; /* in case of verify error */
+ __u16 GoodData;
+ } io; /* for io based adapters */
+ struct
+ {
+ __u32 r3addr;
+ eicon_pci_boot *boot;
+ __u32 BadData; /* in case of verify error */
+ __u32 GoodData;
+ __u16 timeout;
+ } mem; /* for memory based adapters */
+ } dat;
+} t_dsp_download_space;
+
+
+/* Shared memory */
+typedef union {
+ eicon_pci_boot boot;
+} eicon_pci_shmem;
+
+/*
+ * card's description
+ */
+typedef struct {
+ int ramsize;
+ int irq; /* IRQ */
+ unsigned int PCIram;
+ unsigned int PCIreg;
+ unsigned int PCIcfg;
+ long int serial; /* Serial No. */
+ int channels; /* No. of supported channels */
+ void* card;
+ eicon_pci_shmem* shmem; /* Shared-memory area */
+ unsigned char* intack; /* Int-Acknowledge */
+ unsigned char* stopcpu; /* Writing here stops CPU */
+ unsigned char* startcpu; /* Writing here starts CPU */
+ unsigned char type; /* card type */
+ unsigned char irqprobe; /* Flag: IRQ-probing */
+ unsigned char mvalid; /* Flag: Memory is valid */
+ unsigned char ivalid; /* Flag: IRQ is valid */
+ unsigned char master; /* Flag: Card is Quadro 1/4 */
+ void* generic; /* Ptr to generic card struct */
+} eicon_pci_card;
+
+
+
+extern int eicon_pci_load_pri(eicon_pci_card *card, eicon_pci_codebuf *cb);
+extern int eicon_pci_load_bri(eicon_pci_card *card, eicon_pci_codebuf *cb);
+extern void eicon_pci_release(eicon_pci_card *card);
+extern void eicon_pci_printpar(eicon_pci_card *card);
+extern int eicon_pci_find_card(char *ID);
+
+#endif /* __KERNEL__ */
+
+#endif /* eicon_pci_h */
O_TARGET :=
O_OBJS := isdnl1.o tei.o isdnl2.o isdnl3.o \
- lmgr.o q931.o callc.o fsm.o
+ lmgr.o q931.o callc.o fsm.o cert.o
# EXTRA_CFLAGS += -S
ISAC_OBJ :=
ARCOFI_OBJ :=
HSCX_OBJ :=
+ISAR_OBJ :=
HFC_OBJ :=
HFC_2BDS0 :=
RAWHDLC_OBJ :=
HSCX_OBJ := hscx.o
endif
+ifeq ($(CONFIG_HISAX_TELESPCI),y)
+ O_OBJS += telespci.o
+ ISAC_OBJ := isac.o
+ HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_S0BOX),y)
+ O_OBJS += s0box.o
+ ISAC_OBJ := isac.o
+ HSCX_OBJ := hscx.o
+endif
+
ifeq ($(CONFIG_HISAX_AVM_A1),y)
O_OBJS += avm_a1.o
ISAC_OBJ := isac.o
HSCX_OBJ := hscx.o
endif
+ifeq ($(CONFIG_HISAX_AVM_A1_PCMCIA),y)
+ O_OBJS += avm_a1p.o
+ ISAC_OBJ := isac.o
+ HSCX_OBJ := hscx.o
+endif
+
+ifeq ($(CONFIG_HISAX_FRITZPCI),y)
+ O_OBJS += avm_pci.o
+ ISAC_OBJ := isac.o
+endif
+
+
ifeq ($(CONFIG_HISAX_ELSA),y)
O_OBJS += elsa.o
ISAC_OBJ := isac.o
O_OBJS += sedlbauer.o
ISAC_OBJ := isac.o
HSCX_OBJ := hscx.o
+ ISAR_OBJ := isar.o
endif
ifeq ($(CONFIG_HISAX_SPORTSTER),y)
ifeq ($(CONFIG_HISAX_NETJET),y)
O_OBJS += netjet.o
ISAC_OBJ := isac.o
+# RAWHDLC_OBJ := rawhdlc.o
endif
ifeq ($(CONFIG_HISAX_TELES3C),y)
HFC_2BDS0 := hfc_2bds0.o
endif
ifeq ($(CONFIG_HISAX_AMD7930),y)
- RAWHDLC_OBJ := foreign.o rawhdlc.o
-endif
-ifeq ($(CONFIG_HISAX_DBRI),y)
- RAWHDLC_OBJ := foreign.o rawhdlc.o
+ O_OBJS += amd7930.o
+ RAWHDLC_OBJ := rawhdlc.o
endif
ifeq ($(CONFIG_HISAX_NICCY),y)
HSCX_OBJ := hscx.o
endif
-O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(HFC_OBJ) $(ARCOFI_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ)
+O_OBJS += $(ISAC_OBJ) $(HSCX_OBJ) $(ISAR_OBJ) $(ARCOFI_OBJ)
+O_OBJS += $(HFC_OBJ) $(HFC_2BDS0) $(RAWHDLC_OBJ)
OX_OBJS += config.o
O_TARGET :=
endif
endif
+
include $(TOPDIR)/Rules.make
+
+MD5FILES += isac.c isdnl1.c isdnl2.c isdnl3.c \
+ tei.c callc.c cert.c l3dss1.c l3_1tr6.c elsa.c
+
+CERT = $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
+
+cert.o: $(MD5FILES) md5sums.asc
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -D CERTIFICATION=$(CERT) -c -o cert.o cert.c
+
-/* $Id: arcofi.c,v 1.1 1997/10/29 18:51:20 keil Exp $
+/* $Id: arcofi.c,v 1.6 1998/09/30 22:21:56 keil Exp $
- * arcofi.h Ansteuerung ARCOFI 2165
+ * arcofi.c Ansteuerung ARCOFI 2165
*
* Author Karsten Keil (keil@temic-ech.spacenet.de)
*
*
*
* $Log: arcofi.c,v $
+ * Revision 1.6 1998/09/30 22:21:56 keil
+ * cosmetics
+ *
+ * Revision 1.5 1998/09/27 12:52:57 keil
+ * cosmetics
+ *
+ * Revision 1.4 1998/08/20 13:50:24 keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 1.3 1998/05/25 12:57:38 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.2 1998/04/15 16:47:16 keil
+ * new interface
+ *
* Revision 1.1 1997/10/29 18:51:20 keil
* New files
*
#include "isac.h"
int
-send_arcofi(struct IsdnCardState *cs, const u_char *msg) {
+send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive) {
u_char val;
- char tmp[32];
long flags;
- int cnt=2;
+ int cnt=30;
cs->mon_txp = 0;
cs->mon_txc = msg[0];
memcpy(cs->mon_tx, &msg[1], cs->mon_txc);
+ switch(bc) {
+ case 0: break;
+ case 1: cs->mon_tx[1] |= 0x40;
+ break;
+ default: break;
+ }
cs->mocr &= 0x0f;
cs->mocr |= 0xa0;
test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags);
+ if (receive)
+ test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags);
cs->writeisac(cs, ISAC_MOCR, cs->mocr);
val = cs->readisac(cs, ISAC_MOSR);
cs->writeisac(cs, ISAC_MOX1, cs->mon_tx[cs->mon_txp++]);
sti();
while (cnt && !test_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
cnt--;
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ udelay(500);
+ }
+ if (receive) {
+ while (cnt && !test_bit(HW_MON1_RX_END, &cs->HW_Flags)) {
+ cnt--;
+ udelay(500);
+ }
}
restore_flags(flags);
- sprintf(tmp, "arcofi tout %d", cnt);
- debugl1(cs, tmp);
+ if (cnt <= 0) {
+ printk(KERN_WARNING"HiSax arcofi monitor timed out\n");
+ debugl1(cs, "HiSax arcofi monitor timed out");
+ }
return(cnt);
}
-
-/* $Id: arcofi.h,v 1.1 1997/10/29 18:51:20 keil Exp $
+/* $Id: arcofi.h,v 1.3 1998/05/25 12:57:39 keil Exp $
* arcofi.h Ansteuerung ARCOFI 2165
*
*
*
* $Log: arcofi.h,v $
+ * Revision 1.3 1998/05/25 12:57:39 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.2 1998/04/15 16:47:17 keil
+ * new interface
+ *
* Revision 1.1 1997/10/29 18:51:20 keil
* New files
*
#define ARCOFI_USE 1
-extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg);
+extern int send_arcofi(struct IsdnCardState *cs, const u_char *msg, int bc, int receive);
-/* $Id: asuscom.c,v 1.2 1998/02/02 13:27:06 keil Exp $
+/* $Id: asuscom.c,v 1.5 1998/11/15 23:54:19 keil Exp $
* asuscom.c low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
* Thanks to ASUSCOM NETWORK INC. Taiwan and Dynalink NL for informations
*
*
* $Log: asuscom.c,v $
+ * Revision 1.5 1998/11/15 23:54:19 keil
+ * changes from 2.0
+ *
+ * Revision 1.4 1998/06/18 23:18:20 keil
+ * Support for new IPAC card
+ *
+ * Revision 1.3 1998/04/15 16:46:53 keil
+ * new init code
+ *
* Revision 1.2 1998/02/02 13:27:06 keil
* New
*
#define __NO_VERSION__
#include "hisax.h"
#include "isac.h"
+#include "ipac.h"
#include "hscx.h"
#include "isdnl1.h"
extern const char *CardType[];
-const char *Asuscom_revision = "$Revision: 1.2 $";
+const char *Asuscom_revision = "$Revision: 1.5 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
#define ASUS_CTRL_U7 3
#define ASUS_CTRL_POTS 5
+#define ASUS_IPAC_ALE 0
+#define ASUS_IPAC_DATA 1
+
+#define ASUS_ISACHSCX 1
+#define ASUS_IPAC 2
+
/* CARD_ADR (Write) */
#define ASUS_RESET 0x80 /* Bit 7 Reset-Leitung */
writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
}
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
static u_char
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
{
}
}
+static void
+asuscom_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista, val, icnt = 20;
+
+ if (!cs) {
+ printk(KERN_WARNING "ISDNLink: Spurious interrupt!\n");
+ return;
+ }
+ ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+Start_IPAC:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPAC;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "ASUS IRQ LOOP\n");
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0);
+}
+
void
release_io_asuscom(struct IsdnCardState *cs)
{
{
long flags;
- byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */
+ if (cs->subtyp == ASUS_IPAC)
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20);
+ else
+ byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */
save_flags(flags);
sti();
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
- byteout(cs->hw.asus.adr, 0); /* Reset Off */
+ if (cs->subtyp == ASUS_IPAC)
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
+ else
+ byteout(cs->hw.asus.adr, 0); /* Reset Off */
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(1);
+ if (cs->subtyp == ASUS_IPAC) {
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12);
+ }
restore_flags(flags);
}
release_io_asuscom(cs);
return(0);
case CARD_SETIRQ:
- return(request_irq(cs->irq, &asuscom_interrupt,
+ if (cs->subtyp == ASUS_IPAC)
+ return(request_irq(cs->irq, &asuscom_interrupt_ipac,
+ I4L_IRQ_FLAG, "HiSax", cs));
+ else
+ return(request_irq(cs->irq, &asuscom_interrupt,
I4L_IRQ_FLAG, "HiSax", cs));
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ cs->debug |= L1_DEB_IPAC;
+ inithscxisac(cs, 3);
return(0);
case CARD_TEST:
return(0);
{
int bytecnt;
struct IsdnCardState *cs = card->cs;
+ u_char val;
char tmp[64];
strcpy(tmp, Asuscom_revision);
bytecnt = 8;
cs->hw.asus.cfg_reg = card->para[1];
cs->irq = card->para[0];
- cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
- cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
- cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
- cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
- cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
-
if (check_region((cs->hw.asus.cfg_reg), bytecnt)) {
printk(KERN_WARNING
"HiSax: %s config port %x-%x already in use\n",
} else {
request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn");
}
-
- printk(KERN_INFO
- "ISDNLink: defined at 0x%x IRQ %d\n",
- cs->hw.asus.cfg_reg,
- cs->irq);
- printk(KERN_INFO "ISDNLink: resetting card\n");
- reset_asuscom(cs);
- cs->readisac = &ReadISAC;
- cs->writeisac = &WriteISAC;
- cs->readisacfifo = &ReadISACfifo;
- cs->writeisacfifo = &WriteISACfifo;
+ printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
+ cs->hw.asus.cfg_reg, cs->irq);
cs->BC_Read_Reg = &ReadHSCX;
cs->BC_Write_Reg = &WriteHSCX;
cs->BC_Send_Data = &hscx_fill_fifo;
cs->cardmsg = &Asus_card_msg;
- ISACVersion(cs, "ISDNLink:");
- if (HscxVersion(cs, "ISDNLink:")) {
- printk(KERN_WARNING
- "ISDNLink: wrong HSCX versions check IO address\n");
- release_io_asuscom(cs);
- return (0);
+ val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE,
+ cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
+ if (val == 1) {
+ cs->subtyp = ASUS_IPAC;
+ cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE;
+ cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+ cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ cs->readisac = &ReadISAC_IPAC;
+ cs->writeisac = &WriteISAC_IPAC;
+ cs->readisacfifo = &ReadISACfifo_IPAC;
+ cs->writeisacfifo = &WriteISACfifo_IPAC;
+ printk(KERN_INFO "Asus: IPAC version %x\n", val);
+ } else {
+ cs->subtyp = ASUS_ISACHSCX;
+ cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
+ cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
+ cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
+ cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
+ cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ ISACVersion(cs, "ISDNLink:");
+ if (HscxVersion(cs, "ISDNLink:")) {
+ printk(KERN_WARNING
+ "ISDNLink: wrong HSCX versions check IO address\n");
+ release_io_asuscom(cs);
+ return (0);
+ }
}
+ printk(KERN_INFO "ISDNLink: resetting card\n");
+ reset_asuscom(cs);
return (1);
}
-/* $Id: avm_a1.c,v 2.7 1998/02/02 13:29:37 keil Exp $
+/* $Id: avm_a1.c,v 2.10 1998/11/15 23:54:21 keil Exp $
* avm_a1.c low level stuff for AVM A1 (Fritz) isdn cards
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
*
* $Log: avm_a1.c,v $
+ * Revision 2.10 1998/11/15 23:54:21 keil
+ * changes from 2.0
+ *
+ * Revision 2.9 1998/08/13 23:36:12 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.8 1998/04/15 16:44:27 keil
+ * new init code
+ *
* Revision 2.7 1998/02/02 13:29:37 keil
* fast io
*
#include "isdnl1.h"
extern const char *CardType[];
-const char *avm_revision = "$Revision: 2.7 $";
+static const char *avm_revision = "$Revision: 2.10 $";
#define AVM_A1_STAT_ISAC 0x01
#define AVM_A1_STAT_HSCX 0x02
{
struct IsdnCardState *cs = dev_id;
u_char val, sval, stat = 0;
- char tmp[32];
if (!cs) {
printk(KERN_WARNING "AVM A1: Spurious interrupt!\n");
if (!(sval & AVM_A1_STAT_TIMER)) {
byteout(cs->hw.avm.cfg_reg, 0x1E);
sval = bytein(cs->hw.avm.cfg_reg);
- } else if (cs->debug & L1_DEB_INTSTAT) {
- sprintf(tmp, "avm IntStatus %x", sval);
- debugl1(cs, tmp);
- }
+ } else if (cs->debug & L1_DEB_INTSTAT)
+ debugl1(cs, "avm IntStatus %x", sval);
if (!(sval & AVM_A1_STAT_HSCX)) {
val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
if (val) {
return(request_irq(cs->irq, &avm_a1_interrupt,
I4L_IRQ_FLAG, "HiSax", cs));
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ inithscxisac(cs, 1);
+ byteout(cs->hw.avm.cfg_reg, 0x16);
+ byteout(cs->hw.avm.cfg_reg, 0x1E);
+ inithscxisac(cs, 2);
return(0);
case CARD_TEST:
return(0);
val = bytein(cs->hw.avm.cfg_reg + 2);
printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
cs->hw.avm.cfg_reg + 2, val);
- byteout(cs->hw.avm.cfg_reg, 0x1E);
val = bytein(cs->hw.avm.cfg_reg);
printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
cs->hw.avm.cfg_reg, val);
--- /dev/null
+/* $Id: avm_a1p.c,v 2.3 1998/11/15 23:54:22 keil Exp $
+ *
+ * avm_a1p.c low level stuff for the following AVM cards:
+ * A1 PCMCIA
+ * FRITZ!Card PCMCIA
+ * FRITZ!Card PCMCIA 2.0
+ *
+ * Author Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: avm_a1p.c,v $
+ * Revision 2.3 1998/11/15 23:54:22 keil
+ * changes from 2.0
+ *
+ * Revision 2.2 1998/08/13 23:36:13 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.1 1998/07/15 15:01:23 calle
+ * Support for AVM passive PCMCIA cards:
+ * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 1.1.2.1 1998/07/15 14:43:26 calle
+ * Support for AVM passive PCMCIA cards:
+ * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+/* register offsets */
+#define ADDRREG_OFFSET 0x02
+#define DATAREG_OFFSET 0x03
+#define ASL0_OFFSET 0x04
+#define ASL1_OFFSET 0x05
+#define MODREG_OFFSET 0x06
+#define VERREG_OFFSET 0x07
+
+/* address offsets */
+#define ISAC_FIFO_OFFSET 0x00
+#define ISAC_REG_OFFSET 0x20
+#define HSCX_CH_DIFF 0x40
+#define HSCX_FIFO_OFFSET 0x80
+#define HSCX_REG_OFFSET 0xa0
+
+/* read bits ASL0 */
+#define ASL0_R_TIMER 0x10 /* active low */
+#define ASL0_R_ISAC 0x20 /* active low */
+#define ASL0_R_HSCX 0x40 /* active low */
+#define ASL0_R_TESTBIT 0x80
+#define ASL0_R_IRQPENDING (ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER)
+
+/* write bits ASL0 */
+#define ASL0_W_RESET 0x01
+#define ASL0_W_TDISABLE 0x02
+#define ASL0_W_TRESET 0x04
+#define ASL0_W_IRQENABLE 0x08
+#define ASL0_W_TESTBIT 0x80
+
+/* write bits ASL1 */
+#define ASL1_W_LED0 0x10
+#define ASL1_W_LED1 0x20
+#define ASL1_W_ENABLE_S0 0xC0
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static const char *avm_revision = "$Revision: 2.3 $";
+
+static inline u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ long flags;
+ u_char ret;
+
+ offset -= 0x20;
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+ ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+ restore_flags(flags);
+ return ret;
+}
+
+static inline void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ long flags;
+
+ offset -= 0x20;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+ byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+ restore_flags(flags);
+}
+
+static inline void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+ insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+static inline void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+ outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+static inline u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ u_char ret;
+ long flags;
+
+ offset -= 0x20;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+ ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+ restore_flags(flags);
+ return ret;
+}
+
+static inline void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ long flags;
+
+ offset -= 0x20;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+ byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+ restore_flags(flags);
+}
+
+static inline void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+ insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+static inline void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+ outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, sval, stat = 0;
+
+ if (!cs) {
+ printk(KERN_WARNING "AVM A1 PCMCIA: Spurious interrupt!\n");
+ return;
+ }
+ while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
+ if (cs->debug & L1_DEB_INTSTAT)
+ debugl1(cs, "avm IntStatus %x", sval);
+ if (sval & ASL0_R_HSCX) {
+ val = ReadHSCX(cs, 1, HSCX_ISTA);
+ if (val) {
+ hscx_int_main(cs, val);
+ stat |= 1;
+ }
+ }
+ if (sval & ASL0_R_ISAC) {
+ val = ReadISAC(cs, ISAC_ISTA);
+ if (val) {
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ }
+ }
+ if (stat & 1) {
+ WriteHSCX(cs, 0, HSCX_MASK, 0xff);
+ WriteHSCX(cs, 1, HSCX_MASK, 0xff);
+ WriteHSCX(cs, 0, HSCX_MASK, 0x00);
+ WriteHSCX(cs, 1, HSCX_MASK, 0x00);
+ }
+ if (stat & 2) {
+ WriteISAC(cs, ISAC_MASK, 0xff);
+ WriteISAC(cs, ISAC_MASK, 0x00);
+ }
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ int ret;
+ switch (mt) {
+ case CARD_RESET:
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ return 0;
+
+ case CARD_RELEASE:
+ /* free_irq is done in HiSax_closecard(). */
+ /* free_irq(cs->irq, cs); */
+ return 0;
+
+ case CARD_SETIRQ:
+ ret = request_irq(cs->irq, &avm_a1p_interrupt,
+ I4L_IRQ_FLAG, "HiSax", cs);
+ if (ret)
+ return ret;
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,
+ ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE);
+ return 0;
+
+ case CARD_INIT:
+ clear_pending_isac_ints(cs);
+ clear_pending_hscx_ints(cs);
+ inithscxisac(cs, 1);
+ inithscxisac(cs, 2);
+ return 0;
+
+ case CARD_TEST:
+ /* we really don't need it for the PCMCIA Version */
+ return 0;
+
+ default:
+ /* all card drivers ignore others, so we do the same */
+ return 0;
+ }
+ return 0;
+}
+
+__initfunc(int
+setup_avm_a1_pcmcia(struct IsdnCard *card))
+{
+ u_char model, vers;
+ struct IsdnCardState *cs = card->cs;
+ long flags;
+ char tmp[64];
+
+
+ strcpy(tmp, avm_revision);
+ printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
+ HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
+ return (0);
+
+ cs->hw.avm.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+
+
+ save_flags(flags);
+ outb(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0);
+ sti();
+
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET);
+
+ restore_flags(flags);
+
+ model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET);
+ vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET);
+
+ printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
+ cs->hw.avm.cfg_reg, cs->irq, model, vers);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &AVM_card_msg;
+
+ ISACVersion(cs, "AVM A1 PCMCIA:");
+ if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
+ printk(KERN_WARNING
+ "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
+ return (0);
+ }
+ return (1);
+}
--- /dev/null
+/* $Id: avm_pci.c,v 1.7 1999/02/22 18:26:30 keil Exp $
+
+ * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
+ * Thanks to AVM, Berlin for informations
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: avm_pci.c,v $
+ * Revision 1.7 1999/02/22 18:26:30 keil
+ * Argh ! ISAC address was only set with PCI
+ *
+ * Revision 1.6 1998/11/27 19:59:28 keil
+ * set subtype for Fritz!PCI
+ *
+ * Revision 1.5 1998/11/27 12:56:45 keil
+ * forgot to update setup function name
+ *
+ * Revision 1.4 1998/11/15 23:53:19 keil
+ * Fritz!PnP; changes from 2.0
+ *
+ * Revision 1.3 1998/09/27 23:53:39 keil
+ * Fix error handling
+ *
+ * Revision 1.2 1998/09/27 12:54:55 keil
+ * bcs assign was lost in setstack, very bad results
+ *
+ * Revision 1.1 1998/08/20 13:47:30 keil
+ * first version
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+static const char *avm_pci_rev = "$Revision: 1.7 $";
+
+#define AVM_FRITZ_PCI 1
+#define AVM_FRITZ_PNP 2
+
+#define PCI_VENDOR_AVM 0x1244
+#define PCI_FRITZPCI_ID 0xa00
+
+#define HDLC_FIFO 0x0
+#define HDLC_STATUS 0x4
+
+#define AVM_HDLC_1 0x00
+#define AVM_HDLC_2 0x01
+#define AVM_ISAC_FIFO 0x02
+#define AVM_ISAC_REG_LOW 0x04
+#define AVM_ISAC_REG_HIGH 0x06
+
+#define AVM_STATUS0_IRQ_ISAC 0x01
+#define AVM_STATUS0_IRQ_HDLC 0x02
+#define AVM_STATUS0_IRQ_TIMER 0x04
+#define AVM_STATUS0_IRQ_MASK 0x07
+
+#define AVM_STATUS0_RESET 0x01
+#define AVM_STATUS0_DIS_TIMER 0x02
+#define AVM_STATUS0_RES_TIMER 0x04
+#define AVM_STATUS0_ENA_IRQ 0x08
+#define AVM_STATUS0_TESTBIT 0x10
+
+#define AVM_STATUS1_INT_SEL 0x0f
+#define AVM_STATUS1_ENA_IOM 0x80
+
+#define HDLC_MODE_ITF_FLG 0x01
+#define HDLC_MODE_TRANS 0x02
+#define HDLC_MODE_CCR_7 0x04
+#define HDLC_MODE_CCR_16 0x08
+#define HDLC_MODE_TESTLOOP 0x80
+
+#define HDLC_INT_XPR 0x80
+#define HDLC_INT_XDU 0x40
+#define HDLC_INT_RPR 0x20
+#define HDLC_INT_MASK 0xE0
+
+#define HDLC_STAT_RME 0x01
+#define HDLC_STAT_RDO 0x10
+#define HDLC_STAT_CRCVFRRAB 0x0E
+#define HDLC_STAT_CRCVFR 0x06
+#define HDLC_STAT_RML_MASK 0x3f00
+
+#define HDLC_CMD_XRS 0x80
+#define HDLC_CMD_XME 0x01
+#define HDLC_CMD_RRS 0x20
+#define HDLC_CMD_XML_MASK 0x3f00
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+ register u_char val;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ val = inb(cs->hw.avm.isac + (offset & 0xf));
+ restore_flags(flags);
+ return (val);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ outb(value, cs->hw.avm.isac + (offset & 0xf));
+ restore_flags(flags);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+ insb(cs->hw.avm.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+ outsb(cs->hw.avm.isac, data, size);
+}
+
+static inline u_int
+ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register u_int val;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ val = inl(cs->hw.avm.isac + offset);
+ restore_flags(flags);
+ return (val);
+}
+
+static inline void
+WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value)
+{
+ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ outl(value, cs->hw.avm.isac + offset);
+ restore_flags(flags);
+}
+
+static inline u_char
+ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register u_char val;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ val = inb(cs->hw.avm.isac + offset);
+ restore_flags(flags);
+ return (val);
+}
+
+static inline void
+WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ outb(value, cs->hw.avm.isac + offset);
+ restore_flags(flags);
+}
+
+static u_char
+ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ return(0xff & ReadHDLCPCI(cs, chan, offset));
+}
+
+static void
+WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+ WriteHDLCPCI(cs, chan, offset, value);
+}
+
+static inline
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+ if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+ return(&cs->bcs[0]);
+ else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+ return(&cs->bcs[1]);
+ else
+ return(NULL);
+}
+
+void inline
+hdlc_sched_event(struct BCState *bcs, int event)
+{
+ bcs->event |= 1 << event;
+ queue_task(&bcs->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+void
+write_ctrl(struct BCState *bcs, int which) {
+
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "hdlc %c wr%x ctrl %x",
+ 'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl);
+ if (bcs->cs->subtyp == AVM_FRITZ_PCI) {
+ WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl);
+ } else {
+ if (which & 4)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2,
+ bcs->hw.hdlc.ctrl.sr.mode);
+ if (which & 2)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1,
+ bcs->hw.hdlc.ctrl.sr.xml);
+ if (which & 1)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS,
+ bcs->hw.hdlc.ctrl.sr.cmd);
+ }
+}
+
+void
+modehdlc(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int hdlc = bcs->channel;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hdlc %c mode %d ichan %d",
+ 'A' + hdlc, mode, bc);
+ bcs->mode = mode;
+ bcs->channel = bc;
+ bcs->hw.hdlc.ctrl.ctrl = 0;
+ switch (mode) {
+ case (L1_MODE_NULL):
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bcs, 5);
+ break;
+ case (L1_MODE_TRANS):
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bcs, 5);
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd = 0;
+ hdlc_sched_event(bcs, B_XMTBUFREADY);
+ break;
+ case (L1_MODE_HDLC):
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+ write_ctrl(bcs, 5);
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd = 0;
+ hdlc_sched_event(bcs, B_XMTBUFREADY);
+ break;
+ }
+}
+
+static inline void
+hdlc_empty_fifo(struct BCState *bcs, int count)
+{
+ register u_int *ptr;
+ u_char *p;
+ u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1;
+ int cnt=0;
+ struct IsdnCardState *cs = bcs->cs;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_empty_fifo %d", count);
+ if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hdlc_empty_fifo: incoming packet too large");
+ return;
+ }
+ ptr = (u_int *) p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
+ bcs->hw.hdlc.rcvidx += count;
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ while (cnt < count) {
+ *ptr++ = inl(cs->hw.avm.isac);
+ cnt += 4;
+ }
+ } else {
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ while (cnt < count) {
+ *p++ = inb(cs->hw.avm.isac);
+ cnt++;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ if (cs->subtyp == AVM_FRITZ_PNP)
+ p = (u_char *) ptr;
+ t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
+ bcs->channel ? 'B' : 'A', count);
+ QuickHex(t, p, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static inline void
+hdlc_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int count, cnt =0;
+ int fifo_size = 32;
+ u_char *p;
+ u_int *ptr;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_fill_fifo");
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME;
+ if (bcs->tx_skb->len > fifo_size) {
+ count = fifo_size;
+ } else {
+ count = bcs->tx_skb->len;
+ if (bcs->mode != L1_MODE_TRANS)
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME;
+ }
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len);
+ ptr = (u_int *) p = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.hdlc.count += count;
+ bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+ write_ctrl(bcs, 3); /* sets the correct index too */
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ while (cnt<count) {
+ outl(*ptr++, cs->hw.avm.isac);
+ cnt += 4;
+ }
+ } else {
+ while (cnt<count) {
+ outb(*p++, cs->hw.avm.isac);
+ cnt++;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ if (cs->subtyp == AVM_FRITZ_PNP)
+ p = (u_char *) ptr;
+ t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
+ bcs->channel ? 'B' : 'A', count);
+ QuickHex(t, p, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+fill_hdlc(struct BCState *bcs)
+{
+ long flags;
+ save_flags(flags);
+ cli();
+ hdlc_fill_fifo(bcs);
+ restore_flags(flags);
+}
+
+static inline void
+HDLC_irq(struct BCState *bcs, u_int stat) {
+ int len;
+ struct sk_buff *skb;
+
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+ if (stat & HDLC_INT_RPR) {
+ if (stat & HDLC_STAT_RDO) {
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "RDO");
+ else
+ debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+ bcs->hw.hdlc.ctrl.sr.xml = 0;
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.rcvidx = 0;
+ } else {
+ if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
+ len = 32;
+ hdlc_empty_fifo(bcs, len);
+ if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+ if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
+ (bcs->mode == L1_MODE_TRANS)) {
+ if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx)))
+ printk(KERN_WARNING "HDLC: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx),
+ bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.hdlc.rcvidx = 0;
+ hdlc_sched_event(bcs, B_RCVBUFREADY);
+ } else {
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "invalid frame");
+ else
+ debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat);
+ bcs->hw.hdlc.rcvidx = 0;
+ }
+ }
+ }
+ }
+ if (stat & HDLC_INT_XDU) {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hdlc.count);
+ bcs->tx_cnt += bcs->hw.hdlc.count;
+ bcs->hw.hdlc.count = 0;
+// hdlc_sched_event(bcs, B_XMTBUFREADY);
+ if (bcs->cs->debug & L1_DEB_WARN)
+ debugl1(bcs->cs, "ch%d XDU", bcs->channel);
+ } else if (bcs->cs->debug & L1_DEB_WARN)
+ debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel);
+ bcs->hw.hdlc.ctrl.sr.xml = 0;
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ hdlc_fill_fifo(bcs);
+ } else if (stat & HDLC_INT_XPR) {
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ hdlc_fill_fifo(bcs);
+ return;
+ } else {
+ if (bcs->st->lli.l1writewakeup &&
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hdlc.count);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->hw.hdlc.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hdlc.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ hdlc_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ hdlc_sched_event(bcs, B_XMTBUFREADY);
+ }
+ }
+}
+
+inline void
+HDLC_irq_main(struct IsdnCardState *cs)
+{
+ u_int stat;
+ long flags;
+ struct BCState *bcs;
+
+ save_flags(flags);
+ cli();
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ stat = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+ } else {
+ stat = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+ if (stat & HDLC_INT_RPR)
+ stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8;
+ }
+ if (stat & HDLC_INT_MASK) {
+ if (!(bcs = Sel_BCS(cs, 0))) {
+ if (cs->debug)
+ debugl1(cs, "hdlc spurious channel 0 IRQ");
+ } else
+ HDLC_irq(bcs, stat);
+ }
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ stat = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+ } else {
+ stat = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+ if (stat & HDLC_INT_RPR)
+ stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8;
+ }
+ if (stat & HDLC_INT_MASK) {
+ if (!(bcs = Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hdlc spurious channel 1 IRQ");
+ } else
+ HDLC_irq(bcs, stat);
+ }
+ restore_flags(flags);
+}
+
+void
+hdlc_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ long flags;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ save_flags(flags);
+ cli();
+ if (st->l1.bcs->tx_skb) {
+ skb_queue_tail(&st->l1.bcs->squeue, skb);
+ restore_flags(flags);
+ } else {
+ st->l1.bcs->tx_skb = skb;
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ st->l1.bcs->hw.hdlc.count = 0;
+ restore_flags(flags);
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ }
+ break;
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
+ printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n");
+ break;
+ }
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ st->l1.bcs->tx_skb = skb;
+ st->l1.bcs->hw.hdlc.count = 0;
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ break;
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
+ test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ } else
+ test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ modehdlc(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ modehdlc(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+void
+close_hdlcstate(struct BCState *bcs)
+{
+ modehdlc(bcs, 0, 0);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (bcs->hw.hdlc.rcvbuf) {
+ kfree(bcs->hw.hdlc.rcvbuf);
+ bcs->hw.hdlc.rcvbuf = NULL;
+ }
+ if (bcs->blog) {
+ kfree(bcs->blog);
+ bcs->blog = NULL;
+ }
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ }
+ }
+}
+
+int
+open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for hdlc.rcvbuf\n");
+ return (1);
+ }
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for bcs->blog\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.hdlc.rcvbuf);
+ bcs->hw.hdlc.rcvbuf = NULL;
+ return (2);
+ }
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->event = 0;
+ bcs->hw.hdlc.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+int
+setstack_hdlc(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hdlcstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hdlc_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_hdlc_ints(struct IsdnCardState *cs))
+{
+ u_int val;
+
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ val = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+ debugl1(cs, "HDLC 1 STA %x", val);
+ val = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+ debugl1(cs, "HDLC 2 STA %x", val);
+ } else {
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+ debugl1(cs, "HDLC 1 STA %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1);
+ debugl1(cs, "HDLC 1 RML %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2);
+ debugl1(cs, "HDLC 1 MODE %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3);
+ debugl1(cs, "HDLC 1 VIN %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+ debugl1(cs, "HDLC 2 STA %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1);
+ debugl1(cs, "HDLC 2 RML %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2);
+ debugl1(cs, "HDLC 2 MODE %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3);
+ debugl1(cs, "HDLC 2 VIN %x", val);
+ }
+}
+
+HISAX_INITFUNC(void
+inithdlc(struct IsdnCardState *cs))
+{
+ cs->bcs[0].BC_SetStack = setstack_hdlc;
+ cs->bcs[1].BC_SetStack = setstack_hdlc;
+ cs->bcs[0].BC_Close = close_hdlcstate;
+ cs->bcs[1].BC_Close = close_hdlcstate;
+ modehdlc(cs->bcs, 0, 0);
+ modehdlc(cs->bcs + 1, 0, 0);
+}
+
+static void
+avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, stat = 0;
+ u_char sval;
+
+ if (!cs) {
+ printk(KERN_WARNING "AVM PCI: Spurious interrupt!\n");
+ return;
+ }
+ sval = inb(cs->hw.avm.cfg_reg + 2);
+ if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+ /* possible a shared IRQ reqest */
+ return;
+ if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+ val = ReadISAC(cs, ISAC_ISTA);
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
+ HDLC_irq_main(cs);
+ }
+ if (stat & 2) {
+ WriteISAC(cs, ISAC_MASK, 0xFF);
+ WriteISAC(cs, ISAC_MASK, 0x0);
+ }
+}
+
+static void
+reset_avmpcipnp(struct IsdnCardState *cs)
+{
+ long flags;
+
+ printk(KERN_INFO "AVM PCI/PnP: reset\n");
+ save_flags(flags);
+ sti();
+ outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+ outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_int irq_flag;
+
+ switch (mt) {
+ case CARD_RESET:
+ reset_avmpcipnp(cs);
+ return(0);
+ case CARD_RELEASE:
+ outb(0, cs->hw.avm.cfg_reg + 2);
+ release_region(cs->hw.avm.cfg_reg, 32);
+ return(0);
+ case CARD_SETIRQ:
+ if (cs->subtyp == AVM_FRITZ_PCI)
+ irq_flag = I4L_IRQ_FLAG | SA_SHIRQ;
+ else
+ irq_flag = I4L_IRQ_FLAG;
+ return(request_irq(cs->irq, &avm_pcipnp_interrupt,
+ irq_flag, "HiSax", cs));
+ case CARD_INIT:
+ clear_pending_isac_ints(cs);
+ initisac(cs);
+ clear_pending_hdlc_ints(cs);
+ inithdlc(cs);
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER,
+ cs->hw.avm.cfg_reg + 2);
+ WriteISAC(cs, ISAC_MASK, 0);
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+ AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+ /* RESET Receiver and Transmitter */
+ WriteISAC(cs, ISAC_CMDR, 0x41);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static struct pci_dev *dev_avm __initdata = NULL;
+
+__initfunc(int
+setup_avm_pcipnp(struct IsdnCard *card))
+{
+ u_int val, ver;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, avm_pci_rev);
+ printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_FRITZPCI)
+ return (0);
+ if (card->para[1]) {
+ cs->hw.avm.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ cs->subtyp = AVM_FRITZ_PNP;
+ } else {
+#if CONFIG_PCI
+ if (!pci_present()) {
+ printk(KERN_ERR "FritzPCI: no PCI bus present\n");
+ return(0);
+ }
+ if ((dev_avm = pci_find_device(PCI_VENDOR_AVM,
+ PCI_FRITZPCI_ID, dev_avm))) {
+ cs->irq = dev_avm->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.avm.cfg_reg = dev_avm->base_address[1] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ if (!cs->hw.avm.cfg_reg) {
+ printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+ cs->subtyp = AVM_FRITZ_PCI;
+ } else {
+ printk(KERN_WARNING "FritzPCI: No PCI card found\n");
+ return(0);
+ }
+#else
+ printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n");
+ return (0);
+#endif /* CONFIG_PCI */
+ }
+ cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
+ if (check_region((cs->hw.avm.cfg_reg), 32)) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.avm.cfg_reg,
+ cs->hw.avm.cfg_reg + 31);
+ return (0);
+ } else {
+ request_region(cs->hw.avm.cfg_reg, 32,
+ (cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP");
+ }
+ switch (cs->subtyp) {
+ case AVM_FRITZ_PCI:
+ val = inl(cs->hw.avm.cfg_reg);
+ printk(KERN_INFO "AVM PCI: stat %#x\n", val);
+ printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
+ val & 0xff, (val>>8) & 0xff);
+ cs->BC_Read_Reg = &ReadHDLC_s;
+ cs->BC_Write_Reg = &WriteHDLC_s;
+ break;
+ case AVM_FRITZ_PNP:
+ val = inb(cs->hw.avm.cfg_reg);
+ ver = inb(cs->hw.avm.cfg_reg + 1);
+ printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
+ reset_avmpcipnp(cs);
+ cs->BC_Read_Reg = &ReadHDLCPnP;
+ cs->BC_Write_Reg = &WriteHDLCPnP;
+ break;
+ default:
+ printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
+ outb(0, cs->hw.avm.cfg_reg + 2);
+ release_region(cs->hw.avm.cfg_reg, 32);
+ return(0);
+ }
+ printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n",
+ (cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP",
+ cs->irq, cs->hw.avm.cfg_reg);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Send_Data = &fill_hdlc;
+ cs->cardmsg = &AVM_card_msg;
+ ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
+ return (1);
+}
-/* $Id: callc.c,v 2.13 1998/02/12 23:07:16 keil Exp $
+/* $Id: callc.c,v 2.25 1999/01/02 11:17:20 keil Exp $
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
* based on the teles driver from Jan den Ouden
*
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
+ *
* Thanks to Jan den Ouden
* Fritz Elfert
*
* $Log: callc.c,v $
+ * Revision 2.25 1999/01/02 11:17:20 keil
+ * Changes for 2.2
+ *
+ * Revision 2.24 1998/11/15 23:54:24 keil
+ * changes from 2.0
+ *
+ * Revision 2.23 1998/09/30 22:21:57 keil
+ * cosmetics
+ *
+ * Revision 2.22 1998/08/20 13:50:29 keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 2.21 1998/08/13 23:36:15 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.20 1998/06/26 15:13:05 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 2.19 1998/05/25 14:08:06 keil
+ * HiSax 3.0
+ * fixed X.75 and leased line to work again
+ * Point2Point and fixed TEI are runtime options now:
+ * hisaxctrl <id> 7 1 set PTP
+ * hisaxctrl <id> 8 <TEIVALUE *2 >
+ * set fixed TEI to TEIVALUE (0-63)
+ *
+ * Revision 2.18 1998/05/25 12:57:40 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.17 1998/04/15 16:46:06 keil
+ * RESUME support
+ *
+ * Revision 2.16 1998/04/10 10:35:17 paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 2.15 1998/03/19 13:18:37 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 2.14 1998/03/07 22:56:54 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 2.13 1998/02/12 23:07:16 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#define __NO_VERSION__
#include "hisax.h"
+#include "../avmb1/capicmd.h" /* this should be moved in a common place */
#ifdef MODULE
#define MOD_USE_COUNT ( GET_USE_COUNT (&__this_module))
#endif /* MODULE */
-const char *lli_revision = "$Revision: 2.13 $";
+const char *lli_revision = "$Revision: 2.25 $";
extern struct IsdnCard cards[];
extern int nrcards;
static struct Fsm callcfsm =
{NULL, 0, 0, NULL, NULL};
-static struct Fsm lcfsm =
-{NULL, 0, 0, NULL, NULL};
static int chancount = 0;
-/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
-#define ALERT_REJECT 1
+/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
+#define ALERT_REJECT 0
/* Value to delay the sending of the first B-channel paket after CONNECT
* here is no value given by ITU, but experience shows that 300 ms will
* work on many networks, if you or your other side is behind local exchanges
* a greater value may be recommented. If the delay is to short the first paket
* will be lost and autodetect on many comercial routers goes wrong !
- * You can adjust this value on runtime with
+ * You can adjust this value on runtime with
* hisaxctrl <id> 2 <value>
* value is in milliseconds
*/
#define FLG_DO_HANGUP 13
#define FLG_DO_CONNECT 14
#define FLG_DO_ESTAB 15
+#define FLG_RESUME 16
/*
* Because of callback it's a good idea to delay the shutdown of the d-channel
*/
-#define DREL_TIMER_VALUE 10000
+#define DREL_TIMER_VALUE 40000
/*
* Find card with given driverId
return (struct IsdnCardState *) 0;
}
+int
+discard_queue(struct sk_buff_head *q)
+{
+ struct sk_buff *skb;
+ int ret=0;
+
+ while ((skb = skb_dequeue(q))) {
+ dev_kfree_skb(skb);
+ ret++;
+ }
+ return(ret);
+}
+
static void
-link_debug(struct Channel *chanp, char *s, int direction)
+link_debug(struct Channel *chanp, int direction, char *fmt, ...)
{
- char tmp[100], tm[32];
+ va_list args;
+ char tmp[16];
- jiftime(tm, jiffies);
- sprintf(tmp, "%s Channel %d %s %s\n", tm, chanp->chan,
- direction ? "LL->HL" : "HL->LL", s);
- HiSax_putstatus(chanp->cs, tmp);
+ va_start(args, fmt);
+ sprintf(tmp, "Ch%d %s ", chanp->chan,
+ direction ? "LL->HL" : "HL->LL");
+ VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+ va_end(args);
}
"EV_RELEASE_ERR",
};
-enum {
- ST_LC_NULL,
- ST_LC_ACTIVATE_WAIT,
- ST_LC_DELAY,
- ST_LC_ESTABLISH_WAIT,
- ST_LC_CONNECTED,
- ST_LC_FLUSH_WAIT,
- ST_LC_RELEASE_WAIT,
-};
-
-#define LC_STATE_COUNT (ST_LC_RELEASE_WAIT+1)
-
-static char *strLcState[] =
-{
- "ST_LC_NULL",
- "ST_LC_ACTIVATE_WAIT",
- "ST_LC_DELAY",
- "ST_LC_ESTABLISH_WAIT",
- "ST_LC_CONNECTED",
- "ST_LC_FLUSH_WAIT",
- "ST_LC_RELEASE_WAIT",
-};
-
-enum {
- EV_LC_ESTABLISH,
- EV_LC_PH_ACTIVATE,
- EV_LC_PH_DEACTIVATE,
- EV_LC_DL_ESTABLISH,
- EV_LC_TIMER,
- EV_LC_DL_RELEASE,
- EV_LC_RELEASE,
-};
-
-#define LC_EVENT_COUNT (EV_LC_RELEASE+1)
-
-static char *strLcEvent[] =
-{
- "EV_LC_ESTABLISH",
- "EV_LC_PH_ACTIVATE",
- "EV_LC_PH_DEACTIVATE",
- "EV_LC_DL_ESTABLISH",
- "EV_LC_TIMER",
- "EV_LC_DL_RELEASE",
- "EV_LC_RELEASE",
-};
-
-#define LC_D 0
-#define LC_B 1
-
static inline void
-lli_deliver_cause(struct Channel *chanp)
+lli_deliver_cause(struct Channel *chanp, isdn_ctrl *ic)
{
- isdn_ctrl ic;
-
if (chanp->proc->para.cause < 0)
return;
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_CAUSE;
- ic.arg = chanp->chan;
+ ic->driver = chanp->cs->myid;
+ ic->command = ISDN_STAT_CAUSE;
+ ic->arg = chanp->chan;
if (chanp->cs->protocol == ISDN_PTYPE_EURO)
- sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
+ sprintf(ic->parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
chanp->proc->para.cause & 0x7f);
else
- sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
+ sprintf(ic->parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
chanp->proc->para.cause & 0x7f);
- chanp->cs->iif.statcallb(&ic);
+ chanp->cs->iif.statcallb(ic);
}
static void
if (chanp->leased) {
isdn_ctrl ic;
int ret;
- char txt[32];
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
FsmChangeState(fi, ST_IN_WAIT_LL);
test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
if (chanp->debug & 1)
- link_debug(chanp, "STAT_ICALL_LEASED", 0);
+ link_debug(chanp, 0, "STAT_ICALL_LEASED");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_ICALL;
ic.arg = chanp->chan;
ic.parm.setup.si2 = 0;
ic.parm.setup.plan = 0;
ic.parm.setup.screen = 0;
- sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
+ sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
ret = chanp->cs->iif.statcallb(&ic);
- if (chanp->debug & 1) {
- sprintf(txt, "statcallb ret=%d", ret);
- link_debug(chanp, txt, 1);
- }
+ if (chanp->debug & 1)
+ link_debug(chanp, 1, "statcallb ret=%d", ret);
if (!ret) {
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
FsmChangeState(fi, ST_NULL);
}
} else if (fi->state == ST_WAIT_DSHUTDOWN)
FsmDelTimer(&chanp->dial_timer, 73);
chanp->l2_active_protocol = chanp->l2_protocol;
chanp->incoming = 0;
- chanp->lc_b->l2_start = !0;
- switch (chanp->l2_active_protocol) {
- case (ISDN_PROTO_L2_X75I):
- chanp->lc_b->l2_establish = !0;
- break;
- case (ISDN_PROTO_L2_HDLC):
- case (ISDN_PROTO_L2_TRANS):
- chanp->lc_b->l2_establish = 0;
- break;
- default:
- printk(KERN_WARNING "lli_prep_dialout unknown protocol\n");
- break;
- }
if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
FsmEvent(fi, EV_DLEST, NULL);
} else {
chanp->Flags = 0;
+ if (EV_RESUME == event)
+ test_and_set_bit(FLG_RESUME, &chanp->Flags);
test_and_set_bit(FLG_START_D, &chanp->Flags);
- if (chanp->leased) {
- chanp->lc_d->l2_establish = 0;
- }
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
}
}
lli_do_dialout(struct FsmInst *fi, int event, void *arg)
{
struct Channel *chanp = fi->userdata;
+ int ev;
FsmChangeState(fi, ST_OUT_DIAL);
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+ if (test_and_clear_bit(FLG_RESUME, &chanp->Flags))
+ ev = CC_RESUME | REQUEST;
+ else
+ ev = CC_SETUP | REQUEST;
if (chanp->leased) {
FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
} else {
test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_REQ, chanp);
+ chanp->d_st->lli.l4l3(chanp->d_st, ev, chanp);
test_and_set_bit(FLG_CALL_SEND, &chanp->Flags);
}
}
FsmChangeState(fi, ST_WAIT_BCONN);
test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
if (chanp->debug & 1)
- link_debug(chanp, "STAT_DCONN", 0);
+ link_debug(chanp, 0, "STAT_DCONN");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_DCONN;
ic.arg = chanp->chan;
chanp->cs->iif.statcallb(&ic);
init_b_st(chanp, 0);
test_and_set_bit(FLG_START_B, &chanp->Flags);
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
}
static void
chanp->data_open = !0;
test_and_set_bit(FLG_CONNECT_B, &chanp->Flags);
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BCONN", 0);
+ link_debug(chanp, 0, "STAT_BCONN");
test_and_set_bit(FLG_LL_BCONN, &chanp->Flags);
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BCONN;
ic.arg = chanp->chan;
chanp->cs->iif.statcallb(&ic);
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) chanp->chan);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan);
}
+/*
+ * RESUME
+ */
+
/* incomming call */
static void
test_and_set_bit(FLG_DO_CONNECT, &chanp->Flags);
else if (event == EV_HANGUP) {
test_and_set_bit(FLG_DO_HANGUP, &chanp->Flags);
-#ifdef ALERT_REJECT
+#ifdef ALERT_REJECT
test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
#endif
- }
+ }
if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
FsmEvent(fi, EV_DLEST, NULL);
} else if (!test_and_set_bit(FLG_START_D, &chanp->Flags))
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
}
static void
struct Channel *chanp = fi->userdata;
isdn_ctrl ic;
int ret;
- char txt[32];
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) chanp->chan);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
/*
* Report incoming calls only once to linklevel, use CallFlags
* which is set to 3 with each broadcast message in isdnl1.c
FsmChangeState(fi, ST_IN_WAIT_LL);
test_and_set_bit(FLG_CALL_REC, &chanp->Flags);
if (chanp->debug & 1)
- link_debug(chanp, "STAT_ICALL", 0);
+ link_debug(chanp, 0, "STAT_ICALL");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_ICALL;
ic.arg = chanp->chan;
*/
ic.parm.setup = chanp->proc->para.setup;
ret = chanp->cs->iif.statcallb(&ic);
- if (chanp->debug & 1) {
- sprintf(txt, "statcallb ret=%d", ret);
- link_debug(chanp, txt, 1);
- }
+ if (chanp->debug & 1)
+ link_debug(chanp, 1, "statcallb ret=%d", ret);
switch (ret) {
case 1: /* OK, anybody likes this call */
FsmDelTimer(&chanp->drel_timer, 61);
if (test_bit(FLG_ESTAB_D, &chanp->Flags)) {
FsmChangeState(fi, ST_IN_ALERT_SEND);
test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
} else {
test_and_set_bit(FLG_DO_ALERT, &chanp->Flags);
FsmChangeState(fi, ST_IN_WAIT_D);
test_and_set_bit(FLG_START_D, &chanp->Flags);
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st,
+ DL_ESTABLISH | REQUEST, NULL);
}
break;
case 2: /* Rejecting Call */
break;
case 0: /* OK, nobody likes this call */
default: /* statcallb problems */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc);
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
FsmChangeState(fi, ST_NULL);
-#ifndef LAYER2_WATCHING
- if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+ if (test_bit(FLG_ESTAB_D, &chanp->Flags) &&
+ !test_bit(FLG_PTP, &chanp->d_st->l2.flag))
FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 61);
-#endif
break;
}
} else {
- chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE, chanp->proc);
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
FsmChangeState(fi, ST_NULL);
-#ifndef LAYER2_WATCHING
- if (test_bit(FLG_ESTAB_D, &chanp->Flags))
+ if (test_bit(FLG_ESTAB_D, &chanp->Flags) &&
+ !test_bit(FLG_PTP, &chanp->d_st->l2.flag))
FsmRestartTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 62);
-#endif
}
}
static void
lli_establish_d(struct FsmInst *fi, int event, void *arg)
{
- /* This establish the D-channel for pending L3 messages
- * without blocking th channel
+ /* This establish the D-channel for pending L3 messages
+ * without blocking the channel
*/
struct Channel *chanp = fi->userdata;
test_and_set_bit(FLG_DO_ESTAB, &chanp->Flags);
FsmChangeState(fi, ST_IN_WAIT_D);
test_and_set_bit(FLG_START_D, &chanp->Flags);
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_ESTABLISH | REQUEST, NULL);
}
static void
!test_bit(FLG_DO_HANGUP, &chanp->Flags)) {
FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
} else if (test_and_clear_bit(FLG_DO_ALERT, &chanp->Flags)) {
if (test_bit(FLG_DO_HANGUP, &chanp->Flags))
FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
FsmChangeState(fi, ST_IN_ALERT_SEND);
test_and_set_bit(FLG_CALL_ALERT, &chanp->Flags);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING_REQ, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
} else if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags)) {
FsmChangeState(fi, ST_WAIT_DRELEASE);
chanp->proc->para.cause = 0x15; /* Call Rejected */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT_REQ, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
- } else if (test_and_clear_bit(FLG_DO_ESTAB, &chanp->Flags)) {
- FsmChangeState(fi, ST_NULL);
- chanp->Flags = 0;
- test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_ESTABLISH, chanp->proc);
- chanp->proc = NULL;
-#ifndef LAYER2_WATCHING
- FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
-#endif
}
}
struct Channel *chanp = fi->userdata;
FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP_RSP, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
}
static void
FsmChangeState(fi, ST_WAIT_BCONN);
test_and_set_bit(FLG_LL_DCONN, &chanp->Flags);
if (chanp->debug & 1)
- link_debug(chanp, "STAT_DCONN", 0);
+ link_debug(chanp, 0, "STAT_DCONN");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_DCONN;
ic.arg = chanp->chan;
chanp->cs->iif.statcallb(&ic);
chanp->l2_active_protocol = chanp->l2_protocol;
chanp->incoming = !0;
- chanp->lc_b->l2_start = 0;
- switch (chanp->l2_active_protocol) {
- case (ISDN_PROTO_L2_X75I):
- chanp->lc_b->l2_establish = !0;
- break;
- case (ISDN_PROTO_L2_HDLC):
- case (ISDN_PROTO_L2_TRANS):
- chanp->lc_b->l2_establish = 0;
- break;
- default:
- printk(KERN_WARNING "bchannel unknown protocol\n");
- break;
- }
init_b_st(chanp, !0);
test_and_set_bit(FLG_START_B, &chanp->Flags);
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_ESTABLISH, NULL);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+/* Call suspend */
+
+static void
+lli_suspend(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
}
/* Call clearing */
FsmChangeState(fi, ST_WAIT_DRELEASE);
if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BHUP", 0);
+ link_debug(chanp, 0, "STAT_BHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BHUP;
ic.arg = chanp->chan;
if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
release_b_st(chanp);
chanp->proc->para.cause = 0x10; /* Normal Call Clearing */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
}
struct Channel *chanp = fi->userdata;
FsmDelTimer(&chanp->drel_timer, 62);
-#ifdef LAYER2_WATCHING
- FsmChangeState(fi, ST_NULL);
-#else
- if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) {
- if (chanp->chan) {
- if (chanp->cs->channel[0].fi.state != ST_NULL)
- return;
- } else {
- if (chanp->cs->channel[1].fi.state != ST_NULL)
- return;
+ if (test_bit(FLG_PTP, &chanp->d_st->l2.flag)) {
+ FsmChangeState(fi, ST_NULL);
+ } else {
+ if (!test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags)) {
+ if (chanp->chan) {
+ if (chanp->cs->channel[0].fi.state != ST_NULL)
+ return;
+ } else {
+ if (chanp->cs->channel[1].fi.state != ST_NULL)
+ return;
+ }
}
+ FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
+ test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
}
- FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
- test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
-#endif
}
static void
struct Channel *chanp = fi->userdata;
isdn_ctrl ic;
- if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
- if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
- lli_deliver_cause(chanp);
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_DHUP;
- ic.arg = chanp->chan;
- chanp->cs->iif.statcallb(&ic);
- }
+ test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ lli_deliver_cause(chanp, &ic);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
FsmChangeState(fi, ST_NULL);
chanp->Flags = 0;
test_and_set_bit(FLG_ESTAB_D, &chanp->Flags);
-#ifndef LAYER2_WATCHING
- FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
-#endif
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+ if (!test_bit(FLG_PTP, &chanp->d_st->l2.flag))
+ FsmAddTimer(&chanp->drel_timer, DREL_TIMER_VALUE, EV_SHUTDOWN_D, NULL, 60);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
}
static void
FsmChangeState(fi, ST_NULL);
chanp->Flags = 0;
FsmDelTimer(&chanp->drel_timer, 63);
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
}
static void
chanp->data_open = 0;
FsmChangeState(fi, ST_WAIT_BRELEASE);
test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
}
static void
FsmChangeState(fi, ST_WAIT_DRELEASE);
if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BHUP", 0);
+ link_debug(chanp, 0, "STAT_BHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BHUP;
ic.arg = chanp->chan;
sprintf(ic.parm.num, "L0010");
chanp->cs->iif.statcallb(&ic);
if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
+ link_debug(chanp, 0, "STAT_DHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_DHUP;
ic.arg = chanp->chan;
chanp->cs->iif.statcallb(&ic);
FsmChangeState(fi, ST_WAIT_DSHUTDOWN);
test_and_clear_bit(FLG_ESTAB_D, &chanp->Flags);
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
} else {
if (test_and_clear_bit(FLG_DO_HANGUP, &chanp->Flags))
chanp->proc->para.cause = 0x15; /* Call Reject */
else
chanp->proc->para.cause = 0x10; /* Normal Call Clearing */
- chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT_REQ, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST, chanp->proc);
test_and_set_bit(FLG_DISC_SEND, &chanp->Flags);
}
}
chanp->data_open = 0;
if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BHUP", 0);
+ link_debug(chanp, 0, "STAT_BHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BHUP;
ic.arg = chanp->chan;
test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
FsmChangeState(fi, ST_WAIT_BREL_DISC);
test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags);
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
}
static void
FsmChangeState(fi, ST_NULL);
test_and_set_bit(FLG_REL_REC, &chanp->Flags);
if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
- chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
}
if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BHUP", 0);
+ link_debug(chanp, 0, "STAT_BHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BHUP;
ic.arg = chanp->chan;
}
if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
release_b_st(chanp);
- if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
- test_bit(FLG_CALL_SEND, &chanp->Flags) ||
- test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
- if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
- lli_deliver_cause(chanp);
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_DHUP;
- ic.arg = chanp->chan;
- chanp->cs->iif.statcallb(&ic);
- }
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ lli_deliver_cause(chanp, &ic);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
chanp->data_open = 0;
FsmChangeState(fi, ST_NULL);
if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
- chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
}
if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BHUP", 0);
+ link_debug(chanp, 0, "STAT_BHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BHUP;
ic.arg = chanp->chan;
}
if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
release_b_st(chanp);
- if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
- test_bit(FLG_CALL_SEND, &chanp->Flags) ||
- test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
- if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
- lli_deliver_cause(chanp);
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_DHUP;
- ic.arg = chanp->chan;
- chanp->cs->iif.statcallb(&ic);
- }
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ lli_deliver_cause(chanp, &ic);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
test_and_clear_bit(FLG_DISC_SEND, &chanp->Flags);
test_and_clear_bit(FLG_CALL_REC, &chanp->Flags);
test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
test_and_set_bit(FLG_DISC_REC, &chanp->Flags);
if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BHUP", 0);
+ link_debug(chanp, 0, "STAT_BHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BHUP;
ic.arg = chanp->chan;
}
if (test_and_clear_bit(FLG_START_B, &chanp->Flags))
release_b_st(chanp);
- if (test_bit(FLG_LL_DCONN, &chanp->Flags) ||
- test_bit(FLG_CALL_SEND, &chanp->Flags) ||
- test_bit(FLG_CALL_ALERT, &chanp->Flags)) {
- if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
- lli_deliver_cause(chanp);
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_DHUP;
- ic.arg = chanp->chan;
- chanp->cs->iif.statcallb(&ic);
- }
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ lli_deliver_cause(chanp, &ic);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
test_and_clear_bit(FLG_CALL_ALERT, &chanp->Flags);
test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE_REQ, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST, chanp->proc);
}
/* processing charge info */
isdn_ctrl ic;
if (chanp->debug & 1)
- link_debug(chanp, "STAT_NODCH", 0);
+ link_debug(chanp, 0, "STAT_NODCH");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_NODCH;
ic.arg = chanp->chan;
chanp->cs->iif.statcallb(&ic);
chanp->Flags = 0;
FsmChangeState(fi, ST_NULL);
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
}
static void
isdn_ctrl ic;
if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
+ link_debug(chanp, 0, "STAT_DHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_DHUP;
ic.arg = chanp->chan;
isdn_ctrl ic;
if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
+ link_debug(chanp, 0, "STAT_DHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_DHUP;
ic.arg = chanp->chan;
chanp->cs->iif.statcallb(&ic);
- chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc);
chanp->Flags = 0;
FsmChangeState(fi, ST_NULL);
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
}
static void
FsmChangeState(fi, ST_NULL);
test_and_clear_bit(FLG_CALL_SEND, &chanp->Flags);
if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
+ link_debug(chanp, 0, "STAT_DHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_DHUP;
ic.arg = chanp->chan;
isdn_ctrl ic;
FsmChangeState(fi, ST_WAIT_DRELEASE);
- if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
- if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
- lli_deliver_cause(chanp);
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_DHUP;
- ic.arg = chanp->chan;
- chanp->cs->iif.statcallb(&ic);
- }
+ test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ lli_deliver_cause(chanp, &ic);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */
}
isdn_ctrl ic;
FsmChangeState(fi, ST_WAIT_DRELEASE);
- if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
- if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
- lli_deliver_cause(chanp);
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_DHUP;
- ic.arg = chanp->chan;
- chanp->cs->iif.statcallb(&ic);
- }
+ test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ lli_deliver_cause(chanp, &ic);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
test_and_set_bit(FLG_DISC_SEND, &chanp->Flags); /* DISCONN was sent from L3 */
}
chanp->data_open = 0;
FsmChangeState(fi, ST_NULL);
if (test_and_clear_bit(FLG_CONNECT_B, &chanp->Flags)) {
- chanp->lc_b->l2_establish = 0; /* direct reset in lc_b->lcfi */
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_RELEASE, NULL);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
}
if (test_and_clear_bit(FLG_LL_BCONN, &chanp->Flags)) {
if (chanp->debug & 1)
- link_debug(chanp, "STAT_BHUP", 0);
+ link_debug(chanp, 0, "STAT_BHUP");
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BHUP;
ic.arg = chanp->chan;
chanp->cs->iif.statcallb(&ic);
chanp->Flags = 0;
} else {
- if (test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags)) {
- if (chanp->debug & 1)
- link_debug(chanp, "STAT_DHUP", 0);
- if (chanp->cs->protocol == ISDN_PTYPE_EURO) {
- chanp->proc->para.cause = 0x2f;
- chanp->proc->para.loc = 0;
- } else {
- chanp->proc->para.cause = 0x70;
- chanp->proc->para.loc = 0;
- }
- lli_deliver_cause(chanp);
- ic.driver = chanp->cs->myid;
- ic.command = ISDN_STAT_DHUP;
- ic.arg = chanp->chan;
- chanp->cs->iif.statcallb(&ic);
+ test_and_clear_bit(FLG_LL_DCONN, &chanp->Flags);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ if (chanp->cs->protocol == ISDN_PTYPE_EURO) {
+ chanp->proc->para.cause = 0x2f;
+ chanp->proc->para.loc = 0;
+ } else {
+ chanp->proc->para.cause = 0x70;
+ chanp->proc->para.loc = 0;
}
- chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL, chanp->proc);
+ lli_deliver_cause(chanp, &ic);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_DHUP;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_DLRL | REQUEST, chanp->proc);
chanp->Flags = 0;
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_RELEASE, NULL);
+ chanp->d_st->lli.l4l3(chanp->d_st, DL_RELEASE | REQUEST, NULL);
}
- chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) chanp->chan);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
}
/* *INDENT-OFF* */
static struct FsmNode fnlist[] HISAX_INITDATA =
{
{ST_NULL, EV_DIAL, lli_prep_dialout},
+ {ST_NULL, EV_RESUME, lli_prep_dialout},
{ST_NULL, EV_SETUP_IND, lli_deliver_call},
{ST_NULL, EV_SHUTDOWN_D, lli_shutdown_d},
{ST_NULL, EV_DLRL, lli_go_null},
{ST_WAIT_BCONN, EV_CINF, lli_charge_info},
{ST_ACTIVE, EV_CINF, lli_charge_info},
{ST_ACTIVE, EV_BC_REL, lli_released_bchan},
+ {ST_ACTIVE, EV_SUSPEND, lli_suspend},
{ST_ACTIVE, EV_HANGUP, lli_disconn_bchan},
{ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan},
{ST_ACTIVE, EV_RELEASE_CNF, lli_received_d_relcnf},
{ST_WAIT_DSHUTDOWN, EV_DLRL, lli_go_null},
{ST_WAIT_DSHUTDOWN, EV_DLEST, lli_d_established},
{ST_WAIT_DSHUTDOWN, EV_DIAL, lli_prep_dialout},
+ {ST_WAIT_DSHUTDOWN, EV_RESUME, lli_prep_dialout},
{ST_WAIT_DSHUTDOWN, EV_SETUP_IND, lli_deliver_call},
};
/* *INDENT-ON* */
#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
-static void
-lc_activate_l1(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- FsmDelTimer(&lf->act_timer, 50);
- FsmChangeState(fi, ST_LC_ACTIVATE_WAIT);
- /* This timeout is to avoid a hang if no L1 activation is possible */
- FsmAddTimer(&lf->act_timer, 30000, EV_LC_TIMER, NULL, 50);
- lf->st->ma.manl1(lf->st, PH_ACTIVATE_REQ, NULL);
-}
-
-static void
-lc_activated_from_l1(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- if (lf->l2_establish)
- FsmChangeState(fi, ST_LC_DELAY);
- else {
- FsmChangeState(fi, ST_LC_CONNECTED);
- lf->lccall(lf, LC_ESTABLISH, NULL);
- }
-}
-
-static void
-lc_l1_activated(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- FsmDelTimer(&lf->act_timer, 50);
- FsmChangeState(fi, ST_LC_DELAY);
- /* This timer is needed for delay the first paket on a channel
- to be shure that the other side is ready too */
- if (lf->delay)
- FsmAddTimer(&lf->act_timer, lf->delay, EV_LC_TIMER, NULL, 51);
- else
- FsmEvent(fi, EV_LC_TIMER, NULL);
-}
-
-static void
-lc_start_l2(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
-/* if (!lf->st->l1.act_state)
- lf->st->l1.act_state = 2;
-*/ if (lf->l2_establish) {
- FsmChangeState(fi, ST_LC_ESTABLISH_WAIT);
- if (lf->l2_start)
- lf->st->ma.manl2(lf->st, DL_ESTABLISH, NULL);
- } else {
- FsmChangeState(fi, ST_LC_CONNECTED);
- lf->lccall(lf, LC_ESTABLISH, NULL);
- }
-}
-
-static void
-lc_connected(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- FsmDelTimer(&lf->act_timer, 50);
- FsmChangeState(fi, ST_LC_CONNECTED);
- lf->lccall(lf, LC_ESTABLISH, NULL);
-}
-
-static void
-lc_release_l2(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- if (lf->l2_establish) {
- FsmChangeState(fi, ST_LC_RELEASE_WAIT);
- lf->st->ma.manl2(lf->st, DL_RELEASE, NULL);
- } else {
- FsmChangeState(fi, ST_LC_NULL);
- lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL);
- lf->lccall(lf, LC_RELEASE, NULL);
- }
-}
-
-static void
-lc_l2_released(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- FsmChangeState(fi, ST_LC_RELEASE_WAIT);
- FsmDelTimer(&lf->act_timer, 51);
- /* This delay is needed for send out the UA frame before
- * PH_DEACTIVATE the interface
- */
- FsmAddTimer(&lf->act_timer, 20, EV_LC_TIMER, NULL, 54);
-}
-
-static void
-lc_release_l1(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- FsmDelTimer(&lf->act_timer, 54);
- FsmChangeState(fi, ST_LC_NULL);
- lf->st->ma.manl1(lf->st, PH_DEACTIVATE_REQ, NULL);
- lf->lccall(lf, LC_RELEASE, NULL);
-}
-
-static void
-lc_l1_deactivated(struct FsmInst *fi, int event, void *arg)
-{
- struct LcFsm *lf = fi->userdata;
-
- FsmDelTimer(&lf->act_timer, 54);
- FsmChangeState(fi, ST_LC_NULL);
- lf->lccall(lf, LC_RELEASE, NULL);
-}
-/* *INDENT-OFF* */
-static struct FsmNode LcFnList[] HISAX_INITDATA =
-{
- {ST_LC_NULL, EV_LC_ESTABLISH, lc_activate_l1},
- {ST_LC_NULL, EV_LC_PH_ACTIVATE, lc_activated_from_l1},
- {ST_LC_NULL, EV_LC_DL_ESTABLISH, lc_connected},
- {ST_LC_ACTIVATE_WAIT, EV_LC_PH_ACTIVATE, lc_l1_activated},
- {ST_LC_ACTIVATE_WAIT, EV_LC_TIMER, lc_release_l1},
- {ST_LC_ACTIVATE_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated},
- {ST_LC_DELAY, EV_LC_ESTABLISH, lc_start_l2},
- {ST_LC_DELAY, EV_LC_TIMER, lc_start_l2},
- {ST_LC_DELAY, EV_LC_DL_ESTABLISH, lc_connected},
- {ST_LC_DELAY, EV_LC_PH_DEACTIVATE, lc_l1_deactivated},
- {ST_LC_ESTABLISH_WAIT, EV_LC_DL_ESTABLISH, lc_connected},
- {ST_LC_ESTABLISH_WAIT, EV_LC_RELEASE, lc_release_l1},
- {ST_LC_ESTABLISH_WAIT, EV_LC_DL_RELEASE, lc_release_l1},
- {ST_LC_ESTABLISH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated},
- {ST_LC_CONNECTED, EV_LC_ESTABLISH, lc_connected},
- {ST_LC_CONNECTED, EV_LC_RELEASE, lc_release_l2},
- {ST_LC_CONNECTED, EV_LC_DL_RELEASE, lc_l2_released},
- {ST_LC_CONNECTED, EV_LC_PH_DEACTIVATE, lc_l1_deactivated},
- {ST_LC_FLUSH_WAIT, EV_LC_TIMER, lc_release_l2},
- {ST_LC_FLUSH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated},
- {ST_LC_RELEASE_WAIT, EV_LC_DL_RELEASE, lc_release_l1},
- {ST_LC_RELEASE_WAIT, EV_LC_TIMER, lc_release_l1},
- {ST_LC_FLUSH_WAIT, EV_LC_PH_DEACTIVATE, lc_l1_deactivated},
-};
-/* *INDENT-ON* */
-
-
-#define LC_FN_COUNT (sizeof(LcFnList)/sizeof(struct FsmNode))
-
HISAX_INITFUNC(void
CallcNew(void))
{
callcfsm.strEvent = strEvent;
callcfsm.strState = strState;
FsmNew(&callcfsm, fnlist, FNCOUNT);
-
- lcfsm.state_count = LC_STATE_COUNT;
- lcfsm.event_count = LC_EVENT_COUNT;
- lcfsm.strEvent = strLcEvent;
- lcfsm.strState = strLcState;
- FsmNew(&lcfsm, LcFnList, LC_FN_COUNT);
}
void
CallcFree(void)
{
- FsmFree(&lcfsm);
FsmFree(&callcfsm);
}
break;
case (ISDN_PROTO_L2_HDLC):
case (ISDN_PROTO_L2_TRANS):
+// case (ISDN_PROTO_L2_MODEM):
releasestack_transl2(st);
break;
}
- /* Reset B-Channel Statemachine */
- FsmDelTimer(&chanp->lc_b->act_timer, 79);
- FsmChangeState(&chanp->lc_b->lcfi, ST_LC_NULL);
-}
-
-static void
-dc_l1man(struct PStack *st, int pr, void *arg)
-{
- struct Channel *chanp;
-
- chanp = (struct Channel *) st->lli.userdata;
- switch (pr) {
- case (PH_ACTIVATE_CNF):
- case (PH_ACTIVATE_IND):
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_ACTIVATE, NULL);
- break;
- case (PH_DEACTIVATE_IND):
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_PH_DEACTIVATE, NULL);
- break;
- }
-}
-
-static void
-dc_l2man(struct PStack *st, int pr, void *arg)
-{
- struct Channel *chanp = (struct Channel *) st->lli.userdata;
-
- switch (pr) {
- case (DL_ESTABLISH):
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_DL_ESTABLISH, NULL);
- break;
- case (DL_RELEASE):
- FsmEvent(&chanp->lc_d->lcfi, EV_LC_DL_RELEASE, NULL);
- break;
- }
-}
-
-static void
-bc_l1man(struct PStack *st, int pr, void *arg)
-{
- struct Channel *chanp = (struct Channel *) st->lli.userdata;
-
- switch (pr) {
- case (PH_ACTIVATE_IND):
- case (PH_ACTIVATE_CNF):
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_ACTIVATE, NULL);
- break;
- case (PH_DEACTIVATE_IND):
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_PH_DEACTIVATE, NULL);
- break;
- }
-}
-
-static void
-bc_l2man(struct PStack *st, int pr, void *arg)
-{
- struct Channel *chanp = (struct Channel *) st->lli.userdata;
-
- switch (pr) {
- case (DL_ESTABLISH):
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL);
- break;
- case (DL_RELEASE):
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_RELEASE, NULL);
- break;
- }
}
struct Channel
return (chanp);
chanp++;
i++;
- }
+ }
return (NULL);
}
return (1);
chanp++;
i++;
- }
+ }
return (0);
}
static void
-ll_handler(struct l3_process *pc, int pr, void *arg)
+dchan_l3l4(struct PStack *st, int pr, void *arg)
{
+ struct l3_process *pc = arg;
+ struct IsdnCardState *cs = st->l1.hardware;
struct Channel *chanp;
- char tmp[64], tm[32];
+ int event;
+
+ switch (pr) {
+ case (DL_ESTABLISH | INDICATION):
+ event = EV_DLEST;
+ break;
+ case (DL_RELEASE | INDICATION):
+ event = EV_DLRL;
+ break;
+ default:
+ event = -1;
+ break;
+ }
+ if (event >= 0) {
+ int i;
- if (pr == CC_SETUP_IND) {
+ chanp = st->lli.userdata;
+ if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+ i = 1;
+ else
+ i = 0;
+ while (i < 2) {
+ FsmEvent(&chanp->fi, event, NULL);
+ chanp++;
+ i++;
+ }
+ return;
+ } else if (pr == (CC_SETUP | INDICATION)) {
if (!(chanp = selectfreechannel(pc->st))) {
- pc->st->lli.l4l3(pc->st, CC_DLRL, pc);
- return;
+ pc->st->lli.l4l3(pc->st, CC_DLRL | REQUEST, pc);
} else {
chanp->proc = pc;
pc->chan = chanp;
FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
- return;
}
- } else if (pr == CC_ESTABLISH) {
- if (is_activ(pc->st)) {
- pc->st->lli.l4l3(pc->st, CC_ESTABLISH, pc);
- return;
- } else if (!(chanp = selectfreechannel(pc->st))) {
- pc->st->lli.l4l3(pc->st, CC_DLRL, pc);
- return;
- } else {
- chanp->proc = pc;
- FsmEvent(&chanp->fi, EV_ESTABLISH, NULL);
- return;
- }
-
-
+ return;
}
- chanp = pc->chan;
+ if (!(chanp = pc->chan))
+ return;
+
switch (pr) {
- case (CC_DISCONNECT_IND):
+ case (CC_DISCONNECT | INDICATION):
FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
break;
- case (CC_RELEASE_CNF):
+ case (CC_RELEASE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+ break;
+ case (CC_SUSPEND | CONFIRM):
FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
break;
- case (CC_RELEASE_IND):
+ case (CC_RESUME | CONFIRM):
+ FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+ break;
+ case (CC_RESUME_ERR):
+ FsmEvent(&chanp->fi, EV_RELEASE_CNF, NULL);
+ break;
+ case (CC_RELEASE | INDICATION):
FsmEvent(&chanp->fi, EV_RELEASE_IND, NULL);
break;
- case (CC_SETUP_COMPLETE_IND):
+ case (CC_SETUP_COMPL | INDICATION):
FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
break;
- case (CC_SETUP_CNF):
+ case (CC_SETUP | CONFIRM):
FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
break;
- case (CC_INFO_CHARGE):
+ case (CC_CHARGE | INDICATION):
FsmEvent(&chanp->fi, EV_CINF, NULL);
break;
- case (CC_NOSETUP_RSP_ERR):
+ case (CC_NOSETUP_RSP):
FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
break;
case (CC_SETUP_ERR):
case (CC_RELEASE_ERR):
FsmEvent(&chanp->fi, EV_RELEASE_ERR, NULL);
break;
- case (CC_PROCEEDING_IND):
- case (CC_ALERTING_IND):
+ case (CC_PROCEEDING | INDICATION):
+ case (CC_ALERTING | INDICATION):
break;
default:
if (chanp->debug & 0x800) {
- jiftime(tm, jiffies);
- sprintf(tmp, "%s Channel %d L3->L4 unknown primitiv %d\n",
- tm, chanp->chan, pr);
- HiSax_putstatus(chanp->cs, tmp);
+ HiSax_putstatus(chanp->cs, "Ch",
+ "%d L3->L4 unknown primitiv %x",
+ chanp->chan, pr);
}
}
}
{
struct PStack *st = chanp->d_st;
struct IsdnCardState *cs = chanp->cs;
- char tmp[128];
+ char tmp[16];
HiSax_addlist(cs, st);
setstack_HiSax(st, cs);
st->l2.window = 1;
st->l2.T200 = 1000; /* 1000 milliseconds */
st->l2.N200 = 3; /* try 3 times */
- if (st->protocol == ISDN_PTYPE_1TR6)
- st->l2.T203 = 10000; /* 10000 milliseconds */
+ st->l2.T203 = 10000; /* 10000 milliseconds */
+ if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+ sprintf(tmp, "DCh%d Q.921 ", chanp->chan);
else
- st->l2.T203 = 10000; /* 5000 milliseconds */
-
- sprintf(tmp, "Channel %d q.921", chanp->chan);
+ sprintf(tmp, "DCh Q.921 ");
setstack_isdnl2(st, tmp);
- setstack_isdnl3(st, chanp);
+ setstack_l3dc(st, chanp);
st->lli.userdata = chanp;
st->lli.l2writewakeup = NULL;
- st->l3.l3l4 = ll_handler;
- st->l1.l1man = dc_l1man;
- st->l2.l2man = dc_l2man;
+ st->l3.l3l4 = dchan_l3l4;
}
static void
-callc_debug(struct FsmInst *fi, char *s)
+callc_debug(struct FsmInst *fi, char *fmt, ...)
{
- char str[80], tm[32];
+ va_list args;
struct Channel *chanp = fi->userdata;
+ char tmp[16];
- jiftime(tm, jiffies);
- sprintf(str, "%s Channel %d callc %s\n", tm, chanp->chan, s);
- HiSax_putstatus(chanp->cs, str);
-}
-
-static void
-lc_debug(struct FsmInst *fi, char *s)
-{
- char str[256], tm[32];
- struct LcFsm *lf = fi->userdata;
-
- jiftime(tm, jiffies);
- sprintf(str, "%s Channel %d dc %s\n", tm, lf->ch->chan, s);
- HiSax_putstatus(lf->ch->cs, str);
-}
-
-static void
-dlc_debug(struct FsmInst *fi, char *s)
-{
- char str[256], tm[32];
- struct LcFsm *lf = fi->userdata;
-
- jiftime(tm, jiffies);
- sprintf(str, "%s Channel %d bc %s\n", tm, lf->ch->chan, s);
- HiSax_putstatus(lf->ch->cs, str);
+ va_start(args, fmt);
+ sprintf(tmp, "Ch%d callc ", chanp->chan);
+ VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+ va_end(args);
}
static void
-lccall_d(struct LcFsm *lf, int pr, void *arg)
-{
- struct IsdnCardState *cs = lf->st->l1.hardware;
- struct Channel *chanp;
- int i;
-
- if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags)) {
- chanp = lf->ch;
- i = 1;
- } else {
- chanp = cs->channel;
- i = 0;
- }
- while (i < 2) {
- switch (pr) {
- case (LC_ESTABLISH):
- FsmEvent(&chanp->fi, EV_DLEST, NULL);
- break;
- case (LC_RELEASE):
- FsmEvent(&chanp->fi, EV_DLRL, NULL);
- break;
- }
- chanp++;
- i++;
- }
+dummy_pstack(struct PStack *st, int pr, void *arg) {
+ printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg);
}
static void
-lccall_b(struct LcFsm *lf, int pr, void *arg)
-{
- struct Channel *chanp = lf->ch;
-
- switch (pr) {
- case (LC_ESTABLISH):
- FsmEvent(&chanp->fi, EV_BC_EST, NULL);
- break;
- case (LC_RELEASE):
- FsmEvent(&chanp->fi, EV_BC_REL, NULL);
- break;
- }
+init_PStack(struct PStack **stp) {
+ *stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+ (*stp)->next = NULL;
+ (*stp)->l1.l1l2 = dummy_pstack;
+ (*stp)->l1.l1hw = dummy_pstack;
+ (*stp)->l1.l1tei = dummy_pstack;
+ (*stp)->l2.l2tei = dummy_pstack;
+ (*stp)->l2.l2l1 = dummy_pstack;
+ (*stp)->l2.l2l3 = dummy_pstack;
+ (*stp)->l3.l3l2 = dummy_pstack;
+ (*stp)->l3.l3l4 = dummy_pstack;
+ (*stp)->lli.l4l3 = dummy_pstack;
+ (*stp)->ma.layer = dummy_pstack;
}
static void
chanp->debug = 0;
chanp->Flags = 0;
chanp->leased = 0;
- chanp->b_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
- chanp->b_st->next = NULL;
-
+ init_PStack(&chanp->b_st);
+ chanp->b_st->l1.delay = DEFAULT_B_DELAY;
chanp->fi.fsm = &callcfsm;
chanp->fi.state = ST_NULL;
chanp->fi.debug = 0;
FsmInitTimer(&chanp->fi, &chanp->dial_timer);
FsmInitTimer(&chanp->fi, &chanp->drel_timer);
if (!chan || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
- chanp->d_st = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+ init_PStack(&chanp->d_st);
+ if (chan)
+ csta->channel->d_st->next = chanp->d_st;
chanp->d_st->next = NULL;
init_d_st(chanp);
- chanp->lc_d = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC);
- chanp->lc_d->lcfi.fsm = &lcfsm;
- chanp->lc_d->lcfi.state = ST_LC_NULL;
- chanp->lc_d->lcfi.debug = 0;
- chanp->lc_d->lcfi.userdata = chanp->lc_d;
- chanp->lc_d->lcfi.printdebug = lc_debug;
- chanp->lc_d->type = LC_D;
- chanp->lc_d->delay = 0;
- chanp->lc_d->ch = chanp;
- chanp->lc_d->st = chanp->d_st;
- chanp->lc_d->l2_establish = !0;
- chanp->lc_d->l2_start = !0;
- chanp->lc_d->lccall = lccall_d;
- FsmInitTimer(&chanp->lc_d->lcfi, &chanp->lc_d->act_timer);
} else {
chanp->d_st = csta->channel->d_st;
- chanp->lc_d = csta->channel->lc_d;
}
- chanp->lc_b = kmalloc(sizeof(struct LcFsm), GFP_ATOMIC);
- chanp->lc_b->lcfi.fsm = &lcfsm;
- chanp->lc_b->lcfi.state = ST_LC_NULL;
- chanp->lc_b->lcfi.debug = 0;
- chanp->lc_b->lcfi.userdata = chanp->lc_b;
- chanp->lc_b->lcfi.printdebug = dlc_debug;
- chanp->lc_b->type = LC_B;
- chanp->lc_b->delay = DEFAULT_B_DELAY;
- chanp->lc_b->ch = chanp;
- chanp->lc_b->st = chanp->b_st;
- chanp->lc_b->l2_establish = !0;
- chanp->lc_b->l2_start = !0;
- chanp->lc_b->lccall = lccall_b;
- FsmInitTimer(&chanp->lc_b->lcfi, &chanp->lc_b->act_timer);
chanp->data_open = 0;
}
init_chan(0, csta);
init_chan(1, csta);
printk(KERN_INFO "HiSax: 2 channels added\n");
-#ifdef LAYER2_WATCHING
- printk(KERN_INFO "LAYER2 ESTABLISH\n");
- test_and_set_bit(FLG_START_D, &csta->channel->Flags);
- FsmEvent(&csta->channel->lc_d->lcfi, EV_LC_ESTABLISH, NULL);
-#endif
+ if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
+ printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+ test_and_set_bit(FLG_START_D, &csta->channel->Flags);
+ csta->channel->d_st->lli.l4l3(csta->channel->d_st,
+ DL_ESTABLISH | REQUEST, NULL);
+ }
return (2);
}
for (i = 0; i < 2; i++) {
FsmDelTimer(&csta->channel[i].drel_timer, 74);
FsmDelTimer(&csta->channel[i].dial_timer, 75);
- FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77);
- FsmDelTimer(&csta->channel[i].lc_b->act_timer, 76);
if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
release_d_st(csta->channel + i);
if (csta->channel[i].b_st) {
csta->channel[i].b_st = NULL;
} else
printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
- if (csta->channel[i].lc_b) {
- kfree(csta->channel[i].lc_b);
- csta->channel[i].b_st = NULL;
- }
if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
release_d_st(csta->channel + i);
- FsmDelTimer(&csta->channel[i].lc_d->act_timer, 77);
- if (csta->channel[i].lc_d) {
- kfree(csta->channel[i].lc_d);
- csta->channel[i].d_st = NULL;
- } else
- printk(KERN_WARNING "CallcFreeChan lc_d ch%d allready freed\n", i);
} else
csta->channel[i].d_st = NULL;
}
struct sk_buff *skb = arg;
switch (pr) {
- case (DL_DATA):
+ case (DL_DATA | INDICATION):
if (chanp->data_open)
chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
else {
dev_kfree_skb(skb);
}
break;
+ case (DL_ESTABLISH | INDICATION):
+ case (DL_ESTABLISH | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+ break;
+ case (DL_RELEASE | INDICATION):
+ case (DL_RELEASE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+ break;
default:
- printk(KERN_WARNING "lldata_handler unknown primitive %d\n",
+ printk(KERN_WARNING "lldata_handler unknown primitive %x\n",
pr);
break;
}
struct sk_buff *skb = arg;
switch (pr) {
- case (PH_DATA_IND):
+ case (PH_DATA | INDICATION):
if (chanp->data_open)
chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
else {
- if (chanp->lc_b->lcfi.state == ST_LC_DELAY)
- FsmEvent(&chanp->lc_b->lcfi, EV_LC_DL_ESTABLISH, NULL);
- if (chanp->data_open) {
- link_debug(chanp, "channel now open", 0);
- chanp->cs->iif.rcvcallb_skb(chanp->cs->myid,
- chanp->chan, skb);
- } else
- dev_kfree_skb(skb);
+ link_debug(chanp, 0, "channel not open");
+ dev_kfree_skb(skb);
}
break;
+ case (PH_ACTIVATE | INDICATION):
+ case (PH_ACTIVATE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+ break;
+ case (PH_DEACTIVATE | INDICATION):
+ case (PH_DEACTIVATE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+ break;
default:
- printk(KERN_WARNING "lltrans_handler unknown primitive %d\n",
+ printk(KERN_WARNING "lltrans_handler unknown primitive %x\n",
pr);
break;
}
ic.driver = chanp->cs->myid;
ic.command = ISDN_STAT_BSENT;
ic.arg = chanp->chan;
- ic.parm.length = len;
+// ic.parm.length = len;
chanp->cs->iif.statcallb(&ic);
}
{
struct PStack *st = chanp->b_st;
struct IsdnCardState *cs = chanp->cs;
- char tmp[128];
+ char tmp[16];
st->l1.hardware = cs;
- chanp->bcs->mode = 2;
+ if (chanp->leased)
+ st->l1.bc = chanp->chan & 1;
+ else
+ st->l1.bc = chanp->proc->para.bchannel - 1;
+ switch (chanp->l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ case (ISDN_PROTO_L2_HDLC):
+ st->l1.mode = L1_MODE_HDLC;
+ break;
+ case (ISDN_PROTO_L2_TRANS):
+ st->l1.mode = L1_MODE_TRANS;
+ break;
+#if 0
+ case (ISDN_PROTO_L2_MODEM):
+ st->l1.mode = L1_MODE_MODEM;
+ break;
+#endif
+ }
if (chanp->bcs->BC_SetStack(st, chanp->bcs))
return (-1);
st->l2.flag = 0;
st->l3.debug = 0;
switch (chanp->l2_active_protocol) {
case (ISDN_PROTO_L2_X75I):
- sprintf(tmp, "Channel %d x.75", chanp->chan);
+ sprintf(tmp, "Ch%d X.75", chanp->chan);
setstack_isdnl2(st, tmp);
+ setstack_l3bc(st, chanp);
st->l2.l2l3 = lldata_handler;
- st->l1.l1man = bc_l1man;
- st->l2.l2man = bc_l2man;
st->lli.userdata = chanp;
st->lli.l1writewakeup = NULL;
st->lli.l2writewakeup = ll_writewakeup;
st->l2.l2m.debug = chanp->debug & 16;
st->l2.debug = chanp->debug & 64;
- st->ma.manl2(st, MDL_NOTEIPROC, NULL);
- st->l1.mode = L1_MODE_HDLC;
- if (chanp->leased)
- st->l1.bc = chanp->chan & 1;
- else
- st->l1.bc = chanp->proc->para.bchannel - 1;
break;
case (ISDN_PROTO_L2_HDLC):
- st->l1.l1l2 = lltrans_handler;
- st->l1.l1man = bc_l1man;
- st->lli.userdata = chanp;
- st->lli.l1writewakeup = ll_writewakeup;
- st->l1.mode = L1_MODE_HDLC;
- if (chanp->leased)
- st->l1.bc = chanp->chan & 1;
- else
- st->l1.bc = chanp->proc->para.bchannel - 1;
- break;
case (ISDN_PROTO_L2_TRANS):
+// case (ISDN_PROTO_L2_MODEM):
st->l1.l1l2 = lltrans_handler;
- st->l1.l1man = bc_l1man;
st->lli.userdata = chanp;
st->lli.l1writewakeup = ll_writewakeup;
- st->l1.mode = L1_MODE_TRANS;
- if (chanp->leased)
- st->l1.bc = chanp->chan & 1;
- else
- st->l1.bc = chanp->proc->para.bchannel - 1;
+ setstack_transl2(st);
+ setstack_l3bc(st, chanp);
break;
}
return (0);
}
+static void
+leased_l4l3(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->lli.userdata;
+ struct sk_buff *skb = arg;
+
+ switch (pr) {
+ case (DL_DATA | REQUEST):
+ link_debug(chanp, 0, "leased line d-channel DATA");
+ dev_kfree_skb(skb);
+ break;
+ case (DL_ESTABLISH | REQUEST):
+ st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+ break;
+ case (DL_RELEASE | REQUEST):
+ break;
+ default:
+ printk(KERN_WARNING "transd_l4l3 unknown primitive %x\n",
+ pr);
+ break;
+ }
+}
+
+static void
+leased_l1l2(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->lli.userdata;
+ struct sk_buff *skb = arg;
+ int i,event = EV_DLRL;
+
+ switch (pr) {
+ case (PH_DATA | INDICATION):
+ link_debug(chanp, 0, "leased line d-channel DATA");
+ dev_kfree_skb(skb);
+ break;
+ case (PH_ACTIVATE | INDICATION):
+ case (PH_ACTIVATE | CONFIRM):
+ event = EV_DLEST;
+ case (PH_DEACTIVATE | INDICATION):
+ case (PH_DEACTIVATE | CONFIRM):
+ if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags))
+ i = 1;
+ else
+ i = 0;
+ while (i < 2) {
+ FsmEvent(&chanp->fi, event, NULL);
+ chanp++;
+ i++;
+ }
+ break;
+ default:
+ printk(KERN_WARNING
+ "transd_l1l2 unknown primitive %x\n", pr);
+ break;
+ }
+}
+
static void
channel_report(struct Channel *chanp)
{
chanp[i].b_st->l2.l2m.debug = debugflags & 0x10;
chanp[i].d_st->l2.debug = debugflags & 0x20;
chanp[i].b_st->l2.debug = debugflags & 0x40;
- chanp[i].lc_d->lcfi.debug = debugflags & 0x80;
- chanp[i].lc_b->lcfi.debug = debugflags & 0x100;
+ chanp[i].d_st->l3.l3m.debug = debugflags & 0x80;
+ chanp[i].b_st->l3.l3m.debug = debugflags & 0x100;
chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200;
chanp[i].b_st->ma.debug = debugflags & 0x200;
chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000;
+ chanp[i].b_st->l1.l1m.debug = debugflags & 0x2000;
+ }
+ if (debugflags & 4)
+ csta->debug |= DEB_DLOG_HEX;
+ else
+ csta->debug &= ~DEB_DLOG_HEX;
+}
+
+static char tmpbuf[256];
+
+static void
+capi_debug(struct Channel *chanp, capi_msg *cm)
+{
+ char *t = tmpbuf;
+
+ t += sprintf(tmpbuf, "%d CAPIMSG", chanp->chan);
+ t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length);
+ t--;
+ *t= 0;
+ HiSax_putstatus(chanp->cs, "Ch", "%d CAPIMSG %s", chanp->chan, tmpbuf);
+}
+
+void
+lli_got_fac_req(struct Channel *chanp, capi_msg *cm) {
+ if ((cm->para[0] != 3) || (cm->para[1] != 0))
+ return;
+ if (cm->para[2]<3)
+ return;
+ if (cm->para[4] != 0)
+ return;
+ switch(cm->para[3]) {
+ case 4: /* Suspend */
+ if (cm->para[5]) {
+ strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+ FsmEvent(&chanp->fi, EV_SUSPEND, cm);
+ }
+ break;
+ case 5: /* Resume */
+ if (cm->para[5]) {
+ strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+ if (chanp->fi.state == ST_NULL) {
+ FsmEvent(&chanp->fi, EV_RESUME, cm);
+ } else {
+ FsmDelTimer(&chanp->dial_timer, 72);
+ FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73);
+ }
+ }
+ break;
+ }
+}
+
+void
+lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) {
+ if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) ||
+ (cs->typ == ISDN_CTYPE_ELSA_PCI)) {
+ if (cs->hw.elsa.MFlag) {
+ cs->cardmsg(cs, CARD_AUX_IND, cm->para);
+ }
}
- csta->dlogflag = debugflags & 4;
}
int
{
struct IsdnCardState *csta = hisax_findcard(ic->driver);
struct Channel *chanp;
- char tmp[128];
int i;
- unsigned int num;
+ u_int num;
+ u_long adr;
if (!csta) {
printk(KERN_ERR
ic->command, ic->driver);
return -ENODEV;
}
+
switch (ic->command) {
case (ISDN_CMD_SETEAZ):
chanp = csta->channel + ic->arg;
- if (chanp->debug & 1) {
- sprintf(tmp, "SETEAZ card %d %s", csta->cardnr + 1,
- ic->parm.num);
- link_debug(chanp, tmp, 1);
- }
break;
+
case (ISDN_CMD_SETL2):
chanp = csta->channel + (ic->arg & 0xff);
- if (chanp->debug & 1) {
- sprintf(tmp, "SETL2 card %d %ld", csta->cardnr + 1,
- ic->arg >> 8);
- link_debug(chanp, tmp, 1);
- }
+ if (chanp->debug & 1)
+ link_debug(chanp, 1, "SETL2 card %d %ld",
+ csta->cardnr + 1, ic->arg >> 8);
chanp->l2_protocol = ic->arg >> 8;
break;
+ case (ISDN_CMD_SETL3):
+ chanp = csta->channel + (ic->arg & 0xff);
+ if (chanp->debug & 1)
+ link_debug(chanp, 1, "SETL3 card %d %ld",
+ csta->cardnr + 1, ic->arg >> 8);
+ chanp->l3_protocol = ic->arg >> 8;
+ break;
case (ISDN_CMD_DIAL):
chanp = csta->channel + (ic->arg & 0xff);
- if (chanp->debug & 1) {
- sprintf(tmp, "DIAL %s -> %s (%d,%d)",
+ if (chanp->debug & 1)
+ link_debug(chanp, 1, "DIAL %s -> %s (%d,%d)",
ic->parm.setup.eazmsn, ic->parm.setup.phone,
- ic->parm.setup.si1, ic->parm.setup.si2);
- link_debug(chanp, tmp, 1);
- }
+ ic->parm.setup.si1, ic->parm.setup.si2);
chanp->setup = ic->parm.setup;
if (!strcmp(chanp->setup.eazmsn, "0"))
chanp->setup.eazmsn[0] = '\0';
case (ISDN_CMD_ACCEPTB):
chanp = csta->channel + ic->arg;
if (chanp->debug & 1)
- link_debug(chanp, "ACCEPTB", 1);
+ link_debug(chanp, 1, "ACCEPTB");
FsmEvent(&chanp->fi, EV_ACCEPTB, NULL);
break;
case (ISDN_CMD_ACCEPTD):
chanp = csta->channel + ic->arg;
if (chanp->debug & 1)
- link_debug(chanp, "ACCEPTD", 1);
+ link_debug(chanp, 1, "ACCEPTD");
FsmEvent(&chanp->fi, EV_ACCEPTD, NULL);
break;
case (ISDN_CMD_HANGUP):
chanp = csta->channel + ic->arg;
if (chanp->debug & 1)
- link_debug(chanp, "HANGUP", 1);
+ link_debug(chanp, 1, "HANGUP");
FsmEvent(&chanp->fi, EV_HANGUP, NULL);
break;
- case (ISDN_CMD_SUSPEND):
- chanp = csta->channel + ic->arg;
- if (chanp->debug & 1) {
- sprintf(tmp, "SUSPEND %s", ic->parm.num);
- link_debug(chanp, tmp, 1);
- }
- FsmEvent(&chanp->fi, EV_SUSPEND, ic);
- break;
- case (ISDN_CMD_RESUME):
+ case (CAPI_PUT_MESSAGE):
chanp = csta->channel + ic->arg;
- if (chanp->debug & 1) {
- sprintf(tmp, "RESUME %s", ic->parm.num);
- link_debug(chanp, tmp, 1);
+ if (chanp->debug & 1)
+ capi_debug(chanp, &ic->parm.cmsg);
+ if (ic->parm.cmsg.Length < 8)
+ break;
+ switch(ic->parm.cmsg.Command) {
+ case CAPI_FACILITY:
+ if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+ lli_got_fac_req(chanp, &ic->parm.cmsg);
+ break;
+ case CAPI_MANUFACTURER:
+ if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+ lli_got_manufacturer(chanp, csta, &ic->parm.cmsg);
+ break;
+ default:
+ break;
}
- FsmEvent(&chanp->fi, EV_RESUME, ic);
break;
case (ISDN_CMD_LOCK):
HiSax_mod_inc_use_count();
#ifdef MODULE
- if (csta->channel[0].debug & 0x400) {
- jiftime(tmp, jiffies);
- i = strlen(tmp);
- sprintf(tmp + i, " LOCK modcnt %d\n", MOD_USE_COUNT);
- HiSax_putstatus(csta, tmp);
- }
+ if (csta->channel[0].debug & 0x400)
+ HiSax_putstatus(csta, " LOCK ", "modcnt %x",
+ MOD_USE_COUNT);
#endif /* MODULE */
break;
case (ISDN_CMD_UNLOCK):
HiSax_mod_dec_use_count();
#ifdef MODULE
- if (csta->channel[0].debug & 0x400) {
- jiftime(tmp, jiffies);
- i = strlen(tmp);
- sprintf(tmp + i, " UNLOCK modcnt %d\n", MOD_USE_COUNT);
- HiSax_putstatus(csta, tmp);
- }
+ if (csta->channel[0].debug & 0x400)
+ HiSax_putstatus(csta, " UNLOCK ", "modcnt %x",
+ MOD_USE_COUNT);
#endif /* MODULE */
break;
case (ISDN_CMD_IOCTL):
case (1):
num = *(unsigned int *) ic->parm.num;
distr_debug(csta, num);
- sprintf(tmp, "debugging flags card %d set to %x\n",
+ printk(KERN_DEBUG "HiSax: debugging flags card %d set to %x\n",
csta->cardnr + 1, num);
- HiSax_putstatus(csta, tmp);
- printk(KERN_DEBUG "HiSax: %s", tmp);
+ HiSax_putstatus(csta, "debugging flags ",
+ "card %d set to %x", csta->cardnr + 1, num);
break;
case (2):
- num = *(unsigned int *) ic->parm.num;
- csta->channel[0].lc_b->delay = num;
- csta->channel[1].lc_b->delay = num;
- sprintf(tmp, "delay card %d set to %d ms\n",
+ num = *(unsigned int *) ic->parm.num;
+ csta->channel[0].b_st->l1.delay = num;
+ csta->channel[1].b_st->l1.delay = num;
+ HiSax_putstatus(csta, "delay ", "card %d set to %d ms",
+ csta->cardnr + 1, num);
+ printk(KERN_DEBUG "HiSax: delay card %d set to %d ms\n",
csta->cardnr + 1, num);
- HiSax_putstatus(csta, tmp);
- printk(KERN_DEBUG "HiSax: %s", tmp);
break;
case (3):
for (i = 0; i < *(unsigned int *) ic->parm.num; i++)
case (5): /* set card in leased mode */
num = *(unsigned int *) ic->parm.num;
if ((num <1) || (num > 2)) {
- sprintf(tmp, "Set LEASED wrong channel %d\n",
+ HiSax_putstatus(csta, "Set LEASED ",
+ "wrong channel %d", num);
+ printk(KERN_WARNING "HiSax: Set LEASED wrong channel %d\n",
num);
- HiSax_putstatus(csta, tmp);
- printk(KERN_WARNING "HiSax: %s", tmp);
} else {
num--;
- csta->channel[num].leased = 1;
- csta->channel[num].lc_d->l2_establish = 0;
- sprintf(tmp, "card %d channel %d set leased mode\n",
+ chanp = csta->channel +num;
+ chanp->leased = 1;
+ HiSax_putstatus(csta, "Card",
+ "%d channel %d set leased mode\n",
csta->cardnr + 1, num + 1);
- HiSax_putstatus(csta, tmp);
- FsmEvent(&csta->channel[num].lc_d->lcfi, EV_LC_ESTABLISH, NULL);
+ chanp->d_st->l1.l1l2 = leased_l1l2;
+ chanp->d_st->lli.l4l3 = leased_l4l3;
+ chanp->d_st->lli.l4l3(chanp->d_st,
+ DL_ESTABLISH | REQUEST, NULL);
}
break;
case (6): /* set B-channel test loop */
num = *(unsigned int *) ic->parm.num;
if (csta->stlist)
- csta->stlist->ma.manl1(csta->stlist,
- PH_TESTLOOP_REQ, (void *) num);
+ csta->stlist->l2.l2l1(csta->stlist,
+ PH_TESTLOOP | REQUEST, (void *) (long)num);
+ break;
+ case (7): /* set card in PTP mode */
+ num = *(unsigned int *) ic->parm.num;
+ if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+ printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n");
+ } else if (num) {
+ test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+ test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+ csta->channel[0].d_st->l2.tei = 0;
+ HiSax_putstatus(csta, "set card ", "in PTP mode");
+ printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
+ printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+ test_and_set_bit(FLG_START_D, &csta->channel[0].Flags);
+ test_and_set_bit(FLG_START_D, &csta->channel[1].Flags);
+ csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
+ DL_ESTABLISH | REQUEST, NULL);
+ } else {
+ test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+ test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+ HiSax_putstatus(csta, "set card ", "in PTMP mode");
+ printk(KERN_DEBUG "HiSax: set card in PTMP mode\n");
+ }
+ break;
+ case (8): /* set card in FIXED TEI mode */
+ num = *(unsigned int *) ic->parm.num;
+ chanp = csta->channel + (num & 1);
+ num = num >>1;
+ test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+ chanp->d_st->l2.tei = num;
+ HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
+ printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
+ num);
+ break;
+ case (9): /* load firmware */
+ memcpy(&adr, ic->parm.num, sizeof(ulong));
+ csta->cardmsg(csta, CARD_LOAD_FIRM,
+ (void *) adr);
break;
#ifdef MODULE
case (55):
while ( MOD_USE_COUNT > 0)
- MOD_DEC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
HiSax_mod_inc_use_count();
break;
#endif /* MODULE */
case (11):
+ num = csta->debug & DEB_DLOG_HEX;
csta->debug = *(unsigned int *) ic->parm.num;
- sprintf(tmp, "l1 debugging flags card %d set to %x\n",
+ csta->debug |= num;
+ HiSax_putstatus(cards[0].cs, "l1 debugging ",
+ "flags card %d set to %x",
+ csta->cardnr + 1, csta->debug);
+ printk(KERN_DEBUG "HiSax: l1 debugging flags card %d set to %x\n",
csta->cardnr + 1, csta->debug);
- HiSax_putstatus(cards[0].cs, tmp);
- printk(KERN_DEBUG "HiSax: %s", tmp);
break;
case (13):
csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num;
csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num;
- sprintf(tmp, "l3 debugging flags card %d set to %x\n",
+ HiSax_putstatus(cards[0].cs, "l3 debugging ",
+ "flags card %d set to %x\n", csta->cardnr + 1,
+ *(unsigned int *) ic->parm.num);
+ printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n",
csta->cardnr + 1, *(unsigned int *) ic->parm.num);
- HiSax_putstatus(cards[0].cs, tmp);
- printk(KERN_DEBUG "HiSax: %s", tmp);
break;
default:
printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
int len = skb->len;
unsigned long flags;
struct sk_buff *nskb;
- char tmp[64];
if (!csta) {
printk(KERN_ERR
chanp = csta->channel + chan;
st = chanp->b_st;
if (!chanp->data_open) {
- link_debug(chanp, "writebuf: channel not open", 1);
+ link_debug(chanp, 1, "writebuf: channel not open");
return -EIO;
}
if (len > MAX_DATA_SIZE) {
- sprintf(tmp, "writebuf: packet too large (%d bytes)", len);
- printk(KERN_WARNING "HiSax_%s !\n", tmp);
- link_debug(chanp, tmp, 1);
+ link_debug(chanp, 1, "writebuf: packet too large (%d bytes)", len);
+ printk(KERN_WARNING "HiSax_writebuf: packet too large (%d bytes) !\n",
+ len);
return -EINVAL;
}
if (len) {
/* Must return 0 here, since this is not an error
* but a temporary lack of resources.
*/
- if (chanp->debug & 0x800) {
- sprintf(tmp, "writebuf: no buffers for %d bytes", len);
- link_debug(chanp, tmp, 1);
- }
+ if (chanp->debug & 0x800)
+ link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len);
return 0;
}
save_flags(flags);
if (nskb) {
if (!ack)
nskb->pkt_type = PACKET_NOACK;
- if (chanp->lc_b->l2_establish)
- st->l3.l3l2(st, DL_DATA, nskb);
+ if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
+ st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
else {
chanp->bcs->tx_cnt += len;
- st->l2.l2l1(st, PH_DATA_REQ, nskb);
+ st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
}
dev_kfree_skb(skb);
} else
--- /dev/null
+/* $Id: cert.c,v 2.1 1998/11/15 23:51:15 keil Exp $
+
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
+ *
+ * $Log: cert.c,v $
+ * Revision 2.1 1998/11/15 23:51:15 keil
+ * certification stuff
+ *
+ * Revision 1.2.2.1 1998/11/03 21:46:37 keil
+ * first version
+ *
+ *
+ */
+
+#include <linux/kernel.h>
+
+int
+certification_check(int output) {
+
+#ifdef CERTIFICATION
+#if CERTIFICATION == 0
+ if (output) {
+ printk(KERN_INFO "HiSax: Approval certification valid\n");
+ printk(KERN_INFO "HiSax: Approved with ELSA Quickstep series cards\n");
+ printk(KERN_INFO "HiSax: Approval registration numbers:\n");
+ printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n");
+ printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n");
+ }
+ return(0);
+#endif
+#if CERTIFICATION == 1
+ if (output) {
+ printk(KERN_INFO "HiSax: Approval certification failed because of\n");
+ printk(KERN_INFO "HiSax: unauthorized source code changes\n");
+ }
+ return(1);
+#endif
+#if CERTIFICATION == 127
+ if (output) {
+ printk(KERN_INFO "HiSax: Approval certification not possible\n");
+ printk(KERN_INFO "HiSax: because \"md5sum\" is not available\n");
+ }
+ return(2);
+#endif
+#else
+ if (output) {
+ printk(KERN_INFO "HiSax: Certification not verified\n");
+ }
+ return(3);
+#endif
+}
-/* $Id: config.c,v 2.12 1998/02/11 17:28:02 keil Exp $
+/* $Id: config.c,v 2.23 1999/02/17 10:53:02 cpetig Exp $
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
* based on the teles driver from Jan den Ouden
*
*
* $Log: config.c,v $
+ * Revision 2.23 1999/02/17 10:53:02 cpetig
+ * Added Hisax_closecard to exported symbols.
+ * As indicated by Oliver Schoett <os@sdm.de>.
+ *
+ * If anyone is annoyed by exporting symbols deep inside the code, please
+ * contact me.
+ *
+ * Revision 2.22 1999/02/04 21:41:53 keil
+ * Fix printk msg
+ *
+ * Revision 2.21 1999/02/04 10:48:52 keil
+ * Fix readstat bug
+ *
+ * Revision 2.20 1998/11/15 23:54:28 keil
+ * changes from 2.0
+ *
+ * Revision 2.19 1998/08/13 23:36:18 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.18 1998/07/30 21:01:37 niemann
+ * Fixed Sedlbauer Speed Fax PCMCIA missing isdnl3new
+ *
+ * Revision 2.17 1998/07/15 15:01:26 calle
+ * Support for AVM passive PCMCIA cards:
+ * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 2.16 1998/05/25 14:10:03 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.15 1998/05/25 12:57:43 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.14 1998/04/15 16:38:25 keil
+ * Add S0Box and Teles PCI support
+ *
+ * Revision 2.13 1998/03/09 23:19:23 keil
+ * Changes for PCMCIA
+ *
* Revision 2.12 1998/02/11 17:28:02 keil
* Niccy PnP/PCI support
*
#include <linux/timer.h>
#include <linux/config.h>
#include "hisax.h"
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#define HISAX_STATUS_BUFSIZE 4096
+#define INCLUDE_INLINE_FUNCS
/*
* This structure array contains one entry per card. An entry looks
* 13 Teleint p0=irq p1=iobase
* 14 Teles 16.3c p0=irq p1=iobase
* 15 Sedlbauer speed p0=irq p1=iobase
+ * 15 Sedlbauer PC/104 p0=irq p1=iobase
+ * 15 Sedlbauer speed pci no parameter
* 16 USR Sportster internal p0=irq p1=iobase
* 17 MIC card p0=irq p1=iobase
* 18 ELSA Quickstep 1000PCI no parameter
* 19 Compaq ISDN S0 ISA card p0=irq p1=IO0 (HSCX) p2=IO1 (ISAC) p3=IO2
* 20 Travers Technologies NETjet PCI card
- * 21 reserved TELES PCI
+ * 21 TELES PCI no parameter
* 22 Sedlbauer Speed Star p0=irq p1=iobase
* 23 reserved
* 24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only)
- *
+ * 25 Teles S0Box p0=irq p1=iobase (from isapnp setup)
+ * 26 AVM A1 PCMCIA (Fritz) p0=irq p1=iobase
+ * 27 AVM PnP/PCI p0=irq p1=iobase (PCI no parameter)
+ * 28 Sedlbauer Speed Fax+ p0=irq p1=iobase (from isapnp setup)
*
* protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
*
*
*/
+const char *CardType[] =
+{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP",
+ "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2",
+ "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c",
+ "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
+ "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
+ "AMD 7930", "NICCY", "S0Box", "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI",
+ "Sedlbauer Speed Fax +"
+};
+
#ifdef CONFIG_HISAX_ELSA
#define DEFAULT_CARD ISDN_CTYPE_ELSA
#define DEFAULT_CFG {0,0,0,0}
-#ifdef MODULE
int elsa_init_pcmcia(void*, int, int*, int);
EXPORT_SYMBOL(elsa_init_pcmcia);
#endif
-#endif
#ifdef CONFIG_HISAX_AVM_A1
#undef DEFAULT_CARD
#undef DEFAULT_CFG
#define DEFAULT_CARD ISDN_CTYPE_A1
#define DEFAULT_CFG {10,0x340,0,0}
#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA
+#define DEFAULT_CFG {11,0x170,0,0}
+int avm_a1_init_pcmcia(void*, int, int*, int);
+EXPORT_SYMBOL(avm_a1_init_pcmcia);
+#endif
+
+#ifdef CONFIG_HISAX_FRITZPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_FRITZPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
#ifdef CONFIG_HISAX_16_3
#undef DEFAULT_CARD
#undef DEFAULT_CFG
#define DEFAULT_CARD ISDN_CTYPE_16_3
#define DEFAULT_CFG {15,0x180,0,0}
#endif
+#ifdef CONFIG_HISAX_S0BOX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_S0BOX
+#define DEFAULT_CFG {7,0x378,0,0}
+#endif
#ifdef CONFIG_HISAX_16_0
#undef DEFAULT_CARD
#undef DEFAULT_CFG
#define DEFAULT_CFG {15,0xd0000,0xd80,0}
#endif
+#ifdef CONFIG_HISAX_TELESPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELESPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
#ifdef CONFIG_HISAX_IX1MICROR2
#undef DEFAULT_CARD
#undef DEFAULT_CFG
#define DEFAULT_CFG {12,0x3e0,0,0}
#endif
-#ifdef CONFIG_HISAX_DBRI
-#undef DEFAULT_CARD
-#undef DEFAULT_CFG
-#define DEFAULT_CARD ISDN_CTYPE_DBRI
-#define DEFAULT_CFG {0,0x0,0,0}
-#endif
-
#ifdef CONFIG_HISAX_NICCY
#undef DEFAULT_CARD
#undef DEFAULT_CFG
#endif
#define FIRST_CARD { \
- DEFAULT_CARD, \
- DEFAULT_PROTO, \
- DEFAULT_CFG, \
- NULL, \
+ DEFAULT_CARD, \
+ DEFAULT_PROTO, \
+ DEFAULT_CFG, \
+ NULL, \
}
#define EMPTY_CARD {0, DEFAULT_PROTO, {0, 0, 0, 0}, NULL}
EMPTY_CARD,
EMPTY_CARD,
EMPTY_CARD,
- EMPTY_CARD,
- EMPTY_CARD,
- EMPTY_CARD,
- EMPTY_CARD,
- EMPTY_CARD,
- EMPTY_CARD,
- EMPTY_CARD,
- EMPTY_CARD,
};
-static char HiSaxID[96] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
+static char HiSaxID[64] HISAX_INITDATA = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" \
-"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
char *HiSax_id HISAX_INITDATA = HiSaxID;
#ifdef MODULE
/* Variables for insmod */
static int type[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
static int protocol[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
static int io[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
#undef IO0_IO1
#ifdef CONFIG_HISAX_16_3
#define IO0_IO1
#endif
#ifdef IO0_IO1
static int io0[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
static int io1[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
#endif
static int irq[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
static int mem[] HISAX_INITDATA =
-{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+{0, 0, 0, 0, 0, 0, 0, 0};
static char *id HISAX_INITDATA = HiSaxID;
MODULE_AUTHOR("Karsten Keil");
-MODULE_PARM(type, "1-3i");
-MODULE_PARM(protocol, "1-2i");
+MODULE_PARM(type, "1-8i");
+MODULE_PARM(protocol, "1-8i");
MODULE_PARM(io, "1-8i");
-MODULE_PARM(irq, "1-2i");
-MODULE_PARM(mem, "1-12i");
+MODULE_PARM(irq, "1-8i");
+MODULE_PARM(mem, "1-8i");
MODULE_PARM(id, "s");
#ifdef CONFIG_HISAX_16_3 /* For Creatix/Teles PnP */
MODULE_PARM(io0, "1-8i");
MODULE_PARM(io1, "1-8i");
-#endif
+#endif /* CONFIG_HISAX_16_3 */
-#endif
+#endif /* MODULE */
int nrcards;
HISAX_INITFUNC(void
HiSaxVersion(void))
{
- char tmp[64], rev[64];
- char *r = rev;
+ char tmp[64];
+ printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
+#ifdef MODULE
+ printk(KERN_INFO "HiSax: Version 3.1a (module)\n");
+#else
+ printk(KERN_INFO "HiSax: Version 3.1a (kernel)\n");
+#endif
strcpy(tmp, l1_revision);
- r += sprintf(r, "%s/", HiSax_getrev(tmp));
+ printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
strcpy(tmp, l2_revision);
- r += sprintf(r, "%s/", HiSax_getrev(tmp));
+ printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp));
+ strcpy(tmp, tei_revision);
+ printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp));
strcpy(tmp, l3_revision);
- r += sprintf(r, "%s/", HiSax_getrev(tmp));
+ printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp));
strcpy(tmp, lli_revision);
- r += sprintf(r, "%s/", HiSax_getrev(tmp));
- strcpy(tmp, tei_revision);
- r += sprintf(r, "%s", HiSax_getrev(tmp));
-
- printk(KERN_INFO "HiSax: Driver for Siemens chip set ISDN cards\n");
- printk(KERN_INFO "HiSax: Version 2.8\n");
- printk(KERN_INFO "HiSax: Revisions %s\n", rev);
+ printk(KERN_INFO "HiSax: LinkLayer Revision %s\n", HiSax_getrev(tmp));
+ certification_check(1);
}
void
argc = ints[0];
i = 0;
j = 1;
- while (argc && (i < 16)) {
+ while (argc && (i < HISAX_MAX_CARDS)) {
if (argc) {
cards[i].typ = ints[j];
j++;
}
#endif
+#if CARD_TELES0
+extern int setup_teles0(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3
+extern int setup_teles3(struct IsdnCard *card);
+#endif
+
+#if CARD_S0BOX
+extern int setup_s0box(struct IsdnCard *card);
+#endif
+
+#if CARD_TELESPCI
+extern int setup_telespci(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1
+extern int setup_avm_a1(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1_PCMCIA
+extern int setup_avm_a1_pcmcia(struct IsdnCard *card);
+#endif
+
+#if CARD_FRITZPCI
+extern int setup_avm_pcipnp(struct IsdnCard *card);
+#endif
+
+#if CARD_ELSA
+extern int setup_elsa(struct IsdnCard *card);
+#endif
+
+#if CARD_IX1MICROR2
+extern int setup_ix1micro(struct IsdnCard *card);
+#endif
+
+#if CARD_DIEHLDIVA
+extern int setup_diva(struct IsdnCard *card);
+#endif
+
+#if CARD_ASUSCOM
+extern int setup_asuscom(struct IsdnCard *card);
+#endif
+
+#if CARD_TELEINT
+extern int setup_TeleInt(struct IsdnCard *card);
+#endif
+
+#if CARD_SEDLBAUER
+extern int setup_sedlbauer(struct IsdnCard *card);
+#endif
+
+#if CARD_SPORTSTER
+extern int setup_sportster(struct IsdnCard *card);
+#endif
+
+#if CARD_MIC
+extern int setup_mic(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET
+extern int setup_netjet(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3C
+extern int setup_t163c(struct IsdnCard *card);
+#endif
+
+#if CARD_AMD7930
+extern int setup_amd7930(struct IsdnCard *card);
+#endif
+
+#if CARD_NICCY
+extern int setup_niccy(struct IsdnCard *card);
+#endif
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState
+*hisax_findcard(int driverid)
+{
+ int i;
+
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].cs)
+ if (cards[i].cs->myid == driverid)
+ return (cards[i].cs);
+ return (NULL);
+}
+
+int
+HiSax_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+ int count,cnt;
+ u_char *p = buf;
+ struct IsdnCardState *cs = hisax_findcard(id);
+
+ if (cs) {
+ if (len > HISAX_STATUS_BUFSIZE) {
+ printk(KERN_WARNING "HiSax: status overflow readstat %d/%d\n",
+ len, HISAX_STATUS_BUFSIZE);
+ }
+ count = cs->status_end - cs->status_read +1;
+ if (count >= len)
+ count = len;
+ if (user)
+ copy_to_user(p, cs->status_read, count);
+ else
+ memcpy(p, cs->status_read, count);
+ cs->status_read += count;
+ if (cs->status_read > cs->status_end)
+ cs->status_read = cs->status_buf;
+ p += count;
+ count = len - count;
+ while (count) {
+ if (count > HISAX_STATUS_BUFSIZE)
+ cnt = HISAX_STATUS_BUFSIZE;
+ else
+ cnt = count;
+ if (user)
+ copy_to_user(p, cs->status_read, cnt);
+ else
+ memcpy(p, cs->status_read, cnt);
+ p += cnt;
+ cs->status_read += cnt % HISAX_STATUS_BUFSIZE;
+ count -= cnt;
+ }
+ return len;
+ } else {
+ printk(KERN_ERR
+ "HiSax: if_readstatus called with invalid driverId!\n");
+ return -ENODEV;
+ }
+}
+
+inline int
+jiftime(char *s, long mark)
+{
+ s += 8;
+
+ *s-- = '\0';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = '.';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 6 + '0';
+ mark /= 6;
+ *s-- = ':';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 10 + '0';
+ return(8);
+}
+
+static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
+
+void
+VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args)
+{
+/* if head == NULL the fmt contains the full info */
+
+ long flags;
+ int count, i;
+ u_char *p;
+ isdn_ctrl ic;
+ int len;
+
+ save_flags(flags);
+ cli();
+ p = tmpbuf;
+ if (head) {
+ p += jiftime(p, jiffies);
+ p += sprintf(p, " %s", head);
+ p += vsprintf(p, fmt, args);
+ *p++ = '\n';
+ *p = 0;
+ len = p - tmpbuf;
+ p = tmpbuf;
+ } else {
+ p = fmt;
+ len = strlen(fmt);
+ }
+ if (!cs) {
+ printk(KERN_WARNING "HiSax: No CardStatus for message %s", p);
+ restore_flags(flags);
+ return;
+ }
+ if (len > HISAX_STATUS_BUFSIZE) {
+ printk(KERN_WARNING "HiSax: status overflow %d/%d\n",
+ len, HISAX_STATUS_BUFSIZE);
+ restore_flags(flags);
+ return;
+ }
+ count = len;
+ i = cs->status_end - cs->status_write +1;
+ if (i >= len)
+ i = len;
+ len -= i;
+ memcpy(cs->status_write, p, i);
+ cs->status_write += i;
+ if (cs->status_write > cs->status_end)
+ cs->status_write = cs->status_buf;
+ p += i;
+ if (len) {
+ memcpy(cs->status_write, p, len);
+ cs->status_write += len;
+ }
+#ifdef KERNELSTACK_DEBUG
+ i = (ulong)&len - current->kernel_stack_page;
+ sprintf(tmpbuf, "kstack %s %lx use %ld\n", current->comm,
+ current->kernel_stack_page, i);
+ len = strlen(tmpbuf);
+ for (p = tmpbuf, i = len; i > 0; i--, p++) {
+ *cs->status_write++ = *p;
+ if (cs->status_write > cs->status_end)
+ cs->status_write = cs->status_buf;
+ count++;
+ }
+#endif
+ restore_flags(flags);
+ if (count) {
+ ic.command = ISDN_STAT_STAVAIL;
+ ic.driver = cs->myid;
+ ic.arg = count;
+ cs->iif.statcallb(&ic);
+ }
+}
+
+void
+HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ VHiSax_putstatus(cs, head, fmt, args);
+ va_end(args);
+}
+
+int
+ll_run(struct IsdnCardState *cs)
+{
+ long flags;
+ isdn_ctrl ic;
+
+ save_flags(flags);
+ cli();
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_RUN;
+ cs->iif.statcallb(&ic);
+ restore_flags(flags);
+ return 0;
+}
+
+void
+ll_stop(struct IsdnCardState *cs)
+{
+ isdn_ctrl ic;
+
+ ic.command = ISDN_STAT_STOP;
+ ic.driver = cs->myid;
+ cs->iif.statcallb(&ic);
+ CallcFreeChan(cs);
+}
+
+static void
+ll_unload(struct IsdnCardState *cs)
+{
+ isdn_ctrl ic;
+
+ ic.command = ISDN_STAT_UNLOAD;
+ ic.driver = cs->myid;
+ cs->iif.statcallb(&ic);
+ if (cs->status_buf)
+ kfree(cs->status_buf);
+ cs->status_read = NULL;
+ cs->status_write = NULL;
+ cs->status_end = NULL;
+ kfree(cs->dlog);
+}
+
+static void
+closecard(int cardnr)
+{
+ struct IsdnCardState *csta = cards[cardnr].cs;
+
+ if (csta->bcs->BC_Close != NULL) {
+ csta->bcs->BC_Close(csta->bcs + 1);
+ csta->bcs->BC_Close(csta->bcs);
+ }
+
+ if (csta->rcvbuf) {
+ kfree(csta->rcvbuf);
+ csta->rcvbuf = NULL;
+ }
+ discard_queue(&csta->rq);
+ discard_queue(&csta->sq);
+ if (csta->tx_skb) {
+ dev_kfree_skb(csta->tx_skb);
+ csta->tx_skb = NULL;
+ }
+ if (csta->mon_rx) {
+ kfree(csta->mon_rx);
+ csta->mon_rx = NULL;
+ }
+ if (csta->mon_tx) {
+ kfree(csta->mon_tx);
+ csta->mon_tx = NULL;
+ }
+ csta->cardmsg(csta, CARD_RELEASE, NULL);
+ if (csta->dbusytimer.function != NULL)
+ del_timer(&csta->dbusytimer);
+ ll_unload(csta);
+}
+
+HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs))
+{
+ int irq_cnt, cnt = 3;
+ long flags;
+
+ save_flags(flags);
+ cli();
+ irq_cnt = kstat_irqs(cs->irq);
+ printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq,
+ irq_cnt);
+ if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) {
+ printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+ cs->irq);
+ restore_flags(flags);
+ return(1);
+ }
+ while (cnt) {
+ cs->cardmsg(cs, CARD_INIT, NULL);
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ /* Timeout 10ms */
+ schedule_timeout((10 * HZ) / 1000);
+ restore_flags(flags);
+ printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
+ cs->irq, kstat_irqs(cs->irq));
+ if (kstat_irqs(cs->irq) == irq_cnt) {
+ printk(KERN_WARNING
+ "%s: IRQ(%d) getting no interrupts during init %d\n",
+ CardType[cs->typ], cs->irq, 4 - cnt);
+ if (cnt == 1) {
+ free_irq(cs->irq, cs);
+ return (2);
+ } else {
+ cs->cardmsg(cs, CARD_RESET, NULL);
+ cnt--;
+ }
+ } else {
+ cs->cardmsg(cs, CARD_TEST, NULL);
+ return(0);
+ }
+ }
+ restore_flags(flags);
+ return(3);
+}
+
+HISAX_INITFUNC(static int
+checkcard(int cardnr, char *id, int *busy_flag))
+{
+ long flags;
+ int ret = 0;
+ struct IsdnCard *card = cards + cardnr;
+ struct IsdnCardState *cs;
+
+ save_flags(flags);
+ cli();
+ if (!(cs = (struct IsdnCardState *)
+ kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for IsdnCardState(card %d)\n",
+ cardnr + 1);
+ restore_flags(flags);
+ return (0);
+ }
+ memset(cs, 0, sizeof(struct IsdnCardState));
+ card->cs = cs;
+ cs->cardnr = cardnr;
+ cs->debug = L1_DEB_WARN;
+ cs->HW_Flags = 0;
+ cs->busy_flag = busy_flag;
+#if TEI_PER_CARD
+#else
+ test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#endif
+ cs->protocol = card->protocol;
+
+ if ((card->typ > 0) && (card->typ < 31)) {
+ if (!((1 << card->typ) & SUPORTED_CARDS)) {
+ printk(KERN_WARNING
+ "HiSax: Support for %s Card not selected\n",
+ CardType[card->typ]);
+ restore_flags(flags);
+ return (0);
+ }
+ } else {
+ printk(KERN_WARNING
+ "HiSax: Card Type %d out of range\n",
+ card->typ);
+ restore_flags(flags);
+ return (0);
+ }
+ if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for dlog(card %d)\n",
+ cardnr + 1);
+ restore_flags(flags);
+ return (0);
+ }
+ if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for status_buf(card %d)\n",
+ cardnr + 1);
+ kfree(cs->dlog);
+ restore_flags(flags);
+ return (0);
+ }
+ cs->stlist = NULL;
+ cs->mon_tx = NULL;
+ cs->mon_rx = NULL;
+ cs->status_read = cs->status_buf;
+ cs->status_write = cs->status_buf;
+ cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
+ cs->typ = card->typ;
+ strcpy(cs->iif.id, id);
+ cs->iif.channels = 2;
+ cs->iif.maxbufsize = MAX_DATA_SIZE;
+ cs->iif.hl_hdrlen = MAX_HEADER_LEN;
+ cs->iif.features =
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L2_MODEM |
+ ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L3_TRANS |
+#ifdef CONFIG_HISAX_1TR6
+ ISDN_FEATURE_P_1TR6 |
+#endif
+#ifdef CONFIG_HISAX_EURO
+ ISDN_FEATURE_P_EURO |
+#endif
+#ifdef CONFIG_HISAX_NI1
+ ISDN_FEATURE_P_NI1 |
+#endif
+ 0;
+
+ cs->iif.command = HiSax_command;
+ cs->iif.writecmd = NULL;
+ cs->iif.writebuf_skb = HiSax_writebuf_skb;
+ cs->iif.readstat = HiSax_readstatus;
+ register_isdn(&cs->iif);
+ cs->myid = cs->iif.channels;
+ printk(KERN_INFO
+ "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
+ (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
+ (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
+ (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
+ (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
+ "NONE", cs->iif.id, cs->myid);
+ switch (card->typ) {
+#if CARD_TELES0
+ case ISDN_CTYPE_16_0:
+ case ISDN_CTYPE_8_0:
+ ret = setup_teles0(card);
+ break;
+#endif
+#if CARD_TELES3
+ case ISDN_CTYPE_16_3:
+ case ISDN_CTYPE_PNP:
+ case ISDN_CTYPE_TELESPCMCIA:
+ case ISDN_CTYPE_COMPAQ_ISA:
+ ret = setup_teles3(card);
+ break;
+#endif
+#if CARD_S0BOX
+ case ISDN_CTYPE_S0BOX:
+ ret = setup_s0box(card);
+ break;
+#endif
+#if CARD_TELESPCI
+ case ISDN_CTYPE_TELESPCI:
+ ret = setup_telespci(card);
+ break;
+#endif
+#if CARD_AVM_A1
+ case ISDN_CTYPE_A1:
+ ret = setup_avm_a1(card);
+ break;
+#endif
+#if CARD_AVM_A1_PCMCIA
+ case ISDN_CTYPE_A1_PCMCIA:
+ ret = setup_avm_a1_pcmcia(card);
+ break;
+#endif
+#if CARD_FRITZPCI
+ case ISDN_CTYPE_FRITZPCI:
+ ret = setup_avm_pcipnp(card);
+ break;
+#endif
+#if CARD_ELSA
+ case ISDN_CTYPE_ELSA:
+ case ISDN_CTYPE_ELSA_PNP:
+ case ISDN_CTYPE_ELSA_PCMCIA:
+ case ISDN_CTYPE_ELSA_PCI:
+ ret = setup_elsa(card);
+ break;
+#endif
+#if CARD_IX1MICROR2
+ case ISDN_CTYPE_IX1MICROR2:
+ ret = setup_ix1micro(card);
+ break;
+#endif
+#if CARD_DIEHLDIVA
+ case ISDN_CTYPE_DIEHLDIVA:
+ ret = setup_diva(card);
+ break;
+#endif
+#if CARD_ASUSCOM
+ case ISDN_CTYPE_ASUSCOM:
+ ret = setup_asuscom(card);
+ break;
+#endif
+#if CARD_TELEINT
+ case ISDN_CTYPE_TELEINT:
+ ret = setup_TeleInt(card);
+ break;
+#endif
+#if CARD_SEDLBAUER
+ case ISDN_CTYPE_SEDLBAUER:
+ case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+ case ISDN_CTYPE_SEDLBAUER_FAX:
+ ret = setup_sedlbauer(card);
+ break;
+#endif
+#if CARD_SPORTSTER
+ case ISDN_CTYPE_SPORTSTER:
+ ret = setup_sportster(card);
+ break;
+#endif
+#if CARD_MIC
+ case ISDN_CTYPE_MIC:
+ ret = setup_mic(card);
+ break;
+#endif
+#if CARD_NETJET
+ case ISDN_CTYPE_NETJET:
+ ret = setup_netjet(card);
+ break;
+#endif
+#if CARD_TELES3C
+ case ISDN_CTYPE_TELES3C:
+ ret = setup_t163c(card);
+ break;
+#endif
+#if CARD_NICCY
+ case ISDN_CTYPE_NICCY:
+ ret = setup_niccy(card);
+ break;
+#endif
+#if CARD_AMD7930
+ case ISDN_CTYPE_AMD7930:
+ ret = setup_amd7930(card);
+ break;
+#endif
+ default:
+ printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
+ card->typ);
+ ll_unload(cs);
+ restore_flags(flags);
+ return (0);
+ }
+ if (!ret) {
+ ll_unload(cs);
+ restore_flags(flags);
+ return (0);
+ }
+ if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for isac rcvbuf\n");
+ return (1);
+ }
+ cs->rcvidx = 0;
+ cs->tx_skb = NULL;
+ cs->tx_cnt = 0;
+ cs->event = 0;
+ cs->tqueue.next = 0;
+ cs->tqueue.sync = 0;
+ cs->tqueue.data = cs;
+
+ skb_queue_head_init(&cs->rq);
+ skb_queue_head_init(&cs->sq);
+
+ init_bcstate(cs, 0);
+ init_bcstate(cs, 1);
+ ret = init_card(cs);
+ if (ret) {
+ closecard(cardnr);
+ restore_flags(flags);
+ return (0);
+ }
+ init_tei(cs, cs->protocol);
+ CallcNewChan(cs);
+ /* ISAR needs firmware download first */
+ if (!test_bit(HW_ISAR, &cs->HW_Flags))
+ ll_run(cs);
+ restore_flags(flags);
+ return (1);
+}
+
+HISAX_INITFUNC(void
+HiSax_shiftcards(int idx))
+{
+ int i;
+
+ for (i = idx; i < (HISAX_MAX_CARDS - 1); i++)
+ memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+HISAX_INITFUNC(int
+HiSax_inithardware(int *busy_flag))
+{
+ int foundcards = 0;
+ int i = 0;
+ int t = ',';
+ int flg = 0;
+ char *id;
+ char *next_id = HiSax_id;
+ char ids[20];
+
+ if (strchr(HiSax_id, ','))
+ t = ',';
+ else if (strchr(HiSax_id, '%'))
+ t = '%';
+
+ while (i < nrcards) {
+ if (cards[i].typ < 1)
+ break;
+ id = next_id;
+ if ((next_id = strchr(id, t))) {
+ *next_id++ = 0;
+ strcpy(ids, id);
+ flg = i + 1;
+ } else {
+ next_id = id;
+ if (flg >= i)
+ strcpy(ids, id);
+ else
+ sprintf(ids, "%s%d", id, i);
+ }
+ if (checkcard(i, ids, busy_flag)) {
+ foundcards++;
+ i++;
+ } else {
+ printk(KERN_WARNING "HiSax: Card %s not installed !\n",
+ CardType[cards[i].typ]);
+ if (cards[i].cs)
+ kfree((void *) cards[i].cs);
+ cards[i].cs = NULL;
+ HiSax_shiftcards(i);
+ }
+ }
+ return foundcards;
+}
+
+void
+HiSax_closecard(int cardnr)
+{
+ int i,last=nrcards - 1;
+
+ if (cardnr>last)
+ return;
+ if (cards[cardnr].cs) {
+ ll_stop(cards[cardnr].cs);
+ release_tei(cards[cardnr].cs);
+ closecard(cardnr);
+ free_irq(cards[cardnr].cs->irq, cards[cardnr].cs);
+ kfree((void *) cards[cardnr].cs);
+ cards[cardnr].cs = NULL;
+ }
+ i = cardnr;
+ while (i!=last) {
+ cards[i] = cards[i+1];
+ i++;
+ }
+ nrcards--;
+}
+
+EXPORT_SYMBOL(HiSax_closecard);
+
+void
+HiSax_reportcard(int cardnr)
+{
+ struct IsdnCardState *cs = cards[cardnr].cs;
+ struct PStack *stptr;
+ struct l3_process *pc;
+ int j, i = 1;
+
+ printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+ printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
+ printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
+ printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+ (ulong) & HiSax_reportcard);
+ printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
+ printk(KERN_DEBUG "HiSax: HW_Flags %x bc0 flg %x bc0 flg %x\n",
+ cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag);
+ printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n",
+ cs->bcs[0].mode, cs->bcs[0].channel);
+ printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n",
+ cs->bcs[1].mode, cs->bcs[1].channel);
+ printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist));
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr);
+ printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp);
+ printk(KERN_DEBUG "HiSax: tei %d sapi %d\n",
+ stptr->l2.tei, stptr->l2.sap);
+ printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer);
+ pc = stptr->l3.proc;
+ while (pc) {
+ printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref,
+ (ulong) pc);
+ printk(KERN_DEBUG "HiSax: state %d st 0x%lX chan 0x%lX\n",
+ pc->state, (ulong) pc->st, (ulong) pc->chan);
+ pc = pc->next;
+ }
+ stptr = stptr->next;
+ i++;
+ }
+ for (j = 0; j < 2; j++) {
+ printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j,
+ (ulong) & cs->channel[j]);
+ stptr = cs->channel[j].b_st;
+ i = 1;
+ while (stptr != NULL) {
+ printk(KERN_DEBUG "HiSax: b_st%d 0x%lX\n", i, (ulong) stptr);
+ printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer);
+ stptr = stptr->next;
+ i++;
+ }
+ }
+}
+
+
__initfunc(int
HiSax_init(void))
{
int i;
-
+
#ifdef MODULE
int nzproto = 0;
#ifdef CONFIG_HISAX_ELSA
return 0;
}
#endif
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+ if (type[0] == ISDN_CTYPE_A1_PCMCIA) {
+ /* we have to export and return in this case */
+ return 0;
+ }
+#endif
#endif
- HiSaxVersion();
nrcards = 0;
+ HiSaxVersion();
#ifdef MODULE
if (id) /* If id= string used */
HiSax_id = id;
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < HISAX_MAX_CARDS; i++) {
cards[i].typ = type[i];
if (protocol[i]) {
cards[i].protocol = protocol[i];
case ISDN_CTYPE_16_3:
case ISDN_CTYPE_TELESPCMCIA:
case ISDN_CTYPE_A1:
+ case ISDN_CTYPE_A1_PCMCIA:
case ISDN_CTYPE_ELSA_PNP:
case ISDN_CTYPE_ELSA_PCMCIA:
case ISDN_CTYPE_IX1MICROR2:
case ISDN_CTYPE_TELEINT:
case ISDN_CTYPE_SEDLBAUER:
case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+ case ISDN_CTYPE_SEDLBAUER_FAX:
case ISDN_CTYPE_SPORTSTER:
case ISDN_CTYPE_MIC:
case ISDN_CTYPE_TELES3C:
+ case ISDN_CTYPE_S0BOX:
+ case ISDN_CTYPE_FRITZPCI:
cards[i].para[0] = irq[i];
cards[i].para[1] = io[i];
break;
case ISDN_CTYPE_ELSA_PCI:
case ISDN_CTYPE_NETJET:
case ISDN_CTYPE_AMD7930:
- case ISDN_CTYPE_DBRI:
+ case ISDN_CTYPE_TELESPCI:
break;
}
}
HiSax_id = HiSaxID;
if (!HiSaxID[0])
strcpy(HiSaxID, "HiSax");
- for (i = 0; i < 16; i++)
+ for (i = 0; i < HISAX_MAX_CARDS; i++)
if (cards[i].typ > 0)
nrcards++;
printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
nrcards, (nrcards > 1) ? "s" : "");
CallcNew();
+ Isdnl3New();
Isdnl2New();
TeiNew();
Isdnl1New();
/* No symbols to export, hide all symbols */
#ifdef MODULE
- EXPORT_NO_SYMBOLS;
printk(KERN_INFO "HiSax: module installed\n");
#endif
return (0);
Isdnl1Free();
TeiFree();
Isdnl2Free();
+ Isdnl3Free();
CallcFree();
return -EIO;
}
void
cleanup_module(void)
{
- HiSax_closehardware();
+ int cardnr = nrcards -1;
+ long flags;
+
+ save_flags(flags);
+ cli();
+ while(cardnr>=0)
+ HiSax_closecard(cardnr--);
+ Isdnl1Free();
+ TeiFree();
+ Isdnl2Free();
+ Isdnl3Free();
+ CallcFree();
+ restore_flags(flags);
printk(KERN_INFO "HiSax module removed\n");
}
+#endif
#ifdef CONFIG_HISAX_ELSA
int elsa_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
{
+#ifdef MODULE
int i;
int nzproto = 0;
HiSaxVersion();
if (id) /* If id= string used */
HiSax_id = id;
- /* Initialize all 16 structs, even though we only accept
+ /* Initialize all 8 structs, even though we only accept
two pcmcia cards
*/
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < HISAX_MAX_CARDS; i++) {
cards[i].para[0] = irq[i];
cards[i].para[1] = io[i];
cards[i].typ = type[i];
HiSax_id = HiSaxID;
if (!HiSaxID[0])
strcpy(HiSaxID, "HiSax");
- for (i = 0; i < 16; i++)
+ for (i = 0; i < HISAX_MAX_CARDS; i++)
if (cards[i].typ > 0)
nrcards++;
printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
Isdnl1New();
CallcNew();
+ Isdnl3New();
Isdnl2New();
TeiNew();
HiSax_inithardware(busy_flag);
printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
return (0);
}
#endif
+
#ifdef CONFIG_HISAX_SEDLBAUER
int sedl_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
{
+#ifdef MODULE
+ int i;
+ int nzproto = 0;
+
+ nrcards = 0;
+ HiSaxVersion();
+ if (id) /* If id= string used */
+ HiSax_id = id;
+ /* Initialize all 8 structs, even though we only accept
+ two pcmcia cards
+ */
+ for (i = 0; i < HISAX_MAX_CARDS; i++) {
+ cards[i].para[0] = irq[i];
+ cards[i].para[1] = io[i];
+ cards[i].typ = type[i];
+ if (protocol[i]) {
+ cards[i].protocol = protocol[i];
+ nzproto++;
+ }
+ }
+ cards[0].para[0] = pcm_irq;
+ cards[0].para[1] = (int)pcm_iob;
+ cards[0].protocol = prot;
+ cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+ nzproto = 1;
+
+ if (!HiSax_id)
+ HiSax_id = HiSaxID;
+ if (!HiSaxID[0])
+ strcpy(HiSaxID, "HiSax");
+ for (i = 0; i < HISAX_MAX_CARDS; i++)
+ if (cards[i].typ > 0)
+ nrcards++;
+ printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+ nrcards, (nrcards > 1) ? "s" : "");
+
+ CallcNew();
+ Isdnl3New();
+ Isdnl2New();
+ Isdnl1New();
+ TeiNew();
+ HiSax_inithardware(busy_flag);
+ printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
+ return (0);
+}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+int avm_a1_init_pcmcia(void *pcm_iob, int pcm_irq, int *busy_flag, int prot)
+{
+#ifdef MODULE
int i;
int nzproto = 0;
cards[0].para[0] = pcm_irq;
cards[0].para[1] = (int)pcm_iob;
cards[0].protocol = prot;
- cards[0].typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+ cards[0].typ = ISDN_CTYPE_A1_PCMCIA;
nzproto = 1;
if (!HiSax_id)
HiSax_id = HiSaxID;
if (!HiSaxID[0])
strcpy(HiSaxID, "HiSax");
- for (i = 0; i < 16; i++)
+ for (i = 0; i < HISAX_MAX_CARDS; i++)
if (cards[i].typ > 0)
nrcards++;
printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
Isdnl1New();
CallcNew();
+ Isdnl3New();
Isdnl2New();
TeiNew();
HiSax_inithardware(busy_flag);
printk(KERN_NOTICE "HiSax: module installed\n");
+#endif
return (0);
}
#endif
-#endif
-/* $Id: diva.c,v 1.5 1998/02/02 13:29:38 keil Exp $
+/* $Id: diva.c,v 1.10 1998/11/15 23:54:31 keil Exp $
* diva.c low level stuff for Eicon.Diehl Diva Family ISDN cards
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
* Thanks to Eicon Technology Diehl GmbH & Co. oHG for documents and informations
*
*
* $Log: diva.c,v $
+ * Revision 1.10 1998/11/15 23:54:31 keil
+ * changes from 2.0
+ *
+ * Revision 1.9 1998/06/27 22:52:03 keil
+ * support for Diva 2.01
+ *
+ * Revision 1.8 1998/05/25 12:57:46 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.7 1998/04/15 16:42:36 keil
+ * new init code
+ * new PCI init (2.1.94)
+ *
+ * Revision 1.6 1998/03/07 22:56:57 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 1.5 1998/02/02 13:29:38 keil
* fast io
*
#include "hisax.h"
#include "isac.h"
#include "hscx.h"
+#include "ipac.h"
#include "isdnl1.h"
#include <linux/pci.h>
-#include <linux/bios32.h>
extern const char *CardType[];
-const char *Diva_revision = "$Revision: 1.5 $";
+const char *Diva_revision = "$Revision: 1.10 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
#define DIVA_ISA_ISAC_DATA 2
#define DIVA_ISA_ISAC_ADR 6
#define DIVA_ISA_CTRL 7
+#define DIVA_IPAC_ADR 0
+#define DIVA_IPAC_DATA 1
#define DIVA_PCI_ISAC_DATA 8
#define DIVA_PCI_ISAC_ADR 0xc
/* SUB Types */
#define DIVA_ISA 1
#define DIVA_PCI 2
+#define DIVA_IPAC_ISA 3
/* PCI stuff */
#define PCI_VENDOR_EICON_DIEHL 0x1133
readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
}
-static void
+static void
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
{
writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
}
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
static u_char
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
{
cs->hw.diva.hscx, reg + (nr ? 0x40 : 0))
#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \
cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data)
-
+
#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \
cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
-
+
#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \
cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
-
+
#include "hscx_irq.c"
static void
}
}
+static void
+diva_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista,val;
+ int icnt=20;
+
+ if (!cs) {
+ printk(KERN_WARNING "Diva: Spurious interrupt!\n");
+ return;
+ }
+ ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+Start_IPAC:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPAC;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n");
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF);
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0);
+}
+
+
void
release_io_diva(struct IsdnCardState *cs)
{
int bytecnt;
-
- del_timer(&cs->hw.diva.tl);
- if (cs->subtyp == DIVA_ISA)
+
+ if (cs->subtyp != DIVA_IPAC_ISA) {
+ del_timer(&cs->hw.diva.tl);
+ if (cs->hw.diva.cfg_reg)
+ byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
+ }
+ if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA))
bytecnt = 8;
else
bytecnt = 32;
if (cs->hw.diva.cfg_reg) {
- byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
release_region(cs->hw.diva.cfg_reg, bytecnt);
}
}
save_flags(flags);
sti();
- cs->hw.diva.ctrl_reg = 0; /* Reset On */
- byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
- cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */
- byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
- if (cs->subtyp == DIVA_ISA)
- cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
- else
- cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
- byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ if (cs->subtyp == DIVA_IPAC_ISA) {
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
+ } else {
+ cs->hw.diva.ctrl_reg = 0; /* Reset On */
+ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */
+ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ if (cs->subtyp == DIVA_ISA)
+ cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
+ else
+ cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
+ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ }
+ restore_flags(flags);
}
#define DIVA_ASSIGN 1
{
int blink = 0;
+ if (cs->subtyp == DIVA_IPAC_ISA)
+ return;
del_timer(&cs->hw.diva.tl);
if (cs->hw.diva.status & DIVA_ASSIGN)
cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
if (cs->hw.diva.status & 0xf000)
cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
DIVA_ISA_LED_B : DIVA_PCI_LED_B;
- else if (cs->hw.diva.status & 0x0f00) {
+ else if (cs->hw.diva.status & 0x0f00) {
cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
DIVA_ISA_LED_B : DIVA_PCI_LED_B;
blink = 500;
} else
cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ?
DIVA_ISA_LED_B : DIVA_PCI_LED_B);
-
+
byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
if (blink) {
init_timer(&cs->hw.diva.tl);
static int
Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
{
+ u_int irq_flag = I4L_IRQ_FLAG;
+
switch (mt) {
case CARD_RESET:
reset_diva(cs);
release_io_diva(cs);
return(0);
case CARD_SETIRQ:
- return(request_irq(cs->irq, &diva_interrupt,
- I4L_IRQ_FLAG, "HiSax", cs));
+ if (cs->subtyp == DIVA_PCI)
+ irq_flag |= SA_SHIRQ;
+ if (cs->subtyp == DIVA_IPAC_ISA) {
+ return(request_irq(cs->irq, &diva_interrupt_ipac,
+ irq_flag, "HiSax", cs));
+ } else {
+ return(request_irq(cs->irq, &diva_interrupt,
+ irq_flag, "HiSax", cs));
+ }
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ inithscxisac(cs, 3);
return(0);
case CARD_TEST:
return(0);
- case MDL_REMOVE_REQ:
+ case (MDL_REMOVE | REQUEST):
cs->hw.diva.status = 0;
break;
- case MDL_ASSIGN_REQ:
+ case (MDL_ASSIGN | REQUEST):
cs->hw.diva.status |= DIVA_ASSIGN;
break;
case MDL_INFO_SETUP:
- if ((int)arg)
+ if ((long)arg)
cs->hw.diva.status |= 0x0200;
else
cs->hw.diva.status |= 0x0100;
break;
case MDL_INFO_CONN:
- if ((int)arg)
+ if ((long)arg)
cs->hw.diva.status |= 0x2000;
else
cs->hw.diva.status |= 0x1000;
break;
case MDL_INFO_REL:
- if ((int)arg) {
+ if ((long)arg) {
cs->hw.diva.status &= ~0x2000;
cs->hw.diva.status &= ~0x0200;
} else {
}
break;
}
- diva_led_handler(cs);
+ if (cs->subtyp != DIVA_IPAC_ISA)
+ diva_led_handler(cs);
return(0);
}
-
-
-static int pci_index __initdata = 0;
+static struct pci_dev *dev_diva __initdata = NULL;
+static struct pci_dev *dev_diva_u __initdata = NULL;
__initfunc(int
setup_diva(struct IsdnCard *card))
{
int bytecnt;
+ u_char val;
struct IsdnCardState *cs = card->cs;
char tmp[64];
return(0);
cs->hw.diva.status = 0;
if (card->para[1]) {
- cs->subtyp = DIVA_ISA;
cs->hw.diva.ctrl_reg = 0;
cs->hw.diva.cfg_reg = card->para[1];
- cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
- cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
- cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
- cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
- cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+ val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR,
+ cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID);
+ printk(KERN_INFO "Diva: IPAC version %x\n", val);
+ if (val == 1) {
+ cs->subtyp = DIVA_IPAC_ISA;
+ cs->hw.diva.ctrl = 0;
+ cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA;
+ cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA;
+ cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR;
+ cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ } else {
+ cs->subtyp = DIVA_ISA;
+ cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
+ cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
+ cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
+ cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
+ cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+ }
cs->irq = card->para[0];
bytecnt = 8;
} else {
#if CONFIG_PCI
- u_char pci_bus, pci_device_fn, pci_irq;
- u_int pci_ioaddr;
+ if (!pci_present()) {
+ printk(KERN_ERR "Diva: no PCI bus present\n");
+ return(0);
+ }
cs->subtyp = 0;
- for (; pci_index < 0xff; pci_index++) {
- if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
- PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
- == PCIBIOS_SUCCESSFUL)
+ if ((dev_diva = pci_find_device(PCI_VENDOR_EICON_DIEHL,
+ PCI_DIVA20_ID, dev_diva))) {
cs->subtyp = DIVA_PCI;
- else if (pcibios_find_device(PCI_VENDOR_EICON_DIEHL,
- PCI_DIVA20_ID, pci_index, &pci_bus, &pci_device_fn)
- == PCIBIOS_SUCCESSFUL)
+ /* get IRQ */
+ cs->irq = dev_diva->irq;
+ /* get IO address */
+ cs->hw.diva.cfg_reg = dev_diva->base_address[2]
+ & PCI_BASE_ADDRESS_IO_MASK;
+ } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_EICON_DIEHL,
+ PCI_DIVA20_U_ID, dev_diva_u))) {
cs->subtyp = DIVA_PCI;
- else
- break;
/* get IRQ */
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &pci_irq);
-
+ cs->irq = dev_diva_u->irq;
/* get IO address */
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_2, &pci_ioaddr);
- if (cs->subtyp)
- break;
- }
- if (!cs->subtyp) {
+ cs->hw.diva.cfg_reg = dev_diva_u->base_address[2]
+ & PCI_BASE_ADDRESS_IO_MASK;
+ } else {
printk(KERN_WARNING "Diva: No PCI card found\n");
return(0);
}
- if (!pci_irq) {
+
+ if (!cs->irq) {
printk(KERN_WARNING "Diva: No IRQ for PCI card found\n");
return(0);
}
- if (!pci_ioaddr) {
+ if (!cs->hw.diva.cfg_reg) {
printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n");
return(0);
}
- pci_ioaddr &= ~3; /* remove io/mem flag */
- cs->hw.diva.cfg_reg = pci_ioaddr;
- cs->hw.diva.ctrl = pci_ioaddr + DIVA_PCI_CTRL;
- cs->hw.diva.isac = pci_ioaddr + DIVA_PCI_ISAC_DATA;
- cs->hw.diva.hscx = pci_ioaddr + DIVA_HSCX_DATA;
- cs->hw.diva.isac_adr = pci_ioaddr + DIVA_PCI_ISAC_ADR;
- cs->hw.diva.hscx_adr = pci_ioaddr + DIVA_HSCX_ADR;
- cs->irq = pci_irq;
+ cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL;
+ cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA;
+ cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA;
+ cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR;
+ cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR;
bytecnt = 32;
#else
printk(KERN_WARNING "Diva: cfgreg 0 and NO_PCI_BIOS\n");
printk(KERN_INFO
"Diva: %s card configured at 0x%x IRQ %d\n",
- (cs->subtyp == DIVA_ISA) ? "ISA" : "PCI",
+ (cs->subtyp == DIVA_PCI) ? "PCI" :
+ (cs->subtyp == DIVA_ISA) ? "ISA" : "IPAC",
cs->hw.diva.cfg_reg, cs->irq);
if (check_region(cs->hw.diva.cfg_reg, bytecnt)) {
printk(KERN_WARNING
}
reset_diva(cs);
- cs->hw.diva.tl.function = (void *) diva_led_handler;
- cs->hw.diva.tl.data = (long) cs;
- init_timer(&cs->hw.diva.tl);
- cs->readisac = &ReadISAC;
- cs->writeisac = &WriteISAC;
- cs->readisacfifo = &ReadISACfifo;
- cs->writeisacfifo = &WriteISACfifo;
cs->BC_Read_Reg = &ReadHSCX;
cs->BC_Write_Reg = &WriteHSCX;
cs->BC_Send_Data = &hscx_fill_fifo;
cs->cardmsg = &Diva_card_msg;
-
- ISACVersion(cs, "Diva:");
- if (HscxVersion(cs, "Diva:")) {
- printk(KERN_WARNING
+ if (cs->subtyp == DIVA_IPAC_ISA) {
+ cs->readisac = &ReadISAC_IPAC;
+ cs->writeisac = &WriteISAC_IPAC;
+ cs->readisacfifo = &ReadISACfifo_IPAC;
+ cs->writeisacfifo = &WriteISACfifo_IPAC;
+ val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID);
+ printk(KERN_INFO "Diva: IPAC version %x\n", val);
+ } else {
+ cs->hw.diva.tl.function = (void *) diva_led_handler;
+ cs->hw.diva.tl.data = (long) cs;
+ init_timer(&cs->hw.diva.tl);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ ISACVersion(cs, "Diva:");
+ if (HscxVersion(cs, "Diva:")) {
+ printk(KERN_WARNING
"Diva: wrong HSCX versions check IO address\n");
- release_io_diva(cs);
- return (0);
+ release_io_diva(cs);
+ return (0);
+ }
}
return (1);
}
-/* $Id: elsa.c,v 2.6 1998/02/02 13:29:40 keil Exp $
+/* $Id: elsa.c,v 2.12 1998/11/15 23:54:35 keil Exp $
* elsa.c low level stuff for Elsa isdn cards
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
*
* Thanks to Elsa GmbH for documents and informations
*
+ * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
+ * for ELSA PCMCIA support
+ *
*
* $Log: elsa.c,v $
+ * Revision 2.12 1998/11/15 23:54:35 keil
+ * changes from 2.0
+ *
+ * Revision 2.11 1998/08/20 13:50:34 keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 2.10 1998/08/13 23:36:22 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.9 1998/05/25 12:57:48 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.8 1998/04/15 16:41:42 keil
+ * QS3000 PCI support
+ * new init code
+ * new PCI init (2.1.94)
+ *
+ * Revision 2.7 1998/03/07 22:56:58 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 2.6 1998/02/02 13:29:40 keil
* fast io
*
* Revision 2.0 1997/06/26 11:02:40 keil
* New Layer and card interface
*
- * Revision 1.14 1997/04/13 19:53:25 keil
- * Fixed QS1000 init, change in IRQ check delay for SMP
- *
- * Revision 1.13 1997/04/07 22:58:07 keil
- * need include config.h
- *
- * Revision 1.12 1997/04/06 22:54:14 keil
- * Using SKB's
- *
* old changes removed KKe
*
*/
#include "hscx.h"
#include "isdnl1.h"
#include <linux/pci.h>
-#include <linux/bios32.h>
+
+//#define KDEBUG_DEF
+//#include "../kdebug.h"
extern const char *CardType[];
-const char *Elsa_revision = "$Revision: 2.6 $";
+static const char *Elsa_revision = "$Revision: 2.12 $";
const char *Elsa_Types[] =
{"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
- "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI"};
+ "PCMCIA", "QS 1000", "QS 3000", "QS 1000 PCI", "QS 3000 PCI"};
const char *ITACVer[] =
{"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
#define ELSA_QS1000 7
#define ELSA_QS3000 8
#define ELSA_QS1000PCI 9
+#define ELSA_QS3000PCI 10
/* PCI stuff */
#define PCI_VENDOR_ELSA 0x1048
#define PCI_QS1000_ID 0x1000
-
+#define PCI_QS3000_ID 0x3000
+#define ELSA_PCI_IRQ_MASK 0x04
/* ITAC Registeradressen (only Microlink PC) */
#define ITAC_SYS 0x34
#define ELSA_BAD_PWR 2
#define ELSA_ASSIGN 4
+#define RS_ISR_PASS_LIMIT 256
+#define _INLINE_ inline
+#define FLG_MODEM_ACTIVE 1
+/* IPAC AUX */
+#define ELSA_IPAC_LINE_LED 0x40 /* Bit 6 Gelbe LED */
+#define ELSA_IPAC_STAT_LED 0x80 /* Bit 7 Gruene LED */
+
+const u_char ARCOFI_VERSION[] = {2,0xa0,0};
+const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */
+const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */
+const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */
+const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */
+const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}; /* RX */
+const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */
+const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR UP */
+const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* Normal OP */
+const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12};
+
+static void set_arcofi(struct IsdnCardState *cs, int bc);
+
+#if ARCOFI_USE
+#include "elsa_ser.c"
+#endif
+
static inline u_char
readreg(unsigned int ale, unsigned int adr, u_char off)
{
printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
return;
}
+ if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
+ /* The card tends to generate interrupts while being removed
+ causing us to just crash the kernel. bad. */
+ printk(KERN_WARNING "Elsa: card not available!\n");
+ return;
+ }
+#if ARCOFI_USE
+ if (cs->hw.elsa.MFlag) {
+ val = serial_inp(cs, UART_IIR);
+ if (!(val & UART_IIR_NO_INT)) {
+ debugl1(cs,"IIR %02x", val);
+ rs_interrupt_elsa(intno, cs);
+ }
+ }
+#endif
val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
Start_HSCX:
if (val) {
cs->hw.elsa.counter++;
}
}
+ if (cs->hw.elsa.MFlag) {
+ val = serial_inp(cs, UART_MCR);
+ val ^= 0x8;
+ serial_outp(cs, UART_MCR, val);
+ val = serial_inp(cs, UART_MCR);
+ val ^= 0x8;
+ serial_outp(cs, UART_MCR, val);
+ }
if (cs->hw.elsa.trig)
byteout(cs->hw.elsa.trig, 0x00);
writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0);
{
struct IsdnCardState *cs = dev_id;
u_char ista,val;
- char tmp[64];
int icnt=20;
if (!cs) {
printk(KERN_WARNING "Elsa: Spurious interrupt!\n");
return;
}
- if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
- /* The card tends to generate interrupts while being removed
- causing us to just crash the kernel. bad. */
- printk(KERN_WARNING "Elsa: card not available!\n");
- return;
+ val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
+ if (!(val & ELSA_PCI_IRQ_MASK))
+ return;
+#if ARCOFI_USE
+ if (cs->hw.elsa.MFlag) {
+ val = serial_inp(cs, UART_IIR);
+ if (!(val & UART_IIR_NO_INT)) {
+ debugl1(cs,"IIR %02x", val);
+ rs_interrupt_elsa(intno, cs);
+ }
}
+#endif
ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
Start_IPAC:
- if (cs->debug & L1_DEB_IPAC) {
- sprintf(tmp, "IPAC ISTA %02X", ista);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
if (ista & 0x0f) {
val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
if (ista & 0x01)
del_timer(&cs->hw.elsa.tl);
if (cs->hw.elsa.ctrl)
byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */
- if ((cs->subtyp == ELSA_PCFPRO) ||
- (cs->subtyp == ELSA_QS3000) ||
- (cs->subtyp == ELSA_PCF))
- bytecnt = 16;
if (cs->subtyp == ELSA_QS1000PCI) {
byteout(cs->hw.elsa.cfg + 0x4c, 0x01); /* disable IRQ */
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
bytecnt = 2;
release_region(cs->hw.elsa.cfg, 0x80);
}
+ if (cs->subtyp == ELSA_QS3000PCI) {
+ byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* disable ELSA PCI IRQ */
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ release_region(cs->hw.elsa.cfg, 0x80);
+ }
+ if ((cs->subtyp == ELSA_PCFPRO) ||
+ (cs->subtyp == ELSA_QS3000) ||
+ (cs->subtyp == ELSA_PCF) ||
+ (cs->subtyp == ELSA_QS3000PCI)) {
+ bytecnt = 16;
+ release_modem(cs);
+ }
if (cs->hw.elsa.base)
release_region(cs->hw.elsa.base, bytecnt);
}
if (cs->hw.elsa.trig)
byteout(cs->hw.elsa.trig, 0xff);
}
- if (cs->subtyp == ELSA_QS1000PCI) {
+ if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
save_flags(flags);
sti();
writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
current->state = TASK_INTERRUPTIBLE;
schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
- schedule();
restore_flags(flags);
- byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ if (cs->subtyp == ELSA_QS1000PCI)
+ byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+ else if (cs->subtyp == ELSA_QS3000PCI)
+ byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */
}
}
-const u_char ARCOFI_VERSION[] = {2,0xa0,0};
-const u_char ARCOFI_COP_5[] = {4,0xa1,0x25,0xbb,0x4a}; /* GTX */
-const u_char ARCOFI_COP_6[] = {6,0xa1,0x26,0,0,0x82,0x7c}; /* GRL GRH */
-const u_char ARCOFI_COP_7[] = {4,0xa1,0x27,0x80,0x80}; /* GZ */
-const u_char ARCOFI_COP_8[] = {10,0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}; /* TX */
-const u_char ARCOFI_COP_9[] = {10,0xa1,0x29,0x80,0xcb,0x9e,0x88,0x00,0xc8,0xd8,0x80}; /* RX */
-const u_char ARCOFI_XOP_0[] = {2,0xa1,0x30}; /* PWR Down */
-const u_char ARCOFI_XOP_1[] = {2,0xa1,0x31}; /* PWR Down */
-const u_char ARCOFI_XOP_F[] = {2,0xa1,0x3f}; /* PWR Down */
-const u_char ARCOFI_SOP_F[] = {10,0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12};
-
static void
init_arcofi(struct IsdnCardState *cs) {
- send_arcofi(cs, ARCOFI_COP_5);
- send_arcofi(cs, ARCOFI_COP_6);
- send_arcofi(cs, ARCOFI_COP_7);
- send_arcofi(cs, ARCOFI_COP_8);
- send_arcofi(cs, ARCOFI_COP_9);
- send_arcofi(cs, ARCOFI_SOP_F);
- send_arcofi(cs, ARCOFI_XOP_F);
+ send_arcofi(cs, ARCOFI_XOP_0, 1, 0);
+/* send_arcofi(cs, ARCOFI_XOP_F, 1);
+*/
}
+#define ARCDEL 500
+
static void
+set_arcofi(struct IsdnCardState *cs, int bc) {
+ long flags;
+
+ debugl1(cs,"set_arcofi bc=%d", bc);
+ save_flags(flags);
+ sti();
+ send_arcofi(cs, ARCOFI_XOP_0, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_COP_5, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_COP_6, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_COP_7, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_COP_8, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_COP_9, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_SOP_F, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_XOP_1, bc, 0);
+ udelay(ARCDEL);
+ send_arcofi(cs, ARCOFI_XOP_F, bc, 0);
+ restore_flags(flags);
+ debugl1(cs,"end set_arcofi bc=%d", bc);
+}
+
+static int
check_arcofi(struct IsdnCardState *cs)
{
#if ARCOFI_USE
if (!(cs->mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
if (cs->debug & L1_DEB_WARN)
debugl1(cs, "ISAC MON TX out of buffers!");
- return;
+ return(0);
}
- send_arcofi(cs, ARCOFI_VERSION);
+ send_arcofi(cs, ARCOFI_VERSION, 0, 1);
if (test_and_clear_bit(HW_MON1_TX_END, &cs->HW_Flags)) {
if (test_and_clear_bit(HW_MON1_RX_END, &cs->HW_Flags)) {
- sprintf(tmp, "Arcofi response received %d bytes", cs->mon_rxp);
- debugl1(cs, tmp);
+ debugl1(cs, "Arcofi response received %d bytes", cs->mon_rxp);
p = cs->mon_rx;
t = tmp;
t += sprintf(tmp, "Arcofi data");
cs->mon_rxp = 0;
}
} else if (cs->mon_tx) {
- sprintf(tmp, "Arcofi not detected");
- debugl1(cs, tmp);
+ debugl1(cs, "Arcofi not detected");
}
if (arcofi_present) {
if (cs->subtyp==ELSA_QS1000) {
Elsa_Types[cs->subtyp],
cs->hw.elsa.base+8);
init_arcofi(cs);
+ return(1);
}
#endif
+ return(0);
}
static void
{
int blink = 0;
- if ((cs->subtyp == ELSA_PCMCIA) &&
- (cs->subtyp == ELSA_QS1000PCI))
+ if (cs->subtyp == ELSA_PCMCIA)
return;
del_timer(&cs->hw.elsa.tl);
if (cs->hw.elsa.status & ELSA_ASSIGN)
} else
cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED;
- byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+ if ((cs->subtyp == ELSA_QS1000PCI) ||
+ (cs->subtyp == ELSA_QS3000PCI)) {
+ u_char led = 0xff;
+ if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED)
+ led ^= ELSA_IPAC_LINE_LED;
+ if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED)
+ led ^= ELSA_IPAC_STAT_LED;
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led);
+ } else
+ byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
if (blink) {
init_timer(&cs->hw.elsa.tl);
cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000);
static int
Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
{
- int pwr, ret = 0;
- long flags;
+ int len, ret = 0;
+ u_char *msg;
+ long flags;
switch (mt) {
case CARD_RESET:
release_io_elsa(cs);
return(0);
case CARD_SETIRQ:
- if (cs->subtyp == ELSA_QS1000PCI)
+ if ((cs->subtyp == ELSA_QS1000PCI) ||
+ (cs->subtyp == ELSA_QS3000PCI))
ret = request_irq(cs->irq, &elsa_interrupt_ipac,
- I4L_IRQ_FLAG, "HiSax", cs);
+ I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs);
else
ret = request_irq(cs->irq, &elsa_interrupt,
I4L_IRQ_FLAG, "HiSax", cs);
return(ret);
case CARD_INIT:
- if (cs->hw.elsa.trig)
- byteout(cs->hw.elsa.trig, 0xff);
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
- if (cs->subtyp == ELSA_QS1000) {
+ cs->debug |= L1_DEB_IPAC;
+ inithscxisac(cs, 1);
+ if ((cs->subtyp == ELSA_QS1000) ||
+ (cs->subtyp == ELSA_QS3000))
+ {
byteout(cs->hw.elsa.timer, 0);
- byteout(cs->hw.elsa.trig, 0xff);
}
+ if (cs->hw.elsa.trig)
+ byteout(cs->hw.elsa.trig, 0xff);
+ inithscxisac(cs, 2);
return(0);
case CARD_TEST:
- if ((cs->subtyp != ELSA_PCMCIA) &&
- (cs->subtyp != ELSA_QS1000PCI)) {
+ if ((cs->subtyp == ELSA_PCMCIA) ||
+ (cs->subtyp == ELSA_QS1000PCI)) {
+ return(0);
+ } else if (cs->subtyp == ELSA_QS3000PCI) {
+ ret = 0;
+ } else {
save_flags(flags);
cs->hw.elsa.counter = 0;
sti();
cs->hw.elsa.status |= ELSA_TIMER_AKTIV;
byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
byteout(cs->hw.elsa.timer, 0);
- } else
- return(0);
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout((110*HZ)/1000); /* Timeout 110ms */
- restore_flags(flags);
- cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
- byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
- cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
- printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
- cs->hw.elsa.counter);
- if (abs(cs->hw.elsa.counter - 13) < 3) {
- printk(KERN_INFO "Elsa: timer and irq OK\n");
- ret = 0;
- } else {
- printk(KERN_WARNING
- "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
- cs->hw.elsa.counter, cs->irq);
- ret = 1;
+ current->state = TASK_INTERRUPTIBLE;
+ /* Timeout 110ms */
+ schedule_timeout((110*HZ)/1000);
+ restore_flags(flags);
+ cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
+ byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+ cs->hw.elsa.status &= ~ELSA_TIMER_AKTIV;
+ printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+ cs->hw.elsa.counter);
+ if (abs(cs->hw.elsa.counter - 13) < 3) {
+ printk(KERN_INFO "Elsa: timer and irq OK\n");
+ ret = 0;
+ } else {
+ printk(KERN_WARNING
+ "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+ cs->hw.elsa.counter, cs->irq);
+ ret = 1;
+ }
}
- check_arcofi(cs);
+#if ARCOFI_USE
+ if (check_arcofi(cs)) {
+ init_modem(cs);
+ }
+#endif
elsa_led_handler(cs);
return(ret);
- case MDL_REMOVE_REQ:
+ case (MDL_REMOVE | REQUEST):
cs->hw.elsa.status &= 0;
break;
- case MDL_ASSIGN_REQ:
+ case (MDL_ASSIGN | REQUEST):
cs->hw.elsa.status |= ELSA_ASSIGN;
break;
case MDL_INFO_SETUP:
- if ((int) arg)
+ if ((long) arg)
cs->hw.elsa.status |= 0x0200;
else
cs->hw.elsa.status |= 0x0100;
break;
case MDL_INFO_CONN:
- if ((int) arg)
+ if ((long) arg)
cs->hw.elsa.status |= 0x2000;
else
cs->hw.elsa.status |= 0x1000;
break;
case MDL_INFO_REL:
- if ((int) arg) {
+ if ((long) arg) {
cs->hw.elsa.status &= ~0x2000;
cs->hw.elsa.status &= ~0x0200;
} else {
}
break;
case CARD_AUX_IND:
+ if (cs->hw.elsa.MFlag) {
+ if (!arg)
+ return(0);
+ msg = arg;
+ len = *msg;
+ msg++;
+ modem_write_cmd(cs, msg, len);
+ }
break;
}
- pwr = bytein(cs->hw.elsa.ale);
- if (pwr & 0x08)
- cs->hw.elsa.status |= ELSA_BAD_PWR;
- else
- cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+ if (cs->typ == ISDN_CTYPE_ELSA) {
+ int pwr = bytein(cs->hw.elsa.ale);
+ if (pwr & 0x08)
+ cs->hw.elsa.status |= ELSA_BAD_PWR;
+ else
+ cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+ }
elsa_led_handler(cs);
return(ret);
}
return (CARD_portlist[i]);
}
-static int pci_index __initdata = 0;
+static struct pci_dev *dev_qs1000 __initdata = NULL;
+static struct pci_dev *dev_qs3000 __initdata = NULL;
int
setup_elsa(struct IsdnCard *card)
printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
cs->hw.elsa.ctrl_reg = 0;
cs->hw.elsa.status = 0;
+ cs->hw.elsa.MFlag = 0;
if (cs->typ == ISDN_CTYPE_ELSA) {
cs->hw.elsa.base = card->para[0];
printk(KERN_INFO "Elsa: Microlink IO probing\n");
cs->irq);
} else if (cs->typ == ISDN_CTYPE_ELSA_PCI) {
#if CONFIG_PCI
- u_char pci_bus, pci_device_fn, pci_irq;
- u_int pci_ioaddr;
-
+ if (!pci_present()) {
+ printk(KERN_ERR "Elsa: no PCI bus present\n");
+ return(0);
+ }
cs->subtyp = 0;
- for (; pci_index < 0xff; pci_index++) {
- if (pcibios_find_device(PCI_VENDOR_ELSA,
- PCI_QS1000_ID, pci_index, &pci_bus, &pci_device_fn)
- == PCIBIOS_SUCCESSFUL)
+ if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ELSA, PCI_QS1000_ID,
+ dev_qs1000))) {
cs->subtyp = ELSA_QS1000PCI;
- else
- break;
- /* get IRQ */
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &pci_irq);
-
- /* get IO address */
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_1, &pci_ioaddr);
- pci_ioaddr &= ~3; /* remove io/mem flag */
- cs->hw.elsa.cfg = pci_ioaddr;
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_3, &pci_ioaddr);
- if (cs->subtyp)
- break;
- }
- if (!cs->subtyp) {
+ cs->irq = dev_qs1000->irq;
+ cs->hw.elsa.cfg = dev_qs1000->base_address[1] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ cs->hw.elsa.base = dev_qs1000->base_address[3] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ELSA,
+ PCI_QS3000_ID, dev_qs3000))) {
+ cs->subtyp = ELSA_QS3000PCI;
+ cs->irq = dev_qs3000->irq;
+ cs->hw.elsa.cfg = dev_qs3000->base_address[1] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ cs->hw.elsa.base = dev_qs3000->base_address[3] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ } else {
printk(KERN_WARNING "Elsa: No PCI card found\n");
return(0);
}
- if (!pci_irq) {
+ if (!cs->irq) {
printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n");
return(0);
}
- if (!pci_ioaddr) {
+ if (!(cs->hw.elsa.base && cs->hw.elsa.cfg)) {
printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n");
return(0);
}
- pci_ioaddr &= ~3; /* remove io/mem flag */
- cs->hw.elsa.base = pci_ioaddr;
- cs->hw.elsa.ale = pci_ioaddr;
- cs->hw.elsa.isac = pci_ioaddr +1;
- cs->hw.elsa.hscx = pci_ioaddr +1;
- cs->irq = pci_irq;
+ cs->hw.elsa.ale = cs->hw.elsa.base;
+ cs->hw.elsa.isac = cs->hw.elsa.base +1;
+ cs->hw.elsa.hscx = cs->hw.elsa.base +1;
test_and_set_bit(HW_IPAC, &cs->HW_Flags);
cs->hw.elsa.timer = 0;
cs->hw.elsa.trig = 0;
printk(KERN_INFO
- "Elsa: %s defined at 0x%x/0x%x IRQ %d\n",
- Elsa_Types[cs->subtyp],
- cs->hw.elsa.base,
- cs->hw.elsa.cfg,
- cs->irq);
+ "Elsa: %s defined at 0x%x/0x%x IRQ %d\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base,
+ cs->hw.elsa.cfg,
+ cs->irq);
+ if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) {
+ printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n");
+ printk(KERN_WARNING "Elsa: If your system hangs now, read\n");
+ printk(KERN_WARNING "Elsa: Documentation/isdn/README.HiSax\n");
+ printk(KERN_WARNING "Elsa: Waiting 5 sec to sync discs\n");
+ save_flags(flags);
+ sti();
+ HZDELAY(500); /* wait 500*10 ms */
+ restore_flags(flags);
+ }
#else
printk(KERN_WARNING "Elsa: Elsa PCI and NO_PCI_BIOS\n");
printk(KERN_WARNING "Elsa: unable to config Elsa PCI\n");
break;
case ELSA_PCFPRO:
case ELSA_PCF:
+ case ELSA_QS3000PCI:
bytecnt = 16;
break;
case ELSA_QS1000PCI:
} else {
request_region(cs->hw.elsa.base, bytecnt, "elsa isdn");
}
- if (cs->subtyp == ELSA_QS1000PCI) {
+ if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
if (check_region(cs->hw.elsa.cfg, 0x80)) {
printk(KERN_WARNING
"HiSax: %s pci port %x-%x already in use\n",
}
printk(KERN_INFO "Elsa: timer OK; resetting card\n");
}
- reset_elsa(cs);
cs->BC_Read_Reg = &ReadHSCX;
cs->BC_Write_Reg = &WriteHSCX;
cs->BC_Send_Data = &hscx_fill_fifo;
cs->cardmsg = &Elsa_card_msg;
- if (cs->subtyp == ELSA_QS1000PCI) {
+ reset_elsa(cs);
+ if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
cs->readisac = &ReadISAC_IPAC;
cs->writeisac = &WriteISAC_IPAC;
cs->readisacfifo = &ReadISACfifo_IPAC;
}
return (1);
}
-
--- /dev/null
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+#define MAX_MODEM_BUF 256
+#define WAKEUP_CHARS (MAX_MODEM_BUF/2)
+#define RS_ISR_PASS_LIMIT 256
+#define BASE_BAUD ( 1843200 / 16 )
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+//#define SERIAL_DEBUG_OPEN 1
+//#define SERIAL_DEBUG_INTR 1
+//#define SERIAL_DEBUG_FLOW 1
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_REG
+//#define SERIAL_DEBUG_REG 1
+
+#ifdef SERIAL_DEBUG_REG
+static u_char deb[32];
+const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"};
+const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"};
+#endif
+
+static char *MInit_1 = "AT&F&C1E0&D2\r\0";
+static char *MInit_2 = "ATL2M1S64=13\r\0";
+static char *MInit_3 = "AT+FCLASS=0\r\0";
+static char *MInit_4 = "ATV1S2=128X1\r\0";
+static char *MInit_5 = "AT\\V8\\N3\r\0";
+static char *MInit_6 = "ATL0M0&G0%E1\r\0";
+static char *MInit_7 = "AT%L1%M0%C3\r\0";
+
+static char *MInit_speed28800 = "AT%G0%B28800\r\0";
+
+static char *MInit_dialout = "ATs7=60 x1 d\r\0";
+static char *MInit_dialin = "ATs7=60 x1 a\r\0";
+
+
+static inline unsigned int serial_in(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+ u_int val = inb(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"in %s %02x",ModemIn[offset], val);
+ return(val);
+#else
+ return inb(cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ u_int val = inb(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"inp %s %02x",ModemIn[offset], val);
+#else
+ u_int val = inb_p(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"inP %s %02x",ModemIn[offset], val);
+#endif
+ return(val);
+#else
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ return inb(cs->hw.elsa.base + 8 + offset);
+#else
+ return inb_p(cs->hw.elsa.base + 8 + offset);
+#endif
+#endif
+}
+
+static inline void serial_out(struct IsdnCardState *cs, int offset, int value)
+{
+#ifdef SERIAL_DEBUG_REG
+ debugl1(cs,"out %s %02x",ModemOut[offset], value);
+#endif
+ outb(value, cs->hw.elsa.base + 8 + offset);
+}
+
+static inline void serial_outp(struct IsdnCardState *cs, int offset,
+ int value)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ debugl1(cs,"outp %s %02x",ModemOut[offset], value);
+#else
+ debugl1(cs,"outP %s %02x",ModemOut[offset], value);
+#endif
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ outb(value, cs->hw.elsa.base + 8 + offset);
+#else
+ outb_p(value, cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct IsdnCardState *cs, int baud)
+{
+ int quot = 0, baud_base;
+ unsigned cval, fcr = 0;
+ int bits;
+ unsigned long flags;
+
+
+ /* byte size and parity */
+ cval = 0x03; bits = 10;
+ /* Determine divisor based on baud rate */
+ baud_base = BASE_BAUD;
+ quot = baud_base / baud;
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = baud_base / 9600;
+
+ /* Set up FIFO's */
+ if ((baud_base / quot) < 2400)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+ else
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+ serial_outp(cs, UART_FCR, fcr);
+ /* CTS flow control flag and modem status interrupts */
+ cs->hw.elsa.IER &= ~UART_IER_MSI;
+ cs->hw.elsa.IER |= UART_IER_MSI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+
+ debugl1(cs,"modem quot=0x%x", quot);
+ save_flags(flags);
+ cli();
+ serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+ serial_outp(cs, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_outp(cs, UART_DLM, quot >> 8); /* MS of divisor */
+ serial_outp(cs, UART_LCR, cval); /* reset DLAB */
+ serial_inp(cs, UART_RX);
+ restore_flags(flags);
+}
+
+static int mstartup(struct IsdnCardState *cs)
+{
+ unsigned long flags;
+ int retval=0;
+
+
+ save_flags(flags); cli();
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+
+ /*
+ * At this point there's no way the LSR could still be 0xFF;
+ * if it is, then bail out, because there's likely no UART
+ * here.
+ */
+ if (serial_inp(cs, UART_LSR) == 0xff) {
+ retval = -ENODEV;
+ goto errout;
+ }
+
+ /*
+ * Clear the interrupt registers.
+ */
+ (void) serial_inp(cs, UART_RX);
+ (void) serial_inp(cs, UART_IIR);
+ (void) serial_inp(cs, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ serial_outp(cs, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+
+ cs->hw.elsa.MCR = 0;
+ cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+ serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+ /*
+ * Finally, enable interrupts
+ */
+ cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER); /* enable interrupts */
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ (void)serial_inp(cs, UART_LSR);
+ (void)serial_inp(cs, UART_RX);
+ (void)serial_inp(cs, UART_IIR);
+ (void)serial_inp(cs, UART_MSR);
+
+ cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0;
+ cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0;
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(cs, BASE_BAUD);
+ cs->hw.elsa.MFlag = 1;
+errout:
+ restore_flags(flags);
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void mshutdown(struct IsdnCardState *cs)
+{
+ unsigned long flags;
+
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk(KERN_DEBUG"Shutting down serial ....");
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+
+ cs->hw.elsa.IER = 0;
+ serial_outp(cs, UART_IER, 0x00); /* disable all intrs */
+ cs->hw.elsa.MCR &= ~UART_MCR_OUT2;
+
+ /* disable break condition */
+ serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC);
+
+ cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+ serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+ /* disable FIFO's */
+ serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+ serial_inp(cs, UART_RX); /* read data port to reset things */
+
+ restore_flags(flags);
+#ifdef SERIAL_DEBUG_OPEN
+ printk(" done\n");
+#endif
+}
+
+inline int
+write_modem(struct BCState *bcs) {
+ int ret=0;
+ struct IsdnCardState *cs = bcs->cs;
+ int count, len, fp, buflen;
+ long flags;
+
+ if (!bcs->tx_skb)
+ return 0;
+ if (bcs->tx_skb->len <= 0)
+ return 0;
+ save_flags(flags);
+ cli();
+ buflen = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
+ len = MIN(buflen, bcs->tx_skb->len);
+ fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+ fp &= (MAX_MODEM_BUF -1);
+ count = MIN(len, MAX_MODEM_BUF - fp);
+ if (count < len) {
+ memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+ skb_pull(bcs->tx_skb, count);
+ cs->hw.elsa.transcnt += count;
+ ret = count;
+ count = len - count;
+ fp = 0;
+ }
+ memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+ skb_pull(bcs->tx_skb, count);
+ cs->hw.elsa.transcnt += count;
+ ret += count;
+
+ if (cs->hw.elsa.transcnt &&
+ !(cs->hw.elsa.IER & UART_IER_THRI)) {
+ cs->hw.elsa.IER |= UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+ restore_flags(flags);
+ return(ret);
+}
+
+inline void
+modem_fill(struct BCState *bcs) {
+
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ write_modem(bcs);
+ return;
+ } else {
+ if (bcs->st->lli.l1writewakeup &&
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st,
+ bcs->hw.hscx.count);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hscx.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ write_modem(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ hscx_sched_event(bcs, B_XMTBUFREADY);
+ }
+}
+
+static inline void receive_chars(struct IsdnCardState *cs,
+ int *status)
+{
+ unsigned char ch;
+ struct sk_buff *skb;
+
+ do {
+ ch = serial_in(cs, UART_RX);
+ if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF)
+ break;
+ cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch;
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, *status);
+#endif
+ if (*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)) {
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("handling exept....");
+#endif
+ }
+ *status = serial_inp(cs, UART_LSR);
+ } while (*status & UART_LSR_DR);
+ if (cs->hw.elsa.MFlag == 2) {
+ if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt)))
+ printk(KERN_WARNING "ElsaSER: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf,
+ cs->hw.elsa.rcvcnt);
+ skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb);
+ }
+ hscx_sched_event(cs->hw.elsa.bcs, B_RCVBUFREADY);
+ } else {
+ char tmp[128];
+ char *t = tmp;
+
+ t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt);
+ QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt);
+ debugl1(cs, tmp);
+ }
+ cs->hw.elsa.rcvcnt = 0;
+}
+
+static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done)
+{
+ int count;
+
+ debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp,
+ cs->hw.elsa.transcnt);
+
+ if (cs->hw.elsa.transcnt <= 0) {
+ cs->hw.elsa.IER &= ~UART_IER_THRI;
+ serial_out(cs, UART_IER, cs->hw.elsa.IER);
+ return;
+ }
+ count = 16;
+ do {
+ serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]);
+ if (cs->hw.elsa.transp >= MAX_MODEM_BUF)
+ cs->hw.elsa.transp=0;
+ if (--cs->hw.elsa.transcnt <= 0)
+ break;
+ } while (--count > 0);
+ if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2))
+ modem_fill(cs->hw.elsa.bcs);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+ if (intr_done)
+ *intr_done = 0;
+ if (cs->hw.elsa.transcnt <= 0) {
+ cs->hw.elsa.IER &= ~UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+}
+
+#if 0
+static inline void check_modem_status(struct IsdnCardState *cs)
+{
+ int status;
+ struct async_struct *info = cs->hw.elsa.info;
+ struct async_icount *icount;
+
+ status = serial_inp(info, UART_MSR);
+
+ if (status & UART_MSR_ANY_DELTA) {
+ icount = &info->state->icount;
+ /* update input line counters */
+ if (status & UART_MSR_TERI)
+ icount->rng++;
+ if (status & UART_MSR_DDSR)
+ icount->dsr++;
+ if (status & UART_MSR_DDCD) {
+ icount->dcd++;
+ }
+ if (status & UART_MSR_DCTS)
+ icount->cts++;
+// wake_up_interruptible(&info->delta_msr_wait);
+ }
+
+ if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttys%d CD now %s...", info->line,
+ (status & UART_MSR_DCD) ? "on" : "off");
+#endif
+ if (status & UART_MSR_DCD)
+// wake_up_interruptible(&info->open_wait);
+;
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("doing serial hangup...");
+#endif
+ if (info->tty)
+ tty_hangup(info->tty);
+ }
+ }
+#if 0
+ if (info->flags & ASYNC_CTS_FLOW) {
+ if (info->tty->hw_stopped) {
+ if (status & UART_MSR_CTS) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx start...");
+#endif
+ info->tty->hw_stopped = 0;
+ info->IER |= UART_IER_THRI;
+ serial_outp(info, UART_IER, info->IER);
+// rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+ return;
+ }
+ } else {
+ if (!(status & UART_MSR_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx stop...");
+#endif
+ info->tty->hw_stopped = 1;
+ info->IER &= ~UART_IER_THRI;
+ serial_outp(info, UART_IER, info->IER);
+ }
+ }
+ }
+#endif 0
+}
+#endif
+
+static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs)
+{
+ int status, iir, msr;
+ int pass_counter = 0;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+ do {
+ status = serial_inp(cs, UART_LSR);
+ debugl1(cs,"rs LSR %02x", status);
+#ifdef SERIAL_DEBUG_INTR
+ printk("status = %x...", status);
+#endif
+ if (status & UART_LSR_DR)
+ receive_chars(cs, &status);
+ if (status & UART_LSR_THRE)
+ transmit_chars(cs, 0);
+ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+ printk("rs_single loop break.\n");
+ break;
+ }
+ iir = serial_inp(cs, UART_IIR);
+ debugl1(cs,"rs IIR %02x", iir);
+ if ((iir & 0xf) == 0) {
+ msr = serial_inp(cs, UART_MSR);
+ debugl1(cs,"rs MSR %02x", msr);
+ }
+ } while (!(iir & UART_IIR_NO_INT));
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void hscx_l2l1(struct PStack *st, int pr, void *arg);
+
+void
+close_elsastate(struct BCState *bcs)
+{
+ struct sk_buff *skb;
+
+ modehscx(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (bcs->hw.hscx.rcvbuf) {
+ if (bcs->mode != L1_MODE_MODEM)
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ }
+ while ((skb = skb_dequeue(&bcs->rqueue))) {
+ dev_kfree_skb(skb);
+ }
+ while ((skb = skb_dequeue(&bcs->squeue))) {
+ dev_kfree_skb(skb);
+ }
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ }
+ }
+}
+
+void
+modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
+ int count, fp;
+ u_char *msg = buf;
+ long flags;
+
+ if (!len)
+ return;
+ save_flags(flags);
+ cli();
+ if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
+ restore_flags(flags);
+ return;
+ }
+ fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+ fp &= (MAX_MODEM_BUF -1);
+ count = MIN(len, MAX_MODEM_BUF - fp);
+ if (count < len) {
+ memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+ cs->hw.elsa.transcnt += count;
+ msg += count;
+ count = len - count;
+ fp = 0;
+ }
+ memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+ cs->hw.elsa.transcnt += count;
+ if (cs->hw.elsa.transcnt &&
+ !(cs->hw.elsa.IER & UART_IER_THRI)) {
+ cs->hw.elsa.IER |= UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+ restore_flags(flags);
+}
+
+void
+modem_set_init(struct IsdnCardState *cs) {
+ long flags;
+ int timeout;
+
+#define RCV_DELAY 20000
+ save_flags(flags);
+ sti();
+ modem_write_cmd(cs, MInit_1, strlen(MInit_1));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_2, strlen(MInit_2));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_3, strlen(MInit_3));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_4, strlen(MInit_4));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY );
+ modem_write_cmd(cs, MInit_5, strlen(MInit_5));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_6, strlen(MInit_6));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_7, strlen(MInit_7));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ restore_flags(flags);
+}
+
+void
+modem_set_dial(struct IsdnCardState *cs, int outgoing) {
+ long flags;
+ int timeout;
+#define RCV_DELAY 20000
+
+ save_flags(flags);
+ sti();
+ modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ if (outgoing)
+ modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout));
+ else
+ modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ restore_flags(flags);
+}
+
+void
+modem_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ long flags;
+
+ if (pr == (PH_DATA | REQUEST)) {
+ save_flags(flags);
+ cli();
+ if (st->l1.bcs->tx_skb) {
+ skb_queue_tail(&st->l1.bcs->squeue, skb);
+ restore_flags(flags);
+ } else {
+ st->l1.bcs->tx_skb = skb;
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ st->l1.bcs->hw.hscx.count = 0;
+ restore_flags(flags);
+ write_modem(st->l1.bcs);
+ }
+ } else if (pr == (PH_ACTIVATE | REQUEST)) {
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ set_arcofi(st->l1.bcs->cs, st->l1.bc);
+ mstartup(st->l1.bcs->cs);
+ modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
+ st->l1.bcs->cs->hw.elsa.MFlag=2;
+ } else if (pr == (PH_DEACTIVATE | REQUEST)) {
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ send_arcofi(st->l1.bcs->cs, ARCOFI_XOP_0, st->l1.bc, 0);
+ st->l1.bcs->cs->hw.elsa.MFlag=1;
+ } else {
+ printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr);
+ }
+}
+
+int
+setstack_elsa(struct PStack *st, struct BCState *bcs)
+{
+
+ bcs->channel = st->l1.bc;
+ switch (st->l1.mode) {
+ case L1_MODE_HDLC:
+ case L1_MODE_TRANS:
+ if (open_hscxstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l2.l2l1 = hscx_l2l1;
+ break;
+ case L1_MODE_MODEM:
+ bcs->mode = L1_MODE_MODEM;
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf;
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->event = 0;
+ bcs->hw.hscx.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ bcs->cs->hw.elsa.bcs = bcs;
+ st->l2.l2l1 = modem_l2l1;
+ break;
+ }
+ st->l1.bcs = bcs;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+void
+init_modem(struct IsdnCardState *cs) {
+
+ cs->bcs[0].BC_SetStack = setstack_elsa;
+ cs->bcs[1].BC_SetStack = setstack_elsa;
+ cs->bcs[0].BC_Close = close_elsastate;
+ cs->bcs[1].BC_Close = close_elsastate;
+ if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF,
+ GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "Elsa: No modem mem hw.elsa.rcvbuf\n");
+ return;
+ }
+ if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF,
+ GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "Elsa: No modem mem hw.elsa.transbuf\n");
+ kfree(cs->hw.elsa.rcvbuf);
+ cs->hw.elsa.rcvbuf = NULL;
+ return;
+ }
+ if (mstartup(cs)) {
+ printk(KERN_WARNING "Elsa: problem startup modem\n");
+ }
+ modem_set_init(cs);
+}
+
+void
+release_modem(struct IsdnCardState *cs) {
+
+ cs->hw.elsa.MFlag = 0;
+ if (cs->hw.elsa.transbuf) {
+ if (cs->hw.elsa.rcvbuf) {
+ mshutdown(cs);
+ kfree(cs->hw.elsa.rcvbuf);
+ cs->hw.elsa.rcvbuf = NULL;
+ }
+ kfree(cs->hw.elsa.transbuf);
+ cs->hw.elsa.transbuf = NULL;
+ }
+}
-/* $Id: fsm.c,v 1.7 1997/11/06 17:09:13 keil Exp $
+/* $Id: fsm.c,v 1.10 1998/11/15 23:54:39 keil Exp $
* Author Karsten Keil (keil@temic-ech.spacenet.de)
* based on the teles driver from Jan den Ouden
* Fritz Elfert
*
* $Log: fsm.c,v $
+ * Revision 1.10 1998/11/15 23:54:39 keil
+ * changes from 2.0
+ *
+ * Revision 1.9 1998/03/26 07:10:02 paul
+ * The jumpmatrix table in struct Fsm was an array of "int". This is not
+ * large enough for pointers to functions on Linux/Alpha (instant crash
+ * on "insmod hisax). Now there is a typedef for the pointer to function.
+ * This also prevents warnings about "incompatible pointer types".
+ *
+ * Revision 1.8 1998/03/07 22:56:59 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 1.7 1997/11/06 17:09:13 keil
* New 2.1 init code
*
{
int i;
- fsm->jumpmatrix = (int *)
- kmalloc(4L * fsm->state_count * fsm->event_count, GFP_KERNEL);
- memset(fsm->jumpmatrix, 0, 4L * fsm->state_count * fsm->event_count);
+ fsm->jumpmatrix = (FSMFNPTR *)
+ kmalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
+ memset(fsm->jumpmatrix, 0, sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count);
for (i = 0; i < fncount; i++)
if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) {
- printk(KERN_ERR "FsmNew Error line %d st(%d/%d) ev(%d/%d)\n",
- i,fnlist[i].state,fsm->state_count,
- fnlist[i].event,fsm->event_count);
+ printk(KERN_ERR "FsmNew Error line %d st(%ld/%ld) ev(%ld/%ld)\n",
+ i,(long)fnlist[i].state,(long)fsm->state_count,
+ (long)fnlist[i].event,(long)fsm->event_count);
} else
fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
- fnlist[i].state] = (int) fnlist[i].routine;
+ fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
}
void
int
FsmEvent(struct FsmInst *fi, int event, void *arg)
{
- void (*r) (struct FsmInst *, int, void *);
- char str[80];
+ FSMFNPTR r;
if ((fi->state>=fi->fsm->state_count) || (event >= fi->fsm->event_count)) {
- printk(KERN_ERR "FsmEvent Error st(%d/%d) ev(%d/%d)\n",
- fi->state,fi->fsm->state_count,event,fi->fsm->event_count);
+ printk(KERN_ERR "FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
+ (long)fi->state,(long)fi->fsm->state_count,event,(long)fi->fsm->event_count);
return(1);
}
- r = (void (*)) fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
+ r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
if (r) {
- if (fi->debug) {
- sprintf(str, "State %s Event %s",
+ if (fi->debug)
+ fi->printdebug(fi, "State %s Event %s",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
- fi->printdebug(fi, str);
- }
r(fi, event, arg);
return (0);
} else {
- if (fi->debug) {
- sprintf(str, "State %s Event %s no routine",
+ if (fi->debug)
+ fi->printdebug(fi, "State %s Event %s no routine",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
- fi->printdebug(fi, str);
- }
return (!0);
}
}
void
FsmChangeState(struct FsmInst *fi, int newstate)
{
- char str[80];
-
fi->state = newstate;
- if (fi->debug) {
- sprintf(str, "ChangeState %s",
+ if (fi->debug)
+ fi->printdebug(fi, "ChangeState %s",
fi->fsm->strState[newstate]);
- fi->printdebug(fi, str);
- }
}
static void
FsmExpireTimer(struct FsmTimer *ft)
{
#if FSM_TIMER_DEBUG
- if (ft->fi->debug) {
- char str[40];
- sprintf(str, "FsmExpireTimer %lx", (long) ft);
- ft->fi->printdebug(ft->fi, str);
- }
+ if (ft->fi->debug)
+ ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
#endif
FsmEvent(ft->fi, ft->event, ft->arg);
}
ft->tl.function = (void *) FsmExpireTimer;
ft->tl.data = (long) ft;
#if FSM_TIMER_DEBUG
- if (ft->fi->debug) {
- char str[40];
- sprintf(str, "FsmInitTimer %lx", (long) ft);
- ft->fi->printdebug(ft->fi, str);
- }
+ if (ft->fi->debug)
+ ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
#endif
init_timer(&ft->tl);
}
FsmDelTimer(struct FsmTimer *ft, int where)
{
#if FSM_TIMER_DEBUG
- if (ft->fi->debug) {
- char str[40];
- sprintf(str, "FsmDelTimer %lx %d", (long) ft, where);
- ft->fi->printdebug(ft->fi, str);
- }
+ if (ft->fi->debug)
+ ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
#endif
del_timer(&ft->tl);
}
{
#if FSM_TIMER_DEBUG
- if (ft->fi->debug) {
- char str[40];
- sprintf(str, "FsmAddTimer %lx %d %d", (long) ft, millisec, where);
- ft->fi->printdebug(ft->fi, str);
- }
+ if (ft->fi->debug)
+ ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
+ (long) ft, millisec, where);
#endif
if (ft->tl.next || ft->tl.prev) {
{
#if FSM_TIMER_DEBUG
- if (ft->fi->debug) {
- char str[40];
- sprintf(str, "FsmRestartTimer %lx %d %d", (long) ft, millisec, where);
- ft->fi->printdebug(ft->fi, str);
- }
+ if (ft->fi->debug)
+ ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
+ (long) ft, millisec, where);
#endif
if (ft->tl.next || ft->tl.prev)
ft->tl.expires = jiffies + (millisec * HZ) / 1000;
add_timer(&ft->tl);
}
-
-void
-jiftime(char *s, long mark)
-{
- s += 8;
-
- *s-- = '\0';
- *s-- = mark % 10 + '0';
- mark /= 10;
- *s-- = mark % 10 + '0';
- mark /= 10;
- *s-- = '.';
- *s-- = mark % 10 + '0';
- mark /= 10;
- *s-- = mark % 6 + '0';
- mark /= 6;
- *s-- = ':';
- *s-- = mark % 10 + '0';
- mark /= 10;
- *s-- = mark % 10 + '0';
-}
-/* $Id: hfc_2bds0.c,v 1.3 1998/02/12 23:07:22 keil Exp $
+/* $Id: hfc_2bds0.c,v 1.8 1998/11/15 23:54:40 keil Exp $
*
* specific routines for CCD's HFC 2BDS0
*
*
*
* $Log: hfc_2bds0.c,v $
+ * Revision 1.8 1998/11/15 23:54:40 keil
+ * changes from 2.0
+ *
+ * Revision 1.7 1998/09/30 22:24:45 keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.6 1998/08/13 23:36:26 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.5 1998/06/27 22:52:58 keil
+ * make 16.3c working with 3.0
+ *
+ * Revision 1.4 1998/05/25 12:57:52 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
* Revision 1.3 1998/02/12 23:07:22 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
}
ret = bytein(cs->hw.hfcD.addr);
#if HFC_REG_DEBUG
- if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
- char tmp[32];
- sprintf(tmp, "t3c RD %02x %02x", reg, ret);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+ debugl1(cs, "t3c RD %02x %02x", reg, ret);
#endif
} else
ret = bytein(cs->hw.hfcD.addr | 1);
if (data)
byteout(cs->hw.hfcD.addr, value);
#if HFC_REG_DEBUG
- if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB)) {
- char tmp[16];
- sprintf(tmp, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB))
+ debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
#endif
}
int chksum;
long flags;
u_char stat, cip;
- char tmp[64];
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
debugl1(cs, "hfc_empty_fifo");
WaitNoBusy(cs);
stat = ReadReg(cs, HFCD_DATA, cip);
sti();
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
bcs->channel, chksum, stat);
- debugl1(cs, tmp);
- }
if (stat) {
debugl1(cs, "FIFO CRC error");
dev_kfree_skb(skb);
int idx, fcnt;
int count;
u_char cip;
- char tmp[64];
-
- if (!bcs->hw.hfc.tx_skb)
+ if (!bcs->tx_skb)
return;
- if (bcs->hw.hfc.tx_skb->len <= 0)
+ if (bcs->tx_skb->len <= 0)
return;
-
save_flags(flags);
cli();
SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel));
bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip);
bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
sti();
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
bcs->hw.hfc.send[bcs->hw.hfc.f1]);
- debugl1(cs, tmp);
- }
fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
if (fcnt < 0)
fcnt += 32;
return;
}
count = GetFreeFifoBytes_B(bcs);
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc_fill_fifo %d count(%d/%d),%lx",
- bcs->channel, bcs->hw.hfc.tx_skb->len,
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d count(%ld/%d),%lx",
+ bcs->channel, bcs->tx_skb->len,
count, current->state);
- debugl1(cs, tmp);
- }
- if (count < bcs->hw.hfc.tx_skb->len) {
+ if (count < bcs->tx_skb->len) {
if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "hfc_fill_fifo no fifo mem");
restore_flags(flags);
cli();
WaitForBusy(cs);
WaitNoBusy(cs);
- WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]);
- while (idx < bcs->hw.hfc.tx_skb->len) {
+ WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+ while (idx < bcs->tx_skb->len) {
cli();
if (!WaitNoBusy(cs))
break;
- WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx]);
+ WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx]);
sti();
idx++;
}
- if (idx != bcs->hw.hfc.tx_skb->len) {
+ if (idx != bcs->tx_skb->len) {
sti();
debugl1(cs, "FIFO Send BUSY error");
printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
} else {
- bcs->tx_cnt -= bcs->hw.hfc.tx_skb->len;
+ bcs->tx_cnt -= bcs->tx_skb->len;
if (bcs->st->lli.l1writewakeup &&
- (PACKET_NOACK != bcs->hw.hfc.tx_skb->pkt_type))
- bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hfc.tx_skb->len);
- dev_kfree_skb(bcs->hw.hfc.tx_skb);
- bcs->hw.hfc.tx_skb = NULL;
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
}
WaitForBusy(cs);
cli();
hfc_send_data(struct BCState *bcs)
{
struct IsdnCardState *cs = bcs->cs;
- char tmp[32];
if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
hfc_fill_fifo(bcs);
test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
- } else {
- sprintf(tmp,"send_data %d blocked", bcs->channel);
- debugl1(cs, tmp);
- }
+ } else
+ debugl1(cs,"send_data %d blocked", bcs->channel);
}
void
u_char f1, f2, cip;
int receive, count = 5;
struct sk_buff *skb;
- char tmp[64];
save_flags(flags);
Begin:
count--;
cli();
if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
- sprintf(tmp,"rec_data %d blocked", bcs->channel);
- debugl1(cs, tmp);
+ debugl1(cs,"rec_data %d blocked", bcs->channel);
restore_flags(flags);
return;
}
f2 = ReadReg(cs, HFCD_DATA, cip);
sti();
if (f1 != f2) {
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc rec %d f1(%d) f2(%d)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
bcs->channel, f1, f2);
- debugl1(cs, tmp);
- }
cli();
z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
if (rcnt < 0)
rcnt += cs->hw.hfcD.bfifosize;
rcnt++;
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
bcs->channel, z1, z2, rcnt);
- debugl1(cs, tmp);
- }
if ((skb = hfc_empty_fifo(bcs, rcnt))) {
cli();
skb_queue_tail(&bcs->rqueue, skb);
{
struct IsdnCardState *cs = bcs->cs;
- if (cs->debug & L1_DEB_HSCX) {
- char tmp[40];
- sprintf(tmp, "HFCD bchannel mode %d bchan %d/%d",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HFCD bchannel mode %d bchan %d/%d",
mode, bc, bcs->channel);
- debugl1(cs, tmp);
- }
bcs->mode = mode;
bcs->channel = bc;
switch (mode) {
long flags;
switch (pr) {
- case (PH_DATA_REQ):
+ case (PH_DATA | REQUEST):
save_flags(flags);
cli();
- if (st->l1.bcs->hw.hfc.tx_skb) {
+ if (st->l1.bcs->tx_skb) {
skb_queue_tail(&st->l1.bcs->squeue, skb);
restore_flags(flags);
} else {
- st->l1.bcs->hw.hfc.tx_skb = skb;
+ st->l1.bcs->tx_skb = skb;
/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
*/ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
restore_flags(flags);
}
break;
- case (PH_PULL_IND):
- if (st->l1.bcs->hw.hfc.tx_skb) {
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
break;
}
save_flags(flags);
cli();
/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
-*/ st->l1.bcs->hw.hfc.tx_skb = skb;
+*/ st->l1.bcs->tx_skb = skb;
st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
restore_flags(flags);
break;
- case (PH_PULL_REQ):
- if (!st->l1.bcs->hw.hfc.tx_skb) {
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL_CNF, NULL);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ mode_2bs0(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
}
}
void
close_2bs0(struct BCState *bcs)
{
- struct sk_buff *skb;
-
- mode_2bs0(bcs, 0, 0);
+ mode_2bs0(bcs, 0, bcs->channel);
if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
- while ((skb = skb_dequeue(&bcs->rqueue))) {
- dev_kfree_skb(skb);
- }
- while ((skb = skb_dequeue(&bcs->squeue))) {
- dev_kfree_skb(skb);
- }
- if (bcs->hw.hfc.tx_skb) {
- dev_kfree_skb(bcs->hw.hfc.tx_skb);
- bcs->hw.hfc.tx_skb = NULL;
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
}
}
}
static int
-open_hfcstate(struct IsdnCardState *cs,
- int bc)
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
{
- struct BCState *bcs = cs->bcs + bc;
-
if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
skb_queue_head_init(&bcs->rqueue);
skb_queue_head_init(&bcs->squeue);
}
- bcs->hw.hfc.tx_skb = NULL;
+ bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
bcs->event = 0;
bcs->tx_cnt = 0;
return (0);
}
-static void
-hfc_manl1(struct PStack *st, int pr,
- void *arg)
-{
- switch (pr) {
- case (PH_ACTIVATE_REQ):
- test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- mode_2bs0(st->l1.bcs, st->l1.mode, st->l1.bc);
- st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
- break;
- case (PH_DEACTIVATE_REQ):
- if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
- mode_2bs0(st->l1.bcs, 0, 0);
- test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- break;
- }
-}
-
int
setstack_2b(struct PStack *st, struct BCState *bcs)
{
- if (open_hfcstate(st->l1.hardware, bcs->channel))
+ bcs->channel = st->l1.bc;
+ if (open_hfcstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
st->l2.l2l1 = hfc_l2l1;
- st->ma.manl1 = hfc_manl1;
setstack_manager(st);
bcs->st = st;
+ setstack_l1_B(st);
return (0);
}
-static void
-manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
- struct PStack *st;
-
- st = cs->stlist;
- while (st) {
- st->ma.manl1(st, msg, arg);
- st = st->next;
- }
-}
-
static void
hfcd_bh(struct IsdnCardState *cs)
{
debugl1(cs, "D-Channel Busy cleared");
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL);
+ stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
stptr = stptr->next;
}
}
if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
switch (cs->ph_state) {
case (0):
- manl1_msg(cs, PH_RESET_IND, NULL);
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
break;
case (3):
- manl1_msg(cs, PH_DEACT_IND, NULL);
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
break;
case (8):
- manl1_msg(cs, PH_RSYNC_IND, NULL);
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
break;
case (6):
- manl1_msg(cs, PH_INFO2_IND, NULL);
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
break;
case (7):
- manl1_msg(cs, PH_I4_P8_IND, NULL);
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
break;
default:
break;
int chksum;
int count=5;
u_char *ptr;
- char tmp[64];
save_flags(flags);
cli();
if (rcnt < 0)
rcnt += cs->hw.hfcD.dfifosize;
rcnt++;
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
f1, f2, z1, z2, rcnt);
- debugl1(cs, tmp);
- }
sti();
idx = 0;
cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC;
WaitNoBusy(cs);
stat = ReadReg(cs, HFCD_DATA, cip);
sti();
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "empty_dfifo chksum %x stat %x",
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "empty_dfifo chksum %x stat %x",
chksum, stat);
- debugl1(cs, tmp);
- }
if (stat) {
debugl1(cs, "FIFO CRC error");
dev_kfree_skb(skb);
int idx, fcnt;
int count;
u_char cip;
- char tmp[64];
if (!cs->tx_skb)
return;
cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND);
sti();
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
cs->hw.hfcD.f1, cs->hw.hfcD.f2,
cs->hw.hfcD.send[cs->hw.hfcD.f1]);
- debugl1(cs, tmp);
- }
fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2;
if (fcnt < 0)
fcnt += 16;
return;
}
count = GetFreeFifoBytes_D(cs);
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "hfc_fill_Dfifo count(%d/%d)",
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfc_fill_Dfifo count(%ld/%d)",
cs->tx_skb->len, count);
- debugl1(cs, tmp);
- }
if (count < cs->tx_skb->len) {
if (cs->debug & L1_DEB_ISAC)
debugl1(cs, "hfc_fill_Dfifo no fifo mem");
{
u_char exval;
struct BCState *bcs;
- char tmp[32];
int count=15;
long flags;
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "HFCD irq %x %s", val,
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCD irq %x %s", val,
test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
"locked" : "unlocked");
- debugl1(cs, tmp);
- }
val &= cs->hw.hfcD.int_m1;
if (val & 0x40) { /* TE state machine irq */
exval = cs->readisac(cs, HFCD_STATES) & 0xf;
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "ph_state chg %d->%d", cs->ph_state,
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state chg %d->%d", cs->ph_state,
exval);
- debugl1(cs, tmp);
- }
cs->ph_state = exval;
sched_event_D(cs, D_L1STATECHANGE);
val &= ~0x40;
if (cs->debug)
debugl1(cs, "hfcd spurious 0x01 IRQ");
} else {
- if (bcs->hw.hfc.tx_skb) {
+ if (bcs->tx_skb) {
if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
hfc_fill_fifo(bcs);
test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
- } else {
- sprintf(tmp,"fill_data %d blocked", bcs->channel);
- debugl1(cs, tmp);
- }
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
} else {
- if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
hfc_fill_fifo(bcs);
test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
- } else {
- sprintf(tmp,"fill_data %d blocked", bcs->channel);
- debugl1(cs, tmp);
- }
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
} else {
hfc_sched_event(bcs, B_XMTBUFREADY);
}
if (cs->debug)
debugl1(cs, "hfcd spurious 0x02 IRQ");
} else {
- if (bcs->hw.hfc.tx_skb) {
+ if (bcs->tx_skb) {
if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
hfc_fill_fifo(bcs);
test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
- } else {
- sprintf(tmp,"fill_data %d blocked", bcs->channel);
- debugl1(cs, tmp);
- }
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
} else {
- if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
hfc_fill_fifo(bcs);
test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
- } else {
- sprintf(tmp,"fill_data %d blocked", bcs->channel);
- debugl1(cs, tmp);
- }
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
} else {
hfc_sched_event(bcs, B_XMTBUFREADY);
}
del_timer(&cs->dbusytimer);
if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
sched_event_D(cs, D_CLEARBUSY);
- if (cs->tx_skb) {
+ if (cs->tx_skb)
if (cs->tx_skb->len) {
if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
hfc_fill_dfifo(cs);
cs->tx_cnt = 0;
cs->tx_skb = NULL;
}
- }
if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
cs->tx_cnt = 0;
if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
if (cs->hw.hfcD.int_s1 && count--) {
val = cs->hw.hfcD.int_s1;
cs->hw.hfcD.int_s1 = 0;
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "HFCD irq %x loop %d", val, 15-count);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCD irq %x loop %d", val, 15-count);
} else
val = 0;
restore_flags(flags);
}
static void
-HFCD_l2l1(struct PStack *st, int pr, void *arg)
+HFCD_l1hw(struct PStack *st, int pr, void *arg)
{
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
struct sk_buff *skb = arg;
- char str[64];
+
switch (pr) {
- case (PH_DATA_REQ):
+ case (PH_DATA | REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
if (cs->tx_skb) {
skb_queue_tail(&cs->sq, skb);
#ifdef L2FRAME_DEBUG /* psa */
Logl2Frame(cs, skb, "PH_DATA Queued", 0);
#endif
} else {
- if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */
- LogFrame(cs, skb->data, skb->len);
- sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
- dlogframe(cs, skb->data + 4, skb->len - 4,
- str);
- }
cs->tx_skb = skb;
cs->tx_cnt = 0;
#ifdef L2FRAME_DEBUG /* psa */
}
break;
- case (PH_PULL_IND):
+ case (PH_PULL | INDICATION):
if (cs->tx_skb) {
if (cs->debug & L1_DEB_WARN)
debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
skb_queue_tail(&cs->sq, skb);
break;
}
- if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */
+ if (cs->debug & DEB_DLOG_HEX)
LogFrame(cs, skb->data, skb->len);
- sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
- dlogframe(cs, skb->data + 4, skb->len - 4,
- str);
- }
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
cs->tx_skb = skb;
cs->tx_cnt = 0;
#ifdef L2FRAME_DEBUG /* psa */
} else
debugl1(cs, "hfc_fill_dfifo blocked");
break;
- case (PH_PULL_REQ):
+ case (PH_PULL | REQUEST):
#ifdef L2FRAME_DEBUG /* psa */
if (cs->debug & L1_DEB_LAPD)
debugl1(cs, "-> PH_REQUEST_PULL");
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL_CNF, NULL);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
- }
-}
-
-void
-hfcd_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
-{
- char tmp[32];
- switch(msg) {
- case PH_RESET_REQ:
+ case (HW_RESET | REQUEST):
cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */
udelay(6);
cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */
cs->hw.hfcD.mst_m |= HFCD_MASTER;
cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
- manl1_msg(cs, PH_POWERUP_CNF, NULL);
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
break;
- case PH_ENABLE_REQ:
+ case (HW_ENABLE | REQUEST):
cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
break;
- case PH_DEACT_ACK:
+ case (HW_DEACTIVATE | REQUEST):
cs->hw.hfcD.mst_m &= ~HFCD_MASTER;
cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
break;
- case PH_INFO3_REQ:
+ case (HW_INFO3 | REQUEST):
cs->hw.hfcD.mst_m |= HFCD_MASTER;
cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
break;
#if 0
- case PH_TESTLOOP_REQ:
+ case (HW_TESTLOOP | REQUEST):
u_char val = 0;
if (1 & (int) arg)
val |= 0x0c;
break;
#endif
default:
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "hfcd_l1cmd unknown %4x", msg);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcd_l1hw unknown pr %4x", pr);
break;
}
}
void
setstack_hfcd(struct PStack *st, struct IsdnCardState *cs)
{
- st->l2.l2l1 = HFCD_l2l1;
+ st->l1.l1hw = HFCD_l1hw;
}
static void
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL);
+ stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
stptr = stptr->next;
}
}
init2bds0(struct IsdnCardState *cs))
{
cs->setstack_d = setstack_hfcd;
- cs->l1cmd = hfcd_l1cmd;
cs->dbusytimer.function = (void *) hfc_dbusy_timer;
cs->dbusytimer.data = (long) cs;
init_timer(&cs->dbusytimer);
-/* $Id: hfc_2bs0.c,v 1.4 1998/02/12 23:07:29 keil Exp $
+/* $Id: hfc_2bs0.c,v 1.8 1998/11/15 23:54:43 keil Exp $
* specific routines for CCD's HFC 2BS0
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
*
* $Log: hfc_2bs0.c,v $
+ * Revision 1.8 1998/11/15 23:54:43 keil
+ * changes from 2.0
+ *
+ * Revision 1.7 1998/09/30 22:24:46 keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.6 1998/08/13 23:36:28 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.5 1998/05/25 12:57:54 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
* Revision 1.4 1998/02/12 23:07:29 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
int idx, cnt;
int rcnt, z1, z2;
u_char cip, f1, f2;
- char tmp[64];
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
debugl1(cs, "hfc_clear_fifo");
z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
cnt = 32;
while (((f1 != f2) || (z1 != z2)) && cnt--) {
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc clear %d f1(%d) f2(%d)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc clear %d f1(%d) f2(%d)",
bcs->channel, f1, f2);
- debugl1(cs, tmp);
- }
rcnt = z1 - z2;
if (rcnt < 0)
rcnt += cs->hw.hfc.fifosize;
if (rcnt)
rcnt++;
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
bcs->channel, z1, z2, rcnt);
- debugl1(cs, tmp);
- }
cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
idx = 0;
while ((idx < rcnt) && WaitNoBusy(cs)) {
int idx;
int chksum;
u_char stat, cip;
- char tmp[64];
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
debugl1(cs, "hfc_empty_fifo");
chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
WaitNoBusy(cs);
stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc_empty_fifo %d chksum %x stat %x",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
bcs->channel, chksum, stat);
- debugl1(cs, tmp);
- }
if (stat) {
debugl1(cs, "FIFO CRC error");
dev_kfree_skb(skb);
int idx, fcnt;
int count;
u_char cip;
- char tmp[64];
- if (!bcs->hw.hfc.tx_skb)
+ if (!bcs->tx_skb)
return;
- if (bcs->hw.hfc.tx_skb->len <= 0)
+ if (bcs->tx_skb->len <= 0)
return;
save_flags(flags);
WaitNoBusy(cs);
bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
bcs->hw.hfc.send[bcs->hw.hfc.f1]);
- debugl1(cs, tmp);
- }
fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
if (fcnt < 0)
fcnt += 32;
return;
}
count = GetFreeFifoBytes(bcs);
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc_fill_fifo %d count(%d/%d)",
- bcs->channel, bcs->hw.hfc.tx_skb->len,
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)",
+ bcs->channel, bcs->tx_skb->len,
count);
- debugl1(cs, tmp);
- }
- if (count < bcs->hw.hfc.tx_skb->len) {
+ if (count < bcs->tx_skb->len) {
if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "hfc_fill_fifo no fifo mem");
restore_flags(flags);
}
cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
idx = 0;
- while ((idx < bcs->hw.hfc.tx_skb->len) && WaitNoBusy(cs))
- cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->hw.hfc.tx_skb->data[idx++]);
- if (idx != bcs->hw.hfc.tx_skb->len) {
+ while ((idx < bcs->tx_skb->len) && WaitNoBusy(cs))
+ cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+ if (idx != bcs->tx_skb->len) {
debugl1(cs, "FIFO Send BUSY error");
printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
} else {
- count = bcs->hw.hfc.tx_skb->len;
+ count = bcs->tx_skb->len;
bcs->tx_cnt -= count;
- if (PACKET_NOACK == bcs->hw.hfc.tx_skb->pkt_type)
+ if (PACKET_NOACK == bcs->tx_skb->pkt_type)
count = -1;
- dev_kfree_skb(bcs->hw.hfc.tx_skb);
- bcs->hw.hfc.tx_skb = NULL;
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
WaitForBusy(cs);
WaitNoBusy(cs);
cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
u_char f1, f2, cip;
int receive, transmit, count = 5;
struct sk_buff *skb;
- char tmp[64];
save_flags(flags);
Begin:
WaitNoBusy(cs);
f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
if (f1 != f2) {
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc rec %d f1(%d) f2(%d)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
bcs->channel, f1, f2);
- debugl1(cs, tmp);
- }
WaitForBusy(cs);
z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
if (rcnt < 0)
rcnt += cs->hw.hfc.fifosize;
rcnt++;
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
bcs->channel, z1, z2, rcnt);
- debugl1(cs, tmp);
- }
/* sti(); */
if ((skb = hfc_empty_fifo(bcs, rcnt))) {
skb_queue_tail(&bcs->rqueue, skb);
restore_flags(flags);
udelay(1);
cli();
- if (bcs->hw.hfc.tx_skb) {
+ if (bcs->tx_skb) {
transmit = 1;
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
hfc_fill_fifo(bcs);
if (test_bit(BC_FLG_BUSY, &bcs->Flag))
transmit = 0;
} else {
- if ((bcs->hw.hfc.tx_skb = skb_dequeue(&bcs->squeue))) {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
transmit = 1;
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
hfc_fill_fifo(bcs);
{
struct IsdnCardState *cs = bcs->cs;
- if (cs->debug & L1_DEB_HSCX) {
- char tmp[40];
- sprintf(tmp, "HFC 2BS0 mode %d bchan %d/%d",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d",
mode, bc, bcs->channel);
- debugl1(cs, tmp);
- }
bcs->mode = mode;
+ bcs->channel = bc;
switch (mode) {
case (L1_MODE_NULL):
long flags;
switch (pr) {
- case (PH_DATA_REQ):
+ case (PH_DATA | REQUEST):
save_flags(flags);
cli();
- if (st->l1.bcs->hw.hfc.tx_skb) {
+ if (st->l1.bcs->tx_skb) {
skb_queue_tail(&st->l1.bcs->squeue, skb);
restore_flags(flags);
} else {
- st->l1.bcs->hw.hfc.tx_skb = skb;
+ st->l1.bcs->tx_skb = skb;
test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
restore_flags(flags);
}
break;
- case (PH_PULL_IND):
- if (st->l1.bcs->hw.hfc.tx_skb) {
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
break;
}
save_flags(flags);
cli();
test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
- st->l1.bcs->hw.hfc.tx_skb = skb;
+ st->l1.bcs->tx_skb = skb;
st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
restore_flags(flags);
break;
- case (PH_PULL_REQ):
- if (!st->l1.bcs->hw.hfc.tx_skb) {
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL_CNF, NULL);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ mode_hfc(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
}
}
+
void
close_hfcstate(struct BCState *bcs)
{
- struct sk_buff *skb;
-
- mode_hfc(bcs, 0, 0);
+ mode_hfc(bcs, 0, bcs->channel);
if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
- while ((skb = skb_dequeue(&bcs->rqueue))) {
- dev_kfree_skb(skb);
- }
- while ((skb = skb_dequeue(&bcs->squeue))) {
- dev_kfree_skb(skb);
- }
- if (bcs->hw.hfc.tx_skb) {
- dev_kfree_skb(bcs->hw.hfc.tx_skb);
- bcs->hw.hfc.tx_skb = NULL;
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
}
}
}
static int
-open_hfcstate(struct IsdnCardState *cs,
- int bc)
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
{
- struct BCState *bcs = cs->bcs + bc;
-
if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
skb_queue_head_init(&bcs->rqueue);
skb_queue_head_init(&bcs->squeue);
}
- bcs->hw.hfc.tx_skb = NULL;
+ bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
bcs->event = 0;
bcs->tx_cnt = 0;
return (0);
}
-static void
-hfc_manl1(struct PStack *st, int pr,
- void *arg)
-{
- switch (pr) {
- case (PH_ACTIVATE_REQ):
- test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- mode_hfc(st->l1.bcs, st->l1.mode, st->l1.bc);
- st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
- break;
- case (PH_DEACTIVATE_REQ):
- if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
- mode_hfc(st->l1.bcs, 0, 0);
- test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- break;
- }
-}
-
int
setstack_hfc(struct PStack *st, struct BCState *bcs)
{
- if (open_hfcstate(st->l1.hardware, bcs->channel))
+ bcs->channel = st->l1.bc;
+ if (open_hfcstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
st->l2.l2l1 = hfc_l2l1;
- st->ma.manl1 = hfc_manl1;
setstack_manager(st);
bcs->st = st;
+ setstack_l1_B(st);
return (0);
}
-/* $Id: hisax.h,v 2.14 1998/02/11 17:28:04 keil Exp $
+/* $Id: hisax.h,v 2.26 1998/11/15 23:54:45 keil Exp $
* Basic declarations, defines and prototypes
*
* $Log: hisax.h,v $
+ * Revision 2.26 1998/11/15 23:54:45 keil
+ * changes from 2.0
+ *
+ * Revision 2.25 1998/09/30 22:28:42 keil
+ * More work for ISAR support
+ *
+ * Revision 2.24 1998/08/20 13:50:39 keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 2.23 1998/08/13 23:36:31 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.22 1998/07/15 15:01:28 calle
+ * Support for AVM passive PCMCIA cards:
+ * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 2.21 1998/05/25 14:10:05 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.20 1998/05/25 12:57:57 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.19 1998/04/15 16:39:15 keil
+ * Add S0Box and Teles PCI support
+ *
+ * Revision 2.18 1998/03/26 07:10:04 paul
+ * The jumpmatrix table in struct Fsm was an array of "int". This is not
+ * large enough for pointers to functions on Linux/Alpha (instant crash
+ * on "insmod hisax). Now there is a typedef for the pointer to function.
+ * This also prevents warnings about "incompatible pointer types".
+ *
+ * Revision 2.17 1998/03/19 13:18:43 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 2.16 1998/03/09 23:19:25 keil
+ * Changes for PCMCIA
+ *
* Revision 2.14 1998/02/11 17:28:04 keil
* Niccy PnP/PCI support
*
#include <linux/isdnif.h>
#include <linux/tty.h>
#include <linux/init.h>
+#include <linux/serialP.h>
+
+#define REQUEST 0
+#define CONFIRM 1
+#define INDICATION 2
+#define RESPONSE 3
+
+#define HW_ENABLE 0x0000
+#define HW_RESET 0x0004
+#define HW_POWERUP 0x0008
+#define HW_ACTIVATE 0x0010
+#define HW_DEACTIVATE 0x0018
+#define HW_INFO2 0x0020
+#define HW_INFO3 0x0030
+#define HW_INFO4_P8 0x0040
+#define HW_INFO4_P10 0x0048
+#define HW_RSYNC 0x0060
+#define HW_TESTLOOP 0x0070
+#define CARD_RESET 0x00F0
+#define CARD_SETIRQ 0x00F1
+#define CARD_INIT 0x00F2
+#define CARD_RELEASE 0x00F3
+#define CARD_TEST 0x00F4
+#define CARD_AUX_IND 0x00F5
+#define CARD_LOAD_FIRM 0x00F6
+
+#define PH_ACTIVATE 0x0100
+#define PH_DEACTIVATE 0x0110
+#define PH_DATA 0x0120
+#define PH_PULL 0x0130
+#define PH_TESTLOOP 0x0140
+#define PH_PAUSE 0x0150
+#define MPH_ACTIVATE 0x0180
+#define MPH_DEACTIVATE 0x0190
+#define MPH_INFORMATION 0x01A0
+
+#define DL_ESTABLISH 0x0200
+#define DL_RELEASE 0x0210
+#define DL_DATA 0x0220
+#define DL_FLUSH 0x0224
+#define DL_UNIT_DATA 0x0230
+#define MDL_ASSIGN 0x0280
+#define MDL_REMOVE 0x0284
+#define MDL_ERROR 0x0288
+#define MDL_INFO_SETUP 0x02E0
+#define MDL_INFO_CONN 0x02E4
+#define MDL_INFO_REL 0x02E8
+
+#define CC_SETUP 0x0300
+#define CC_RESUME 0x0304
+#define CC_MORE_INFO 0x0310
+#define CC_IGNORE 0x0320
+#define CC_REJECT 0x0324
+#define CC_SETUP_COMPL 0x0330
+#define CC_PROCEEDING 0x0340
+#define CC_ALERTING 0x0344
+#define CC_CONNECT 0x0350
+#define CC_CHARGE 0x0354
+#define CC_DISCONNECT 0x0360
+#define CC_RELEASE 0x0368
+#define CC_SUSPEND 0x0370
+#define CC_T303 0x0383
+#define CC_T304 0x0384
+#define CC_T305 0x0385
+#define CC_T308_1 0x0388
+#define CC_T308_2 0x0389
+#define CC_T310 0x0390
+#define CC_T313 0x0393
+#define CC_T318 0x0398
+#define CC_T319 0x0399
+#define CC_NOSETUP_RSP 0x03E0
+#define CC_SETUP_ERR 0x03E1
+#define CC_SUSPEND_ERR 0x03E2
+#define CC_RESUME_ERR 0x03E3
+#define CC_CONNECT_ERR 0x03E4
+#define CC_RELEASE_ERR 0x03E5
+#define CC_DLRL 0x03F0
+#define CC_RESTART 0x03F4
-#define PH_ACTIVATE_REQ 0x0010
-#define PH_ACTIVATE_CNF 0x0011
-#define PH_ACTIVATE_IND 0x0012
-#define PH_DEACTIVATE_REQ 0x0020
-#define PH_DEACTIVATE_CNF 0x0021
-#define PH_DEACTIVATE_IND 0x0022
-#define PH_DEACT_REQ 0x0024
-#define PH_DEACT_CNF 0x0025
-#define PH_DEACT_IND 0x0026
-#define PH_DEACT_ACK 0x0027
-#define PH_TESTLOOP_REQ 0x0030
-#define PH_PAUSE_CNF 0x0035
-#define PH_PAUSE_IND 0x0036
-#define PH_PULL_REQ 0x0038
-#define PH_PULL_CNF 0x0039
-#define PH_PULL_IND 0x003A
-#define PH_DATA_REQ 0x0040
-#define PH_DATA_IND 0x0042
-
-#define PH_INFO3_REQ 0x0008
-#define PH_INFO2_IND 0x000A
-#define PH_ENABLE_REQ 0x0004
-#define PH_RSYNC_IND 0x0006
-#define PH_RESET_REQ 0x0000
-#define PH_RESET_IND 0x0002
-#define PH_POWERUP_CNF 0x0003
-#define PH_ACTIV_REQ 0x000C
-#define PH_I4_P8_IND 0x000D
-#define PH_I4_P10_IND 0x000F
-
-#define MDL_ASSIGN_REQ 0x0050
-#define MDL_ASSIGN_IND 0x0052
-#define MDL_REMOVE_REQ 0x0054
-#define MDL_ERROR_REQ 0x0058
-#define MDL_ERROR_IND 0x005A
-#define CARD_AUX_IND 0x005E
-
-#define DL_UNIT_DATA 6
-#define CC_ESTABLISH 7
-#define DL_ESTABLISH 8
-#define DL_DATA 9
-
-#define CC_CONNECT 15
-#define DL_RELEASE 20
-#define DL_FLUSH 21
-
-#define CC_REJECT 23
-
-#define CC_SETUP_REQ 24
-#define CC_SETUP_CNF 25
-#define CC_SETUP_IND 26
-#define CC_SETUP_RSP 27
-#define CC_SETUP_COMPLETE_IND 28
-
-#define CC_DISCONNECT_REQ 29
-#define CC_DISCONNECT_IND 30
-
-#define CC_RELEASE_CNF 31
-#define CC_RELEASE_IND 32
-#define CC_RELEASE_REQ 33
-
-#define CC_REJECT_REQ 34
-
-#define CC_PROCEEDING_IND 35
-
-#define CC_DLRL 36
-#define CC_DLEST 37
-
-#define CC_ALERTING_REQ 38
-#define CC_ALERTING_IND 39
-
-#define DL_STOP 40
-#define DL_START 41
-
-#define MDL_INFO_SETUP 42
-#define MDL_INFO_CONN 43
-#define MDL_INFO_REL 44
-#define MDL_NOTEIPROC 46
-
-#define LC_ESTABLISH 47
-#define LC_RELEASE 48
-
-#define CC_INFO_CHARGE 52
-
-#define CC_MORE_INFO 53
-#define CC_IGNORE 54
-#define CC_RESTART 55
-
-
-#define CC_T303 60
-#define CC_T304 61
-#define CC_T305 62
-#define CC_T308_1 64
-#define CC_T308_2 65
-#define CC_T310 66
-#define CC_T313 67
-#define CC_T318 68
-#define CC_T319 69
-
-#define CC_NOSETUP_RSP_ERR 70
-#define CC_SETUP_ERR 71
-#define CC_CONNECT_ERR 72
-#define CC_RELEASE_ERR 73
-
-#define CARD_RESET 0x1001
-#define CARD_SETIRQ 0x1002
-#define CARD_INIT 0x1003
-#define CARD_RELEASE 0x1004
-#define CARD_TEST 0x1005
#ifdef __KERNEL__
#define MAX_DFRAME_LEN 260
+#define MAX_DFRAME_LEN_L1 300
#define HSCX_BUFMAX 4096
#define MAX_DATA_SIZE (HSCX_BUFMAX - 4)
#define MAX_DATA_MEM (HSCX_BUFMAX + 64)
#define MAX_HEADER_LEN 4
#define MAX_WINDOW 8
#define MAX_MON_FRAME 32
+#define MAX_DLOG_SPACE 2048
+#define MAX_BLOG_SPACE 256
/* #define I4L_IRQ_FLAG SA_INTERRUPT */
#define I4L_IRQ_FLAG 0
* Statemachine
*/
+struct FsmInst;
+
+typedef void (* FSMFNPTR)(struct FsmInst *, int, void *);
+
struct Fsm {
- int *jumpmatrix;
+ FSMFNPTR *jumpmatrix;
int state_count, event_count;
char **strEvent, **strState;
};
int debug;
void *userdata;
int userint;
- void (*printdebug) (struct FsmInst *, char *);
+ void (*printdebug) (struct FsmInst *, char *, ...);
};
struct FsmNode {
struct FsmInst l1m;
struct FsmTimer timer;
void (*l1l2) (struct PStack *, int, void *);
- void (*l1man) (struct PStack *, int, void *);
+ void (*l1hw) (struct PStack *, int, void *);
void (*l1tei) (struct PStack *, int, void *);
int mode, bc;
+ int delay;
};
#define GROUP_TEI 127
#define FLG_ORIG 2
#define FLG_MOD128 3
#define FLG_PEND_REL 4
-#define FLG_L3_INIT 5
-#define FLG_T200_RUN 6
+#define FLG_L3_INIT 5
+#define FLG_T200_RUN 6
#define FLG_ACK_PEND 7
#define FLG_REJEXC 8
#define FLG_OWN_BUSY 9
#define FLG_PEER_BUSY 10
#define FLG_DCHAN_BUSY 11
+#define FLG_L1_ACTIV 12
+#define FLG_ESTAB_PEND 13
+#define FLG_PTP 14
+#define FLG_FIXED_TEI 15
struct Layer2 {
int tei;
- int tei_wanted;
int sap;
int maxlen;
unsigned int flag;
struct sk_buff_head i_queue;
struct sk_buff_head ui_queue;
void (*l2l1) (struct PStack *, int, void *);
- void (*l2man) (struct PStack *, int, void *);
void (*l2l3) (struct PStack *, int, void *);
void (*l2tei) (struct PStack *, int, void *);
struct FsmInst l2m;
struct FsmTimer t200, t203;
int T200, N200, T203;
int debug;
- char debug_id[32];
+ char debug_id[16];
};
struct Layer3 {
- void (*l3l4) (struct l3_process *, int, void *);
+ void (*l3l4) (struct PStack *, int, void *);
void (*l3l2) (struct PStack *, int, void *);
+ struct FsmInst l3m;
+ struct sk_buff_head squeue;
struct l3_process *proc;
struct l3_process *global;
int N303;
int debug;
+ char debug_id[8];
};
struct LLInterface {
struct FsmTimer t202;
int T202, N202, debug;
void (*layer) (struct PStack *, int, void *);
- void (*manl1) (struct PStack *, int, void *);
- void (*manl2) (struct PStack *, int, void *);
};
struct Layer1 l1;
struct Layer2 l2;
struct Layer3 l3;
- struct LLInterface lli;
+ struct LLInterface lli;
struct Management ma;
int protocol; /* EDSS1 or 1TR6 */
};
};
struct hscx_hw {
+ int hscx;
+ int rcvidx;
+ int count; /* Current skb sent count */
+ u_char *rcvbuf; /* B-Channel receive Buffer */
+};
+
+struct isar_reg {
+ unsigned int Flags;
+ volatile u_char bstat;
+ volatile u_char iis;
+ volatile u_char cmsb;
+ volatile u_char clsb;
+ volatile u_char par[8];
+};
+
+struct isar_hw {
+ int dpath;
+ int rcvidx;
+ int txcnt;
+ int mml;
+ u_char *rcvbuf; /* B-Channel receive Buffer */
+ struct isar_reg *reg;
+};
+
+struct hdlc_stat_reg {
+ u_char cmd __attribute__((packed));
+ u_char xml __attribute__((packed));
+ u_char mode __attribute__((packed));
+ u_char fill __attribute__((packed));
+};
+
+struct hdlc_hw {
+ union {
+ u_int ctrl;
+ struct hdlc_stat_reg sr;
+ } ctrl;
+ u_int stat;
int rcvidx;
int count; /* Current skb sent count */
u_char *rcvbuf; /* B-Channel receive Buffer */
- struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
};
struct hfcB_hw {
unsigned int *send;
int f1;
int f2;
- struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
};
struct tiger_hw {
- struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
u_int *send;
u_int *s_irq;
u_int *s_end;
u_char s_state;
};
-struct foreign_hw {
- int doHDLCprocessing;
+struct amd7930_hw {
u_char *tx_buff;
u_char *rv_buff;
int rv_buff_in;
struct hdlc_state *hdlc_state;
struct tq_struct tq_rcv;
struct tq_struct tq_xmt;
- struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
};
+
#define BC_FLG_INIT 1
#define BC_FLG_ACTIV 2
#define BC_FLG_BUSY 3
#define L1_MODE_NULL 0
#define L1_MODE_TRANS 1
#define L1_MODE_HDLC 2
+#define L1_MODE_MODEM 7
struct BCState {
int channel;
int Flag;
struct IsdnCardState *cs;
int tx_cnt; /* B-Channel transmit counter */
+ struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
struct sk_buff_head rqueue; /* B-Channel receive Queue */
struct sk_buff_head squeue; /* B-Channel send Queue */
struct PStack *st;
+ u_char *blog;
+ struct timer_list transbusy;
struct tq_struct tqueue;
int event;
int (*BC_SetStack) (struct PStack *, struct BCState *);
void (*BC_Close) (struct BCState *);
union {
struct hscx_hw hscx;
+ struct hdlc_hw hdlc;
+ struct isar_hw isar;
struct hfcB_hw hfc;
struct tiger_hw tiger;
- struct foreign_hw foreign;
+ struct amd7930_hw amd7930;
} hw;
};
-struct LcFsm {
- int type;
- int delay;
- struct FsmInst lcfi;
- struct Channel *ch;
- void (*lccall) (struct LcFsm *, int, void *);
- struct PStack *st;
- int l2_establish;
- int l2_start;
- struct FsmTimer act_timer;
- char debug_id[32];
-};
-
struct Channel {
struct PStack *b_st, *d_st;
struct IsdnCardState *cs;
int chan;
int incoming;
struct FsmInst fi;
- struct LcFsm *lc_d;
- struct LcFsm *lc_b;
struct FsmTimer drel_timer, dial_timer;
int debug;
int l2_protocol, l2_active_protocol;
+ int l3_protocol;
int data_open;
struct l3_process *proc;
setup_parm setup; /* from isdnif.h numbers and Serviceindicator */
unsigned int counter;
unsigned int status;
struct timer_list tl;
+ unsigned int MFlag;
+ struct BCState *bcs;
+ u_char *transbuf;
+ u_char *rcvbuf;
+ unsigned int transp;
+ unsigned int rcvp;
+ unsigned int transcnt;
+ unsigned int rcvcnt;
+ u_char IER;
+ u_char FCR;
+ u_char LCR;
+ u_char MCR;
u_char ctrl_reg;
-};
+};
struct teles3_hw {
unsigned int cfg_reg;
- unsigned int isac;
- unsigned int hscx[2];
- unsigned int isacfifo;
- unsigned int hscxfifo[2];
-};
+ signed int isac;
+ signed int hscx[2];
+ signed int isacfifo;
+ signed int hscxfifo[2];
+};
struct teles0_hw {
unsigned int cfg_reg;
unsigned int membase;
-};
+};
struct avm_hw {
unsigned int cfg_reg;
unsigned int isacfifo;
unsigned int hscxfifo[2];
unsigned int counter;
-};
+};
struct ix1_hw {
unsigned int cfg_reg;
unsigned int status;
struct timer_list tl;
u_char ctrl_reg;
-};
+};
struct asus_hw {
unsigned int cfg_reg;
unsigned int hscx;
unsigned int reset_on;
unsigned int reset_off;
+ struct isar_reg isar;
+ unsigned int chip;
+ unsigned int bus;
};
struct spt_hw {
unsigned int isac;
unsigned int hscx[2];
unsigned char res_irq;
-};
+};
struct mic_hw {
unsigned int cfg_reg;
#define HW_IOM1 0
#define HW_IPAC 1
+#define HW_ISAR 2
#define FLG_TWO_DCHAN 4
#define FLG_L1_DBUSY 5
#define FLG_DBUSY_TIMER 6
unsigned char subtyp;
int protocol;
unsigned int irq;
- int HW_Flags;
+ int HW_Flags;
int *busy_flag;
union {
struct elsa_hw elsa;
struct njet_hw njet;
struct hfcD_hw hfcD;
struct ix1_hw niccy;
- struct foreign_interface *foreign;
} hw;
int myid;
isdn_if iif;
void (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char);
void (*BC_Send_Data) (struct BCState *);
int (*cardmsg) (struct IsdnCardState *, int, void *);
- void (*l1cmd) (struct IsdnCardState *, int, void *);
struct Channel channel[2];
struct BCState bcs[2];
struct PStack *stlist;
struct sk_buff_head rq, sq; /* D-channel queues */
int ph_state;
int cardnr;
- int dlogflag;
- char *dlogspace;
+ char *dlog;
int debug;
u_char *mon_tx;
u_char *mon_rx;
#define MON0_TX 4
#define MON1_TX 8
+#define HISAX_MAX_CARDS 8
+
#define ISDN_CTYPE_16_0 1
#define ISDN_CTYPE_8_0 2
#define ISDN_CTYPE_16_3 3
#define ISDN_CTYPE_SEDLBAUER_PCMCIA 22
#define ISDN_CTYPE_AMD7930 23
#define ISDN_CTYPE_NICCY 24
-#define ISDN_CTYPE_DBRI 25
+#define ISDN_CTYPE_S0BOX 25
+#define ISDN_CTYPE_A1_PCMCIA 26
+#define ISDN_CTYPE_FRITZPCI 27
+#define ISDN_CTYPE_SEDLBAUER_FAX 28
-#define ISDN_CTYPE_COUNT 25
+#define ISDN_CTYPE_COUNT 28
#ifdef ISDN_CHIP_ISAC
#undef ISDN_CHIP_ISAC
#define CARD_TELES3 0
#endif
+#ifdef CONFIG_HISAX_TELESPCI
+#define CARD_TELESPCI (1<< ISDN_CTYPE_TELESPCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELESPCI 0
+#endif
+
#ifdef CONFIG_HISAX_AVM_A1
#define CARD_AVM_A1 (1<< ISDN_CTYPE_A1)
-#ifndef ISDN_CHIP_ISAC
+#ifndef ISDN_CHIP_ISAC
#define ISDN_CHIP_ISAC 1
#endif
#else
#define CARD_AVM_A1 0
#endif
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#define CARD_AVM_A1_PCMCIA (1<< ISDN_CTYPE_A1_PCMCIA)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_AVM_A1_PCMCIA 0
+#endif
+
+#ifdef CONFIG_HISAX_FRITZPCI
+#define CARD_FRITZPCI (1<< ISDN_CTYPE_FRITZPCI)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_FRITZPCI 0
+#endif
+
#ifdef CONFIG_HISAX_ELSA
#define CARD_ELSA (1<< ISDN_CTYPE_ELSA) | (1<< ISDN_CTYPE_ELSA_PNP) | \
(1<< ISDN_CTYPE_ELSA_PCMCIA) | (1<< ISDN_CTYPE_ELSA_PCI)
#endif
#ifdef CONFIG_HISAX_SEDLBAUER
-#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA)
+#define CARD_SEDLBAUER (1 << ISDN_CTYPE_SEDLBAUER) | (1 << ISDN_CTYPE_SEDLBAUER_PCMCIA) | ( 1 << ISDN_CTYPE_SEDLBAUER_FAX)
#ifndef ISDN_CHIP_ISAC
#define ISDN_CHIP_ISAC 1
#endif
#define CARD_NICCY 0
#endif
-#ifdef CONFIG_HISAX_DBRI
-#define CARD_DBRI (1 << ISDN_CTYPE_DBRI)
+#ifdef CONFIG_HISAX_S0BOX
+#define CARD_S0BOX (1 << ISDN_CTYPE_S0BOX)
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
#else
-#define CARD_DBRI 0
+#define CARD_S0BOX 0
#endif
-
#define SUPORTED_CARDS (CARD_TELES0 | CARD_TELES3 | CARD_AVM_A1 | CARD_ELSA \
| CARD_IX1MICROR2 | CARD_DIEHLDIVA | CARD_ASUSCOM \
| CARD_TELEINT | CARD_SEDLBAUER | CARD_SPORTSTER \
| CARD_MIC | CARD_NETJET | CARD_TELES3C | CARD_AMD7930 \
- | CARD_NICCY | CARD_DBRI)
+ | CARD_AVM_A1_PCMCIA | CARD_FRITZPCI\
+ | CARD_NICCY | CARD_S0BOX | CARD_TELESPCI)
#define TEI_PER_CARD 0
#undef TEI_PER_CARD
#define TEI_PER_CARD 1
#define HISAX_EURO_SENDCOMPLETE 1
-#ifdef CONFIG_HISAX_ML
+#define EXT_BEARER_CAPS 1
+#define HISAX_SEND_STD_LLC_IE 1
+#ifdef CONFIG_HISAX_NO_SENDCOMPLETE
#undef HISAX_EURO_SENDCOMPLETE
#endif
+#ifdef CONFIG_HISAX_NO_LLC
+#undef HISAX_SEND_STD_LLC_IE
+#endif
#undef HISAX_DE_AOC
#ifdef CONFIG_DE_AOC
#define HISAX_DE_AOC 1
#endif
#endif
-#if TEI_PER_CARD
-#undef TEI_FIXED
-#endif
-
-#undef PTP_DATA_LINK
-
-#ifdef PTP_DATA_LINK
-#undef TEI_FIXED
-#define TEI_FIXED 0
-#define LAYER2_WATCHING
+/* L1 Debug */
+#define L1_DEB_WARN 0x01
+#define L1_DEB_INTSTAT 0x02
+#define L1_DEB_ISAC 0x04
+#define L1_DEB_ISAC_FIFO 0x08
+#define L1_DEB_HSCX 0x10
+#define L1_DEB_HSCX_FIFO 0x20
+#define L1_DEB_LAPD 0x40
+#define L1_DEB_IPAC 0x80
+#define L1_DEB_RECEIVE_FRAME 0x100
+#define L1_DEB_MONITOR 0x200
+#define DEB_DLOG_HEX 0x400
+#define DEB_DLOG_VERBOSE 0x800
+
+#define L2FRAME_DEBUG
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
#endif
struct IsdnCard {
struct IsdnCardState *cs;
};
-void setstack_isdnl2(struct PStack *st, char *debug_id);
-int HiSax_inithardware(int *);
-void HiSax_closehardware(void);
+void init_bcstate(struct IsdnCardState *cs, int bc);
void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs);
unsigned int random_ri(void);
-void setstack_isdnl3(struct PStack *st, struct Channel *chanp);
void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
+
+void setstack_l1_B(struct PStack *st);
+
+void setstack_tei(struct PStack *st);
+void setstack_manager(struct PStack *st);
+
+void setstack_isdnl2(struct PStack *st, char *debug_id);
void releasestack_isdnl2(struct PStack *st);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+
+void setstack_l3dc(struct PStack *st, struct Channel *chanp);
+void setstack_l3bc(struct PStack *st, struct Channel *chanp);
void releasestack_isdnl3(struct PStack *st);
-void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
u_char *findie(u_char * p, int size, u_char ie, int wanted_set);
int getcallref(u_char * p);
void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
void *arg, int where);
void FsmDelTimer(struct FsmTimer *ft, int where);
-void jiftime(char *s, long mark);
+int jiftime(char *s, long mark);
int HiSax_command(isdn_ctrl * ic);
int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
-void HiSax_putstatus(struct IsdnCardState *csta, char *buf);
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
void HiSax_reportcard(int cardnr);
int QuickHex(char *txt, u_char * p, int cnt);
-void LogFrame(struct IsdnCardState *sp, u_char * p, int size);
-void dlogframe(struct IsdnCardState *sp, u_char * p, int size, char *comment);
+void LogFrame(struct IsdnCardState *cs, u_char * p, int size);
+void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
void iecpy(u_char * dest, u_char * iestart, int ieoffset);
-void setstack_transl2(struct PStack *st);
-void releasestack_transl2(struct PStack *st);
-void setstack_tei(struct PStack *st);
-void setstack_manager(struct PStack *st);
+int discard_queue(struct sk_buff_head *q);
#ifdef ISDN_CHIP_ISAC
void setstack_isac(struct PStack *st, struct IsdnCardState *cs);
#endif /* ISDN_CHIP_ISAC */
#define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
-int ll_run(struct IsdnCardState *csta);
-void ll_stop(struct IsdnCardState *csta);
+int ll_run(struct IsdnCardState *cs);
+void ll_stop(struct IsdnCardState *cs);
void CallcNew(void);
void CallcFree(void);
-int CallcNewChan(struct IsdnCardState *csta);
-void CallcFreeChan(struct IsdnCardState *csta);
+int CallcNewChan(struct IsdnCardState *cs);
+void CallcFreeChan(struct IsdnCardState *cs);
void Isdnl1New(void);
void Isdnl1Free(void);
void Isdnl2New(void);
void Isdnl2Free(void);
-void init_tei(struct IsdnCardState *sp, int protocol);
-void release_tei(struct IsdnCardState *sp);
+void Isdnl3New(void);
+void Isdnl3Free(void);
+void init_tei(struct IsdnCardState *cs, int protocol);
+void release_tei(struct IsdnCardState *cs);
char *HiSax_getrev(const char *revision);
void TeiNew(void);
void TeiFree(void);
+int certification_check(int output);
-/* $Id: hscx.c,v 1.7 1998/02/12 23:07:36 keil Exp $
+/* $Id: hscx.c,v 1.16 1998/11/15 23:54:48 keil Exp $
* hscx.c HSCX specific routines
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
*
* $Log: hscx.c,v $
+ * Revision 1.16 1998/11/15 23:54:48 keil
+ * changes from 2.0
+ *
+ * Revision 1.15 1998/08/20 13:50:42 keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 1.14 1998/08/13 23:36:33 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.13 1998/06/26 22:03:28 keil
+ * send flags between hdlc frames
+ *
+ * Revision 1.12 1998/06/09 18:26:01 keil
+ * PH_DEACTIVATE B-channel every time signaled to higher layer
+ *
+ * Revision 1.11 1998/05/25 14:10:07 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 1.10 1998/05/25 12:57:59 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.9 1998/04/15 16:45:33 keil
+ * new init code
+ *
+ * Revision 1.8 1998/03/19 13:16:24 keil
+ * fix the correct release of the hscx
+ *
* Revision 1.7 1998/02/12 23:07:36 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#define __NO_VERSION__
#include "hisax.h"
#include "hscx.h"
+#include "isac.h"
#include "isdnl1.h"
#include <linux/interrupt.h>
modehscx(struct BCState *bcs, int mode, int bc)
{
struct IsdnCardState *cs = bcs->cs;
- int hscx = bcs->channel;
+ int hscx = bcs->hw.hscx.hscx;
- if (cs->debug & L1_DEB_HSCX) {
- char tmp[40];
- sprintf(tmp, "hscx %c mode %d ichan %d",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hscx %c mode %d ichan %d",
'A' + hscx, mode, bc);
- debugl1(cs, tmp);
- }
bcs->mode = mode;
- cs->BC_Write_Reg(cs, hscx, HSCX_CCR1, 0x85);
+ bcs->channel = bc;
cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF);
cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF);
cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF);
cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0);
cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0);
+ cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+ test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85);
cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30);
cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7);
cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7);
}
switch (mode) {
case (L1_MODE_NULL):
- cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0xff);
- cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0xff);
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f);
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f);
cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84);
break;
case (L1_MODE_TRANS):
cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4);
break;
case (L1_MODE_HDLC):
+ cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+ test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d);
cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c);
break;
}
mark_bh(IMMEDIATE_BH);
}
-static void
+void
hscx_l2l1(struct PStack *st, int pr, void *arg)
{
struct sk_buff *skb = arg;
long flags;
switch (pr) {
- case (PH_DATA_REQ):
+ case (PH_DATA | REQUEST):
save_flags(flags);
cli();
- if (st->l1.bcs->hw.hscx.tx_skb) {
+ if (st->l1.bcs->tx_skb) {
skb_queue_tail(&st->l1.bcs->squeue, skb);
restore_flags(flags);
} else {
- st->l1.bcs->hw.hscx.tx_skb = skb;
+ st->l1.bcs->tx_skb = skb;
test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
st->l1.bcs->hw.hscx.count = 0;
restore_flags(flags);
st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
}
break;
- case (PH_PULL_IND):
- if (st->l1.bcs->hw.hscx.tx_skb) {
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
break;
}
test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
- st->l1.bcs->hw.hscx.tx_skb = skb;
+ st->l1.bcs->tx_skb = skb;
st->l1.bcs->hw.hscx.count = 0;
st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
break;
- case (PH_PULL_REQ):
- if (!st->l1.bcs->hw.hscx.tx_skb) {
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL_CNF, NULL);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ modehscx(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ modehscx(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
}
-
}
void
close_hscxstate(struct BCState *bcs)
{
- struct sk_buff *skb;
-
- modehscx(bcs, 0, 0);
+ modehscx(bcs, 0, bcs->channel);
if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
if (bcs->hw.hscx.rcvbuf) {
kfree(bcs->hw.hscx.rcvbuf);
bcs->hw.hscx.rcvbuf = NULL;
}
- while ((skb = skb_dequeue(&bcs->rqueue))) {
- dev_kfree_skb(skb);
- }
- while ((skb = skb_dequeue(&bcs->squeue))) {
- dev_kfree_skb(skb);
+ if (bcs->blog) {
+ kfree(bcs->blog);
+ bcs->blog = NULL;
}
- if (bcs->hw.hscx.tx_skb) {
- dev_kfree_skb(bcs->hw.hscx.tx_skb);
- bcs->hw.hscx.tx_skb = NULL;
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
}
}
}
-static int
-open_hscxstate(struct IsdnCardState *cs,
- int bc)
+int
+open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs)
{
- struct BCState *bcs = cs->bcs + bc;
-
if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
printk(KERN_WARNING
- "HiSax: No memory for hscx.rcvbuf\n");
+ "HiSax: No memory for hscx.rcvbuf\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
return (1);
}
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for bcs->blog\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ return (2);
+ }
skb_queue_head_init(&bcs->rqueue);
skb_queue_head_init(&bcs->squeue);
}
- bcs->hw.hscx.tx_skb = NULL;
+ bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
bcs->event = 0;
bcs->hw.hscx.rcvidx = 0;
return (0);
}
-static void
-hscx_manl1(struct PStack *st, int pr,
- void *arg)
-{
- switch (pr) {
- case (PH_ACTIVATE_REQ):
- test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- modehscx(st->l1.bcs, st->l1.mode, st->l1.bc);
- st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
- break;
- case (PH_DEACTIVATE_REQ):
- if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
- modehscx(st->l1.bcs, 0, 0);
- test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- break;
- }
-}
-
int
setstack_hscx(struct PStack *st, struct BCState *bcs)
{
- if (open_hscxstate(st->l1.hardware, bcs->channel))
+ bcs->channel = st->l1.bc;
+ if (open_hscxstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
st->l2.l2l1 = hscx_l2l1;
- st->ma.manl1 = hscx_manl1;
setstack_manager(st);
bcs->st = st;
+ setstack_l1_B(st);
return (0);
}
HISAX_INITFUNC(void
clear_pending_hscx_ints(struct IsdnCardState *cs))
{
- int val;
- char tmp[64];
+ int val, eval;
val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
- sprintf(tmp, "HSCX B ISTA %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "HSCX B ISTA %x", val);
if (val & 0x01) {
- val = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
- sprintf(tmp, "HSCX B EXIR %x", val);
- debugl1(cs, tmp);
- } else if (val & 0x02) {
- val = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
- sprintf(tmp, "HSCX A EXIR %x", val);
- debugl1(cs, tmp);
+ eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
+ debugl1(cs, "HSCX B EXIR %x", eval);
+ }
+ if (val & 0x02) {
+ eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
+ debugl1(cs, "HSCX A EXIR %x", eval);
}
val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA);
- sprintf(tmp, "HSCX A ISTA %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "HSCX A ISTA %x", val);
val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
- sprintf(tmp, "HSCX B STAR %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "HSCX B STAR %x", val);
val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
- sprintf(tmp, "HSCX A STAR %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "HSCX A STAR %x", val);
+ /* disable all IRQ */
cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF);
cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF);
- cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
- cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
}
-HISAX_INITFUNC(void
+HISAX_INITFUNC(void
inithscx(struct IsdnCardState *cs))
{
cs->bcs[0].BC_SetStack = setstack_hscx;
cs->bcs[1].BC_SetStack = setstack_hscx;
cs->bcs[0].BC_Close = close_hscxstate;
cs->bcs[1].BC_Close = close_hscxstate;
+ cs->bcs[0].hw.hscx.hscx = 0;
+ cs->bcs[1].hw.hscx.hscx = 1;
modehscx(cs->bcs, 0, 0);
modehscx(cs->bcs + 1, 0, 0);
}
+
+HISAX_INITFUNC(void
+inithscxisac(struct IsdnCardState *cs, int part))
+{
+ if (part & 1) {
+ clear_pending_isac_ints(cs);
+ clear_pending_hscx_ints(cs);
+ initisac(cs);
+ inithscx(cs);
+ }
+ if (part & 2) {
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
+ cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
+ /* RESET Receiver and Transmitter */
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
+ }
+}
-/* $Id: hscx.h,v 1.3 1997/07/27 21:38:35 keil Exp $
+/* $Id: hscx.h,v 1.4 1998/04/15 16:45:34 keil Exp $
* hscx.h HSCX specific defines
*
*
*
* $Log: hscx.h,v $
+ * Revision 1.4 1998/04/15 16:45:34 keil
+ * new init code
+ *
* Revision 1.3 1997/07/27 21:38:35 keil
* new B-channel interface
*
extern void modehscx(struct BCState *bcs, int mode, int bc);
extern void clear_pending_hscx_ints(struct IsdnCardState *cs);
extern void inithscx(struct IsdnCardState *cs);
+extern void inithscxisac(struct IsdnCardState *cs, int part);
-/* $Id: hscx_irq.c,v 1.7 1998/02/12 23:07:37 keil Exp $
+/* $Id: hscx_irq.c,v 1.11 1998/11/15 23:54:49 keil Exp $
* hscx_irq.c low level b-channel stuff for Siemens HSCX
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
* This is an include file for fast inline IRQ stuff
*
* $Log: hscx_irq.c,v $
+ * Revision 1.11 1998/11/15 23:54:49 keil
+ * changes from 2.0
+ *
+ * Revision 1.10 1998/08/13 23:36:35 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.9 1998/06/24 14:44:51 keil
+ * Fix recovery of TX IRQ loss
+ *
+ * Revision 1.8 1998/04/10 10:35:22 paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
* Revision 1.7 1998/02/12 23:07:37 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
if (cs->debug & L1_DEB_WARN)
debugl1(cs, "hscx_empty_fifo: incoming packet too large");
- WriteHSCXCMDR(cs, bcs->channel, 0x80);
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
bcs->hw.hscx.rcvidx = 0;
return;
}
bcs->hw.hscx.rcvidx += count;
save_flags(flags);
cli();
- READHSCXFIFO(cs, bcs->channel, ptr, count);
- WriteHSCXCMDR(cs, bcs->channel, 0x80);
+ READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
restore_flags(flags);
if (cs->debug & L1_DEB_HSCX_FIFO) {
- char tmp[256];
- char *t = tmp;
+ char *t = bcs->blog;
t += sprintf(t, "hscx_empty_fifo %c cnt %d",
- bcs->channel ? 'B' : 'A', count);
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
QuickHex(t, ptr, count);
- debugl1(cs, tmp);
+ debugl1(cs, bcs->blog);
}
}
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
debugl1(cs, "hscx_fill_fifo");
- if (!bcs->hw.hscx.tx_skb)
+ if (!bcs->tx_skb)
return;
- if (bcs->hw.hscx.tx_skb->len <= 0)
+ if (bcs->tx_skb->len <= 0)
return;
more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
- if (bcs->hw.hscx.tx_skb->len > fifo_size) {
+ if (bcs->tx_skb->len > fifo_size) {
more = !0;
count = fifo_size;
} else
- count = bcs->hw.hscx.tx_skb->len;
+ count = bcs->tx_skb->len;
- waitforXFW(cs, bcs->channel);
+ waitforXFW(cs, bcs->hw.hscx.hscx);
save_flags(flags);
cli();
- ptr = bcs->hw.hscx.tx_skb->data;
- skb_pull(bcs->hw.hscx.tx_skb, count);
+ ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
bcs->tx_cnt -= count;
bcs->hw.hscx.count += count;
- WRITEHSCXFIFO(cs, bcs->channel, ptr, count);
- WriteHSCXCMDR(cs, bcs->channel, more ? 0x8 : 0xa);
+ WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
restore_flags(flags);
if (cs->debug & L1_DEB_HSCX_FIFO) {
- char tmp[256];
- char *t = tmp;
+ char *t = bcs->blog;
t += sprintf(t, "hscx_fill_fifo %c cnt %d",
- bcs->channel ? 'B' : 'A', count);
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
QuickHex(t, ptr, count);
- debugl1(cs, tmp);
+ debugl1(cs, bcs->blog);
}
}
struct sk_buff *skb;
int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
int count;
- char tmp[32];
if (!test_bit(BC_FLG_INIT, &bcs->Flag))
return;
if (cs->debug & L1_DEB_WARN)
debugl1(cs, "HSCX invalid frame");
if ((r & 0x40) && bcs->mode)
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "HSCX RDO mode=%d",
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX RDO mode=%d",
bcs->mode);
- debugl1(cs, tmp);
- }
if (!(r & 0x20))
if (cs->debug & L1_DEB_WARN)
debugl1(cs, "HSCX CRC error");
count = fifo_size;
hscx_empty_fifo(bcs, count);
if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
- if (cs->debug & L1_DEB_HSCX_FIFO) {
- sprintf(tmp, "HX Frame %d", count);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_HSCX_FIFO)
+ debugl1(cs, "HX Frame %d", count);
if (!(skb = dev_alloc_skb(count)))
printk(KERN_WARNING "HSCX: receive out of memory\n");
else {
}
}
if (val & 0x10) { /* XPR */
- if (bcs->hw.hscx.tx_skb) {
- if (bcs->hw.hscx.tx_skb->len) {
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
hscx_fill_fifo(bcs);
return;
} else {
if (bcs->st->lli.l1writewakeup &&
- (PACKET_NOACK != bcs->hw.hscx.tx_skb->pkt_type))
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count);
- dev_kfree_skb(bcs->hw.hscx.tx_skb);
+ dev_kfree_skb(bcs->tx_skb);
bcs->hw.hscx.count = 0;
- bcs->hw.hscx.tx_skb = NULL;
+ bcs->tx_skb = NULL;
}
}
- if ((bcs->hw.hscx.tx_skb = skb_dequeue(&bcs->squeue))) {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
bcs->hw.hscx.count = 0;
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
hscx_fill_fifo(bcs);
u_char exval;
struct BCState *bcs;
- char tmp[32];
if (val & 0x01) {
bcs = cs->bcs + 1;
exval = READHSCX(cs, 1, HSCX_EXIR);
- if (exval == 0x40) {
+ if (exval & 0x40) {
if (bcs->mode == 1)
hscx_fill_fifo(bcs);
else {
/* Here we lost an TX interrupt, so
* restart transmitting the whole frame.
*/
- if (bcs->hw.hscx.tx_skb) {
- skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count);
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
bcs->tx_cnt += bcs->hw.hscx.count;
bcs->hw.hscx.count = 0;
}
- WriteHSCXCMDR(cs, bcs->channel, 0x01);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "HSCX B EXIR %x Lost TX", exval);
- debugl1(cs, tmp);
- }
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
}
- } else if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "HSCX B EXIR %x", exval);
- debugl1(cs, tmp);
- }
+ } else if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX B EXIR %x", exval);
}
if (val & 0xf8) {
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "HSCX B interrupt %x", val);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX B interrupt %x", val);
hscx_interrupt(cs, val, 1);
}
if (val & 0x02) {
bcs = cs->bcs;
exval = READHSCX(cs, 0, HSCX_EXIR);
- if (exval == 0x40) {
+ if (exval & 0x40) {
if (bcs->mode == L1_MODE_TRANS)
hscx_fill_fifo(bcs);
else {
/* Here we lost an TX interrupt, so
* restart transmitting the whole frame.
*/
- if (bcs->hw.hscx.tx_skb) {
- skb_push(bcs->hw.hscx.tx_skb, bcs->hw.hscx.count);
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
bcs->tx_cnt += bcs->hw.hscx.count;
bcs->hw.hscx.count = 0;
}
- WriteHSCXCMDR(cs, bcs->channel, 0x01);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "HSCX A EXIR %x Lost TX", exval);
- debugl1(cs, tmp);
- }
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
}
- } else if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "HSCX A EXIR %x", exval);
- debugl1(cs, tmp);
- }
+ } else if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX A EXIR %x", exval);
}
if (val & 0x04) {
exval = READHSCX(cs, 0, HSCX_ISTA);
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "HSCX A interrupt %x", exval);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX A interrupt %x", exval);
hscx_interrupt(cs, exval, 0);
}
}
-/* $Id: ipac.h,v 1.2 1997/10/29 18:51:21 keil Exp $
+/* $Id: ipac.h,v 1.3 1998/04/15 16:48:09 keil Exp $
* ipac.h IPAC specific defines
*
*
*
* $Log: ipac.h,v $
+ * Revision 1.3 1998/04/15 16:48:09 keil
+ * IPAC_ATX added
+ *
* Revision 1.2 1997/10/29 18:51:21 keil
* New files
*
#define IPAC_ACFG 0xC3
#define IPAC_AOE 0xC4
#define IPAC_ARX 0xC5
+#define IPAC_ATX 0xC5
#define IPAC_PITA1 0xC6
#define IPAC_PITA2 0xC7
#define IPAC_POTA1 0xC8
-/* $Id: isac.c,v 1.12 1998/02/12 23:07:40 keil Exp $
+/* $Id: isac.c,v 1.18 1998/11/15 23:54:51 keil Exp $
* isac.c ISAC specific routines
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
*
* $Log: isac.c,v $
+ * Revision 1.18 1998/11/15 23:54:51 keil
+ * changes from 2.0
+ *
+ * Revision 1.17 1998/08/13 23:36:37 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.16 1998/05/25 12:58:01 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.15 1998/04/15 16:45:32 keil
+ * new init code
+ *
+ * Revision 1.14 1998/04/10 10:35:26 paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 1.13 1998/03/07 22:57:01 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 1.12 1998/02/12 23:07:40 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
int val;
val = cs->readisac(cs, ISAC_RBCH);
- printk(KERN_INFO "%s ISAC version : %s\n", s, ISACVer[(val >> 5) & 3]);
+ printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]);
}
static void
ph_command(struct IsdnCardState *cs, unsigned int command)
{
- if (cs->debug & L1_DEB_ISAC) {
- char tmp[32];
- sprintf(tmp, "ph_command %x", command);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_command %x", command);
cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
}
-static void
-manl1_msg(struct IsdnCardState *cs, int msg, void *arg) {
- struct PStack *st;
-
- st = cs->stlist;
- while (st) {
- st->ma.manl1(st, msg, arg);
- st = st->next;
- }
-}
static void
isac_new_ph(struct IsdnCardState *cs)
case (ISAC_IND_RS):
case (ISAC_IND_EI):
ph_command(cs, ISAC_CMD_DUI);
- manl1_msg(cs, PH_RESET_IND, NULL);
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
break;
case (ISAC_IND_DID):
- manl1_msg(cs, PH_DEACT_CNF, NULL);
+ l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
break;
case (ISAC_IND_DR):
- manl1_msg(cs, PH_DEACT_IND, NULL);
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
break;
case (ISAC_IND_PU):
- manl1_msg(cs, PH_POWERUP_CNF, NULL);
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
break;
case (ISAC_IND_RSY):
- manl1_msg(cs, PH_RSYNC_IND, NULL);
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
break;
case (ISAC_IND_ARD):
- manl1_msg(cs, PH_INFO2_IND, NULL);
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
break;
case (ISAC_IND_AI8):
- manl1_msg(cs, PH_I4_P8_IND, NULL);
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
break;
case (ISAC_IND_AI10):
- manl1_msg(cs, PH_I4_P10_IND, NULL);
+ l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
break;
default:
break;
if (!cs)
return;
-
if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
if (cs->debug)
debugl1(cs, "D-Channel Busy cleared");
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE_CNF, NULL);
+ stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
stptr = stptr->next;
}
}
if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
DChannel_proc_xmt(cs);
if (test_and_clear_bit(D_RX_MON0, &cs->event))
- test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
+ test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
if (test_and_clear_bit(D_RX_MON1, &cs->event))
- test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
+ test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
if (test_and_clear_bit(D_TX_MON0, &cs->event))
- test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
+ test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
if (test_and_clear_bit(D_TX_MON1, &cs->event))
- test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
+ test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
}
void
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
debugl1(cs, "isac_empty_fifo");
- if ((cs->rcvidx + count) >= MAX_DFRAME_LEN) {
- if (cs->debug & L1_DEB_WARN) {
- char tmp[40];
- sprintf(tmp, "isac_empty_fifo overrun %d",
+ if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isac_empty_fifo overrun %d",
cs->rcvidx + count);
- debugl1(cs, tmp);
- }
cs->writeisac(cs, ISAC_CMDR, 0x80);
cs->rcvidx = 0;
return;
cs->writeisac(cs, ISAC_CMDR, 0x80);
restore_flags(flags);
if (cs->debug & L1_DEB_ISAC_FIFO) {
- char tmp[128];
- char *t = tmp;
+ char *t = cs->dlog;
t += sprintf(t, "isac_empty_fifo cnt %d", count);
QuickHex(t, ptr, count);
- debugl1(cs, tmp);
+ debugl1(cs, cs->dlog);
}
}
cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
add_timer(&cs->dbusytimer);
if (cs->debug & L1_DEB_ISAC_FIFO) {
- char tmp[128];
- char *t = tmp;
+ char *t = cs->dlog;
t += sprintf(t, "isac_fill_fifo cnt %d", count);
QuickHex(t, ptr, count);
- debugl1(cs, tmp);
+ debugl1(cs, cs->dlog);
}
}
struct sk_buff *skb;
unsigned int count;
long flags;
- char tmp[32];
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "ISAC interrupt %x", val);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC interrupt %x", val);
if (val & 0x80) { /* RME */
exval = cs->readisac(cs, ISAC_RSTA);
if ((exval & 0x70) != 0x20) {
}
afterXPR:
if (val & 0x04) { /* CISQ */
- cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf;
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "ph_state change %x", cs->ph_state);
- debugl1(cs, tmp);
+ exval = cs->readisac(cs, ISAC_CIR0);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC CIR0 %02X", exval );
+ if (exval & 2) {
+ cs->ph_state = (exval >> 2) & 0xf;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state change %x", cs->ph_state);
+ isac_sched_event(cs, D_L1STATECHANGE);
+ }
+ if (exval & 1) {
+ exval = cs->readisac(cs, ISAC_CIR1);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC CIR1 %02X", exval );
}
- isac_sched_event(cs, D_L1STATECHANGE);
}
if (val & 0x02) { /* SIN */
/* never */
}
if (val & 0x01) { /* EXI */
exval = cs->readisac(cs, ISAC_EXIR);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "ISAC EXIR %02x", exval);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC EXIR %02x", exval);
if (exval & 0x04) {
v1 = cs->readisac(cs, ISAC_MOSR);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "ISAC MOSR %02x", v1);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC MOSR %02x", v1);
#if ARCOFI_USE
if (v1 & 0x08) {
if (!cs->mon_rx) {
goto afterMONR0;
}
cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC MOR0 %02x", cs->mon_rx[cs->mon_rxp -1]);
if (cs->mon_rxp == 1) {
cs->mocr |= 0x04;
cs->writeisac(cs, ISAC_MOCR, cs->mocr);
goto afterMONR1;
}
cs->mon_rx[cs->mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]);
- debugl1(cs, tmp);
- }
- if (cs->mon_rxp == 1) {
- cs->mocr |= 0x40;
- cs->writeisac(cs, ISAC_MOCR, cs->mocr);
- }
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC MOR1 %02x", cs->mon_rx[cs->mon_rxp -1]);
+ cs->mocr |= 0x40;
+ cs->writeisac(cs, ISAC_MOCR, cs->mocr);
}
afterMONR1:
if (v1 & 0x04) {
cs->mocr &= 0xf0;
+ cs->writeisac(cs, ISAC_MOCR, cs->mocr);
cs->mocr |= 0x0a;
cs->writeisac(cs, ISAC_MOCR, cs->mocr);
- isac_sched_event(cs, D_RX_MON0);
+ test_and_set_bit(HW_MON0_RX_END, &cs->HW_Flags);
}
if (v1 & 0x40) {
cs->mocr &= 0x0f;
+ cs->writeisac(cs, ISAC_MOCR, cs->mocr);
cs->mocr |= 0xa0;
cs->writeisac(cs, ISAC_MOCR, cs->mocr);
- isac_sched_event(cs, D_RX_MON1);
+ test_and_set_bit(HW_MON1_RX_END, &cs->HW_Flags);
}
if (v1 & 0x02) {
- if (!cs->mon_tx) {
+ if ((!cs->mon_tx) || (cs->mon_txc &&
+ (cs->mon_txp >= cs->mon_txc) &&
+ !(v1 & 0x08))) {
cs->mocr &= 0xf0;
+ cs->writeisac(cs, ISAC_MOCR, cs->mocr);
cs->mocr |= 0x0a;
cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+ if (cs->mon_txc &&
+ (cs->mon_txp >= cs->mon_txc))
+ test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
goto AfterMOX0;
}
- if (cs->mon_txp >= cs->mon_txc) {
- if (cs->mon_txc)
- isac_sched_event(cs, D_TX_MON0);
+ if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) {
+ test_and_set_bit(HW_MON0_TX_END, &cs->HW_Flags);
goto AfterMOX0;
}
cs->writeisac(cs, ISAC_MOX0,
cs->mon_tx[cs->mon_txp++]);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC %02x -> MOX0", cs->mon_tx[cs->mon_txp -1]);
}
AfterMOX0:
if (v1 & 0x20) {
- if (!cs->mon_tx) {
+ if ((!cs->mon_tx) || (cs->mon_txc &&
+ (cs->mon_txp >= cs->mon_txc) &&
+ !(v1 & 0x80))) {
cs->mocr &= 0x0f;
+ cs->writeisac(cs, ISAC_MOCR, cs->mocr);
cs->mocr |= 0xa0;
cs->writeisac(cs, ISAC_MOCR, cs->mocr);
+ if (cs->mon_txc &&
+ (cs->mon_txp >= cs->mon_txc))
+ test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
goto AfterMOX1;
}
- if (cs->mon_txp >= cs->mon_txc) {
- if (cs->mon_txc)
- isac_sched_event(cs, D_TX_MON1);
+ if (cs->mon_txc && (cs->mon_txp >= cs->mon_txc)) {
+ test_and_set_bit(HW_MON1_TX_END, &cs->HW_Flags);
goto AfterMOX1;
}
cs->writeisac(cs, ISAC_MOX1,
cs->mon_tx[cs->mon_txp++]);
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC %02x -> MOX1", cs->mon_tx[cs->mon_txp -1]);
}
AfterMOX1:
#endif
}
static void
-ISAC_l2l1(struct PStack *st, int pr, void *arg)
+ISAC_l1hw(struct PStack *st, int pr, void *arg)
{
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
struct sk_buff *skb = arg;
- char str[64];
+ int val;
switch (pr) {
- case (PH_DATA_REQ):
+ case (PH_DATA |REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
if (cs->tx_skb) {
skb_queue_tail(&cs->sq, skb);
#ifdef L2FRAME_DEBUG /* psa */
Logl2Frame(cs, skb, "PH_DATA Queued", 0);
#endif
} else {
- if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */
- LogFrame(cs, skb->data, skb->len);
- sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
- dlogframe(cs, skb->data + 4, skb->len - 4,
- str);
- }
cs->tx_skb = skb;
cs->tx_cnt = 0;
#ifdef L2FRAME_DEBUG /* psa */
isac_fill_fifo(cs);
}
break;
- case (PH_PULL_IND):
+ case (PH_PULL |INDICATION):
if (cs->tx_skb) {
if (cs->debug & L1_DEB_WARN)
debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
skb_queue_tail(&cs->sq, skb);
break;
}
- if ((cs->dlogflag) && (!(skb->data[2] & 1))) { /* I-FRAME */
+ if (cs->debug & DEB_DLOG_HEX)
LogFrame(cs, skb->data, skb->len);
- sprintf(str, "Q.931 frame user->network tei %d", st->l2.tei);
- dlogframe(cs, skb->data + 4, skb->len - 4,
- str);
- }
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
cs->tx_skb = skb;
cs->tx_cnt = 0;
#ifdef L2FRAME_DEBUG /* psa */
#endif
isac_fill_fifo(cs);
break;
- case (PH_PULL_REQ):
+ case (PH_PULL | REQUEST):
#ifdef L2FRAME_DEBUG /* psa */
if (cs->debug & L1_DEB_LAPD)
debugl1(cs, "-> PH_REQUEST_PULL");
#endif
if (!cs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL_CNF, NULL);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
- }
-}
-
-void
-isac_l1cmd(struct IsdnCardState *cs, int msg, void *arg)
-{
- u_char val;
- char tmp[32];
-
- switch(msg) {
- case PH_RESET_REQ:
+ case (HW_RESET | REQUEST):
if ((cs->ph_state == ISAC_IND_EI) ||
(cs->ph_state == ISAC_IND_DR) ||
(cs->ph_state == ISAC_IND_RS))
else
ph_command(cs, ISAC_CMD_RS);
break;
- case PH_ENABLE_REQ:
+ case (HW_ENABLE | REQUEST):
ph_command(cs, ISAC_CMD_TIM);
break;
- case PH_INFO3_REQ:
+ case (HW_INFO3 | REQUEST):
ph_command(cs, ISAC_CMD_AR8);
break;
- case PH_TESTLOOP_REQ:
+ case (HW_TESTLOOP | REQUEST):
val = 0;
- if (1 & (int) arg)
+ if (1 & (long) arg)
val |= 0x0c;
- if (2 & (int) arg)
+ if (2 & (long) arg)
val |= 0x3;
if (test_bit(HW_IOM1, &cs->HW_Flags)) {
/* IOM 1 Mode */
cs->writeisac(cs, ISAC_ADF1, 0x0);
}
break;
- default:
- if (cs->debug & L1_DEB_WARN) {
- sprintf(tmp, "isac_l1cmd unknown %4x", msg);
- debugl1(cs, tmp);
+ case (HW_DEACTIVATE | RESPONSE):
+ discard_queue(&cs->rq);
+ discard_queue(&cs->sq);
+ if (cs->tx_skb) {
+ dev_kfree_skb(cs->tx_skb);
+ cs->tx_skb = NULL;
}
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ isac_sched_event(cs, D_CLEARBUSY);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isac_l1hw unknown %04x", pr);
break;
}
}
void
setstack_isac(struct PStack *st, struct IsdnCardState *cs)
{
- st->l2.l2l1 = ISAC_l2l1;
+ st->l1.l1hw = ISAC_l1hw;
}
static void
dbusy_timer_handler(struct IsdnCardState *cs)
{
struct PStack *stptr;
+ int val;
if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
- if (cs->debug)
+ if (cs->debug) {
debugl1(cs, "D-Channel Busy");
+ val = cs->readisac(cs, ISAC_RBCH);
+ if (val & ISAC_RBCH_XAC)
+ debugl1(cs, "ISAC XAC");
+ else
+ debugl1(cs, "ISAC No XAC");
+ }
test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
stptr = cs->stlist;
while (stptr != NULL) {
- stptr->l1.l1l2(stptr, PH_PAUSE_IND, NULL);
+ stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
stptr = stptr->next;
}
}
initisac(struct IsdnCardState *cs))
{
cs->tqueue.routine = (void *) (void *) isac_bh;
- cs->l1cmd = isac_l1cmd;
cs->setstack_d = setstack_isac;
cs->dbusytimer.function = (void *) dbusy_timer_handler;
cs->dbusytimer.data = (long) cs;
HISAX_INITFUNC(void
clear_pending_isac_ints(struct IsdnCardState *cs))
{
- int val;
- char tmp[64];
+ int val, eval;
val = cs->readisac(cs, ISAC_STAR);
- sprintf(tmp, "ISAC STAR %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "ISAC STAR %x", val);
val = cs->readisac(cs, ISAC_MODE);
- sprintf(tmp, "ISAC MODE %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "ISAC MODE %x", val);
val = cs->readisac(cs, ISAC_ADF2);
- sprintf(tmp, "ISAC ADF2 %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "ISAC ADF2 %x", val);
val = cs->readisac(cs, ISAC_ISTA);
- sprintf(tmp, "ISAC ISTA %x", val);
- debugl1(cs, tmp);
+ debugl1(cs, "ISAC ISTA %x", val);
if (val & 0x01) {
- val = cs->readisac(cs, ISAC_EXIR);
- sprintf(tmp, "ISAC EXIR %x", val);
- debugl1(cs, tmp);
- } else if (val & 0x04) {
- val = cs->readisac(cs, ISAC_CIR0);
- sprintf(tmp, "ISAC CIR0 %x", val);
- debugl1(cs, tmp);
- cs->ph_state = (val >> 2) & 0xf;
- } else {
- cs->ph_state = (cs->readisac(cs, ISAC_CIX0) >> 2) & 0xf;
+ eval = cs->readisac(cs, ISAC_EXIR);
+ debugl1(cs, "ISAC EXIR %x", eval);
}
+ val = cs->readisac(cs, ISAC_CIR0);
+ debugl1(cs, "ISAC CIR0 %x", val);
+ cs->ph_state = (val >> 2) & 0xf;
isac_sched_event(cs, D_L1STATECHANGE);
+ /* Disable all IRQ */
cs->writeisac(cs, ISAC_MASK, 0xFF);
- cs->writeisac(cs, ISAC_MASK, 0);
- cs->writeisac(cs, ISAC_CMDR, 0x41);
}
-/* $Id: isac.h,v 1.4 1997/10/29 19:09:34 keil Exp $
+/* $Id: isac.h,v 1.5 1998/05/25 12:58:03 keil Exp $
* isac.h ISAC specific defines
*
*
*
* $Log: isac.h,v $
+ * Revision 1.5 1998/05/25 12:58:03 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
* Revision 1.4 1997/10/29 19:09:34 keil
* new L1
*
#define ISAC_STAR 0x21
#define ISAC_CMDR 0x21
#define ISAC_EXIR 0x24
-#define ISAC_RBCH 0x2a
#define ISAC_ADF2 0x39
#define ISAC_SPCR 0x30
#define ISAC_ADF1 0x38
#define ISAC_CIR0 0x31
#define ISAC_CIX0 0x31
+#define ISAC_CIR1 0x33
+#define ISAC_CIX1 0x33
#define ISAC_STCR 0x37
#define ISAC_MODE 0x22
#define ISAC_RSTA 0x27
#define ISAC_RBCL 0x25
+#define ISAC_RBCH 0x2A
#define ISAC_TIMR 0x23
#define ISAC_SQXR 0x3b
#define ISAC_MOSR 0x3a
#define ISAC_MOR1 0x34
#define ISAC_MOX1 0x34
+#define ISAC_RBCH_XAC 0x80
+
#define ISAC_CMD_TIM 0x0
#define ISAC_CMD_RS 0x1
#define ISAC_CMD_SCZ 0x4
--- /dev/null
+/* $Id: isar.c,v 1.2 1998/11/15 23:54:53 keil Exp $
+
+ * isar.c ISAR (Siemens PSB 7110) specific routines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.c,v $
+ * Revision 1.2 1998/11/15 23:54:53 keil
+ * changes from 2.0
+ *
+ * Revision 1.1 1998/08/13 23:33:47 keil
+ * First version, only init
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBG_LOADFIRM 0
+#define DUMP_MBOXFRAME 2
+
+#define MIN(a,b) ((a<b)?a:b)
+
+void isar_setup(struct IsdnCardState *cs);
+
+static inline int
+waitforHIA(struct IsdnCardState *cs, int timeout)
+{
+
+ while ((cs->BC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout)
+ printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n");
+ return(timeout);
+}
+
+
+int
+sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len,
+ u_char *msg)
+{
+ long flags;
+ int i;
+
+ if (!waitforHIA(cs, 4000))
+ return(0);
+#if DUMP_MBOXFRAME
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len);
+#endif
+ save_flags(flags);
+ cli();
+ cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg);
+ cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len);
+ cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0);
+ if (msg && len) {
+ cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]);
+ for (i=1; i<len; i++)
+ cs->BC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]);
+#if DUMP_MBOXFRAME>1
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char tmp[256], *t;
+
+ i = len;
+ while (i>0) {
+ t = tmp;
+ t += sprintf(t, "sendmbox cnt %d", len);
+ QuickHex(t, &msg[len-i], (i>64) ? 64:i);
+ debugl1(cs, tmp);
+ i -= 64;
+ }
+ }
+#endif
+ }
+ cs->BC_Write_Reg(cs, 1, ISAR_HIS, his);
+ restore_flags(flags);
+ waitforHIA(cs, 10000);
+ return(1);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg)
+{
+ int i;
+
+ cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0);
+ if (msg && ireg->clsb) {
+ msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX);
+ for (i=1; i < ireg->clsb; i++)
+ msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX);
+#if DUMP_MBOXFRAME>1
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char tmp[256], *t;
+
+ i = ireg->clsb;
+ while (i>0) {
+ t = tmp;
+ t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb);
+ QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i);
+ debugl1(cs, tmp);
+ i -= 64;
+ }
+ }
+#endif
+ }
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg)
+{
+ ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS);
+ ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H);
+ ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L);
+#if DUMP_MBOXFRAME
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb,
+ ireg->clsb);
+#endif
+}
+
+int
+waitrecmsg(struct IsdnCardState *cs, u_char *len,
+ u_char *msg, int maxdelay)
+{
+ int timeout = 0;
+ long flags;
+ struct isar_reg *ir = cs->bcs[0].hw.isar.reg;
+
+
+ while((!(cs->BC_Read_Reg(cs, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) &&
+ (timeout++ < maxdelay))
+ udelay(1);
+ if (timeout >= maxdelay) {
+ printk(KERN_WARNING"isar recmsg IRQSTA timeout\n");
+ return(0);
+ }
+ save_flags(flags);
+ cli();
+ get_irq_infos(cs, ir);
+ rcv_mbox(cs, ir, msg);
+ *len = ir->clsb;
+ restore_flags(flags);
+ return(1);
+}
+
+int
+ISARVersion(struct IsdnCardState *cs, char *s)
+{
+ int ver;
+ u_char msg[] = ISAR_MSG_HWVER;
+ u_char tmp[64];
+ u_char len;
+ int debug;
+
+ cs->cardmsg(cs, CARD_RESET, NULL);
+ /* disable ISAR IRQ */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+ debug = cs->debug;
+ cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+ if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg))
+ return(-1);
+ if (!waitrecmsg(cs, &len, tmp, 100000))
+ return(-2);
+ cs->debug = debug;
+ if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) {
+ if (len == 1) {
+ ver = tmp[0] & 0xf;
+ printk(KERN_INFO "%s ISAR version %d\n", s, ver);
+ return(ver);
+ }
+ return(-3);
+ }
+ return(-4);
+}
+
+int
+isar_load_firmware(struct IsdnCardState *cs, u_char *buf)
+{
+ int ret, size, cnt, debug;
+ u_char len, nom, noc;
+ u_short sadr, left, *sp;
+ u_char *p = buf;
+ u_char *msg, *tmpmsg, *mp, tmp[64];
+ long flags;
+ struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+
+ struct {u_short sadr;
+ u_short len;
+ u_short d_key;
+ } blk_head;
+
+#define BLK_HEAD_SIZE 6
+ if (1 != (ret = ISARVersion(cs, "Testing"))) {
+ printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret);
+ return(1);
+ }
+ debug = cs->debug;
+#if DBG_LOADFIRM<2
+ cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+#endif
+ printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf);
+ if ((ret = verify_area(VERIFY_READ, (void *) p, sizeof(int)))) {
+ printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+ return ret;
+ }
+ if ((ret = copy_from_user(&size, p, sizeof(int)))) {
+ printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+ return ret;
+ }
+ p += sizeof(int);
+ printk(KERN_DEBUG"isar_load_firmware size: %d\n", size);
+ if ((ret = verify_area(VERIFY_READ, (void *) p, size))) {
+ printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+ return ret;
+ }
+ cnt = 0;
+ /* disable ISAR IRQ */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+ if (!(msg = kmalloc(256, GFP_KERNEL))) {
+ printk(KERN_ERR"isar_load_firmware no buffer\n");
+ return (1);
+ }
+ if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) {
+ printk(KERN_ERR"isar_load_firmware no tmp buffer\n");
+ kfree(msg);
+ return (1);
+ }
+ while (cnt < size) {
+ if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) {
+ printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+ goto reterror;
+ }
+ cnt += BLK_HEAD_SIZE;
+ p += BLK_HEAD_SIZE;
+ printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n",
+ blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+ sadr = blk_head.sadr;
+ left = blk_head.len;
+ if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) {
+ printk(KERN_ERR"isar sendmsg dkey failed\n");
+ ret = 1;goto reterror;
+ }
+ if (!waitrecmsg(cs, &len, tmp, 100000)) {
+ printk(KERN_ERR"isar waitrecmsg dkey failed\n");
+ ret = 1;goto reterror;
+ }
+ if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) {
+ printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n",
+ ireg->iis, ireg->cmsb, len);
+ ret = 1;goto reterror;
+ }
+ while (left>0) {
+ noc = MIN(126, left);
+ nom = 2*noc;
+ mp = msg;
+ *mp++ = sadr / 256;
+ *mp++ = sadr % 256;
+ left -= noc;
+ *mp++ = noc;
+ if ((ret = copy_from_user(tmpmsg, p, nom))) {
+ printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+ goto reterror;
+ }
+ p += nom;
+ cnt += nom;
+ nom += 3;
+ sp = (u_short *)tmpmsg;
+#if DBG_LOADFIRM
+ printk(KERN_DEBUG"isar: load %3d words at %04x\n",
+ noc, sadr);
+#endif
+ sadr += noc;
+ while(noc) {
+ *mp++ = *sp / 256;
+ *mp++ = *sp % 256;
+ sp++;
+ noc--;
+ }
+ if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) {
+ printk(KERN_ERR"isar sendmsg prog failed\n");
+ ret = 1;goto reterror;
+ }
+ if (!waitrecmsg(cs, &len, tmp, 100000)) {
+ printk(KERN_ERR"isar waitrecmsg prog failed\n");
+ ret = 1;goto reterror;
+ }
+ if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) {
+ printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n",
+ ireg->iis, ireg->cmsb, len);
+ ret = 1;goto reterror;
+ }
+ }
+ printk(KERN_DEBUG"isar firmware block %5d words loaded\n",
+ blk_head.len);
+ }
+ msg[0] = 0xff;
+ msg[1] = 0xfe;
+ ireg->bstat = 0;
+ if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) {
+ printk(KERN_ERR"isar sendmsg start dsp failed\n");
+ ret = 1;goto reterror;
+ }
+ if (!waitrecmsg(cs, &len, tmp, 100000)) {
+ printk(KERN_ERR"isar waitrecmsg start dsp failed\n");
+ ret = 1;goto reterror;
+ }
+ if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) {
+ printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n",
+ ireg->iis, ireg->cmsb, len);
+ ret = 1;goto reterror;
+ } else
+ printk(KERN_DEBUG"isar start dsp success\n");
+ /* NORMAL mode entered */
+ /* Enable IRQs of ISAR */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA);
+ save_flags(flags);
+ sti();
+ cnt = 1000; /* max 1s */
+ while ((!ireg->bstat) && cnt) {
+ udelay(1000);
+ cnt--;
+ }
+ if (!cnt) {
+ printk(KERN_ERR"isar no general status event received\n");
+ ret = 1;goto reterrflg;
+ } else {
+ printk(KERN_DEBUG"isar general status event %x\n",
+ ireg->bstat);
+ }
+ ireg->iis = 0;
+ if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+ printk(KERN_ERR"isar sendmsg self tst failed\n");
+ ret = 1;goto reterrflg;
+ }
+ cnt = 1000; /* max 10 ms */
+ while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+ udelay(10);
+ cnt--;
+ }
+ if (!cnt) {
+ printk(KERN_ERR"isar no self tst response\n");
+ ret = 1;goto reterrflg;
+ } else if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1)
+ && (ireg->par[0] == 0)) {
+ printk(KERN_DEBUG"isar selftest OK\n");
+ } else {
+ printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n",
+ ireg->cmsb, ireg->clsb, ireg->par[0]);
+ ret = 1;goto reterror;
+ }
+ ireg->iis = 0;
+ if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+ printk(KERN_ERR"isar RQST SVN failed\n");
+ ret = 1;goto reterror;
+ }
+ cnt = 10000; /* max 100 ms */
+ while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+ udelay(10);
+ cnt--;
+ }
+ if (!cnt) {
+ printk(KERN_ERR"isar no SVN response\n");
+ ret = 1;goto reterrflg;
+ } else {
+ if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1))
+ printk(KERN_DEBUG"isar software version %#x\n",
+ ireg->par[0]);
+ else {
+ printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n",
+ ireg->cmsb, ireg->clsb, cnt);
+ ret = 1;goto reterrflg;
+ }
+ }
+ cs->debug = debug;
+ isar_setup(cs);
+ ret = 0;
+reterrflg:
+ restore_flags(flags);
+reterror:
+ cs->debug = debug;
+ if (ret)
+ /* disable ISAR IRQ */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+ kfree(msg);
+ kfree(tmpmsg);
+ return(ret);
+}
+
+void
+isar_sched_event(struct BCState *bcs, int event)
+{
+ bcs->event |= 1 << event;
+ queue_task(&bcs->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static inline void
+isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ u_char *ptr;
+ struct sk_buff *skb;
+ struct isar_reg *ireg = bcs->hw.isar.reg;
+
+ if (!ireg->clsb) {
+ debugl1(cs, "isar zero len frame");
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ return;
+ }
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ break;
+ case L1_MODE_TRANS:
+ if ((skb = dev_alloc_skb(ireg->clsb))) {
+ rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb));
+ skb_queue_tail(&bcs->rqueue, skb);
+ isar_sched_event(bcs, B_RCVBUFREADY);
+ } else {
+ printk(KERN_WARNING "HiSax: skb out of memory\n");
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ }
+ break;
+ case L1_MODE_HDLC:
+ if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isar_rcv_frame: incoming packet too large");
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ bcs->hw.isar.rcvidx = 0;
+ } else if (ireg->cmsb & HDLC_ERROR) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isar frame error %x len %d",
+ ireg->cmsb, ireg->clsb);
+ bcs->hw.isar.rcvidx = 0;
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ } else {
+ if (ireg->cmsb & HDLC_FSD)
+ bcs->hw.isar.rcvidx = 0;
+ ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+ bcs->hw.isar.rcvidx += ireg->clsb;
+ rcv_mbox(cs, ireg, ptr);
+ if (ireg->cmsb & HDLC_FED) {
+ if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+ printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
+ bcs->hw.isar.rcvidx);
+ } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2)))
+ printk(KERN_WARNING "ISAR: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2),
+ bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2);
+ skb_queue_tail(&bcs->rqueue, skb);
+ isar_sched_event(bcs, B_RCVBUFREADY);
+ }
+ }
+ }
+ break;
+ default:
+ printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ break;
+ }
+}
+
+void
+isar_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int count;
+ u_char msb;
+ u_char *ptr;
+ long flags;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "isar_fill_fifo");
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+ if (!(bcs->hw.isar.reg->bstat &
+ (bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+ return;
+ if (bcs->tx_skb->len > bcs->hw.isar.mml) {
+ msb = 0;
+ count = bcs->hw.isar.mml;
+ } else {
+ count = bcs->tx_skb->len;
+ msb = HDLC_FED;
+ }
+ if (!bcs->hw.isar.txcnt)
+ msb |= HDLC_FST;
+ save_flags(flags);
+ cli();
+ ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.isar.txcnt += count;
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+ break;
+ case L1_MODE_TRANS:
+ if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+ 0, count, ptr)) {
+ if (cs->debug)
+ debugl1(cs, "isar bin data send dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ case L1_MODE_HDLC:
+ if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+ msb, count, ptr)) {
+ if (cs->debug)
+ debugl1(cs, "isar hdlc data send dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ default:
+ printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode);
+ break;
+ }
+ restore_flags(flags);
+}
+
+inline
+struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath)
+{
+ if ((!dpath) || (dpath == 3))
+ return(NULL);
+ if (cs->bcs[0].hw.isar.dpath == dpath)
+ return(&cs->bcs[0]);
+ if (cs->bcs[1].hw.isar.dpath == dpath)
+ return(&cs->bcs[1]);
+ return(NULL);
+}
+
+inline void
+send_frames(struct BCState *bcs)
+{
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ isar_fill_fifo(bcs);
+ return;
+ } else {
+ if (bcs->st->lli.l1writewakeup &&
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->hw.isar.txcnt = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.isar.txcnt = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ isar_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ isar_sched_event(bcs, B_XMTBUFREADY);
+ }
+}
+
+inline void
+check_send(struct IsdnCardState *cs, u_char rdm)
+{
+ struct BCState *bcs;
+
+ if (rdm & BSTAT_RDM1) {
+ if ((bcs = sel_bcs_isar(cs, 1))) {
+ if (bcs->mode) {
+ send_frames(bcs);
+ }
+ }
+ }
+ if (rdm & BSTAT_RDM2) {
+ if ((bcs = sel_bcs_isar(cs, 2))) {
+ if (bcs->mode) {
+ send_frames(bcs);
+ }
+ }
+ }
+
+}
+
+static char debbuf[64];
+
+void
+isar_int_main(struct IsdnCardState *cs)
+{
+ long flags;
+ struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+ struct BCState *bcs;
+
+ save_flags(flags);
+ cli();
+ get_irq_infos(cs, ireg);
+ switch (ireg->iis & ISAR_IIS_MSCMSD) {
+ case ISAR_IIS_RDATA:
+ if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+ isar_rcv_frame(cs, bcs);
+ } else {
+ debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ }
+ break;
+ case ISAR_IIS_GSTEV:
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ ireg->bstat |= ireg->cmsb;
+ check_send(cs, ireg->cmsb);
+ break;
+ case ISAR_IIS_BSTEV:
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "Buffer STEV dpath%d msb(%x)",
+ ireg->iis>>6, ireg->cmsb);
+ break;
+ case ISAR_IIS_DIAG:
+ case ISAR_IIS_PSTRSP:
+ case ISAR_IIS_PSTEV:
+ case ISAR_IIS_BSTRSP:
+ case ISAR_IIS_IOM2RSP:
+ rcv_mbox(cs, ireg, (u_char *)ireg->par);
+ if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO))
+ == L1_DEB_HSCX) {
+ u_char *tp=debbuf;
+
+ tp += sprintf(debbuf, "msg iis(%x) msb(%x)",
+ ireg->iis, ireg->cmsb);
+ QuickHex(tp, (u_char *)ireg->par, ireg->clsb);
+ debugl1(cs, debbuf);
+ }
+ break;
+ default:
+ rcv_mbox(cs, ireg, debbuf);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ break;
+ }
+ restore_flags(flags);
+}
+
+void
+setup_pump(struct BCState *bcs) {
+ struct IsdnCardState *cs = bcs->cs;
+ u_char dps = SET_DPS(bcs->hw.isar.dpath);
+
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar pump bypass cfg dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar pump status req dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+}
+
+void
+setup_sart(struct BCState *bcs) {
+ struct IsdnCardState *cs = bcs->cs;
+ u_char dps = SET_DPS(bcs->hw.isar.dpath);
+
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar sart disable dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ case L1_MODE_TRANS:
+ if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) {
+ if (cs->debug)
+ debugl1(cs, "isar sart binary dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ case L1_MODE_HDLC:
+ if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) {
+ if (cs->debug)
+ debugl1(cs, "isar sart binary dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar buf stat req dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+}
+
+void
+setup_iom2(struct BCState *bcs) {
+ struct IsdnCardState *cs = bcs->cs;
+ u_char dps = SET_DPS(bcs->hw.isar.dpath);
+ u_char cmsb = 0, msg[5] = {0x10,0,0,0,0};
+
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ /* dummy slot */
+ msg[1] = msg[3] = bcs->hw.isar.dpath + 2;
+ break;
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ cmsb = 0x80;
+ if (bcs->channel)
+ msg[1] = msg[3] = 1;
+ break;
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) {
+ if (cs->debug)
+ debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath);
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar IOM2 cfg req dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+}
+
+int
+modeisar(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ /* Here we are selecting the best datapath for requested mode */
+ if(bcs->mode == L1_MODE_NULL) { /* New Setup */
+ bcs->channel = bc;
+ switch (mode) {
+ case L1_MODE_NULL: /* init */
+ break;
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ /* best is datapath 2 */
+ if (!test_and_set_bit(ISAR_DP2_USE,
+ &bcs->hw.isar.reg->Flags))
+ bcs->hw.isar.dpath = 2;
+ else if (!test_and_set_bit(ISAR_DP1_USE,
+ &bcs->hw.isar.reg->Flags))
+ bcs->hw.isar.dpath = 1;
+ else {
+ printk(KERN_ERR"isar modeisar both pathes in use\n");
+ return(1);
+ }
+ break;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "isar dp%d mode %d->%d ichan %d",
+ bcs->hw.isar.dpath, bcs->mode, mode, bc);
+ bcs->mode = mode;
+ setup_pump(bcs);
+ setup_sart(bcs);
+ setup_iom2(bcs);
+ if (bcs->mode == L1_MODE_NULL) {
+ /* Clear resources */
+ if (bcs->hw.isar.dpath == 1)
+ test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags);
+ else if (bcs->hw.isar.dpath == 2)
+ test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags);
+ bcs->hw.isar.dpath = 0;
+ }
+ return(0);
+}
+
+void
+isar_setup(struct IsdnCardState *cs)
+{
+ u_char msg;
+ int i;
+
+ /* Dpath 1, 2 */
+ msg = 61;
+ for (i=0; i<2; i++) {
+ /* Buffer Config */
+ if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+ ISAR_HIS_P12CFG, 4, 1, &msg)) {
+ if (cs->debug)
+ debugl1(cs, "isar P%dCFG failed", i+1);
+ }
+ cs->bcs[i].hw.isar.mml = msg;
+ cs->bcs[i].mode = 0;
+ cs->bcs[i].hw.isar.dpath = i + 1;
+ modeisar(&cs->bcs[i], 0, 0);
+ }
+}
+
+void
+isar_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ long flags;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ save_flags(flags);
+ cli();
+ if (st->l1.bcs->tx_skb) {
+ skb_queue_tail(&st->l1.bcs->squeue, skb);
+ restore_flags(flags);
+ } else {
+ st->l1.bcs->tx_skb = skb;
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(st->l1.bcs->cs, "DRQ set BC_FLG_BUSY");
+ st->l1.bcs->hw.isar.txcnt = 0;
+ restore_flags(flags);
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ }
+ break;
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
+ printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n");
+ break;
+ }
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(st->l1.bcs->cs, "PUI set BC_FLG_BUSY");
+ st->l1.bcs->tx_skb = skb;
+ st->l1.bcs->hw.isar.txcnt = 0;
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ break;
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
+ test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ } else
+ test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ modeisar(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY");
+ modeisar(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+void
+close_isarstate(struct BCState *bcs)
+{
+ modeisar(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (bcs->hw.isar.rcvbuf) {
+ kfree(bcs->hw.isar.rcvbuf);
+ bcs->hw.isar.rcvbuf = NULL;
+ }
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
+ }
+ }
+}
+
+int
+open_isarstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for isar.rcvbuf\n");
+ return (1);
+ }
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "openisar clear BC_FLG_BUSY");
+ bcs->event = 0;
+ bcs->hw.isar.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+int
+setstack_isar(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_isarstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = isar_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+HISAX_INITFUNC(void
+initisar(struct IsdnCardState *cs))
+{
+ cs->bcs[0].BC_SetStack = setstack_isar;
+ cs->bcs[1].BC_SetStack = setstack_isar;
+ cs->bcs[0].BC_Close = close_isarstate;
+ cs->bcs[1].BC_Close = close_isarstate;
+}
--- /dev/null
+/* $Id: isar.h,v 1.2 1998/11/15 23:54:54 keil Exp $
+ * isar.h ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.h,v $
+ * Revision 1.2 1998/11/15 23:54:54 keil
+ * changes from 2.0
+ *
+ * Revision 1.1 1998/08/13 23:33:48 keil
+ * First version, only init
+ *
+ *
+ */
+
+#define ISAR_IRQMSK 0x04
+#define ISAR_IRQSTA 0x04
+#define ISAR_IRQBIT 0x75
+#define ISAR_CTRL_H 0x61
+#define ISAR_CTRL_L 0x60
+#define ISAR_IIS 0x58
+#define ISAR_IIA 0x58
+#define ISAR_HIS 0x50
+#define ISAR_HIA 0x50
+#define ISAR_MBOX 0x4c
+#define ISAR_WADR 0x4a
+#define ISAR_RADR 0x48
+
+#define ISAR_HIS_VNR 0x14
+#define ISAR_HIS_DKEY 0x02
+#define ISAR_HIS_FIRM 0x1e
+#define ISAR_HIS_STDSP 0x08
+#define ISAR_HIS_DIAG 0x05
+#define ISAR_HIS_P0CFG 0x3c
+#define ISAR_HIS_P12CFG 0x24
+#define ISAR_HIS_SARTCFG 0x25
+#define ISAR_HIS_PUMPCFG 0x26
+#define ISAR_HIS_IOM2CFG 0x27
+#define ISAR_HIS_IOM2REQ 0x07
+#define ISAR_HIS_BSTREQ 0x0c
+#define ISAR_HIS_PSTREQ 0x0e
+#define ISAR_HIS_SDATA 0x20
+#define ISAR_HIS_DPS1 0x40
+#define ISAR_HIS_DPS2 0x80
+#define SET_DPS(x) ((x<<6) & 0xc0)
+
+#define ISAR_IIS_MSCMSD 0x3f
+#define ISAR_IIS_VNR 0x15
+#define ISAR_IIS_DKEY 0x03
+#define ISAR_IIS_FIRM 0x1f
+#define ISAR_IIS_STDSP 0x09
+#define ISAR_IIS_DIAG 0x25
+#define ISAR_IIS_GSTEV 0x0
+#define ISAR_IIS_BSTEV 0x28
+#define ISAR_IIS_BSTRSP 0x2c
+#define ISAR_IIS_PSTRSP 0x2e
+#define ISAR_IIS_PSTEV 0x2a
+#define ISAR_IIS_IOM2RSP 0x27
+
+#define ISAR_IIS_RDATA 0x20
+#define ISAR_CTRL_SWVER 0x10
+#define ISAR_CTRL_STST 0x40
+
+#define ISAR_MSG_HWVER {0x20, 0, 1}
+
+#define ISAR_DP1_USE 1
+#define ISAR_DP2_USE 2
+
+#define PMOD_BYPASS 7
+
+#define SMODE_DISABLE 0
+#define SMODE_HDLC 3
+#define SMODE_BINARY 4
+
+#define HDLC_FED 0x40
+#define HDLC_FSD 0x20
+#define HDLC_FST 0x20
+#define HDLC_ERROR 0x1c
+
+#define BSTAT_RDM0 0x1
+#define BSTAT_RDM1 0x2
+#define BSTAT_RDM2 0x4
+#define BSTAT_RDM3 0x8
+
+
+extern int ISARVersion(struct IsdnCardState *cs, char *s);
+extern int isar_load_firmware(struct IsdnCardState *cs, u_char *buf);
+extern void isar_int_main(struct IsdnCardState *cs);
+extern void initisar(struct IsdnCardState *cs);
+extern void isar_fill_fifo(struct BCState *bcs);
-/* $Id: isdnl1.c,v 2.18 1998/02/12 23:07:42 keil Exp $
+/* $Id: isdnl1.c,v 2.31 1998/11/15 23:54:56 keil Exp $
* isdnl1.c common low level stuff for Siemens Chipsetbased isdn cards
* based on the teles driver from Jan den Ouden
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
*
* Thanks to Jan den Ouden
* Fritz Elfert
*
*
* $Log: isdnl1.c,v $
+ * Revision 2.31 1998/11/15 23:54:56 keil
+ * changes from 2.0
+ *
+ * Revision 2.30 1998/09/30 22:27:00 keil
+ * Add init of l1.Flags
+ *
+ * Revision 2.29 1998/09/27 23:54:43 keil
+ * cosmetics
+ *
+ * Revision 2.28 1998/09/27 12:52:23 keil
+ * Fix against segfault, if the driver cannot allocate an IRQ channel
+ *
+ * Revision 2.27 1998/08/13 23:36:39 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.26 1998/07/15 15:01:31 calle
+ * Support for AVM passive PCMCIA cards:
+ * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 2.25 1998/05/25 14:10:09 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.24 1998/05/25 12:58:04 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.22 1998/04/15 16:40:13 keil
+ * Add S0Box and Teles PCI support
+ * Fix cardnr overwrite bug
+ *
+ * Revision 2.21 1998/04/10 10:35:28 paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 2.20 1998/03/09 23:19:27 keil
+ * Changes for PCMCIA
+ *
* Revision 2.18 1998/02/12 23:07:42 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
*
*/
-const char *l1_revision = "$Revision: 2.18 $";
+const char *l1_revision = "$Revision: 2.31 $";
#define __NO_VERSION__
#include <linux/config.h>
#include "hisax.h"
#include "isdnl1.h"
-#include <linux/kernel_stat.h>
-#if (LINUX_VERSION_CODE < 0x020150) /* 2.1.80 */
-#define kstat_irqs( PAR ) kstat.interrupts( (PAR) )
-#endif
-
-
-
-#if CARD_TELES0
-extern int setup_teles0(struct IsdnCard *card);
-#endif
-
-#if CARD_TELES3
-extern int setup_teles3(struct IsdnCard *card);
-#endif
-
-#if CARD_AVM_A1
-extern int setup_avm_a1(struct IsdnCard *card);
-#endif
-
-#if CARD_ELSA
-extern int setup_elsa(struct IsdnCard *card);
-#endif
-
-#if CARD_IX1MICROR2
-extern int setup_ix1micro(struct IsdnCard *card);
-#endif
-
-#if CARD_DIEHLDIVA
-extern int setup_diva(struct IsdnCard *card);
-#endif
-
-#if CARD_ASUSCOM
-extern int setup_asuscom(struct IsdnCard *card);
-#endif
-
-#if CARD_TELEINT
-extern int setup_TeleInt(struct IsdnCard *card);
-#endif
-
-#if CARD_SEDLBAUER
-extern int setup_sedlbauer(struct IsdnCard *card);
-#endif
-
-#if CARD_SPORTSTER
-extern int setup_sportster(struct IsdnCard *card);
-#endif
-
-#if CARD_MIC
-extern int setup_mic(struct IsdnCard *card);
-#endif
-
-#if CARD_NETJET
-extern int setup_netjet(struct IsdnCard *card);
-#endif
-
-#if CARD_TELES3C
-extern int setup_t163c(struct IsdnCard *card);
-#endif
-#if CARD_AMD7930 || CARD_DBRI
-extern int setup_foreign(struct IsdnCard *card);
-#endif
-
-#if CARD_NICCY
-extern int setup_niccy(struct IsdnCard *card);
-#endif
-
-#define HISAX_STATUS_BUFSIZE 4096
-#define ISDN_CTRL_DEBUG 1
-#define INCLUDE_INLINE_FUNCS
-#include <linux/tqueue.h>
-#include <linux/interrupt.h>
-const char *CardType[] =
-{"No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3", "Creatix/Teles PnP",
- "AVM A1", "Elsa ML", "Elsa Quickstep", "Teles PCMCIA", "ITK ix1-micro Rev.2",
- "Elsa PCMCIA", "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c",
- "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux", "Elsa PCI",
- "Compaq ISA", "NETjet", "Teles PCI", "Sedlbauer Speed Star (PCMCIA)",
- "AMD 7930", "NICCY", "DBRI"
-};
+#define TIMER3_VALUE 7000
-extern struct IsdnCard cards[];
-extern int nrcards;
-extern char *HiSax_id;
-extern struct IsdnBuffers *tracebuf;
-
-#define TIMER3_VALUE 7
+static
+struct Fsm l1fsm_b =
+{NULL, 0, 0, NULL, NULL};
static
-struct Fsm l1fsm =
+struct Fsm l1fsm_d =
{NULL, 0, 0, NULL, NULL};
enum {
ST_L1_F8,
};
-#define L1_STATE_COUNT (ST_L1_F8+1)
+#define L1D_STATE_COUNT (ST_L1_F8+1)
-static char *strL1State[] =
+static char *strL1DState[] =
{
"ST_L1_F2",
"ST_L1_F3",
"ST_L1_F8",
};
+enum {
+ ST_L1_NULL,
+ ST_L1_WAIT_ACT,
+ ST_L1_WAIT_DEACT,
+ ST_L1_ACTIV,
+};
+
+#define L1B_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strL1BState[] =
+{
+ "ST_L1_NULL",
+ "ST_L1_WAIT_ACT",
+ "ST_L1_WAIT_DEACT",
+ "ST_L1_ACTIV",
+};
+
enum {
EV_PH_ACTIVATE,
+ EV_PH_DEACTIVATE,
EV_RESET_IND,
EV_DEACT_CNF,
EV_DEACT_IND,
static char *strL1Event[] =
{
"EV_PH_ACTIVATE",
+ "EV_PH_DEACTIVATE",
"EV_RESET_IND",
"EV_DEACT_CNF",
"EV_DEACT_IND",
"EV_TIMER3",
};
-/*
- * Find card with given driverId
- */
-static inline struct IsdnCardState
-*hisax_findcard(int driverid)
-{
- int i;
-
- for (i = 0; i < nrcards; i++)
- if (cards[i].cs)
- if (cards[i].cs->myid == driverid)
- return (cards[i].cs);
- return (NULL);
-}
-
-int
-HiSax_readstatus(u_char * buf, int len, int user, int id, int channel)
-{
- int count;
- u_char *p;
- struct IsdnCardState *csta = hisax_findcard(id);
-
- if (csta) {
- for (p = buf, count = 0; count < len; p++, count++) {
- if (user)
- put_user(*csta->status_read++, p);
- else
- *p++ = *csta->status_read++;
- if (csta->status_read > csta->status_end)
- csta->status_read = csta->status_buf;
- }
- return count;
- } else {
- printk(KERN_ERR
- "HiSax: if_readstatus called with invalid driverId!\n");
- return -ENODEV;
- }
-}
-
-#if ISDN_CTRL_DEBUG
-void
-HiSax_putstatus(struct IsdnCardState *csta, char *buf)
-{
- long flags;
- int len, count, i;
- u_char *p;
- isdn_ctrl ic;
-
- save_flags(flags);
- cli();
- count = 0;
- len = strlen(buf);
-
- if (!csta) {
- printk(KERN_WARNING "HiSax: No CardStatus for message %s", buf);
- restore_flags(flags);
- return;
- }
- for (p = buf, i = len; i > 0; i--, p++) {
- *csta->status_write++ = *p;
- if (csta->status_write > csta->status_end)
- csta->status_write = csta->status_buf;
- count++;
- }
- restore_flags(flags);
- if (count) {
- ic.command = ISDN_STAT_STAVAIL;
- ic.driver = csta->myid;
- ic.arg = count;
- csta->iif.statcallb(&ic);
- }
-}
-#else
-#define KDEBUG_DEF
-#include "../kdebug.h"
-
-static int DbgLineNr=0,DbgSequenzNr=1;
-
void
-HiSax_putstatus(struct IsdnCardState *csta, char *buf)
+debugl1(struct IsdnCardState *cs, char *fmt, ...)
{
- char tmp[512];
+ va_list args;
+ char tmp[8];
- if (DbgLineNr==23)
- DbgLineNr=0;
- sprintf(tmp, "%5d %s",DbgSequenzNr++,buf);
- gput_str(tmp,0,DbgLineNr++);
-}
-#endif
-
-int
-ll_run(struct IsdnCardState *csta)
-{
- long flags;
- isdn_ctrl ic;
-
- save_flags(flags);
- cli();
- ic.driver = csta->myid;
- ic.command = ISDN_STAT_RUN;
- csta->iif.statcallb(&ic);
- restore_flags(flags);
- return 0;
-}
-
-void
-ll_stop(struct IsdnCardState *csta)
-{
- isdn_ctrl ic;
-
- ic.command = ISDN_STAT_STOP;
- ic.driver = csta->myid;
- csta->iif.statcallb(&ic);
- CallcFreeChan(csta);
+ va_start(args, fmt);
+ sprintf(tmp, "Card%d ", cs->cardnr + 1);
+ VHiSax_putstatus(cs, tmp, fmt, args);
+ va_end(args);
}
static void
-ll_unload(struct IsdnCardState *csta)
-{
- isdn_ctrl ic;
-
- ic.command = ISDN_STAT_UNLOAD;
- ic.driver = csta->myid;
- csta->iif.statcallb(&ic);
- if (csta->status_buf)
- kfree(csta->status_buf);
- csta->status_read = NULL;
- csta->status_write = NULL;
- csta->status_end = NULL;
- kfree(csta->dlogspace);
-}
-
-void
-debugl1(struct IsdnCardState *cs, char *msg)
-{
- char tmp[1024], tm[32];
-
- jiftime(tm, jiffies);
- sprintf(tmp, "%s Card %d %s\n", tm, cs->cardnr + 1, msg);
- HiSax_putstatus(cs, tmp);
-}
-
-static void
-l1m_debug(struct FsmInst *fi, char *s)
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
{
+ va_list args;
struct PStack *st = fi->userdata;
+ struct IsdnCardState *cs = st->l1.hardware;
+ char tmp[8];
- debugl1(st->l1.hardware, s);
+ va_start(args, fmt);
+ sprintf(tmp, "Card%d ", cs->cardnr + 1);
+ VHiSax_putstatus(cs, tmp, fmt, args);
+ va_end(args);
}
void
st = cs->stlist;
while (st) {
if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
- st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
else
- st->l1.l1man(st, PH_ACTIVATE_IND, NULL);
+ st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
st = st->next;
}
}
st = cs->stlist;
while (st) {
if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
- st->l1.l1l2(st, PH_PAUSE_CNF, NULL);
- st->l1.l1man(st, PH_DEACTIVATE_IND, NULL);
+ st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+ st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
st = st->next;
}
test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
stptr = cs->stlist;
while (stptr != NULL)
if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
- stptr->l1.l1l2(stptr, PH_PULL_CNF, NULL);
+ stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
break;
} else
stptr = stptr->next;
struct sk_buff *skb, *nskb;
struct PStack *stptr = cs->stlist;
int found, tei, sapi;
- char tmp[64];
if (stptr)
if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
sapi = skb->data[0] >> 2;
tei = skb->data[1] >> 1;
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 1);
if (tei == GROUP_TEI) {
- if (sapi == CTRL_SAPI) { /* sapi 0 */
- if (cs->dlogflag) {
- LogFrame(cs, skb->data, skb->len);
- dlogframe(cs, skb->data + 3, skb->len - 3,
- "Q.931 frame network->user broadcast");
- }
+ if (sapi == CTRL_SAPI) { /* sapi 0 */
while (stptr != NULL) {
if ((nskb = skb_clone(skb, GFP_ATOMIC)))
- stptr->l1.l1l2(stptr, PH_DATA_IND, nskb);
+ stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
else
printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
stptr = stptr->next;
} else if (sapi == TEI_SAPI) {
while (stptr != NULL) {
if ((nskb = skb_clone(skb, GFP_ATOMIC)))
- stptr->l1.l1tei(stptr, PH_DATA_IND, nskb);
+ stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb);
else
printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
stptr = stptr->next;
}
}
dev_kfree_skb(skb);
- } else if (sapi == CTRL_SAPI) {
+ } else if (sapi == CTRL_SAPI) { /* sapi 0 */
found = 0;
while (stptr != NULL)
if (tei == stptr->l2.tei) {
- stptr->l1.l1l2(stptr, PH_DATA_IND, skb);
+ stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
found = !0;
break;
} else
stptr = stptr->next;
- if (!found) {
- /* BD 10.10.95
- * Print out D-Channel msg not processed
- * by isdn4linux
- */
-
- if ((!(skb->data[0] >> 2)) && (!(skb->data[2] & 0x01))) {
- sprintf(tmp,
- "Q.931 frame network->user with tei %d (not for us)",
- skb->data[1] >> 1);
- LogFrame(cs, skb->data, skb->len);
- dlogframe(cs, skb->data + 4, skb->len - 4, tmp);
- }
+ if (!found)
dev_kfree_skb(skb);
- }
}
}
}
{
struct PStack *st = bcs->st;
- if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+ if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+ debugl1(bcs->cs, "BC_BUSY Error");
return;
+ }
if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
- st->l1.l1l2(st, PH_PULL_CNF, NULL);
- if (!test_bit(BC_FLG_ACTIV, &bcs->Flag))
- if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue)))
- st->ma.manl1(st, PH_DEACTIVATE_CNF, 0);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
+ if (!test_bit(BC_FLG_BUSY, &bcs->Flag) && (!skb_queue_len(&bcs->squeue))) {
+ st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+ }
+ }
}
static void
{
struct sk_buff *skb;
+ if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) {
+ FsmDelTimer(&bcs->st->l1.timer, 4);
+ FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
+ }
while ((skb = skb_dequeue(&bcs->rqueue))) {
- bcs->st->l1.l1l2(bcs->st, PH_DATA_IND, skb);
+ bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
}
}
bcs->Flag = 0;
}
-static void
-closecard(int cardnr)
-{
- struct IsdnCardState *csta = cards[cardnr].cs;
- struct sk_buff *skb;
-
- if (csta->bcs->BC_Close != NULL) {
- csta->bcs->BC_Close(csta->bcs + 1);
- csta->bcs->BC_Close(csta->bcs);
- }
-
- if (csta->rcvbuf) {
- kfree(csta->rcvbuf);
- csta->rcvbuf = NULL;
- }
- while ((skb = skb_dequeue(&csta->rq))) {
- dev_kfree_skb(skb);
- }
- while ((skb = skb_dequeue(&csta->sq))) {
- dev_kfree_skb(skb);
- }
- if (csta->tx_skb) {
- dev_kfree_skb(csta->tx_skb);
- csta->tx_skb = NULL;
- }
- if (csta->mon_rx) {
- kfree(csta->mon_rx);
- csta->mon_rx = NULL;
- }
- if (csta->mon_tx) {
- kfree(csta->mon_tx);
- csta->mon_tx = NULL;
- }
- csta->cardmsg(csta, CARD_RELEASE, NULL);
- del_timer(&csta->dbusytimer);
- ll_unload(csta);
-}
-
-HISAX_INITFUNC(static int init_card(struct IsdnCardState *cs))
-{
- int irq_cnt, cnt = 3;
- long flags;
-
- save_flags(flags);
- cli();
- irq_cnt = kstat_irqs(cs->irq);
- printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ], cs->irq,
- irq_cnt);
- if (cs->cardmsg(cs, CARD_SETIRQ, NULL)) {
- printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
- cs->irq);
- return(1);
- }
- while (cnt) {
- cs->cardmsg(cs, CARD_INIT, NULL);
- sti();
- current->state = TASK_INTERRUPTIBLE;
- /* Timeout 10ms */
- schedule_timeout((10 * HZ) / 1000);
- restore_flags(flags);
- printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
- cs->irq, kstat_irqs(cs->irq));
- if (kstat_irqs(cs->irq) == irq_cnt) {
- printk(KERN_WARNING
- "%s: IRQ(%d) getting no interrupts during init %d\n",
- CardType[cs->typ], cs->irq, 4 - cnt);
- if (cnt == 1) {
- free_irq(cs->irq, cs);
- return (2);
- } else {
- cs->cardmsg(cs, CARD_RESET, NULL);
- cnt--;
- }
- } else {
- cs->cardmsg(cs, CARD_TEST, NULL);
- return(0);
- }
- }
- restore_flags(flags);
- return(3);
-}
-
-HISAX_INITFUNC(static int
-checkcard(int cardnr, char *id, int *busy_flag))
-{
- long flags;
- int ret = 0;
- struct IsdnCard *card = cards + cardnr;
- struct IsdnCardState *cs;
-
- save_flags(flags);
- cli();
- if (!(cs = (struct IsdnCardState *)
- kmalloc(sizeof(struct IsdnCardState), GFP_ATOMIC))) {
- printk(KERN_WARNING
- "HiSax: No memory for IsdnCardState(card %d)\n",
- cardnr + 1);
- restore_flags(flags);
- return (0);
- }
- card->cs = cs;
- cs->cardnr = cardnr;
- cs->debug = L1_DEB_WARN;
- cs->HW_Flags = 0;
- cs->busy_flag = busy_flag;
-#if TEI_PER_CARD
-#else
- test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
-#endif
- cs->protocol = card->protocol;
-
- if ((card->typ > 0) && (card->typ < 31)) {
- if (!((1 << card->typ) & SUPORTED_CARDS)) {
- printk(KERN_WARNING
- "HiSax: Support for %s Card not selected\n",
- CardType[card->typ]);
- restore_flags(flags);
- return (0);
- }
- } else {
- printk(KERN_WARNING
- "HiSax: Card Type %d out of range\n",
- card->typ);
- restore_flags(flags);
- return (0);
- }
- if (!(cs->dlogspace = kmalloc(4096, GFP_ATOMIC))) {
- printk(KERN_WARNING
- "HiSax: No memory for dlogspace(card %d)\n",
- cardnr + 1);
- restore_flags(flags);
- return (0);
- }
- if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
- printk(KERN_WARNING
- "HiSax: No memory for status_buf(card %d)\n",
- cardnr + 1);
- kfree(cs->dlogspace);
- restore_flags(flags);
- return (0);
- }
- cs->stlist = NULL;
- cs->dlogflag = 0;
- cs->mon_tx = NULL;
- cs->mon_rx = NULL;
- cs->status_read = cs->status_buf;
- cs->status_write = cs->status_buf;
- cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
- cs->typ = card->typ;
- strcpy(cs->iif.id, id);
- cs->iif.channels = 2;
- cs->iif.maxbufsize = MAX_DATA_SIZE;
- cs->iif.hl_hdrlen = MAX_HEADER_LEN;
- cs->iif.features =
- ISDN_FEATURE_L2_X75I |
- ISDN_FEATURE_L2_HDLC |
- ISDN_FEATURE_L2_TRANS |
- ISDN_FEATURE_L3_TRANS |
-#ifdef CONFIG_HISAX_1TR6
- ISDN_FEATURE_P_1TR6 |
-#endif
-#ifdef CONFIG_HISAX_EURO
- ISDN_FEATURE_P_EURO |
-#endif
-#ifdef CONFIG_HISAX_NI1
- ISDN_FEATURE_P_NI1 |
-#endif
- 0;
-
- cs->iif.command = HiSax_command;
- cs->iif.writecmd = NULL;
- cs->iif.writebuf_skb = HiSax_writebuf_skb;
- cs->iif.readstat = HiSax_readstatus;
- register_isdn(&cs->iif);
- cs->myid = cs->iif.channels;
- printk(KERN_INFO
- "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
- (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
- (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
- (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
- (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
- "NONE", cs->iif.id, cs->myid);
- switch (card->typ) {
-#if CARD_TELES0
- case ISDN_CTYPE_16_0:
- case ISDN_CTYPE_8_0:
- ret = setup_teles0(card);
- break;
-#endif
-#if CARD_TELES3
- case ISDN_CTYPE_16_3:
- case ISDN_CTYPE_PNP:
- case ISDN_CTYPE_TELESPCMCIA:
- case ISDN_CTYPE_COMPAQ_ISA:
- ret = setup_teles3(card);
- break;
-#endif
-#if CARD_AVM_A1
- case ISDN_CTYPE_A1:
- ret = setup_avm_a1(card);
- break;
-#endif
-#if CARD_ELSA
- case ISDN_CTYPE_ELSA:
- case ISDN_CTYPE_ELSA_PNP:
- case ISDN_CTYPE_ELSA_PCMCIA:
- case ISDN_CTYPE_ELSA_PCI:
- ret = setup_elsa(card);
- break;
-#endif
-#if CARD_IX1MICROR2
- case ISDN_CTYPE_IX1MICROR2:
- ret = setup_ix1micro(card);
- break;
-#endif
-#if CARD_DIEHLDIVA
- case ISDN_CTYPE_DIEHLDIVA:
- ret = setup_diva(card);
- break;
-#endif
-#if CARD_ASUSCOM
- case ISDN_CTYPE_ASUSCOM:
- ret = setup_asuscom(card);
- break;
-#endif
-#if CARD_TELEINT
- case ISDN_CTYPE_TELEINT:
- ret = setup_TeleInt(card);
- break;
-#endif
-#if CARD_SEDLBAUER
- case ISDN_CTYPE_SEDLBAUER:
- case ISDN_CTYPE_SEDLBAUER_PCMCIA:
- ret = setup_sedlbauer(card);
- break;
-#endif
-#if CARD_SPORTSTER
- case ISDN_CTYPE_SPORTSTER:
- ret = setup_sportster(card);
- break;
-#endif
-#if CARD_MIC
- case ISDN_CTYPE_MIC:
- ret = setup_mic(card);
- break;
-#endif
-#if CARD_NETJET
- case ISDN_CTYPE_NETJET:
- ret = setup_netjet(card);
- break;
-#endif
-#if CARD_TELES3C
- case ISDN_CTYPE_TELES3C:
- ret = setup_t163c(card);
- break;
-#endif
-#if CARD_NICCY
- case ISDN_CTYPE_NICCY:
- ret = setup_niccy(card);
- break;
-#endif
-#if CARD_AMD7930 || CARD_DBRI
- case ISDN_CTYPE_AMD7930:
- case ISDN_CTYPE_DBRI:
- ret = setup_foreign(card);
- break;
-#endif
- default:
- printk(KERN_WARNING "HiSax: Unknown Card Typ %d\n",
- card->typ);
- ll_unload(cs);
- restore_flags(flags);
- return (0);
- }
- if (!ret) {
- ll_unload(cs);
- restore_flags(flags);
- return (0);
- }
- if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN, GFP_ATOMIC))) {
- printk(KERN_WARNING
- "HiSax: No memory for isac rcvbuf\n");
- return (1);
- }
- cs->rcvidx = 0;
- cs->tx_skb = NULL;
- cs->tx_cnt = 0;
- cs->event = 0;
- cs->tqueue.next = 0;
- cs->tqueue.sync = 0;
- cs->tqueue.data = cs;
-
- skb_queue_head_init(&cs->rq);
- skb_queue_head_init(&cs->sq);
-
- init_bcstate(cs, 0);
- init_bcstate(cs, 1);
- ret = init_card(cs);
- if (ret) {
- closecard(cardnr);
- restore_flags(flags);
- return (0);
- }
- init_tei(cs, cs->protocol);
- CallcNewChan(cs);
- ll_run(cs);
- cs->l1cmd(cs, PH_RESET_REQ, NULL);
- restore_flags(flags);
- return (1);
-}
-
-HISAX_INITFUNC(void
-HiSax_shiftcards(int idx))
-{
- int i;
-
- for (i = idx; i < 15; i++)
- memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
-}
-
-HISAX_INITFUNC(int
-HiSax_inithardware(int *busy_flag))
-{
- int foundcards = 0;
- int i = 0;
- int t = ',';
- int flg = 0;
- char *id;
- char *next_id = HiSax_id;
- char ids[20];
-
- if (strchr(HiSax_id, ','))
- t = ',';
- else if (strchr(HiSax_id, '%'))
- t = '%';
-
- while (i < nrcards) {
- if (cards[i].typ < 1)
- break;
- id = next_id;
- if ((next_id = strchr(id, t))) {
- *next_id++ = 0;
- strcpy(ids, id);
- flg = i + 1;
- } else {
- next_id = id;
- if (flg >= i)
- strcpy(ids, id);
- else
- sprintf(ids, "%s%d", id, i);
- }
- if (checkcard(i, ids, busy_flag)) {
- foundcards++;
- i++;
- } else {
- printk(KERN_WARNING "HiSax: Card %s not installed !\n",
- CardType[cards[i].typ]);
- if (cards[i].cs)
- kfree((void *) cards[i].cs);
- cards[i].cs = NULL;
- HiSax_shiftcards(i);
- }
- }
- return foundcards;
-}
-
-void
-HiSax_closehardware(void)
-{
- int i;
- long flags;
-
- save_flags(flags);
- cli();
- for (i = 0; i < nrcards; i++)
- if (cards[i].cs) {
- ll_stop(cards[i].cs);
- release_tei(cards[i].cs);
- closecard(i);
- free_irq(cards[i].cs->irq, cards[i].cs);
- kfree((void *) cards[i].cs);
- cards[i].cs = NULL;
- }
- Isdnl1Free();
- TeiFree();
- Isdnl2Free();
- CallcFree();
- restore_flags(flags);
-}
-
-void
-HiSax_reportcard(int cardnr)
-{
- struct IsdnCardState *cs = cards[cardnr].cs;
- struct PStack *stptr;
- struct l3_process *pc;
- int j, i = 1;
-
- printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
- printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
- printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
- printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
- (ulong) & HiSax_reportcard);
- printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
- printk(KERN_DEBUG "HiSax: cs stl 0x%lX\n", (ulong) & (cs->stlist));
- stptr = cs->stlist;
- while (stptr != NULL) {
- printk(KERN_DEBUG "HiSax: dst%d 0x%lX\n", i, (ulong) stptr);
- printk(KERN_DEBUG "HiSax: dst%d stp 0x%lX\n", i, (ulong) stptr->l1.stlistp);
- printk(KERN_DEBUG "HiSax: tei %d sapi %d\n",
- stptr->l2.tei, stptr->l2.sap);
- printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer);
- pc = stptr->l3.proc;
- while (pc) {
- printk(KERN_DEBUG "HiSax: l3proc %x 0x%lX\n", pc->callref,
- (ulong) pc);
- printk(KERN_DEBUG "HiSax: state %d st 0x%lX chan 0x%lX\n",
- pc->state, (ulong) pc->st, (ulong) pc->chan);
- pc = pc->next;
- }
- stptr = stptr->next;
- i++;
- }
- for (j = 0; j < 2; j++) {
- printk(KERN_DEBUG "HiSax: ch%d 0x%lX\n", j,
- (ulong) & cs->channel[j]);
- stptr = cs->channel[j].b_st;
- i = 1;
- while (stptr != NULL) {
- printk(KERN_DEBUG "HiSax: b_st%d 0x%lX\n", i, (ulong) stptr);
- printk(KERN_DEBUG "HiSax: man 0x%lX\n", (ulong) stptr->ma.layer);
- stptr = stptr->next;
- i++;
- }
- }
-}
-
#ifdef L2FRAME_DEBUG /* psa */
char *
}
}
-static char tmp[20];
+static char tmpdeb[32];
char *
l2frames(u_char * ptr)
case 1:
case 5:
case 9:
- sprintf(tmp, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+ sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
break;
case 0x6f:
case 0x0f:
case 0x63:
case 0x87:
case 0xaf:
- sprintf(tmp, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+ sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
break;
default:
if (!(ptr[2] & 1)) {
- sprintf(tmp, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+ sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
break;
} else
return "invalid command";
}
- return tmp;
+ return tmpdeb;
}
void
Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
{
- char tmp[132];
u_char *ptr;
ptr = skb->data;
if (ptr[0] & 1 || !(ptr[1] & 1))
- debugl1(cs, "Addres not LAPD");
- else {
- sprintf(tmp, "%s %s: %s%c (sapi %d, tei %d)",
+ debugl1(cs, "Address not LAPD");
+ else
+ debugl1(cs, "%s %s: %s%c (sapi %d, tei %d)",
(dir ? "<-" : "->"), buf, l2frames(ptr),
((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
- debugl1(cs, tmp);
- }
}
#endif
l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
FsmChangeState(fi, ST_L1_F3);
if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
- cs->l1cmd(cs, PH_ENABLE_REQ, NULL);
+ st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
}
static void
struct PStack *st = fi->userdata;
FsmChangeState(fi, ST_L1_F3);
- if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) {
+// if (!test_bit(FLG_L1_T3RUN, &st->l1.Flags)) {
FsmDelTimer(&st->l1.timer, 1);
FsmAddTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
- }
+// }
}
static void
l1_power_up(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
FsmChangeState(fi, ST_L1_F4);
- cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+ st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
FsmDelTimer(&st->l1.timer, 1);
- FsmAddTimer(&st->l1.timer, TIMER3_VALUE * HZ, EV_TIMER3, NULL, 2);
+ FsmAddTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
} else
FsmChangeState(fi, ST_L1_F3);
l1_info2_ind(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
FsmChangeState(fi, ST_L1_F6);
- cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+ st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
}
static void
l1_info4_ind(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
FsmChangeState(fi, ST_L1_F7);
- cs->l1cmd(cs, PH_INFO3_REQ, NULL);
+ st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
FsmDelTimer(&st->l1.timer, 4);
if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
l1_timer3(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
-
+
test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);
- if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
- L1deactivated(cs);
- if (st->l1.l1m.state != ST_L1_F6)
- FsmChangeState(fi, ST_L1_F3);
+ if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+ L1deactivated(st->l1.hardware);
+ if (st->l1.l1m.state != ST_L1_F6) {
+ FsmChangeState(fi, ST_L1_F3);
+ st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+ }
}
static void
l1_timer_act(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
- L1activated(cs);
+ L1activated(st->l1.hardware);
}
static void
l1_timer_deact(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
- L1deactivated(cs);
- cs->l1cmd(cs, PH_DEACT_ACK, NULL);
+ L1deactivated(st->l1.hardware);
+ st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL);
}
static void
l1_activate(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- struct IsdnCardState *cs = st->l1.hardware;
- cs->l1cmd(cs, PH_RESET_REQ, NULL);
+ st->l1.l1hw(st, HW_RESET | REQUEST, NULL);
}
-static struct FsmNode L1FnList[] HISAX_INITDATA =
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) {
+ test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+ L1deactivated(st->l1.hardware);
+ }
+}
+
+static struct FsmNode L1DFnList[] HISAX_INITDATA =
{
{ST_L1_F3, EV_PH_ACTIVATE, l1_activate},
+ {ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
+ {ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
{ST_L1_F3, EV_RESET_IND, l1_reset},
{ST_L1_F4, EV_RESET_IND, l1_reset},
{ST_L1_F5, EV_RESET_IND, l1_reset},
{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
};
-#define L1_FN_COUNT (sizeof(L1FnList)/sizeof(struct FsmNode))
+#define L1D_FN_COUNT (sizeof(L1DFnList)/sizeof(struct FsmNode))
+
+static void
+l1b_activate(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_WAIT_ACT);
+ FsmAddTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
+}
+
+static void
+l1b_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_WAIT_DEACT);
+ FsmAddTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
+}
+
+static void
+l1b_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_ACTIV);
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+}
+
+static void
+l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_NULL);
+ st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+}
+
+static struct FsmNode L1BFnList[] HISAX_INITDATA =
+{
+ {ST_L1_NULL, EV_PH_ACTIVATE, l1b_activate},
+ {ST_L1_WAIT_ACT, EV_TIMER_ACT, l1b_timer_act},
+ {ST_L1_ACTIV, EV_PH_DEACTIVATE, l1b_deactivate},
+ {ST_L1_WAIT_DEACT, EV_TIMER_DEACT, l1b_timer_deact},
+};
+
+#define L1B_FN_COUNT (sizeof(L1BFnList)/sizeof(struct FsmNode))
HISAX_INITFUNC(void Isdnl1New(void))
{
- l1fsm.state_count = L1_STATE_COUNT;
- l1fsm.event_count = L1_EVENT_COUNT;
- l1fsm.strEvent = strL1Event;
- l1fsm.strState = strL1State;
- FsmNew(&l1fsm, L1FnList, L1_FN_COUNT);
+ l1fsm_d.state_count = L1D_STATE_COUNT;
+ l1fsm_d.event_count = L1_EVENT_COUNT;
+ l1fsm_d.strEvent = strL1Event;
+ l1fsm_d.strState = strL1DState;
+ FsmNew(&l1fsm_d, L1DFnList, L1D_FN_COUNT);
+ l1fsm_b.state_count = L1B_STATE_COUNT;
+ l1fsm_b.event_count = L1_EVENT_COUNT;
+ l1fsm_b.strEvent = strL1Event;
+ l1fsm_b.strState = strL1BState;
+ FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
}
void Isdnl1Free(void)
{
- FsmFree(&l1fsm);
+ FsmFree(&l1fsm_d);
+ FsmFree(&l1fsm_b);
}
static void
-dch_manl1(struct PStack *st, int pr,
- void *arg)
+dch_l2l1(struct PStack *st, int pr, void *arg)
{
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
- char tmp[32];
switch (pr) {
- case PH_ACTIVATE_REQ:
- if (cs->debug) {
- sprintf(tmp, "PH_ACTIVATE_REQ %s",
- strL1State[st->l1.l1m.state]);
- debugl1(cs, tmp);
- }
+ case (PH_DATA | REQUEST):
+ case (PH_PULL | REQUEST):
+ case (PH_PULL |INDICATION):
+ st->l1.l1hw(st, pr, arg);
+ break;
+ case (PH_ACTIVATE | REQUEST):
+ if (cs->debug)
+ debugl1(cs, "PH_ACTIVATE_REQ %s",
+ strL1DState[st->l1.l1m.state]);
if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
- st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
else {
test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
}
break;
- case PH_DEACTIVATE_REQ:
- if (cs->debug) {
- sprintf(tmp, "PH_DEACTIVATE_REQ %s",
- strL1State[st->l1.l1m.state]);
- debugl1(cs, tmp);
- }
- break;
- case PH_TESTLOOP_REQ:
- if (1 & (int) arg)
+ case (PH_TESTLOOP | REQUEST):
+ if (1 & (long) arg)
debugl1(cs, "PH_TEST_LOOP B1");
- if (2 & (int) arg)
+ if (2 & (long) arg)
debugl1(cs, "PH_TEST_LOOP B2");
- if (!(3 & (int) arg))
+ if (!(3 & (long) arg))
debugl1(cs, "PH_TEST_LOOP DISABLED");
- cs->l1cmd(cs, PH_TESTLOOP_REQ, arg);
- break;
- case PH_RESET_IND:
- FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
- break;
- case PH_DEACT_CNF:
- FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
- break;
- case PH_DEACT_IND:
- FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+ st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
break;
- case PH_POWERUP_CNF:
- FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
- break;
- case PH_RSYNC_IND:
- FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
- break;
- case PH_INFO2_IND:
- FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+ default:
+ if (cs->debug)
+ debugl1(cs, "dch_l2l1 msg %04X unhandled", pr);
break;
- case PH_I4_P8_IND:
- case PH_I4_P10_IND:
- FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+ }
+}
+
+void
+l1_msg(struct IsdnCardState *cs, int pr, void *arg) {
+ struct PStack *st;
+
+ st = cs->stlist;
+
+ while (st) {
+ switch(pr) {
+ case (HW_RESET | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
+ break;
+ case (HW_DEACTIVATE | CONFIRM):
+ FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+ break;
+ case (HW_DEACTIVATE | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+ break;
+ case (HW_POWERUP | CONFIRM):
+ FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
+ break;
+ case (HW_RSYNC | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
+ break;
+ case (HW_INFO2 | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+ break;
+ case (HW_INFO4_P8 | INDICATION):
+ case (HW_INFO4_P10 | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+ break;
+ default:
+ if (cs->debug)
+ debugl1(cs, "l1msg %04X unhandled", pr);
+ break;
+ }
+ st = st->next;
+ }
+}
+
+void
+l1_msg_b(struct PStack *st, int pr, void *arg) {
+ switch(pr) {
+ case (PH_ACTIVATE | REQUEST):
+ FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL);
break;
- default:
- if (cs->debug) {
- sprintf(tmp, "dch_manl1 msg %04X unhandled", pr);
- debugl1(cs, tmp);
- }
+ case (PH_DEACTIVATE | REQUEST):
+ FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL);
break;
}
}
{
st->l1.hardware = cs;
st->protocol = cs->protocol;
- st->l1.l1m.fsm = &l1fsm;
+ st->l1.l1m.fsm = &l1fsm_d;
st->l1.l1m.state = ST_L1_F3;
st->l1.l1m.debug = cs->debug;
st->l1.l1m.userdata = st;
setstack_tei(st);
setstack_manager(st);
st->l1.stlistp = &(cs->stlist);
- st->ma.manl1 = dch_manl1;
+ st->l2.l2l1 = dch_l2l1;
st->l1.Flags = 0;
cs->setstack_d(st, cs);
}
+
+void
+setstack_l1_B(struct PStack *st)
+{
+ struct IsdnCardState *cs = st->l1.hardware;
+
+ st->l1.l1m.fsm = &l1fsm_b;
+ st->l1.l1m.state = ST_L1_NULL;
+ st->l1.l1m.debug = cs->debug;
+ st->l1.l1m.userdata = st;
+ st->l1.l1m.userint = 0;
+ st->l1.l1m.printdebug = l1m_debug;
+ st->l1.Flags = 0;
+ FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+}
-/* $Id: isdnl1.h,v 2.5 1998/02/02 13:36:58 keil Exp $
+/* $Id: isdnl1.h,v 2.8 1998/11/15 23:54:59 keil Exp $
* $Log: isdnl1.h,v $
+ * Revision 2.8 1998/11/15 23:54:59 keil
+ * changes from 2.0
+ *
+ * Revision 2.7 1998/09/30 22:21:55 keil
+ * cosmetics
+ *
+ * Revision 2.6 1998/05/25 12:58:06 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
* Revision 2.5 1998/02/02 13:36:58 keil
* more debug
*
*
*/
-
-#define L2FRAME_DEBUG
-
-/* DEBUG Level */
-
-#define L1_DEB_WARN 0x01
-#define L1_DEB_INTSTAT 0x02
-#define L1_DEB_ISAC 0x04
-#define L1_DEB_ISAC_FIFO 0x08
-#define L1_DEB_HSCX 0x10
-#define L1_DEB_HSCX_FIFO 0x20
-#define L1_DEB_LAPD 0x40
-#define L1_DEB_IPAC 0x80
-#define L1_DEB_RECEIVE_FRAME 0x100
-
#define D_RCVBUFREADY 0
#define D_XMTBUFREADY 1
#define D_L1STATECHANGE 2
#define B_RCVBUFREADY 0
#define B_XMTBUFREADY 1
-extern void debugl1(struct IsdnCardState *sp, char *msg);
+extern void debugl1(struct IsdnCardState *cs, char *fmt, ...);
extern void DChannel_proc_xmt(struct IsdnCardState *cs);
extern void DChannel_proc_rcv(struct IsdnCardState *cs);
-
+extern void l1_msg(struct IsdnCardState *cs, int pr, void *arg);
+extern void l1_msg_b(struct PStack *st, int pr, void *arg);
#ifdef L2FRAME_DEBUG
-extern void Logl2Frame(struct IsdnCardState *sp, struct sk_buff *skb, char *buf, int dir);
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
#endif
-/* $Id: isdnl2.c,v 2.7 1998/02/12 23:07:47 keil Exp $
+/* $Id: isdnl2.c,v 2.16 1998/11/15 23:55:01 keil Exp $
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
* based on the teles driver from Jan den Ouden
*
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
+ *
* Thanks to Jan den Ouden
* Fritz Elfert
*
* $Log: isdnl2.c,v $
+ * Revision 2.16 1998/11/15 23:55:01 keil
+ * changes from 2.0
+ *
+ * Revision 2.15 1998/08/13 23:36:42 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.14 1998/06/19 15:19:18 keil
+ * fix LAPB tx_cnt for none I-frames
+ *
+ * Revision 2.13 1998/06/18 23:17:20 keil
+ * LAPB bugfix
+ *
+ * Revision 2.12 1998/05/25 14:10:12 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.11 1998/05/25 12:58:08 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.9 1998/04/10 10:35:30 paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
+ * Revision 2.8 1998/03/07 22:57:04 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 2.7 1998/02/12 23:07:47 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
* Old stuff is still in the separate branch.
*
* Revision 2.2 1997/07/31 11:49:05 keil
- * Eroor handling for no TEI assign
+ * Error handling for no TEI assign
*
* Revision 2.1 1997/07/27 21:34:38 keil
* cosmetics
#include "hisax.h"
#include "isdnl2.h"
-const char *l2_revision = "$Revision: 2.7 $";
+const char *l2_revision = "$Revision: 2.16 $";
-static void l2m_debug(struct FsmInst *fi, char *s);
+static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
static
struct Fsm l2fsm =
EV_L2_MDL_ASSIGN,
EV_L2_MDL_REMOVE,
EV_L2_MDL_ERROR,
- EV_L2_MDL_NOTEIPROC,
EV_L1_DEACTIVATE,
EV_L2_T200,
EV_L2_T203,
"EV_L2_MDL_ASSIGN",
"EV_L2_MDL_REMOVE",
"EV_L2_MDL_ERROR",
- "EV_L2_MDL_NOTEIPROC",
"EV_L1_DEACTIVATE",
"EV_L2_T200",
"EV_L2_T203",
return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag));
}
-static void
-discard_i_queue(struct PStack *st)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&st->l2.i_queue))) {
- dev_kfree_skb(skb);
- }
-}
-
-static void
-discard_ui_queue(struct PStack *st)
-{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&st->l2.ui_queue))) {
- dev_kfree_skb(skb);
- }
-}
-
inline void
clear_exception(struct Layer2 *l2)
{
}
}
-static void
-enqueue_ui(struct PStack *st,
- struct sk_buff *skb)
-{
- st->l2.l2l1(st, PH_DATA_REQ, skb);
-}
-
-static void
+inline static void
enqueue_super(struct PStack *st,
struct sk_buff *skb)
{
- st->l2.l2l1(st, PH_DATA_REQ, skb);
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l1.bcs->tx_cnt += skb->len;
+ st->l2.l2l1(st, PH_DATA | REQUEST, skb);
}
+#define enqueue_ui(a, b) enqueue_super(a, b)
+
inline int
IsUI(u_char * data, int ext)
{
return ((data[0] & 0xf) == 1);
}
+inline int
+IsSFrame(u_char * data, int ext)
+{
+ register u_char d = *data;
+
+ if (!ext)
+ d &= 0xf;
+ return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c));
+}
+
inline int
IsSABMX(u_char * data, int ext)
{
switch (event) {
case EV_L2_UA:
if (get_PollFlagFree(st, skb))
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'C');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C');
else
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'D');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D');
break;
case EV_L2_DM:
if (get_PollFlagFree(st, skb))
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'B');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
else {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'E');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
establishlink(fi);
test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
}
struct PStack *st = fi->userdata;
int state = fi->state;
- FsmChangeState(fi, ST_L2_3);
- if (state == ST_L2_1)
- st->l2.l2tei(st, MDL_ASSIGN_IND, NULL);
+
+ if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+ FsmChangeState(fi, ST_L2_4);
+ establishlink(fi);
+ test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+ } else {
+ FsmChangeState(fi, ST_L2_3);
+ if (state == ST_L2_1)
+ st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+ }
}
static void
skb_queue_tail(&st->l2.ui_queue, skb);
if (fi->state == ST_L2_1) {
FsmChangeState(fi, ST_L2_2);
- st->l2.l2tei(st, MDL_ASSIGN_IND, NULL);
+ st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
}
if (fi->state > ST_L2_3)
l2_send_ui(st);
skb_pull(skb, l2headersize(&st->l2, 1));
if (skb->len > st->l2.maxlen) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
FreeSkb(skb);
} else
- st->l2.l2l3(st, DL_UNIT_DATA, skb);
+ st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
}
static void
struct PStack *st = fi->userdata;
if (fi->state != ST_L2_4)
- discard_i_queue(st);
+ discard_queue(&st->l2.i_queue);
if (fi->state != ST_L2_5)
establishlink(fi);
test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
struct PStack *st = fi->userdata;
if (fi->state == ST_L2_4) {
- st->l2.l2man(st, DL_RELEASE, NULL);
+ st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
return;
} else if (fi->state == ST_L2_5) {
test_and_set_bit(FLG_PEND_REL, &st->l2.flag);
return;
}
- discard_i_queue(st);
+ discard_queue(&st->l2.i_queue);
FsmChangeState(fi, ST_L2_6);
st->l2.rc = 0;
send_uframe(st, DISC | 0x10, CMD);
if (test_bit(FLG_ORIG, &st->l2.flag))
rsp = !rsp;
if (rsp) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
FreeSkb(skb);
if ((state == ST_L2_7) || (state == ST_L2_8))
establishlink(fi);
return;
}
if (skb->len != (l2addrsize(&st->l2) + 1)) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
if ((state == ST_L2_7) || (state == ST_L2_8))
establishlink(fi);
if (ST_L2_5 == state)
return;
if (ST_L2_4 != state) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'F');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F');
if (st->l2.vs != st->l2.va) {
- discard_i_queue(st);
+ discard_queue(&st->l2.i_queue);
est = 1;
} else
est = 0;
FsmChangeState(fi, ST_L2_7);
if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
FsmDelTimer(&st->l2.t200, 2);
- FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+ FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
if (est)
- st->l2.l2man(st, DL_ESTABLISH, NULL);
+ st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
if (ST_L2_8 == state)
if (skb_queue_len(&st->l2.i_queue) && cansend(st))
- st->l2.l2l1(st, PH_PULL_REQ, NULL);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
}
static void
rsp = !rsp;
if (rsp) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
FreeSkb(skb);
if ((state == ST_L2_7) || (state == ST_L2_8))
establishlink(fi);
return;
}
if (skb->len != (l2addrsize(&st->l2) + 1)) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
if ((state == ST_L2_7) || (state == ST_L2_8))
establishlink(fi);
FsmDelTimer(&st->l2.t200, 2);
}
send_uframe(st, cmd | PollFlag, RSP);
- if (rel)
- st->l2.l2man(st, DL_RELEASE, NULL);
+ if (rel) {
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ }
}
{
struct PStack *st = fi->userdata;
struct sk_buff *skb = arg;
- u_char PollFlag, est = 1;
+ int pr=-1;
+ u_char PollFlag;
int state,rsp;
state = fi->state;
rsp = !rsp;
if (!rsp) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
FreeSkb(skb);
if ((state == ST_L2_7) || (state == ST_L2_8))
establishlink(fi);
return;
}
if (skb->len != (l2addrsize(&st->l2) + 1)) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
establishlink(fi);
FsmDelTimer(&st->l2.t200, 2);
if (fi->state == ST_L2_5) {
if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag)) {
- discard_i_queue(st);
+ discard_queue(&st->l2.i_queue);
st->l2.rc = 0;
send_uframe(st, DISC | 0x10, CMD);
FsmChangeState(fi, ST_L2_6);
FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 4);
test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
} else {
- if (!test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
- if (st->l2.vs != st->l2.va)
- discard_i_queue(st);
- else
- est = 0;
+ if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
+ pr = DL_ESTABLISH | CONFIRM;
+ } else if (st->l2.vs != st->l2.va) {
+ discard_queue(&st->l2.i_queue);
+ pr = DL_ESTABLISH | INDICATION;
}
st->l2.vs = 0;
st->l2.va = 0;
st->l2.vr = 0;
st->l2.sow = 0;
FsmChangeState(fi, ST_L2_7);
- FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
- if (est)
- st->l2.l2man(st, DL_ESTABLISH, NULL);
+ if (pr > -1)
+ st->l2.l2l3(st, pr, NULL);
}
} else { /* ST_L2_6 */
- st->l2.l2man(st, DL_RELEASE, NULL);
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
FsmChangeState(fi, ST_L2_4);
}
}
rsp = !rsp;
if (!rsp) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
FreeSkb(skb);
if ((state == ST_L2_7) || (state == ST_L2_8))
establishlink(fi);
return;
}
if (skb->len != (l2addrsize(&st->l2) + 1)) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8))
establishlink(fi);
}
PollFlag = get_PollFlagFree(st, skb);
if (!PollFlag) {
- establishlink(fi);
- test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+ if (fi->state == ST_L2_4) {
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+ FsmChangeState(fi, ST_L2_5);
+ } else if ((fi->state == ST_L2_7) || (fi->state == ST_L2_8)) {
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+ establishlink(fi);
+ }
} else {
- if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
- FsmDelTimer(&st->l2.t200, 2);
- if (fi->state == ST_L2_5 && !test_bit(FLG_L3_INIT, &st->l2.flag))
- discard_i_queue(st);
- st->l2.l2man(st, DL_RELEASE, NULL);
- FsmChangeState(fi, ST_L2_4);
+ switch (fi->state) {
+ case ST_L2_8:
+ establishlink(fi);
+ case ST_L2_7:
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+ break;
+ case ST_L2_4:
+ break;
+ case ST_L2_5:
+ if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+ FsmDelTimer(&st->l2.t200, 2);
+ discard_queue(&st->l2.i_queue);
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ FsmChangeState(fi, ST_L2_4);
+ break;
+ case ST_L2_6:
+ if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+ FsmDelTimer(&st->l2.t200, 2);
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+ FsmChangeState(fi, ST_L2_4);
+ break;
+ }
}
}
{
struct PStack *st = fi->userdata;
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'J');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J');
establishlink(fi);
}
if (p1 < 0)
p1 += (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
p1 = (p1 + l2->sow) % l2->window;
+ if (test_bit(FLG_LAPB, &l2->flag))
+ st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0);
skb_queue_head(&l2->i_queue, l2->windowar[p1]);
l2->windowar[p1] = NULL;
}
restore_flags(flags);
- st->l2.l2l1(st, PH_PULL_REQ, NULL);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
}
}
PollFlag = (skb->data[1] & 0x1) == 0x1;
nr = skb->data[1] >> 1;
} else {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ if (skb->len >2) {
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+ establishlink(fi);
+ }
FreeSkb(skb);
- establishlink(fi);
return;
}
} else {
PollFlag = (skb->data[0] & 0x10);
nr = (skb->data[0] >> 5) & 0x7;
} else {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
establishlink(fi);
return;
if ((!rsp) && PollFlag)
enquiry_response(st);
if (rsp && PollFlag)
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'A');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A');
if (legalnr(st, nr)) {
if (typ == REJ) {
setva(st, nr);
test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
}
if (skb_queue_len(&st->l2.i_queue) && (typ == RR))
- st->l2.l2l1(st, PH_PULL_REQ, NULL);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
} else
nrerrorrecovery(fi);
if ((fi->userint & LC_FLUSH_WAIT) && rsp && !(skb_queue_len(&st->l2.i_queue))) {
fi->userint &= ~LC_FLUSH_WAIT;
- st->l2.l2man(st, DL_FLUSH, NULL);
+ st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL);
}
}
if (!((fi->state == ST_L2_5) && test_bit(FLG_L3_INIT, &st->l2.flag)))
skb_queue_tail(&st->l2.i_queue, skb);
if (fi->state == ST_L2_7)
- st->l2.l2l1(st, PH_PULL_REQ, NULL);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
}
static void
{
struct PStack *st = fi->userdata;
struct sk_buff *skb = arg;
- struct IsdnCardState *sp = st->l1.hardware;
struct Layer2 *l2 = &(st->l2);
- int PollFlag, ns, nr, i, hs, rsp;
- char str[64];
+ int PollFlag, ns, nr, i, rsp;
rsp = *skb->data & 0x2;
if (test_bit(FLG_ORIG, &l2->flag))
rsp = !rsp;
if (rsp) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
FreeSkb(skb);
establishlink(fi);
return;
i = l2addrsize(l2);
if (test_bit(FLG_MOD128, &l2->flag)) {
if (skb->len <= (i + 1)) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
FreeSkb(skb);
- establishlink(fi);
return;
} else if ((skb->len - i - 1) > l2->maxlen) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
FreeSkb(skb);
establishlink(fi);
return;
nr = (skb->data[i + 1] >> 1) & 0x7f;
} else {
if (skb->len <= i) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
establishlink(fi);
return;
} else if ((skb->len - i) > l2->maxlen) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'O');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'O');
FreeSkb(skb);
establishlink(fi);
return;
} else if (l2->vr == ns) {
l2->vr = (l2->vr + 1) % (test_bit(FLG_MOD128, &l2->flag) ? 128 : 8);
test_and_clear_bit(FLG_REJEXC, &l2->flag);
- if (test_bit(FLG_LAPD, &l2->flag))
- if (sp->dlogflag) {
- hs = l2headersize(l2, 0);
- LogFrame(st->l1.hardware, skb->data, skb->len);
- sprintf(str, "Q.931 frame network->user tei %d", st->l2.tei);
- dlogframe(st->l1.hardware, skb->data + hs,
- skb->len - hs, str);
- }
if (PollFlag)
enquiry_response(st);
else
test_and_set_bit(FLG_ACK_PEND, &l2->flag);
skb_pull(skb, l2headersize(l2, 0));
- st->l2.l2l3(st, DL_DATA, skb);
+ st->l2.l2l3(st, DL_DATA | INDICATION, skb);
} else {
/* n(s)!=v(r) */
FreeSkb(skb);
}
if (skb_queue_len(&st->l2.i_queue) && (fi->state == ST_L2_7))
- st->l2.l2l1(st, PH_PULL_REQ, NULL);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
enquiry_cr(st, RR, RSP, 0);
}
{
struct PStack *st = fi->userdata;
- st->l2.tei = (int) arg;
+ st->l2.tei = (long) arg;
if (fi->state == ST_L2_3) {
establishlink(fi);
l2_send_ui(st);
}
-static void
-l2_no_tei(struct FsmInst *fi, int event, void *arg)
-{
- FsmChangeState(fi, ST_L2_4);
-}
-
static void
l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
{
} else if (st->l2.rc == st->l2.N200) {
FsmChangeState(fi, ST_L2_4);
test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
- discard_i_queue(st);
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'G');
- st->l2.l2man(st, DL_RELEASE, NULL);
+ discard_queue(&st->l2.i_queue);
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
} else {
st->l2.rc++;
FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
} else if (st->l2.rc == st->l2.N200) {
FsmChangeState(fi, ST_L2_4);
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'H');
- st->l2.l2man(st, DL_RELEASE, NULL);
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H');
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
} else {
st->l2.rc++;
FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len);
FreeSkb(oskb);
}
- st->l2.l2l1(st, PH_PULL_IND, skb);
+ st->l2.l2l1(st, PH_PULL | INDICATION, skb);
test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
FsmDelTimer(&st->l2.t203, 13);
FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
}
if (skb_queue_len(&l2->i_queue) && cansend(st))
- st->l2.l2l1(st, PH_PULL_REQ, NULL);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
}
static void
PollFlag = (skb->data[1] & 0x1) == 0x1;
nr = skb->data[1] >> 1;
} else {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
establishlink(fi);
return;
PollFlag = (skb->data[0] & 0x10);
nr = (skb->data[0] >> 5) & 0x7;
} else {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
establishlink(fi);
return;
invoke_retransmission(st, nr);
FsmChangeState(fi, ST_L2_7);
if (skb_queue_len(&l2->i_queue) && cansend(st))
- st->l2.l2l1(st, PH_PULL_REQ, NULL);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
else if (fi->userint & LC_FLUSH_WAIT) {
fi->userint &= ~LC_FLUSH_WAIT;
- st->l2.l2man(st, DL_FLUSH, NULL);
+ st->l2.l2l3(st, DL_FLUSH | INDICATION, NULL);
}
}
} else {
{
struct PStack *st = fi->userdata;
struct sk_buff *skb = arg;
- char tmp[64];
skb_pull(skb, l2addrsize(&st->l2) + 1);
if (test_bit(FLG_MOD128, &st->l2.flag)) {
if (skb->len < 5)
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
- else {
- sprintf(tmp, "FRMR information %2x %2x %2x %2x %2x",
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+ else
+ l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x %2x %2x",
skb->data[0], skb->data[1], skb->data[2],
skb->data[3], skb->data[4]);
- l2m_debug(&st->l2.l2m, tmp);
- }
} else {
if (skb->len < 3)
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
- else {
- sprintf(tmp, "FRMR information %2x %2x %2x",
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
+ else
+ l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x",
skb->data[0], skb->data[1], skb->data[2]);
- l2m_debug(&st->l2.l2m, tmp);
- }
}
if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */
(IsUA(skb->data, 0) && (fi->state == ST_L2_7))) {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'K');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K');
establishlink(fi);
test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
}
{
struct PStack *st = fi->userdata;
- discard_i_queue(st);
- discard_ui_queue(st);
+ discard_queue(&st->l2.i_queue);
+ discard_queue(&st->l2.ui_queue);
st->l2.tei = -1;
if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
FsmDelTimer(&st->l2.t200, 18);
FsmDelTimer(&st->l2.t203, 19);
if (fi->state != ST_L2_4)
- st->l2.l2man(st, DL_RELEASE, NULL);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
FsmChangeState(fi, ST_L2_1);
}
l2_persistant_da(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
+ int rel = DL_RELEASE | INDICATION;
+
- discard_i_queue(st);
- discard_ui_queue(st);
+ discard_queue(&st->l2.i_queue);
+ discard_queue(&st->l2.ui_queue);
if (test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
FsmDelTimer(&st->l2.t200, 18);
FsmDelTimer(&st->l2.t203, 19);
- test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
clear_exception(&st->l2);
switch (fi->state) {
+ case ST_L2_1:
+ if (!test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+ break;
case ST_L2_3:
- st->l2.l2man(st, DL_RELEASE, NULL);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
case ST_L2_2:
FsmChangeState(fi, ST_L2_1);
break;
- case ST_L2_5:
case ST_L2_6:
+ rel = DL_RELEASE | CONFIRM;
+ case ST_L2_5:
+ if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+ rel = DL_RELEASE | CONFIRM;
case ST_L2_7:
case ST_L2_8:
- st->l2.l2man(st, DL_RELEASE, NULL);
+ st->l2.l2l3(st, rel, NULL);
FsmChangeState(fi, ST_L2_4);
break;
+ case ST_L2_4:
+ if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ break;
}
+ test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+ test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
}
static struct FsmNode L2FnList[] HISAX_INITDATA =
{
- {ST_L2_1, EV_L2_MDL_NOTEIPROC, l2_no_tei},
{ST_L2_1, EV_L2_DL_ESTABLISH, l2_dl_establish},
{ST_L2_2, EV_L2_DL_ESTABLISH, l2_dl_establish},
{ST_L2_4, EV_L2_DL_ESTABLISH, l2_establish},
{ST_L2_4, EV_L2_DM, l2_got_dm},
{ST_L2_5, EV_L2_DM, l2_got_dm},
{ST_L2_6, EV_L2_DM, l2_got_dm},
- {ST_L2_7, EV_L2_DM, l2_mdl_error},
- {ST_L2_8, EV_L2_DM, l2_mdl_error},
+ {ST_L2_7, EV_L2_DM, l2_got_dm},
+ {ST_L2_8, EV_L2_DM, l2_got_dm},
{ST_L2_1, EV_L2_UI, l2_got_ui},
{ST_L2_2, EV_L2_UI, l2_got_ui},
{ST_L2_3, EV_L2_UI, l2_got_ui},
{ST_L2_8, EV_L2_T200, l2_st78_tout_200},
{ST_L2_7, EV_L2_T203, l2_st7_tout_203},
{ST_L2_7, EV_L2_ACK_PULL, l2_pull_iqueue},
+ {ST_L2_1, EV_L1_DEACTIVATE, l2_persistant_da},
{ST_L2_2, EV_L1_DEACTIVATE, l2_persistant_da},
{ST_L2_3, EV_L1_DEACTIVATE, l2_persistant_da},
{ST_L2_4, EV_L1_DEACTIVATE, l2_persistant_da},
int ret = 1, len;
switch (pr) {
- case (PH_DATA_IND):
+ case (PH_DATA | INDICATION):
datap = skb->data;
len = l2addrsize(&st->l2);
if (skb->len > len)
datap += len;
else {
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'N');
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'N');
FreeSkb(skb);
return;
}
if (!(*datap & 1)) /* I-Frame */
ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb);
- else if ((*datap & 3) == 1) /* S-Frame */
+ else if (IsSFrame(datap, test_bit(FLG_MOD128, &st->l2.flag)))
ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb);
else if (IsUI(datap, test_bit(FLG_MOD128, &st->l2.flag)))
ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
else if (IsFRMR(datap, test_bit(FLG_MOD128, &st->l2.flag)))
ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb);
else {
- ret = 0;
- st->ma.layer(st, MDL_ERROR_IND, (void *) 'L');
- FreeSkb(skb);
+ ret = 1;
+ if ((st->l2.l2m.state == ST_L2_7) ||
+ (st->l2.l2m.state == ST_L2_8))
+ establishlink(&st->l2.l2m);
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'L');
}
if (ret) {
FreeSkb(skb);
}
break;
- case (PH_PULL_CNF):
+ case (PH_PULL | CONFIRM):
FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
break;
- case (PH_PAUSE_IND):
+ case (PH_PAUSE | INDICATION):
test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag);
break;
- case (PH_PAUSE_CNF):
+ case (PH_PAUSE | CONFIRM):
test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag);
break;
+ case (PH_ACTIVATE | CONFIRM):
+ case (PH_ACTIVATE | INDICATION):
+ test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag);
+ if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+ FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+ break;
+ case (PH_DEACTIVATE | INDICATION):
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
+ FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
+ break;
+ default:
+ l2m_debug(&st->l2.l2m, "l2 unknown pr %04x", pr);
+ break;
}
}
isdnl2_l3l2(struct PStack *st, int pr, void *arg)
{
switch (pr) {
- case (DL_DATA):
+ case (DL_DATA | REQUEST):
if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
dev_kfree_skb((struct sk_buff *) arg);
}
break;
- case (DL_UNIT_DATA):
+ case (DL_UNIT_DATA | REQUEST):
if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) {
dev_kfree_skb((struct sk_buff *) arg);
}
break;
- }
-}
-
-static void
-isdnl2_manl2(struct PStack *st, int pr, void *arg)
-{
- switch (pr) {
- case (DL_ESTABLISH):
- FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+ case (DL_ESTABLISH | REQUEST):
+ if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) {
+ if (test_bit(FLG_LAPD, &st->l2.flag) ||
+ test_bit(FLG_ORIG, &st->l2.flag)) {
+ FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH, arg);
+ }
+ } else {
+ if (test_bit(FLG_LAPD, &st->l2.flag) ||
+ test_bit(FLG_ORIG, &st->l2.flag)) {
+ test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
+ }
+ st->l2.l2l1(st, PH_ACTIVATE, NULL);
+ }
break;
- case (DL_RELEASE):
+ case (DL_RELEASE | REQUEST):
+ if (test_bit(FLG_LAPB, &st->l2.flag)) {
+ st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+ }
FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE, arg);
break;
- case (MDL_NOTEIPROC):
- FsmEvent(&st->l2.l2m, EV_L2_MDL_NOTEIPROC, NULL);
- break;
- case (DL_FLUSH):
+ case (DL_FLUSH | REQUEST):
(&st->l2.l2m)->userint |= LC_FLUSH_WAIT;
break;
- case (PH_DEACTIVATE_IND):
- FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
- break;
- case (MDL_ASSIGN_REQ):
+ case (MDL_ASSIGN | REQUEST):
FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
break;
- case (MDL_REMOVE_REQ):
+ case (MDL_REMOVE | REQUEST):
FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
break;
- case (MDL_ERROR_REQ):
+ case (MDL_ERROR | RESPONSE):
FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg);
break;
}
{
FsmDelTimer(&st->l2.t200, 15);
FsmDelTimer(&st->l2.t203, 16);
- discard_i_queue(st);
- discard_ui_queue(st);
+ discard_queue(&st->l2.i_queue);
+ discard_queue(&st->l2.ui_queue);
ReleaseWin(&st->l2);
}
static void
-l2m_debug(struct FsmInst *fi, char *s)
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
{
+ va_list args;
struct PStack *st = fi->userdata;
- char tm[32], str[256];
- jiftime(tm, jiffies);
- sprintf(str, "%s %s %s\n", tm, st->l2.debug_id, s);
- HiSax_putstatus(st->l1.hardware, str);
+ va_start(args, fmt);
+ VHiSax_putstatus(st->l1.hardware, st->l2.debug_id, fmt, args);
+ va_end(args);
}
void
{
st->l1.l1l2 = isdnl2_l1l2;
st->l3.l3l2 = isdnl2_l3l2;
- st->ma.manl2 = isdnl2_manl2;
skb_queue_head_init(&st->l2.i_queue);
skb_queue_head_init(&st->l2.ui_queue);
st->l2.debug = 0;
st->l2.l2m.fsm = &l2fsm;
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2m.state = ST_L2_4;
+ else
st->l2.l2m.state = ST_L2_1;
st->l2.l2m.debug = 0;
st->l2.l2m.userdata = st;
FsmInitTimer(&st->l2.l2m, &st->l2.t203);
}
+static void
+transl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+ switch (pr) {
+ case (DL_DATA | REQUEST):
+ case (DL_UNIT_DATA | REQUEST):
+ st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+ break;
+ case (DL_ESTABLISH | REQUEST):
+ st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+ break;
+ case (DL_RELEASE | REQUEST):
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ break;
+ }
+}
+
void
setstack_transl2(struct PStack *st)
{
+ st->l3.l3l2 = transl2_l3l2;
}
void
-/* $Id: isdnl3.c,v 2.5 1998/02/12 23:07:52 keil Exp $
+/* $Id: isdnl3.c,v 2.8 1998/11/15 23:55:04 keil Exp $
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
* based on the teles driver from Jan den Ouden
*
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
+ *
* Thanks to Jan den Ouden
* Fritz Elfert
*
* $Log: isdnl3.c,v $
+ * Revision 2.8 1998/11/15 23:55:04 keil
+ * changes from 2.0
+ *
+ * Revision 2.7 1998/05/25 14:10:15 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.6 1998/05/25 12:58:11 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
* Revision 2.5 1998/02/12 23:07:52 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#include "isdnl3.h"
#include <linux/config.h>
-const char *l3_revision = "$Revision: 2.5 $";
+const char *l3_revision = "$Revision: 2.8 $";
+
+static
+struct Fsm l3fsm =
+{NULL, 0, 0, NULL, NULL};
+
+enum {
+ ST_L3_LC_REL,
+ ST_L3_LC_ESTAB_WAIT,
+ ST_L3_LC_REL_WAIT,
+ ST_L3_LC_ESTAB,
+};
+
+#define L3_STATE_COUNT (ST_L3_LC_ESTAB+1)
+
+static char *strL3State[] =
+{
+ "ST_L3_LC_REL",
+ "ST_L3_LC_ESTAB_WAIT",
+ "ST_L3_LC_REL_WAIT",
+ "ST_L3_LC_ESTAB",
+};
+
+enum {
+ EV_ESTABLISH_REQ,
+ EV_ESTABLISH_IND,
+ EV_ESTABLISH_CNF,
+ EV_RELEASE_REQ,
+ EV_RELEASE_CNF,
+ EV_RELEASE_IND,
+};
+
+#define L3_EVENT_COUNT (EV_RELEASE_IND+1)
+
+static char *strL3Event[] =
+{
+ "EV_ESTABLISH_REQ",
+ "EV_ESTABLISH_IND",
+ "EV_ESTABLISH_CNF",
+ "EV_RELEASE_REQ",
+ "EV_RELEASE_CNF",
+ "EV_RELEASE_IND",
+};
+
+static void
+l3m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ struct PStack *st = fi->userdata;
+
+ va_start(args, fmt);
+ VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args);
+ va_end(args);
+}
u_char *
findie(u_char * p, int size, u_char ie, int wanted_set)
return (OrigCallRef);
}
-void
-l3_debug(struct PStack *st, char *s)
-{
- char str[256], tm[32];
-
- jiftime(tm, jiffies);
- sprintf(str, "%s l3 %s\n", tm, s);
- HiSax_putstatus(st->l1.hardware, str);
-}
-
void
newl3state(struct l3_process *pc, int state)
{
- char tmp[80];
-
- if (pc->debug & L3_DEB_STATE) {
- sprintf(tmp, "newstate cr %d %d --> %d", pc->callref,
+ if (pc->debug & L3_DEB_STATE)
+ l3_debug(pc->st, "newstate cr %d %d --> %d", pc->callref,
pc->state, state);
- l3_debug(pc->st, tmp);
- }
pc->state = state;
}
{
struct sk_buff *skb = arg;
- HiSax_putstatus(st->l1.hardware, "L3 no D protocol\n");
+ HiSax_putstatus(st->l1.hardware, "L3", "no D protocol");
if (skb) {
dev_kfree_skb(skb);
}
pp = np;
np = np->next;
}
- printk(KERN_ERR "HiSax internal L3 error CR not in list\n");
+ printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref);
+ l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref);
};
void
-setstack_isdnl3(struct PStack *st, struct Channel *chanp)
+setstack_l3dc(struct PStack *st, struct Channel *chanp)
{
char tmp[64];
st->l3.proc = NULL;
st->l3.global = NULL;
+ skb_queue_head_init(&st->l3.squeue);
+ st->l3.l3m.fsm = &l3fsm;
+ st->l3.l3m.state = ST_L3_LC_REL;
+ st->l3.l3m.debug = 1;
+ st->l3.l3m.userdata = st;
+ st->l3.l3m.userint = 0;
+ st->l3.l3m.printdebug = l3m_debug;
+ strcpy(st->l3.debug_id, "L3DC ");
#ifdef CONFIG_HISAX_EURO
if (st->protocol == ISDN_PTYPE_EURO) {
}
}
+void
+isdnl3_trans(struct PStack *st, int pr, void *arg) {
+ st->l3.l3l2(st, pr, arg);
+}
+
void
releasestack_isdnl3(struct PStack *st)
{
kfree(st->l3.global);
st->l3.global = NULL;
}
+ discard_queue(&st->l3.squeue);
+}
+
+void
+setstack_l3bc(struct PStack *st, struct Channel *chanp)
+{
+
+ st->l3.proc = NULL;
+ st->l3.global = NULL;
+ skb_queue_head_init(&st->l3.squeue);
+ st->l3.l3m.fsm = &l3fsm;
+ st->l3.l3m.state = ST_L3_LC_REL;
+ st->l3.l3m.debug = 1;
+ st->l3.l3m.userdata = st;
+ st->l3.l3m.userint = 0;
+ st->l3.l3m.printdebug = l3m_debug;
+ strcpy(st->l3.debug_id, "L3BC ");
+ st->lli.l4l3 = isdnl3_trans;
+}
+
+static void
+lc_activate(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
+ st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lc_connect(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ FsmChangeState(fi, ST_L3_LC_ESTAB);
+ while ((skb = skb_dequeue(&st->l3.squeue))) {
+ st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+ }
+ st->l3.l3l4(st, DL_ESTABLISH | INDICATION, NULL);
+}
+
+static void
+lc_release_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (fi->state == ST_L3_LC_ESTAB_WAIT)
+ FsmChangeState(fi, ST_L3_LC_REL);
+ else
+ FsmChangeState(fi, ST_L3_LC_REL_WAIT);
+ st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+}
+
+static void
+lc_release_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L3_LC_REL);
+ discard_queue(&st->l3.squeue);
+ st->l3.l3l4(st, DL_RELEASE | INDICATION, NULL);
+}
+
+/* *INDENT-OFF* */
+static struct FsmNode L3FnList[] HISAX_INITDATA =
+{
+ {ST_L3_LC_REL, EV_ESTABLISH_REQ, lc_activate},
+ {ST_L3_LC_REL, EV_ESTABLISH_IND, lc_connect},
+ {ST_L3_LC_REL, EV_ESTABLISH_CNF, lc_connect},
+ {ST_L3_LC_ESTAB_WAIT, EV_ESTABLISH_CNF, lc_connect},
+ {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_release_req},
+ {ST_L3_LC_ESTAB_WAIT, EV_RELEASE_IND, lc_release_ind},
+ {ST_L3_LC_ESTAB, EV_RELEASE_IND, lc_release_ind},
+ {ST_L3_LC_ESTAB, EV_RELEASE_REQ, lc_release_req},
+ {ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_ind},
+ {ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate},
+};
+/* *INDENT-ON* */
+
+#define L3_FN_COUNT (sizeof(L3FnList)/sizeof(struct FsmNode))
+
+void
+l3_msg(struct PStack *st, int pr, void *arg)
+{
+
+ switch (pr) {
+ case (DL_DATA | REQUEST):
+ if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
+ st->l3.l3l2(st, pr, arg);
+ } else {
+ struct sk_buff *skb = arg;
+
+ skb_queue_head(&st->l3.squeue, skb);
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+ }
+ break;
+ case (DL_ESTABLISH | REQUEST):
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+ break;
+ case (DL_ESTABLISH | CONFIRM):
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL);
+ break;
+ case (DL_ESTABLISH | INDICATION):
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL);
+ break;
+ case (DL_RELEASE | INDICATION):
+ FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL);
+ break;
+ case (DL_RELEASE | CONFIRM):
+ FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL);
+ break;
+ case (DL_RELEASE | REQUEST):
+ FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+ break;
+ }
+}
+
+HISAX_INITFUNC(void
+Isdnl3New(void))
+{
+ l3fsm.state_count = L3_STATE_COUNT;
+ l3fsm.event_count = L3_EVENT_COUNT;
+ l3fsm.strEvent = strL3Event;
+ l3fsm.strState = strL3State;
+ FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+}
+
+void
+Isdnl3Free(void)
+{
+ FsmFree(&l3fsm);
}
-/* $Id: isdnl3.h,v 2.0 1997/07/27 21:15:42 keil Exp $
+/* $Id: isdnl3.h,v 2.3 1998/11/15 23:55:06 keil Exp $
* $Log: isdnl3.h,v $
+ * Revision 2.3 1998/11/15 23:55:06 keil
+ * changes from 2.0
+ *
+ * Revision 2.2 1998/05/25 14:10:17 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.1 1998/05/25 12:58:13 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
* Revision 2.0 1997/07/27 21:15:42 keil
* New Callref based layer3
*
struct stateentry {
int state;
- u_char primitive;
+ int primitive;
void (*rout) (struct l3_process *, u_char, void *);
};
-extern void l3_debug(struct PStack *st, char *s);
+#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args)
+
extern void newl3state(struct l3_process *pc, int state);
extern void L3InitTimer(struct l3_process *pc, struct L3Timer *t);
extern void L3DelTimer(struct L3Timer *t);
extern struct l3_process *new_l3_process(struct PStack *st, int cr);
extern void release_l3_process(struct l3_process *p);
extern struct l3_process *getl3proc(struct PStack *st, int cr);
+extern void l3_msg(struct PStack *st, int pr, void *arg);
-/* $Id: ix1_micro.c,v 2.6 1998/02/11 17:28:09 keil Exp $
+/* $Id: ix1_micro.c,v 2.7 1998/04/15 16:44:31 keil Exp $
* ix1_micro.c low level stuff for ITK ix1-micro Rev.2 isdn cards
* derived from the original file teles3.c from Karsten Keil
* Beat Doebeli
*
* $Log: ix1_micro.c,v $
+ * Revision 2.7 1998/04/15 16:44:31 keil
+ * new init code
+ *
* Revision 2.6 1998/02/11 17:28:09 keil
* Niccy PnP/PCI support
*
#include "isdnl1.h"
extern const char *CardType[];
-const char *ix1_revision = "$Revision: 2.6 $";
+const char *ix1_revision = "$Revision: 2.7 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
return(request_irq(cs->irq, &ix1micro_interrupt,
I4L_IRQ_FLAG, "HiSax", cs));
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ inithscxisac(cs, 3);
return(0);
case CARD_TEST:
return(0);
-/* $Id: l3_1tr6.c,v 2.4 1998/02/12 23:07:57 keil Exp $
+/* $Id: l3_1tr6.c,v 2.8 1998/11/15 23:55:08 keil Exp $
* German 1TR6 D-channel protocol
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
*
*
* $Log: l3_1tr6.c,v $
+ * Revision 2.8 1998/11/15 23:55:08 keil
+ * changes from 2.0
+ *
+ * Revision 2.7 1998/08/13 23:36:45 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.6 1998/05/25 14:10:18 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.5 1998/05/25 12:58:14 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
* Revision 2.4 1998/02/12 23:07:57 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#include <linux/ctype.h>
extern char *HiSax_getrev(const char *revision);
-const char *l3_1tr6_revision = "$Revision: 2.4 $";
+const char *l3_1tr6_revision = "$Revision: 2.8 $";
#define MsgHead(ptr, cref, mty, dis) \
*ptr++ = dis; \
return;
p = skb_put(skb, 4);
MsgHead(p, pc->callref, mt, pd);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
}
-static int
-l31tr6_check_messagetype_validity(int mt, int pd) {
-/* verify if a message type exists */
-
- if (pd == PROTO_DIS_N0)
- switch(mt) {
- case MT_N0_REG_IND:
- case MT_N0_CANC_IND:
- case MT_N0_FAC_STA:
- case MT_N0_STA_ACK:
- case MT_N0_STA_REJ:
- case MT_N0_FAC_INF:
- case MT_N0_INF_ACK:
- case MT_N0_INF_REJ:
- case MT_N0_CLOSE:
- case MT_N0_CLO_ACK:
- return(1);
- default:
- return(0);
- }
- else if (pd == PROTO_DIS_N1)
- switch(mt) {
- case MT_N1_ESC:
- case MT_N1_ALERT:
- case MT_N1_CALL_SENT:
- case MT_N1_CONN:
- case MT_N1_CONN_ACK:
- case MT_N1_SETUP:
- case MT_N1_SETUP_ACK:
- case MT_N1_RES:
- case MT_N1_RES_ACK:
- case MT_N1_RES_REJ:
- case MT_N1_SUSP:
- case MT_N1_SUSP_ACK:
- case MT_N1_SUSP_REJ:
- case MT_N1_USER_INFO:
- case MT_N1_DET:
- case MT_N1_DISC:
- case MT_N1_REL:
- case MT_N1_REL_ACK:
- case MT_N1_CANC_ACK:
- case MT_N1_CANC_REJ:
- case MT_N1_CON_CON:
- case MT_N1_FAC:
- case MT_N1_FAC_ACK:
- case MT_N1_FAC_CAN:
- case MT_N1_FAC_REG:
- case MT_N1_FAC_REJ:
- case MT_N1_INFO:
- case MT_N1_REG_ACK:
- case MT_N1_REG_REJ:
- case MT_N1_STAT:
- return (1);
- default:
- return(0);
- }
- return(0);
+static void
+l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ StopAllL3Timer(pc);
+ newl3state(pc, 19);
+ l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ dev_kfree_skb(skb);
+ l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb)
+{
+ dev_kfree_skb(skb);
+ if (pc->st->l3.debug & L3_DEB_WARN)
+ l3_debug(pc->st, msg);
+ l3_1tr6_release_req(pc, 0, NULL);
}
static void
L3DelTimer(&pc->timer);
L3AddTimer(&pc->timer, T303, CC_T303);
newl3state(pc, 1);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
}
static void
/* Channel Identification */
p = skb->data;
if ((p = findie(p, skb->len, WE0_chanID, 0))) {
- pc->para.bchannel = p[2] & 0x3;
- bcfound++;
- } else if (pc->st->l3.debug & L3_DEB_WARN)
- l3_debug(pc->st, "setup without bchannel");
+ if (p[1] != 1) {
+ l3_1tr6_error(pc, "setup wrong chanID len", skb);
+ return;
+ }
+ if ((p[2] & 0xf4) != 0x80) {
+ l3_1tr6_error(pc, "setup wrong WE0_chanID", skb);
+ return;
+ }
+ if ((pc->para.bchannel = p[2] & 0x3))
+ bcfound++;
+ } else {
+ l3_1tr6_error(pc, "missing setup chanID", skb);
+ return;
+ }
p = skb->data;
if ((p = findie(p, skb->len, WE6_serviceInd, 6))) {
pc->para.setup.si1 = p[2];
pc->para.setup.si2 = p[3];
- } else if (pc->st->l3.debug & L3_DEB_WARN)
- l3_debug(pc->st, "setup without service indicator");
+ } else {
+ l3_1tr6_error(pc, "missing setup SI", skb);
+ return;
+ }
p = skb->data;
if ((p = findie(p, skb->len, WE0_destAddr, 0)))
l3_debug(pc->st, tmp);
}
newl3state(pc, 6);
- pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
} else
release_l3_process(pc);
}
p = skb->data;
newl3state(pc, 2);
if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+ if (p[1] != 1) {
+ l3_1tr6_error(pc, "setup_ack wrong chanID len", skb);
+ return;
+ }
+ if ((p[2] & 0xf4) != 0x80) {
+ l3_1tr6_error(pc, "setup_ack wrong WE0_chanID", skb);
+ return;
+ }
pc->para.bchannel = p[2] & 0x3;
- } else if (pc->st->l3.debug & L3_DEB_WARN)
- l3_debug(pc->st, "setup answer without bchannel");
+ } else {
+ l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb);
+ return;
+ }
dev_kfree_skb(skb);
L3AddTimer(&pc->timer, T304, CC_T304);
- pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL);
+ pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
}
static void
L3DelTimer(&pc->timer);
p = skb->data;
if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+ if (p[1] != 1) {
+ l3_1tr6_error(pc, "call sent wrong chanID len", skb);
+ return;
+ }
+ if ((p[2] & 0xf4) != 0x80) {
+ l3_1tr6_error(pc, "call sent wrong WE0_chanID", skb);
+ return;
+ }
+ if ((pc->state == 2) && (pc->para.bchannel != (p[2] & 0x3))) {
+ l3_1tr6_error(pc, "call sent wrong chanID value", skb);
+ return;
+ }
pc->para.bchannel = p[2] & 0x3;
- } else if (pc->st->l3.debug & L3_DEB_WARN)
- l3_debug(pc->st, "setup answer without bchannel");
+ } else {
+ l3_1tr6_error(pc, "missing call sent WE0_chanID", skb);
+ return;
+ }
dev_kfree_skb(skb);
L3AddTimer(&pc->timer, T310, CC_T310);
newl3state(pc, 3);
- pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
}
static void
dev_kfree_skb(skb);
L3DelTimer(&pc->timer); /* T304 */
newl3state(pc, 4);
- pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
}
static void
}
if (tmpcharge > pc->para.chargeinfo) {
pc->para.chargeinfo = tmpcharge;
- pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
+ pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
}
if (pc->st->l3.debug & L3_DEB_CHARGE) {
sprintf(tmp, "charging info %d", pc->para.chargeinfo);
struct sk_buff *skb = arg;
L3DelTimer(&pc->timer); /* T310 */
+ if (!findie(skb->data, skb->len, WE6_date, 6)) {
+ l3_1tr6_error(pc, "missing connect date", skb);
+ return;
+ }
newl3state(pc, 10);
dev_kfree_skb(skb);
pc->para.chargeinfo = 0;
- pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
}
static void
pc->para.cause = 0;
pc->para.loc = 0;
}
- } else
+ } else {
pc->para.cause = -1;
+ l3_1tr6_error(pc, "missing REL cause", skb);
+ return;
+ }
dev_kfree_skb(skb);
StopAllL3Timer(pc);
newl3state(pc, 0);
l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
- pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
release_l3_process(pc);
}
StopAllL3Timer(pc);
newl3state(pc, 0);
pc->para.cause = -1;
- pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
release_l3_process(pc);
}
}
if (tmpcharge > pc->para.chargeinfo) {
pc->para.chargeinfo = tmpcharge;
- pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
+ pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
}
if (pc->st->l3.debug & L3_DEB_CHARGE) {
sprintf(tmp, "charging info %d", pc->para.chargeinfo);
l3_debug(pc->st, "cause not found");
pc->para.cause = -1;
}
+ if (!findie(skb->data, skb->len, WE6_date, 6)) {
+ l3_1tr6_error(pc, "missing connack date", skb);
+ return;
+ }
dev_kfree_skb(skb);
newl3state(pc, 12);
- pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
}
{
struct sk_buff *skb = arg;
+ if (!findie(skb->data, skb->len, WE6_date, 6)) {
+ l3_1tr6_error(pc, "missing connack date", skb);
+ return;
+ }
dev_kfree_skb(skb);
newl3state(pc, 10);
pc->para.chargeinfo = 0;
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
}
static void
if (!(skb = l3_alloc_skb(l)))
return;
memcpy(skb_put(skb, l), tmp, l);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
L3DelTimer(&pc->timer);
L3AddTimer(&pc->timer, T313, CC_T313);
}
if (!(skb = l3_alloc_skb(l)))
return;
memcpy(skb_put(skb, l), tmp, l);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
L3AddTimer(&pc->timer, T305, CC_T305);
}
-static void
-l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
-{
- StopAllL3Timer(pc);
- newl3state(pc, 19);
- l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
- L3AddTimer(&pc->timer, T308, CC_T308_1);
-}
-
static void
l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg)
{
l3_1tr6_setup_req(pc, pr, arg);
} else {
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL);
- release_l3_process(pc);
+ pc->para.cause = 0;
+ l3_1tr6_disconnect_req(pc, 0, NULL);
}
}
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3_1tr6_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
}
static void
if (!(skb = l3_alloc_skb(l)))
return;
memcpy(skb_put(skb, l), tmp, l);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
L3AddTimer(&pc->timer, T308, CC_T308_1);
}
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3_1tr6_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
}
static void
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3_1tr6_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
}
static void
l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
release_l3_process(pc);
}
/* *INDENT-OFF* */
static struct stateentry downstl[] =
{
{SBIT(0),
- CC_SETUP_REQ, l3_1tr6_setup_req},
+ CC_SETUP | REQUEST, l3_1tr6_setup_req},
{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) |
SBIT(10),
- CC_DISCONNECT_REQ, l3_1tr6_disconnect_req},
+ CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req},
{SBIT(12),
- CC_RELEASE_REQ, l3_1tr6_release_req},
+ CC_RELEASE | REQUEST, l3_1tr6_release_req},
{ALL_STATES,
- CC_DLRL, l3_1tr6_reset},
+ CC_DLRL | REQUEST, l3_1tr6_reset},
{SBIT(6),
- CC_IGNORE, l3_1tr6_reset},
+ CC_IGNORE | REQUEST, l3_1tr6_reset},
{SBIT(6),
- CC_REJECT_REQ, l3_1tr6_disconnect_req},
+ CC_REJECT | REQUEST, l3_1tr6_disconnect_req},
{SBIT(6),
- CC_ALERTING_REQ, l3_1tr6_alert_req},
+ CC_ALERTING | REQUEST, l3_1tr6_alert_req},
{SBIT(6) | SBIT(7),
- CC_SETUP_RSP, l3_1tr6_setup_rsp},
+ CC_SETUP | RESPONSE, l3_1tr6_setup_rsp},
{SBIT(1),
CC_T303, l3_1tr6_t303},
{SBIT(2),
CC_T308_2, l3_1tr6_t308_2},
};
-static int downstl_len = sizeof(downstl) /
-sizeof(struct stateentry);
+#define DOWNSTL_LEN \
+ (sizeof(downstl) / sizeof(struct stateentry))
static struct stateentry datastln1[] =
{
+ {SBIT(0),
+ MT_N1_INVALID, l3_1tr6_invalid},
{SBIT(0),
MT_N1_SETUP, l3_1tr6_setup},
{SBIT(1),
{SBIT(10),
MT_N1_INFO, l3_1tr6_info},
{SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
- SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
+ SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
MT_N1_REL, l3_1tr6_rel},
+ {SBIT(19),
+ MT_N1_REL, l3_1tr6_rel_ack},
+ {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+ SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+ MT_N1_REL_ACK, l3_1tr6_invalid},
{SBIT(19),
MT_N1_REL_ACK, l3_1tr6_rel_ack}
};
/* *INDENT-ON* */
-
-
-
-static int datastln1_len = sizeof(datastln1) /
-sizeof(struct stateentry);
+#define DATASTLN1_LEN \
+ (sizeof(datastln1) / sizeof(struct stateentry))
static void
up1tr6(struct PStack *st, int pr, void *arg)
struct sk_buff *skb = arg;
char tmp[80];
+ switch (pr) {
+ case (DL_DATA | INDICATION):
+ case (DL_UNIT_DATA | INDICATION):
+ break;
+ case (DL_ESTABLISH | CONFIRM):
+ case (DL_ESTABLISH | INDICATION):
+ case (DL_RELEASE | INDICATION):
+ case (DL_RELEASE | CONFIRM):
+ l3_msg(st, pr, arg);
+ return;
+ break;
+ }
if (skb->len < 4) {
if (st->l3.debug & L3_DEB_PROTERR) {
- sprintf(tmp, "up1tr6 len only %d", skb->len);
+ sprintf(tmp, "up1tr6 len only %ld", skb->len);
l3_debug(st, tmp);
}
dev_kfree_skb(skb);
}
if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
if (st->l3.debug & L3_DEB_PROTERR) {
- sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d",
- (pr == DL_DATA) ? " " : "(broadcast) ",
+ sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %ld",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
skb->data[0], skb->len);
l3_debug(st, tmp);
}
dev_kfree_skb(skb);
if (st->l3.debug & L3_DEB_STATE) {
sprintf(tmp, "up1tr6%s N0 mt %x unhandled",
- (pr == DL_DATA) ? " " : "(broadcast) ", mt);
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt);
l3_debug(st, tmp);
}
} else if (skb->data[0] == PROTO_DIS_N1) {
if (!(proc = getl3proc(st, cr))) {
- if ((mt == MT_N1_SETUP) && (cr < 128)) {
+ if (mt == MT_N1_SETUP) {
+ if (cr < 128) {
+ if (!(proc = new_l3_process(st, cr))) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ sprintf(tmp, "up1tr6 no roc mem");
+ l3_debug(st, tmp);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ } else {
+ dev_kfree_skb(skb);
+ return;
+ }
+ } else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) ||
+ (mt == MT_N1_CANC_ACK) || (mt == MT_N1_CANC_REJ) ||
+ (mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) ||
+ (mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) ||
+ (mt == MT_N1_INFO)) {
+ dev_kfree_skb(skb);
+ return;
+ } else {
if (!(proc = new_l3_process(st, cr))) {
if (st->l3.debug & L3_DEB_PROTERR) {
sprintf(tmp, "up1tr6 no roc mem");
dev_kfree_skb(skb);
return;
}
- } else {
- dev_kfree_skb(skb);
- return;
+ mt = MT_N1_INVALID;
}
}
- for (i = 0; i < datastln1_len; i++)
+ for (i = 0; i < DATASTLN1_LEN; i++)
if ((mt == datastln1[i].primitive) &&
((1 << proc->state) & datastln1[i].state))
break;
- if (i == datastln1_len) {
+ if (i == DATASTLN1_LEN) {
dev_kfree_skb(skb);
if (st->l3.debug & L3_DEB_STATE) {
sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
- (pr == DL_DATA) ? " " : "(broadcast) ",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
proc->state, mt);
l3_debug(st, tmp);
}
} else {
if (st->l3.debug & L3_DEB_STATE) {
sprintf(tmp, "up1tr6%sstate %d mt %x",
- (pr == DL_DATA) ? " " : "(broadcast) ",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
proc->state, mt);
l3_debug(st, tmp);
}
struct Channel *chan;
char tmp[80];
- if (CC_SETUP_REQ == pr) {
+ if (((DL_ESTABLISH | REQUEST)== pr) || ((DL_RELEASE | REQUEST)== pr)) {
+ l3_msg(st, pr, NULL);
+ return;
+ } else if ((CC_SETUP | REQUEST) == pr) {
chan = arg;
cr = newcallref();
cr |= 0x80;
proc = arg;
}
- for (i = 0; i < downstl_len; i++)
+ for (i = 0; i < DOWNSTL_LEN; i++)
if ((pr == downstl[i].primitive) &&
((1 << proc->state) & downstl[i].state))
break;
- if (i == downstl_len) {
+ if (i == DOWNSTL_LEN) {
if (st->l3.debug & L3_DEB_STATE) {
sprintf(tmp, "down1tr6 state %d prim %d unhandled",
proc->state, pr);
-/* $Id: l3_1tr6.h,v 2.0 1997/07/27 21:15:47 keil Exp $
+/* $Id: l3_1tr6.h,v 2.1 1998/08/13 23:36:48 keil Exp $
*
* German 1TR6 D-channel protocol defines
*
* $Log: l3_1tr6.h,v $
+ * Revision 2.1 1998/08/13 23:36:48 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
* Revision 2.0 1997/07/27 21:15:47 keil
* New Callref based layer3
*
#define MT_N1_REG_ACK 0x6C
#define MT_N1_REG_REJ 0x6F
#define MT_N1_STAT 0x63
+#define MT_N1_INVALID 0
/*
* W Elemente
-/* $Id: l3dss1.c,v 2.7 1998/02/12 23:08:01 keil Exp $
+/* $Id: l3dss1.c,v 2.12 1998/11/15 23:55:10 keil Exp $
* EURO/DSS1 D-channel protocol
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
* based on the teles driver from Jan den Ouden
*
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
+ *
* Thanks to Jan den Ouden
* Fritz Elfert
*
* $Log: l3dss1.c,v $
+ * Revision 2.12 1998/11/15 23:55:10 keil
+ * changes from 2.0
+ *
+ * Revision 2.11 1998/08/13 23:36:51 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.10 1998/05/25 14:10:20 keil
+ * HiSax 3.0
+ * X.75 and leased are working again.
+ *
+ * Revision 2.9 1998/05/25 12:58:17 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.8 1998/03/19 13:18:47 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
* Revision 2.7 1998/02/12 23:08:01 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#include <linux/ctype.h>
extern char *HiSax_getrev(const char *revision);
-const char *dss1_revision = "$Revision: 2.7 $";
-
-#define EXT_BEARER_CAPS 1
+const char *dss1_revision = "$Revision: 2.12 $";
#define MsgHead(ptr, cref, mty) \
*ptr++ = 0x8; \
*ptr++ = mty
-#ifdef HISAX_DE_AOC
+#if HISAX_DE_AOC
static void
-l3dss1_parse_facility(struct l3_process *pc, u_char *p)
+l3dss1_parse_facility(struct l3_process *pc, u_char * p)
{
int qd_len = 0;
- char tmp[32];
p++;
qd_len = *p++;
l3_debug(pc->st, "qd_len == 0");
return;
}
- if((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */
+ if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */
l3_debug(pc->st, "supplementary service != 0x11");
return;
}
- while(qd_len > 0 && !(*p & 0x80)) { /* extension ? */
- p++; qd_len--;
- }
- if(qd_len < 2) {
+ while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */
+ p++;
+ qd_len--;
+ }
+ if (qd_len < 2) {
l3_debug(pc->st, "qd_len < 2");
return;
}
- p++; qd_len--;
- if((*p & 0xE0) != 0xA0) { /* class and form */
+ p++;
+ qd_len--;
+ if ((*p & 0xE0) != 0xA0) { /* class and form */
l3_debug(pc->st, "class and form != 0xA0");
return;
}
- switch(*p & 0x1F) { /* component tag */
- case 1: /* invoke */
- {
- unsigned char nlen, ilen;
- int ident;
-
- p++; qd_len--;
- if(qd_len < 1) {
- l3_debug(pc->st, "qd_len < 1");
- break;
- }
- if(*p & 0x80) { /* length format */
- l3_debug(pc->st, "*p & 0x80 length format");
- break;
- }
- nlen = *p++; qd_len--;
- if(qd_len < nlen) {
- l3_debug(pc->st, "qd_len < nlen");
- return;
- }
- qd_len -= nlen;
-
- if(nlen < 2) {
- l3_debug(pc->st, "nlen < 2");
- return;
- }
- if(*p != 0x02) { /* invoke identifier tag */
- l3_debug(pc->st, "invoke identifier tag !=0x02");
- return;
- }
- p++; nlen--;
- if(*p & 0x80) { /* length format */
- l3_debug(pc->st, "*p & 0x80 length format 2");
- break;
- }
- ilen = *p++; nlen--;
- if(ilen > nlen || ilen == 0) {
- l3_debug(pc->st, "ilen > nlen || ilen == 0");
- return;
- }
- nlen -= ilen;
- ident = 0;
- while(ilen > 0) {
- ident = (ident << 8) | (*p++ & 0xFF); /* invoke identifier */
- ilen--;
- }
-
- if(nlen < 2) {
- l3_debug(pc->st, "nlen < 2 22");
- return;
- }
- if(*p != 0x02) { /* operation value */
- l3_debug(pc->st, "operation value !=0x02");
- return;
- }
- p++; nlen--;
- ilen = *p++; nlen--;
- if(ilen > nlen || ilen == 0) {
- l3_debug(pc->st, "ilen > nlen || ilen == 0 22");
- return;
- }
- nlen -= ilen;
- ident = 0;
- while(ilen > 0) {
- ident = (ident << 8) | (*p++ & 0xFF);
- ilen--;
- }
-
- #define FOO1(s,a,b) \
+ switch (*p & 0x1F) { /* component tag */
+ case 1: /* invoke */
+ {
+ unsigned char nlen = 0, ilen;
+ int ident;
+
+ p++;
+ qd_len--;
+ if (qd_len < 1) {
+ l3_debug(pc->st, "qd_len < 1");
+ break;
+ }
+ if (*p & 0x80) { /* length format */
+ l3_debug(pc->st, "*p & 0x80 length format");
+ break;
+ }
+ nlen = *p++;
+ qd_len--;
+ if (qd_len < nlen) {
+ l3_debug(pc->st, "qd_len < nlen");
+ return;
+ }
+ qd_len -= nlen;
+
+ if (nlen < 2) {
+ l3_debug(pc->st, "nlen < 2");
+ return;
+ }
+ if (*p != 0x02) { /* invoke identifier tag */
+ l3_debug(pc->st, "invoke identifier tag !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ if (*p & 0x80) { /* length format */
+ l3_debug(pc->st, "*p & 0x80 length format 2");
+ break;
+ }
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0) {
+ l3_debug(pc->st, "ilen > nlen || ilen == 0");
+ return;
+ }
+ nlen -= ilen;
+ ident = 0;
+ while (ilen > 0) {
+ ident = (ident << 8) | (*p++ & 0xFF); /* invoke identifier */
+ ilen--;
+ }
+
+ if (nlen < 2) {
+ l3_debug(pc->st, "nlen < 2 22");
+ return;
+ }
+ if (*p != 0x02) { /* operation value */
+ l3_debug(pc->st, "operation value !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0) {
+ l3_debug(pc->st, "ilen > nlen || ilen == 0 22");
+ return;
+ }
+ nlen -= ilen;
+ ident = 0;
+ while (ilen > 0) {
+ ident = (ident << 8) | (*p++ & 0xFF);
+ ilen--;
+ }
+
+#define FOO1(s,a,b) \
while(nlen > 1) { \
int ilen = p[1]; \
if(nlen < ilen+2) { \
p += ilen+2; \
} \
}
-
- switch(ident) {
- default:
- break;
- case 0x22: /* during */
- FOO1("1A",0x30,FOO1("1C",0xA1,FOO1("1D",0x30,FOO1("1E",0x02,({
- ident = 0;
- while(ilen > 0) {
- ident = (ident<<8) | *p++;
- ilen--;
- }
- if (ident > pc->para.chargeinfo) {
- pc->para.chargeinfo = ident;
- pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
- }
- if (pc->st->l3.debug & L3_DEB_CHARGE) {
- if (*(p+2) == 0) {
- sprintf(tmp, "charging info during %d", pc->para.chargeinfo);
- l3_debug(pc->st, tmp);
- }
- else {
- sprintf(tmp, "charging info final %d", pc->para.chargeinfo);
- l3_debug(pc->st, tmp);
- }
- }
- })))))
- break;
- case 0x24: /* final */
- FOO1("2A",0x30,FOO1("2B",0x30,FOO1("2C",0xA1,FOO1("2D",0x30,FOO1("2E",0x02,({
- ident = 0;
- while(ilen > 0) {
- ident = (ident<<8) | *p++;
- ilen--;
- }
- if (ident > pc->para.chargeinfo) {
- pc->para.chargeinfo = ident;
- pc->st->l3.l3l4(pc, CC_INFO_CHARGE, NULL);
- }
- if (pc->st->l3.debug & L3_DEB_CHARGE) {
- sprintf(tmp, "charging info final %d", pc->para.chargeinfo);
- l3_debug(pc->st, tmp);
- }
- }))))))
- break;
- }
- #undef FOO1
-
- }
- break;
- case 2: /* return result */
- l3_debug(pc->st, "return result break");
- break;
- case 3: /* return error */
- l3_debug(pc->st, "return error break");
- break;
- default:
- l3_debug(pc->st, "default break");
- break;
+
+ switch (ident) {
+ default:
+ break;
+ case 0x22: /* during */
+ FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( {
+ ident = 0;
+ nlen = (nlen)?nlen:0; /* Make gcc happy */
+ while (ilen > 0) {
+ ident = (ident << 8) | *p++;
+ ilen--;
+ }
+ if (ident > pc->para.chargeinfo) {
+ pc->para.chargeinfo = ident;
+ pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+ }
+ if (pc->st->l3.debug & L3_DEB_CHARGE) {
+ if (*(p + 2) == 0) {
+ l3_debug(pc->st, "charging info during %d", pc->para.chargeinfo);
+ }
+ else {
+ l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo);
+ }
+ }
+ }
+ )))))
+ break;
+ case 0x24: /* final */
+ FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( {
+ ident = 0;
+ nlen = (nlen)?nlen:0; /* Make gcc happy */
+ while (ilen > 0) {
+ ident = (ident << 8) | *p++;
+ ilen--;
+ }
+ if (ident > pc->para.chargeinfo) {
+ pc->para.chargeinfo = ident;
+ pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+ }
+ if (pc->st->l3.debug & L3_DEB_CHARGE) {
+ l3_debug(pc->st, "charging info final %d", pc->para.chargeinfo);
+ }
+ }
+ ))))))
+ break;
+ }
+#undef FOO1
+
+ }
+ break;
+ case 2: /* return result */
+ l3_debug(pc->st, "return result break");
+ break;
+ case 3: /* return error */
+ l3_debug(pc->st, "return error break");
+ break;
+ default:
+ l3_debug(pc->st, "default break");
+ break;
}
}
-#endif
+#endif
-static int
-l3dss1_check_messagetype_validity(int mt) {
+static int
+l3dss1_check_messagetype_validity(int mt)
+{
/* verify if a message type exists */
- switch(mt) {
+ switch (mt) {
case MT_ALERTING:
case MT_CALL_PROCEEDING:
case MT_CONNECT:
return;
p = skb_put(skb, 4);
MsgHead(p, pc->callref, mt);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
}
static void
StopAllL3Timer(pc);
pc->para.cause = cause;
newl3state(pc, 0);
- pc->st->l3.l3l4(pc, CC_RELEASE_CNF, NULL);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
release_l3_process(pc);
}
-#ifdef EXT_BEARER_CAPS
-
-u_char *EncodeASyncParams(u_char *p, u_char si2)
-{ // 7c 06 88 90 21 42 00 bb
-
- p[0] = p[1] = 0; p[2] = 0x80;
- if (si2 & 32) // 7 data bits
- p[2] += 16;
- else // 8 data bits
- p[2] +=24;
-
- if (si2 & 16) // 2 stop bits
- p[2] += 96;
- else // 1 stop bit
- p[2] = 32;
-
- if (si2 & 8) // even parity
- p[2] += 2;
- else // no parity
- p[2] += 3;
-
- switch (si2 & 0x07)
- {
- case 0: p[0] = 66; // 1200 bit/s
- break;
- case 1: p[0] = 88; // 1200/75 bit/s
- break;
- case 2: p[0] = 87; // 75/1200 bit/s
- break;
- case 3: p[0] = 67; // 2400 bit/s
- break;
- case 4: p[0] = 69; // 4800 bit/s
- break;
- case 5: p[0] = 72; // 9600 bit/s
- break;
- case 6: p[0] = 73; // 14400 bit/s
- break;
- case 7: p[0] = 75; // 19200 bit/s
- break;
- }
- return p+3;
+#if EXT_BEARER_CAPS
+
+u_char *
+EncodeASyncParams(u_char * p, u_char si2)
+{ // 7c 06 88 90 21 42 00 bb
+
+ p[0] = p[1] = 0;
+ p[2] = 0x80;
+ if (si2 & 32) // 7 data bits
+
+ p[2] += 16;
+ else // 8 data bits
+
+ p[2] += 24;
+
+ if (si2 & 16) // 2 stop bits
+
+ p[2] += 96;
+ else // 1 stop bit
+
+ p[2] = 32;
+
+ if (si2 & 8) // even parity
+
+ p[2] += 2;
+ else // no parity
+
+ p[2] += 3;
+
+ switch (si2 & 0x07) {
+ case 0:
+ p[0] = 66; // 1200 bit/s
+
+ break;
+ case 1:
+ p[0] = 88; // 1200/75 bit/s
+
+ break;
+ case 2:
+ p[0] = 87; // 75/1200 bit/s
+
+ break;
+ case 3:
+ p[0] = 67; // 2400 bit/s
+
+ break;
+ case 4:
+ p[0] = 69; // 4800 bit/s
+
+ break;
+ case 5:
+ p[0] = 72; // 9600 bit/s
+
+ break;
+ case 6:
+ p[0] = 73; // 14400 bit/s
+
+ break;
+ case 7:
+ p[0] = 75; // 19200 bit/s
+
+ break;
+ }
+ return p + 3;
}
-u_char EncodeSyncParams(u_char si2, u_char ai)
+u_char
+EncodeSyncParams(u_char si2, u_char ai)
{
- switch (si2)
- {
- case 0: return ai + 2; // 1200 bit/s
- case 1: return ai + 24; // 1200/75 bit/s
- case 2: return ai + 23; // 75/1200 bit/s
- case 3: return ai + 3; // 2400 bit/s
- case 4: return ai + 5; // 4800 bit/s
- case 5: return ai + 8; // 9600 bit/s
- case 6: return ai + 9; // 14400 bit/s
- case 7: return ai + 11; // 19200 bit/s
- case 8: return ai + 14; // 48000 bit/s
- case 9: return ai + 15; // 56000 bit/s
- case 15: return ai + 40; // negotiate bit/s
- default: break;
- }
- return ai;
+ switch (si2) {
+ case 0:
+ return ai + 2; // 1200 bit/s
+
+ case 1:
+ return ai + 24; // 1200/75 bit/s
+
+ case 2:
+ return ai + 23; // 75/1200 bit/s
+
+ case 3:
+ return ai + 3; // 2400 bit/s
+
+ case 4:
+ return ai + 5; // 4800 bit/s
+
+ case 5:
+ return ai + 8; // 9600 bit/s
+
+ case 6:
+ return ai + 9; // 14400 bit/s
+
+ case 7:
+ return ai + 11; // 19200 bit/s
+
+ case 8:
+ return ai + 14; // 48000 bit/s
+
+ case 9:
+ return ai + 15; // 56000 bit/s
+
+ case 15:
+ return ai + 40; // negotiate bit/s
+
+ default:
+ break;
+ }
+ return ai;
}
-static u_char DecodeASyncParams(u_char si2, u_char *p)
-{ u_char info;
-
- switch (p[5])
- {
- case 66: // 1200 bit/s
- break; // si2 bleibt gleich
- case 88: // 1200/75 bit/s
- si2 += 1;
- break;
- case 87: // 75/1200 bit/s
- si2 += 2;
- break;
- case 67: // 2400 bit/s
- si2 += 3;
- break;
- case 69: // 4800 bit/s
- si2 += 4;
- break;
- case 72: // 9600 bit/s
- si2 += 5;
- break;
- case 73: // 14400 bit/s
- si2 += 6;
- break;
- case 75: // 19200 bit/s
- si2 += 7;
- break;
- }
-
- info = p[7] & 0x7f;
- if ((info & 16) && (!(info & 8))) // 7 data bits
- si2 += 32; // else 8 data bits
- if ((info & 96) == 96) // 2 stop bits
- si2 += 16; // else 1 stop bit
- if ((info & 2) && (!(info & 1))) // even parity
- si2 += 8; // else no parity
-
- return si2;
+static u_char
+DecodeASyncParams(u_char si2, u_char * p)
+{
+ u_char info;
+
+ switch (p[5]) {
+ case 66: // 1200 bit/s
+
+ break; // si2 don't change
+
+ case 88: // 1200/75 bit/s
+
+ si2 += 1;
+ break;
+ case 87: // 75/1200 bit/s
+
+ si2 += 2;
+ break;
+ case 67: // 2400 bit/s
+
+ si2 += 3;
+ break;
+ case 69: // 4800 bit/s
+
+ si2 += 4;
+ break;
+ case 72: // 9600 bit/s
+
+ si2 += 5;
+ break;
+ case 73: // 14400 bit/s
+
+ si2 += 6;
+ break;
+ case 75: // 19200 bit/s
+
+ si2 += 7;
+ break;
+ }
+
+ info = p[7] & 0x7f;
+ if ((info & 16) && (!(info & 8))) // 7 data bits
+
+ si2 += 32; // else 8 data bits
+
+ if ((info & 96) == 96) // 2 stop bits
+
+ si2 += 16; // else 1 stop bit
+
+ if ((info & 2) && (!(info & 1))) // even parity
+
+ si2 += 8; // else no parity
+
+ return si2;
}
-static u_char DecodeSyncParams(u_char si2, u_char info)
+static u_char
+DecodeSyncParams(u_char si2, u_char info)
{
- info &= 0x7f;
- switch (info)
- {
- case 40: // bit/s aushandeln --- hat nicht geklappt, ai wird 165 statt 175!
- return si2 + 15;
- case 15: // 56000 bit/s --- hat nicht geklappt, ai wird 0 statt 169 !
- return si2 + 9;
- case 14: // 48000 bit/s
- return si2 + 8;
- case 11: // 19200 bit/s
- return si2 + 7;
- case 9: // 14400 bit/s
- return si2 + 6;
- case 8: // 9600 bit/s
- return si2 + 5;
- case 5: // 4800 bit/s
- return si2 + 4;
- case 3: // 2400 bit/s
- return si2 + 3;
- case 23: // 75/1200 bit/s
- return si2 + 2;
- case 24: // 1200/75 bit/s
- return si2 + 1;
- default: // 1200 bit/s
- return si2;
- }
+ info &= 0x7f;
+ switch (info) {
+ case 40: // bit/s negotiation failed ai := 165 not 175!
+
+ return si2 + 15;
+ case 15: // 56000 bit/s failed, ai := 0 not 169 !
+
+ return si2 + 9;
+ case 14: // 48000 bit/s
+
+ return si2 + 8;
+ case 11: // 19200 bit/s
+
+ return si2 + 7;
+ case 9: // 14400 bit/s
+
+ return si2 + 6;
+ case 8: // 9600 bit/s
+
+ return si2 + 5;
+ case 5: // 4800 bit/s
+
+ return si2 + 4;
+ case 3: // 2400 bit/s
+
+ return si2 + 3;
+ case 23: // 75/1200 bit/s
+
+ return si2 + 2;
+ case 24: // 1200/75 bit/s
+
+ return si2 + 1;
+ default: // 1200 bit/s
+
+ return si2;
+ }
}
-static u_char DecodeSI2(struct sk_buff *skb)
-{ u_char *p; //, *pend=skb->data + skb->len;
-
- if ((p = findie(skb->data, skb->len, 0x7c, 0)))
- {
- switch (p[4] & 0x0f)
- {
- case 0x01: if (p[1] == 0x04) // sync. Bitratenadaption
- return DecodeSyncParams(160, p[5]); // V.110/X.30
- else if (p[1] == 0x06) // async. Bitratenadaption
- return DecodeASyncParams(192, p); // V.110/X.30
- break;
- case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption
- return DecodeSyncParams(176, p[5]); // V.120
- break;
- }
- }
- return 0;
+static u_char
+DecodeSI2(struct sk_buff *skb)
+{
+ u_char *p; //, *pend=skb->data + skb->len;
+
+ if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+ switch (p[4] & 0x0f) {
+ case 0x01:
+ if (p[1] == 0x04) // sync. Bitratenadaption
+
+ return DecodeSyncParams(160, p[5]); // V.110/X.30
+
+ else if (p[1] == 0x06) // async. Bitratenadaption
+
+ return DecodeASyncParams(192, p); // V.110/X.30
+
+ break;
+ case 0x08: // if (p[5] == 0x02) // sync. Bitratenadaption
+
+ return DecodeSyncParams(176, p[5]); // V.120
+
+ break;
+ }
+ }
+ return 0;
}
#endif
/*
* Set Bearer Capability, Map info from 1TR6-convention to EDSS1
*/
-#ifdef HISAX_EURO_SENDCOMPLETE
+#if HISAX_EURO_SENDCOMPLETE
*p++ = 0xa1; /* complete indicator */
#endif
switch (pc->para.setup.si1) {
msn = pc->para.setup.eazmsn;
sub = NULL;
sp = msn;
- while (*sp) {
+ while (*sp) {
if ('.' == *sp) {
sub = sp;
*sp = 0;
- } else
+ } else
sp++;
}
if (*msn) {
}
if (sub) {
*sub++ = '.';
- *p++ = 0x6d; /* Calling party subaddress */
- *p++ = strlen(sub) + 2;
+ *p++ = 0x6d; /* Calling party subaddress */
+ *p++ = strlen(sub) + 2;
*p++ = 0x80; /* NSAP coded */
*p++ = 0x50; /* local IDI format */
- while (*sub)
+ while (*sub)
*p++ = *sub++ & 0x7f;
}
sub = NULL;
sp = teln;
- while (*sp) {
+ while (*sp) {
if ('.' == *sp) {
sub = sp;
*sp = 0;
- } else
+ } else
sp++;
}
*p++ = 0x70;
if (sub) {
*sub++ = '.';
- *p++ = 0x71; /* Called party subaddress */
- *p++ = strlen(sub) + 2;
+ *p++ = 0x71; /* Called party subaddress */
+ *p++ = strlen(sub) + 2;
*p++ = 0x80; /* NSAP coded */
*p++ = 0x50; /* local IDI format */
- while (*sub)
+ while (*sub)
*p++ = *sub++ & 0x7f;
}
-
-#ifdef EXT_BEARER_CAPS
- if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175))
- { // sync. Bitratenadaption, V.110/X.30
- *p++ = 0x7c; *p++ = 0x04; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21;
- *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
- }
- else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191))
- { // sync. Bitratenadaption, V.120
- *p++ = 0x7c; *p++ = 0x05; *p++ = 0x88; *p++ = 0x90; *p++ = 0x28;
- *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
- *p++ = 0x82;
- }
- else if (pc->para.setup.si2 >= 192)
- { // async. Bitratenadaption, V.110/X.30
- *p++ = 0x7c; *p++ = 0x06; *p++ = 0x88; *p++ = 0x90; *p++ = 0x21;
- p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
- }
+#if EXT_BEARER_CAPS
+ if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30
+
+ *p++ = 0x7c;
+ *p++ = 0x04;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x21;
+ *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+ } else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) { // sync. Bitratenadaption, V.120
+
+ *p++ = 0x7c;
+ *p++ = 0x05;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x28;
+ *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+ *p++ = 0x82;
+ } else if (pc->para.setup.si2 >= 192) { // async. Bitratenadaption, V.110/X.30
+
+ *p++ = 0x7c;
+ *p++ = 0x06;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x21;
+ p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+#if HISAX_SEND_STD_LLC_IE
+ } else {
+ *p++ = 0x7c;
+ *p++ = 0x02;
+ *p++ = 0x88;
+ *p++ = 0x90;
+#endif
+ }
#endif
l = p - tmp;
if (!(skb = l3_alloc_skb(l)))
L3DelTimer(&pc->timer);
L3AddTimer(&pc->timer, T303, CC_T303);
newl3state(pc, 1);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
}
static void
dev_kfree_skb(skb);
newl3state(pc, 3);
L3AddTimer(&pc->timer, T310, CC_T310);
- pc->st->l3.l3l4(pc, CC_PROCEEDING_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
}
static void
dev_kfree_skb(skb);
newl3state(pc, 2);
L3AddTimer(&pc->timer, T304, CC_T304);
- pc->st->l3.l3l4(pc, CC_MORE_INFO, NULL);
+ pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
}
static void
dev_kfree_skb(skb);
newl3state(pc, 12);
pc->para.cause = cause;
- pc->st->l3.l3l4(pc, CC_DISCONNECT_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
}
static void
L3DelTimer(&pc->timer); /* T310 */
newl3state(pc, 10);
pc->para.chargeinfo = 0;
- pc->st->l3.l3l4(pc, CC_SETUP_CNF, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
}
static void
dev_kfree_skb(skb);
L3DelTimer(&pc->timer); /* T304 */
newl3state(pc, 4);
- pc->st->l3.l3l4(pc, CC_ALERTING_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
}
static void
l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
{
- /* This routine is called if here was no SETUP made (checks in dss1up and in
- * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
- * It is called after it is veryfied that Layer2 is up.
- * The cause value is allready in pc->para.cause
- * MT_STATUS_ENQUIRE in the NULL state is handled too
- */
+ /* This routine is called if here was no SETUP made (checks in dss1up and in
+ * l3dss1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+ * MT_STATUS_ENQUIRE in the NULL state is handled too
+ */
u_char tmp[16];
- u_char *p=tmp;
+ u_char *p = tmp;
int l;
struct sk_buff *skb;
switch (pc->para.cause) {
- case 81: /* 0x51 invalid callreference */
- case 96: /* 0x60 mandory IE missing */
- case 101: /* 0x65 incompatible Callstate */
- MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
- *p++ = IE_CAUSE;
- *p++ = 0x2;
- *p++ = 0x80;
- *p++ = pc->para.cause | 0x80;
- break;
- default:
- printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n");
- return;
- }
+ case 81: /* 0x51 invalid callreference */
+ case 88: /* 0x58 incomp destination */
+ case 96: /* 0x60 mandory IE missing */
+ case 101: /* 0x65 incompatible Callstate */
+ MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = pc->para.cause | 0x80;
+ break;
+ default:
+ printk(KERN_ERR "HiSax internal error l3dss1_msg_without_setup\n");
+ return;
+ }
l = p - tmp;
if (!(skb = l3_alloc_skb(l)))
return;
memcpy(skb_put(skb, l), tmp, l);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
release_l3_process(pc);
}
static void
l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
{
- u_char *p, *ptmp[8];
+ u_char *p, *ptmp[8];
int i;
int bcfound = 0;
char tmp[80];
/* ETS 300-104 1.3.4 and 1.3.5
* we need to detect unknown inform. element from 0 to 7
- */
+ */
p = skb->data;
- for(i = 0; i < 8; i++)
+ for (i = 0; i < 8; i++)
ptmp[i] = skb->data;
if (findie(ptmp[1], skb->len, 0x01, 0)
|| findie(ptmp[2], skb->len, 0x02, 0)
|| findie(ptmp[5], skb->len, 0x05, 0)
|| findie(ptmp[6], skb->len, 0x06, 0)
|| findie(ptmp[7], skb->len, 0x07, 0)) {
- /* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE
- * cause 0x60
- */
- pc->para.cause = 0x60;
+ /* if ie is < 8 and not 0 nor 4, send RELEASE_COMPLETE
+ * cause 0x60
+ */
+ pc->para.cause = 0x60;
dev_kfree_skb(skb);
- if (pc->state == 0)
- pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL);
- else
- l3dss1_msg_without_setup(pc, pr, NULL);
+ l3dss1_msg_without_setup(pc, pr, NULL);
return;
}
-
/*
* Channel Identification
*/
bcfound++;
else if (pc->debug & L3_DEB_WARN)
l3_debug(pc->st, "setup without bchannel");
- } else if (pc->debug & L3_DEB_WARN)
- l3_debug(pc->st, "setup without bchannel");
-
+ } else {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup without bchannel");
+ pc->para.cause = 0x60;
+ dev_kfree_skb(skb);
+ l3dss1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
/*
* Bearer Capabilities
*/
/* Unrestricted digital information */
pc->para.setup.si1 = 7;
/* JIM, 05.11.97 I wanna set service indicator 2 */
-#ifdef EXT_BEARER_CAPS
- pc->para.setup.si2 = DecodeSI2(skb);
- printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n",
- pc->para.setup.si1, pc->para.setup.si2);
+#if EXT_BEARER_CAPS
+ pc->para.setup.si2 = DecodeSI2(skb);
+ printk(KERN_DEBUG "HiSax: SI=%d, AI=%d\n",
+ pc->para.setup.si1, pc->para.setup.si2);
#endif
break;
case 0x09:
if (pc->debug & L3_DEB_WARN)
l3_debug(pc->st, "setup without bearer capabilities");
/* ETS 300-104 1.3.3 */
- pc->para.cause = 0x60;
+ pc->para.cause = 0x60;
dev_kfree_skb(skb);
- if (pc->state == 0)
- pc->st->l3.l3l4(pc, CC_ESTABLISH, NULL);
- else
- l3dss1_msg_without_setup(pc, pr, NULL);
+ l3dss1_msg_without_setup(pc, pr, NULL);
return;
}
p = skb->data;
if ((p = findie(p, skb->len, 0x71, 0))) {
/* Called party subaddress */
- if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) {
- tmp[0]='.';
+ if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+ tmp[0] = '.';
iecpy(&tmp[1], p, 2);
strcat(pc->para.setup.eazmsn, tmp);
} else if (pc->debug & L3_DEB_WARN)
p = skb->data;
if ((p = findie(p, skb->len, 0x6d, 0))) {
/* Calling party subaddress */
- if ((p[1]>=2) && (p[2]==0x80) && (p[3]==0x50)) {
- tmp[0]='.';
+ if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+ tmp[0] = '.';
iecpy(&tmp[1], p, 2);
strcat(pc->para.setup.phone, tmp);
} else if (pc->debug & L3_DEB_WARN)
l3_debug(pc->st, "wrong calling subaddress");
}
-
dev_kfree_skb(skb);
if (bcfound) {
if ((pc->para.setup.si1 != 7) && (pc->debug & L3_DEB_WARN)) {
- sprintf(tmp, "non-digital call: %s -> %s",
- pc->para.setup.phone, pc->para.setup.eazmsn);
- l3_debug(pc->st, tmp);
+ l3_debug(pc->st, "non-digital call: %s -> %s",
+ pc->para.setup.phone, pc->para.setup.eazmsn);
+ }
+ if ((pc->para.setup.si1 != 7) &&
+ test_bit(FLG_PTP, &pc->st->l2.flag)) {
+ pc->para.cause = 0x58;
+ l3dss1_msg_without_setup(pc, pr, NULL);
+ return;
}
newl3state(pc, 6);
- pc->st->l3.l3l4(pc, CC_SETUP_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
} else
release_l3_process(pc);
}
dev_kfree_skb(skb);
newl3state(pc, 10);
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc, CC_SETUP_COMPLETE_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
}
static void
return;
memcpy(skb_put(skb, l), tmp, l);
newl3state(pc, 11);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
L3AddTimer(&pc->timer, T305, CC_T305);
}
if (!(skb = l3_alloc_skb(l)))
return;
memcpy(skb_put(skb, l), tmp, l);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
- pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
release_l3_process(pc);
}
}
p = skb->data;
if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
-#ifdef HISAX_DE_AOC
- l3dss1_parse_facility(pc,p);
+#if HISAX_DE_AOC
+ l3dss1_parse_facility(pc, p);
#else
p = NULL;
#endif
StopAllL3Timer(pc);
pc->para.cause = cause;
l3dss1_message(pc, MT_RELEASE_COMPLETE);
- pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
release_l3_process(pc);
}
if (!(skb = l3_alloc_skb(l)))
return;
memcpy(skb_put(skb, l), tmp, l);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
}
static void
l3dss1_status_req(struct l3_process *pc, u_char pr, void *arg)
{
- /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1...
- if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62 */
- u_char tmp[16];
+ /* ETS 300-104 7.4.1, 8.4.1, 10.3.1, 11.4.1, 12.4.1, 13.4.1, 14.4.1...
+ if setup has been made and a non expected message type is received, we must send MT_STATUS cause 0x62 */
+ u_char tmp[16];
u_char *p = tmp;
int l;
struct sk_buff *skb = arg;
*p++ = IE_CAUSE;
*p++ = 0x2;
*p++ = 0x80;
- *p++ = 0x62 | 0x80; /* status sending */
+ *p++ = 0x62 | 0x80; /* status sending */
*p++ = 0x14; /* CallState */
*p++ = 0x1;
if (!(skb = l3_alloc_skb(l)))
return;
memcpy(skb_put(skb, l), tmp, l);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
}
static void
if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
p++;
- if (1== *p++)
+ if (1 == *p++)
callState = *p;
}
- if(callState == 0) {
+ if (callState == 0) {
/* ETS 300-104 7.6.1, 8.6.1, 10.6.1... and 16.1
* set down layer 3 without sending any message
*/
- pc->st->l3.l3l4(pc, CC_RELEASE_IND, NULL);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
newl3state(pc, 0);
release_l3_process(pc);
} else {
- pc->st->l3.l3l4(pc, CC_IGNORE, NULL);
+ pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
}
}
l3dss1_setup_req(pc, pr, arg);
} else {
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc, CC_NOSETUP_RSP_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
release_l3_process(pc);
}
}
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
}
return;
memcpy(skb_put(skb, l), tmp, l);
newl3state(pc, 19);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
L3AddTimer(&pc->timer, T308, CC_T308_1);
}
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc, CC_SETUP_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
}
static void
L3DelTimer(&pc->timer);
pc->para.cause = 0xE6;
l3dss1_disconnect_req(pc, pr, NULL);
- pc->st->l3.l3l4(pc, CC_CONNECT_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
}
static void
l3dss1_t308_2(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc, CC_RELEASE_ERR, NULL);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
release_l3_process(pc);
}
+static void
+l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 0x66; /* Timer expiry */
+ pc->para.loc = 0; /* local */
+ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ newl3state(pc, 19);
+ l3dss1_message(pc, MT_RELEASE);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 0x66; /* Timer expiry */
+ pc->para.loc = 0; /* local */
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ newl3state(pc, 10);
+}
+
static void
l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
{
L3DelTimer(&pc->timer);
- pc->st->l3.l3l4(pc, CC_DLRL, NULL);
+ pc->st->l3.l3l4(pc->st, CC_DLRL | INDICATION, pc);
release_l3_process(pc);
}
if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
p++;
l = *p++;
- t += sprintf(t,"Status CR %x Cause:", pc->callref);
+ t += sprintf(t, "Status CR %x Cause:", pc->callref);
while (l--) {
- cause = *p;
- t += sprintf(t," %2x",*p++);
+ cause = *p;
+ t += sprintf(t, " %2x", *p++);
}
} else
- sprintf(t,"Status CR %x no Cause", pc->callref);
+ sprintf(t, "Status CR %x no Cause", pc->callref);
l3_debug(pc->st, tmp);
p = skb->data;
t = tmp;
- t += sprintf(t,"Status state %x ", pc->state);
+ t += sprintf(t, "Status state %x ", pc->state);
if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
p++;
- if (1== *p++) {
- callState = *p;
- t += sprintf(t,"peer state %x" , *p);
- }
- else
- t += sprintf(t,"peer state len error");
+ if (1 == *p++) {
+ callState = *p;
+ t += sprintf(t, "peer state %x", *p);
+ } else
+ t += sprintf(t, "peer state len error");
} else
- sprintf(t,"no peer state");
+ sprintf(t, "no peer state");
l3_debug(pc->st, tmp);
- if(((cause & 0x7f) == 0x6f) && (callState == 0)) {
- /* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
- * if received MT_STATUS with cause == 0x6f and call
+ if (((cause & 0x7f) == 0x6f) && (callState == 0)) {
+ /* ETS 300-104 7.6.1, 8.6.1, 10.6.1...
+ * if received MT_STATUS with cause == 0x6f and call
* state == 0, then we must set down layer 3
*/
l3dss1_release_ind(pc, pr, arg);
static void
l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
{
- u_char *p;
+ u_char *p;
struct sk_buff *skb = arg;
p = skb->data;
if ((p = findie(p, skb->len, IE_FACILITY, 0))) {
-#ifdef HISAX_DE_AOC
- l3dss1_parse_facility(pc,p);
+#if HISAX_DE_AOC
+ l3dss1_parse_facility(pc, p);
#else
p = NULL;
#endif
}
}
+static void
+l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[32];
+ u_char *p = tmp;
+ u_char i, l;
+ u_char *msg = pc->chan->setup.phone;
+
+ MsgHead(p, pc->callref, MT_SUSPEND);
+
+ *p++ = IE_CALLID;
+ l = *msg++;
+ if (l && (l <= 10)) { /* Max length 10 octets */
+ *p++ = l;
+ for (i = 0; i < l; i++)
+ *p++ = *msg++;
+ } else {
+ l3_debug(pc->st, "SUS wrong CALLID len %d", l);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ newl3state(pc, 15);
+ L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ L3DelTimer(&pc->timer);
+ newl3state(pc, 0);
+ dev_kfree_skb(skb);
+ pc->para.cause = -1;
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+ release_l3_process(pc);
+}
+
+static void
+l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+ int cause = -1;
+
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+ p++;
+ if (*p++ == 2)
+ pc->para.loc = *p++;
+ cause = *p & 0x7f;
+ }
+ dev_kfree_skb(skb);
+ pc->para.cause = cause;
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ newl3state(pc, 10);
+}
+
+static void
+l3dss1_resume_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[32];
+ u_char *p = tmp;
+ u_char i, l;
+ u_char *msg = pc->para.setup.phone;
+
+ MsgHead(p, pc->callref, MT_RESUME);
+
+ *p++ = IE_CALLID;
+ l = *msg++;
+ if (l && (l <= 10)) { /* Max length 10 octets */
+ *p++ = l;
+ for (i = 0; i < l; i++)
+ *p++ = *msg++;
+ } else {
+ l3_debug(pc->st, "RES wrong CALLID len %d", l);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ newl3state(pc, 17);
+ L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+ pc->para.bchannel = p[2] & 0x3;
+ if ((!pc->para.bchannel) && (pc->debug & L3_DEB_WARN))
+ l3_debug(pc->st, "resume ack without bchannel");
+ } else if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "resume ack without bchannel");
+ dev_kfree_skb(skb);
+ pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+ newl3state(pc, 10);
+}
+
+static void
+l3dss1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+ int cause = -1;
+
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+ p++;
+ if (*p++ == 2)
+ pc->para.loc = *p++;
+ cause = *p & 0x7f;
+ }
+ dev_kfree_skb(skb);
+ pc->para.cause = cause;
+ newl3state(pc, 0);
+ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ release_l3_process(pc);
+}
-
static void
l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
{
u_char tmp[32];
u_char *p;
- u_char ri, chan=0;
+ u_char ri, ch = 0, chan = 0;
int l;
struct sk_buff *skb = arg;
struct l3_process *up;
-
+
newl3state(pc, 2);
L3DelTimer(&pc->timer);
p = skb->data;
if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
- ri = p[2];
- sprintf(tmp, "Restart %x", ri);
+ ri = p[2];
+ l3_debug(pc->st, "Restart %x", ri);
} else {
- sprintf(tmp, "Restart without restart IE");
+ l3_debug(pc->st, "Restart without restart IE");
ri = 0x86;
}
- l3_debug(pc->st, tmp);
p = skb->data;
if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
chan = p[2] & 3;
- sprintf(tmp, "Restart for channel %d", chan);
- l3_debug(pc->st, tmp);
+ ch = p[2];
+ if (pc->st->l3.debug)
+ l3_debug(pc->st, "Restart for channel %d", chan);
}
dev_kfree_skb(skb);
newl3state(pc, 2);
up = pc->st->l3.proc;
while (up) {
- if ((ri & 7)==7)
- up->st->lli.l4l3(up->st, CC_RESTART, up);
+ if ((ri & 7) == 7)
+ up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
else if (up->para.bchannel == chan)
- up->st->lli.l4l3(up->st, CC_RESTART, up);
+ up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
up = up->next;
}
p = tmp;
if (chan) {
*p++ = IE_CHANNEL_ID;
*p++ = 1;
- *p++ = chan | 0x80;
+ *p++ = ch | 0x80;
}
- *p++ = 0x79; /* RESTART Ind */
+ *p++ = 0x79; /* RESTART Ind */
*p++ = 1;
*p++ = ri;
l = p - tmp;
return;
memcpy(skb_put(skb, l), tmp, l);
newl3state(pc, 0);
- pc->st->l3.l3l2(pc->st, DL_DATA, skb);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
}
-
/* *INDENT-OFF* */
static struct stateentry downstatelist[] =
{
{SBIT(0),
- CC_ESTABLISH, l3dss1_msg_without_setup},
+ CC_SETUP | REQUEST, l3dss1_setup_req},
{SBIT(0),
- CC_SETUP_REQ, l3dss1_setup_req},
+ CC_RESUME | REQUEST, l3dss1_resume_req},
{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) | SBIT(10),
- CC_DISCONNECT_REQ, l3dss1_disconnect_req},
+ CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
{SBIT(12),
- CC_RELEASE_REQ, l3dss1_release_req},
+ CC_RELEASE | REQUEST, l3dss1_release_req},
{ALL_STATES,
- CC_DLRL, l3dss1_reset},
+ CC_DLRL | REQUEST, l3dss1_reset},
{ALL_STATES,
- CC_RESTART, l3dss1_restart},
+ CC_RESTART | REQUEST, l3dss1_restart},
{SBIT(6),
- CC_IGNORE, l3dss1_reset},
+ CC_IGNORE | REQUEST, l3dss1_reset},
{SBIT(6),
- CC_REJECT_REQ, l3dss1_reject_req},
+ CC_REJECT | REQUEST, l3dss1_reject_req},
{SBIT(6),
- CC_ALERTING_REQ, l3dss1_alert_req},
+ CC_ALERTING | REQUEST, l3dss1_alert_req},
{SBIT(6) | SBIT(7),
- CC_SETUP_RSP, l3dss1_setup_rsp},
+ CC_SETUP | RESPONSE, l3dss1_setup_rsp},
+ {SBIT(10),
+ CC_SUSPEND | REQUEST, l3dss1_suspend_req},
{SBIT(1),
CC_T303, l3dss1_t303},
{SBIT(2),
CC_T313, l3dss1_t313},
{SBIT(11),
CC_T305, l3dss1_t305},
+ {SBIT(15),
+ CC_T319, l3dss1_t319},
+ {SBIT(17),
+ CC_T318, l3dss1_t318},
{SBIT(19),
CC_T308_1, l3dss1_t308_1},
{SBIT(19),
CC_T308_2, l3dss1_t308_2},
};
-static int downsllen = sizeof(downstatelist) /
-sizeof(struct stateentry);
+#define DOWNSLLEN \
+ (sizeof(downstatelist) / sizeof(struct stateentry))
static struct stateentry datastatelist[] =
{
SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) | SBIT(19),
MT_RELEASE_COMPLETE, l3dss1_release_cmpl},
{SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) |
- SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17) /*| SBIT(19)*/,
+ SBIT(11) | SBIT(12) | SBIT(15) /* | SBIT(17) | SBIT(19)*/,
MT_RELEASE, l3dss1_release},
{SBIT(19), MT_RELEASE, l3dss1_release_ind},
- {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+ {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10) | SBIT(15),
MT_DISCONNECT, l3dss1_disconnect},
{SBIT(11),
MT_DISCONNECT, l3dss1_release_req},
MT_CONNECT_ACKNOWLEDGE, l3dss1_status_req},
{SBIT(8),
MT_CONNECT_ACKNOWLEDGE, l3dss1_connect_ack},
- {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(19),
+ {SBIT(15),
+ MT_SUSPEND_ACKNOWLEDGE, l3dss1_suspend_ack},
+ {SBIT(15),
+ MT_SUSPEND_REJECT, l3dss1_suspend_rej},
+ {SBIT(17),
+ MT_RESUME_ACKNOWLEDGE, l3dss1_resume_ack},
+ {SBIT(17),
+ MT_RESUME_REJECT, l3dss1_resume_rej},
+ {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(8) | SBIT(10) | SBIT(11) | SBIT(15) | SBIT(17) | SBIT(19),
MT_INVALID, l3dss1_status_req},
};
-static int datasllen = sizeof(datastatelist) / sizeof(struct stateentry);
+#define DATASLLEN \
+ (sizeof(datastatelist) / sizeof(struct stateentry))
static struct stateentry globalmes_list[] =
{
{ALL_STATES,
- MT_STATUS, l3dss1_status},
+ MT_STATUS, l3dss1_status},
{SBIT(0),
MT_RESTART, l3dss1_global_restart},
/* {SBIT(1),
- MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
+ MT_RESTART_ACKNOWLEDGE, l3dss1_restart_ack},
*/
};
-static int globalm_len = sizeof(globalmes_list) / sizeof(struct stateentry);
-
-#if 0
-static struct stateentry globalcmd_list[] =
-{
- {ALL_STATES,
- CC_STATUS, l3dss1_status_req},
- {SBIT(0),
- CC_RESTART, l3dss1_restart_req},
-};
-
-static int globalc_len = sizeof(globalcmd_list) / sizeof(struct stateentry);
-#endif
+#define GLOBALM_LEN \
+ (sizeof(globalmes_list) / sizeof(struct stateentry))
/* *INDENT-ON* */
+
static void
global_handler(struct PStack *st, int mt, struct sk_buff *skb)
{
int i;
- char tmp[64];
struct l3_process *proc = st->l3.global;
-
- for (i = 0; i < globalm_len; i++)
+
+ for (i = 0; i < GLOBALM_LEN; i++)
if ((mt == globalmes_list[i].primitive) &&
((1 << proc->state) & globalmes_list[i].state))
break;
- if (i == globalm_len) {
+ if (i == GLOBALM_LEN) {
dev_kfree_skb(skb);
if (st->l3.debug & L3_DEB_STATE) {
- sprintf(tmp, "dss1 global state %d mt %x unhandled",
+ l3_debug(st, "dss1 global state %d mt %x unhandled",
proc->state, mt);
- l3_debug(st, tmp);
}
return;
} else {
if (st->l3.debug & L3_DEB_STATE) {
- sprintf(tmp, "dss1 global %d mt %x",
+ l3_debug(st, "dss1 global %d mt %x",
proc->state, mt);
- l3_debug(st, tmp);
}
globalmes_list[i].rout(proc, mt, skb);
}
char *ptr;
struct sk_buff *skb = arg;
struct l3_process *proc;
- char tmp[80];
+ switch (pr) {
+ case (DL_DATA | INDICATION):
+ case (DL_UNIT_DATA | INDICATION):
+ break;
+ case (DL_ESTABLISH | CONFIRM):
+ case (DL_ESTABLISH | INDICATION):
+ case (DL_RELEASE | INDICATION):
+ case (DL_RELEASE | CONFIRM):
+ l3_msg(st, pr, arg);
+ return;
+ break;
+ }
if (skb->data[0] != PROTO_DIS_EURO) {
if (st->l3.debug & L3_DEB_PROTERR) {
- sprintf(tmp, "dss1up%sunexpected discriminator %x message len %d",
- (pr == DL_DATA) ? " " : "(broadcast) ",
- skb->data[0], skb->len);
- l3_debug(st, tmp);
+ l3_debug(st, "dss1up%sunexpected discriminator %x message len %d",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ skb->data[0], skb->len);
}
dev_kfree_skb(skb);
return;
}
cr = getcallref(skb->data);
mt = skb->data[skb->data[1] + 2];
- if (!cr) { /* Global CallRef */
+ if (!cr) { /* Global CallRef */
global_handler(st, mt, skb);
return;
- } else if (cr == -1) { /* Dummy Callref */
+ } else if (cr == -1) { /* Dummy Callref */
dev_kfree_skb(skb);
return;
} else if (!(proc = getl3proc(st, cr))) {
* this callreference is active
*/
if (mt == MT_SETUP) {
- /* Setup creates a new transaction process */
+ /* Setup creates a new transaction process */
if (!(proc = new_l3_process(st, cr))) {
/* May be to answer with RELEASE_COMPLETE and
* CAUSE 0x2f "Resource unavailable", but this
}
} else if (mt == MT_STATUS) {
cause = 0;
- if((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
- ptr++;
- if (*ptr++ == 2)
- ptr++;
- cause = *ptr & 0x7f;
+ if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+ ptr++;
+ if (*ptr++ == 2)
+ ptr++;
+ cause = *ptr & 0x7f;
}
callState = 0;
- if((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+ if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
ptr++;
if (*ptr++ == 2)
ptr++;
return;
} else {
/* ETS 300-104 part 2.4.2
- * if setup has not been made and a message type
+ * if setup has not been made and a message type
* MT_STATUS is received with call state != 0,
* we must send MT_RELEASE_COMPLETE cause 101
*/
dev_kfree_skb(skb);
if ((proc = new_l3_process(st, cr))) {
- proc->para.cause = 0x65; /* 101 */
- proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL);
+ proc->para.cause = 0x65; /* 101 */
+ l3dss1_msg_without_setup(proc, 0, NULL);
}
return;
}
- } else if (mt == MT_RELEASE_COMPLETE){
+ } else if (mt == MT_RELEASE_COMPLETE) {
dev_kfree_skb(skb);
return;
} else {
/* ETS 300-104 part 2
- * if setup has not been made and a message type
+ * if setup has not been made and a message type
* (except MT_SETUP and RELEASE_COMPLETE) is received,
* we must send MT_RELEASE_COMPLETE cause 81 */
dev_kfree_skb(skb);
if ((proc = new_l3_process(st, cr))) {
- proc->para.cause = 0x51; /* 81 */
- proc->st->l3.l3l4(proc, CC_ESTABLISH, NULL);
+ proc->para.cause = 0x51; /* 81 */
+ l3dss1_msg_without_setup(proc, 0, NULL);
}
return;
}
* if setup has been made and invalid message type is received,
* we must send MT_STATUS cause 0x62
*/
- mt = MT_INVALID; /* sorry, not clean, but do the right thing ;-) */
+ mt = MT_INVALID; /* sorry, not clean, but do the right thing ;-) */
}
-
- for (i = 0; i < datasllen; i++)
+ for (i = 0; i < DATASLLEN; i++)
if ((mt == datastatelist[i].primitive) &&
((1 << proc->state) & datastatelist[i].state))
break;
- if (i == datasllen) {
+ if (i == DATASLLEN) {
dev_kfree_skb(skb);
if (st->l3.debug & L3_DEB_STATE) {
- sprintf(tmp, "dss1up%sstate %d mt %x unhandled",
- (pr == DL_DATA) ? " " : "(broadcast) ",
+ l3_debug(st, "dss1up%sstate %d mt %x unhandled",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
proc->state, mt);
- l3_debug(st, tmp);
}
return;
} else {
if (st->l3.debug & L3_DEB_STATE) {
- sprintf(tmp, "dss1up%sstate %d mt %x",
- (pr == DL_DATA) ? " " : "(broadcast) ",
+ l3_debug(st, "dss1up%sstate %d mt %x",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
proc->state, mt);
- l3_debug(st, tmp);
}
datastatelist[i].rout(proc, pr, skb);
}
int i, cr;
struct l3_process *proc;
struct Channel *chan;
- char tmp[80];
- if (CC_SETUP_REQ == pr) {
+ if (((DL_ESTABLISH | REQUEST) == pr) || ((DL_RELEASE | REQUEST) == pr)) {
+ l3_msg(st, pr, NULL);
+ return;
+ } else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
chan = arg;
cr = newcallref();
cr |= 0x80;
proc = arg;
}
if (!proc) {
- printk(KERN_ERR "HiSax internal error dss1down without proc\n");
+ printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr);
return;
}
- for (i = 0; i < downsllen; i++)
+ for (i = 0; i < DOWNSLLEN; i++)
if ((pr == downstatelist[i].primitive) &&
((1 << proc->state) & downstatelist[i].state))
break;
- if (i == downsllen) {
+ if (i == DOWNSLLEN) {
if (st->l3.debug & L3_DEB_STATE) {
- sprintf(tmp, "dss1down state %d prim %d unhandled",
+ l3_debug(st, "dss1down state %d prim %d unhandled",
proc->state, pr);
- l3_debug(st, tmp);
}
} else {
if (st->l3.debug & L3_DEB_STATE) {
- sprintf(tmp, "dss1down state %d prim %d",
+ l3_debug(st, "dss1down state %d prim %d",
proc->state, pr);
- l3_debug(st, tmp);
}
downstatelist[i].rout(proc, pr, arg);
}
-/* $Id: l3dss1.h,v 1.5 1998/02/02 13:34:30 keil Exp $
+/* $Id: l3dss1.h,v 1.6 1998/03/19 13:18:50 keil Exp $
*
* DSS1 (Euro) D-channel protocol defines
*
* $Log: l3dss1.h,v $
+ * Revision 1.6 1998/03/19 13:18:50 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
* Revision 1.5 1998/02/02 13:34:30 keil
* Support australian Microlink net and german AOCD
*
#define MT_INVALID 0xff
-#define IE_CAUSE 0x08
#define IE_BEARER 0x04
+#define IE_CAUSE 0x08
+#define IE_CALLID 0x10
#define IE_FACILITY 0x1c
#define IE_CALL_STATE 0x14
#define IE_CHANNEL_ID 0x18
-/* $Id: lmgr.c,v 1.2 1997/10/29 19:09:34 keil Exp $
+/* $Id: lmgr.c,v 1.5 1998/11/15 23:55:12 keil Exp $
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
*
* Layermanagement module
*
* $Log: lmgr.c,v $
+ * Revision 1.5 1998/11/15 23:55:12 keil
+ * changes from 2.0
+ *
+ * Revision 1.4 1998/05/25 12:58:19 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.3 1998/03/07 22:57:06 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 1.2 1997/10/29 19:09:34 keil
* new L1
*
case 'D':
case 'G':
case 'H':
- st->l2.l2tei(st, MDL_ERROR_REQ, NULL);
+ st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL);
break;
}
}
static void
hisax_manager(struct PStack *st, int pr, void *arg)
{
- char tm[32], str[256];
- int Code;
+ long Code;
switch (pr) {
- case MDL_ERROR_IND:
- Code = (int) arg;
- jiftime(tm, jiffies);
- sprintf(str, "%s manager: MDL_ERROR %c %s\n", tm,
- Code, test_bit(FLG_LAPD, &st->l2.flag) ?
+ case (MDL_ERROR | INDICATION):
+ Code = (long) arg;
+ HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR",
+ "%c %s\n", (char)Code,
+ test_bit(FLG_LAPD, &st->l2.flag) ?
"D-channel" : "B-channel");
- HiSax_putstatus(st->l1.hardware, str);
if (test_bit(FLG_LAPD, &st->l2.flag))
error_handling_dchan(st, Code);
break;
--- /dev/null
+-----BEGIN PGP SIGNED MESSAGE-----
+
+# This are valid md5sums for certificated HiSax driver.
+# The certification is valid only if the md5sums of all files match.
+# The certification is valid only for ELSA QuickStep cards in the moment.
+# Read ../../../Documentation/isdn/HiSax.cert for more informations.
+#
+a273c532aec063574273ee519975cd9a isac.c
+27c5c5bfa2ceabf02e2e6d686b03abde isdnl1.c
+8c89ac659d3188ab997fb575da22b566 isdnl2.c
+d0fa912aa284b8fd19fed86b65999f6f isdnl3.c
+1bce120740b615006286ad9b2d7fcdcb tei.c
+8845f88dd17917d9b58badeff1605057 callc.c
+f3ec2a634f06074d16167aaba02b6dc1 cert.c
+71840ec8189f42b0db86fb38e5e5984c l3dss1.c
+1882de8bea921b9ccd98fbe77267aa04 l3_1tr6.c
+3bd7af3a11693d028300278744d0da09 elsa.c
+# end of md5sums
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBNrl5JDpxHvX/mS9tAQHm8wP+Nk64UJ2abdDG/igXZSrwcYhX/Kp7cxt9
+ccYp+aaur+pALA0lxwY3xcLt9u36fCYuTLHAVmQoiC9Vbemj37yzM2rUpz9nkw/7
+D6gLqZs2jxVpAwVVJgp0JwDONKXaRX6Lt2EPD9PTW6vxRWEu0HqGhM5hrtd/o4rV
+mC1W7Wj13XM=
+=LdhT
+-----END PGP SIGNATURE-----
-/* $Id: mic.c,v 1.6 1998/02/17 15:39:57 keil Exp $
+/* $Id: mic.c,v 1.7 1998/04/15 16:44:32 keil Exp $
* mic.c low level stuff for mic cards
*
*
*
* $Log: mic.c,v $
+ * Revision 1.7 1998/04/15 16:44:32 keil
+ * new init code
+ *
* Revision 1.6 1998/02/17 15:39:57 keil
* fix reset problem
*
extern const char *CardType[];
-const char *mic_revision = "$Revision: 1.6 $";
+const char *mic_revision = "$Revision: 1.7 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
I4L_IRQ_FLAG, "HiSax", cs));
case CARD_INIT:
inithscx(cs); /* /RTSA := ISAC RST */
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ inithscxisac(cs, 3);
return(0);
case CARD_TEST:
return(0);
-/* $Id: netjet.c,v 1.3 1998/02/12 23:08:05 keil Exp $
+/* $Id: netjet.c,v 1.8 1998/11/15 23:55:14 keil Exp $
* netjet.c low level stuff for Traverse Technologie NETJet ISDN cards
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
* Thanks to Traverse Technologie Australia for documents and informations
*
*
* $Log: netjet.c,v $
+ * Revision 1.8 1998/11/15 23:55:14 keil
+ * changes from 2.0
+ *
+ * Revision 1.7 1998/09/30 22:24:48 keil
+ * Fix missing line in setstack*
+ *
+ * Revision 1.6 1998/08/13 23:36:54 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 1.5 1998/05/25 12:58:21 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 1.4 1998/04/15 16:42:35 keil
+ * new init code
+ * new PCI init (2.1.94)
+ *
* Revision 1.3 1998/02/12 23:08:05 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#include "hscx.h"
#include "isdnl1.h"
#include <linux/pci.h>
-#include <linux/bios32.h>
#include <linux/interrupt.h>
-#define fcstab ppp_crc16_table
#include <linux/ppp_defs.h>
-extern __u16 ppp_crc16_table[256]; /* from ppp code */
extern const char *CardType[];
-const char *NETjet_revision = "$Revision: 1.3 $";
+const char *NETjet_revision = "$Revision: 1.8 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
#define NETJET_ISAC_OFF 0xc0
#define NETJET_ISACIRQ 0x10
+#define NETJET_IRQM0_READ 0x0c
+#define NETJET_IRQM0_READ_1 0x04
+#define NETJET_IRQM0_READ_2 0x08
+#define NETJET_IRQM0_WRITE 0x03
+#define NETJET_IRQM0_WRITE_1 0x01
+#define NETJET_IRQM0_WRITE_2 0x02
#define NETJET_DMA_SIZE 512
insb(cs->hw.njet.isac, data, size);
}
+__u16 fcstab[256] =
+{
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
static void
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
{
mode_tiger(struct BCState *bcs, int mode, int bc)
{
struct IsdnCardState *cs = bcs->cs;
- char tmp[64];
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "Tiger mode %d bchan %d/%d",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "Tiger mode %d bchan %d/%d",
mode, bc, bcs->channel);
- debugl1(cs, tmp);
- }
bcs->mode = mode;
bcs->channel = bc;
switch (mode) {
case (L1_MODE_NULL):
fill_mem(bcs, bcs->hw.tiger.send,
NETJET_DMA_SIZE, bc, 0xff);
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "Tiger stat rec %d/%d send %d",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "Tiger stat rec %d/%d send %d",
bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err,
bcs->hw.tiger.s_tot);
- debugl1(cs, tmp);
- }
if ((cs->bcs[0].mode == L1_MODE_NULL) &&
(cs->bcs[1].mode == L1_MODE_NULL)) {
cs->hw.njet.dmactrl = 0;
test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
break;
}
- if (cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "tiger: set %x %x %x %x/%x pulse=%d",
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "tiger: set %x %x %x %x/%x pulse=%d",
bytein(cs->hw.njet.base + NETJET_DMACTRL),
bytein(cs->hw.njet.base + NETJET_IRQMASK0),
bytein(cs->hw.njet.base + NETJET_IRQSTAT0),
inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
- debugl1(cs, tmp);
- }
}
static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
val >>= 1;\
}
-static void make_raw_data(struct BCState *bcs) {
+static int make_raw_data(struct BCState *bcs) {
register u_int i,s_cnt=0;
register u_char j;
register u_char val;
register u_char s_val = 0;
register u_char bitcnt = 0;
u_int fcs;
- char tmp[64];
-
+ if (!bcs->tx_skb) {
+ debugl1(bcs->cs, "tiger make_raw: NULL skb");
+ return(1);
+ }
bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE;
fcs = PPP_INITFCS;
- for (i=0; i<bcs->hw.tiger.tx_skb->len; i++) {
- val = bcs->hw.tiger.tx_skb->data[i];
+ for (i=0; i<bcs->tx_skb->len; i++) {
+ val = bcs->tx_skb->data[i];
fcs = PPP_FCS (fcs, val);
MAKE_RAW_BYTE;
}
}
val >>= 1;
}
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger make_raw: in %d out %d.%d",
- bcs->hw.tiger.tx_skb->len, s_cnt, bitcnt);
- debugl1(bcs->cs,tmp);
- }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger make_raw: in %ld out %d.%d",
+ bcs->tx_skb->len, s_cnt, bitcnt);
if (bitcnt) {
while (8>bitcnt++) {
s_val >>= 1;
bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
}
bcs->hw.tiger.sendcnt = s_cnt;
- bcs->tx_cnt -= bcs->hw.tiger.tx_skb->len;
+ bcs->tx_cnt -= bcs->tx_skb->len;
bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+ return(0);
}
static void got_frame(struct BCState *bcs, int count) {
register u_char r_val = bcs->hw.tiger.r_val;
register u_int bitcnt = bcs->hw.tiger.r_bitcnt;
u_int *p = buf;
- char tmp[64];
for (i=0;i<cnt;i++) {
val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff);
} else {
r_one=0;
state= HDLC_FLAG_SEARCH;
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger read_raw: zBit(%d,%d,%d) %x",
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger read_raw: zBit(%d,%d,%d) %x",
bcs->hw.tiger.r_tot,i,j,val);
- debugl1(bcs->cs,tmp);
- }
}
} else if (state == HDLC_FLAG_SEARCH) {
if (val & 1) {
bitcnt=0;
r_val=0;
state=HDLC_FLAG_FOUND;
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger read_raw: flag(%d,%d,%d) %x",
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger read_raw: flag(%d,%d,%d) %x",
bcs->hw.tiger.r_tot,i,j,val);
- debugl1(bcs->cs,tmp);
- }
}
r_one=0;
}
bcs->hw.tiger.r_fcs = PPP_INITFCS;
bcs->hw.tiger.rcvbuf[0] = r_val;
bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
bcs->hw.tiger.r_tot,i,j,r_val,val,
bcs->cs->hw.njet.irqstat0);
- debugl1(bcs->cs,tmp);
- }
}
} else if (state == HDLC_FRAME_FOUND) {
if (val & 1) {
state=HDLC_FLAG_SEARCH;
bcs->hw.tiger.r_err++;
} else {
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger frame end(%d,%d): fcs(%x) i %x",
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x",
i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
- debugl1(bcs->cs, tmp);
- }
if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
got_frame(bcs, (bitcnt>>3)-3);
} else
u_int *p;
int cnt = NETJET_DMA_SIZE/2;
- if (cs->hw.njet.irqstat0 & 4)
+ if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) {
+ debugl1(cs,"tiger warn read double dma %x/%x",
+ cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+ return;
+ } else {
+ cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ;
+ cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ);
+ }
+ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1)
p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1;
else
p = cs->bcs[0].hw.tiger.rec + cnt - 1;
read_raw(cs->bcs, p, cnt);
if (cs->bcs[1].mode == L1_MODE_HDLC)
read_raw(cs->bcs + 1, p, cnt);
- cs->hw.njet.irqstat0 &= 0xf3;
+ cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ;
}
static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
static void fill_dma(struct BCState *bcs)
{
- char tmp[64];
register u_int *p, *sp;
register int cnt;
- if (!bcs->hw.tiger.tx_skb)
+ if (!bcs->tx_skb)
return;
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger fill_dma1: c%d %4x", bcs->channel,
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger fill_dma1: c%d %4x", bcs->channel,
bcs->Flag);
- debugl1(bcs->cs,tmp);
- }
if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag))
return;
- make_raw_data(bcs);
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger fill_dma2: c%d %4x", bcs->channel,
+ if (make_raw_data(bcs))
+ return;
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger fill_dma2: c%d %4x", bcs->channel,
bcs->Flag);
- debugl1(bcs->cs,tmp);
- }
if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
}
write_raw(bcs, p, cnt);
}
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger fill_dma3: c%d %4x", bcs->channel,
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger fill_dma3: c%d %4x", bcs->channel,
bcs->Flag);
- debugl1(bcs->cs,tmp);
- }
}
static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
u_int mask, val, *p=buf;
u_int i, s_cnt;
- char tmp[64];
if (cnt <= 0)
return;
p = bcs->hw.tiger.send;
}
bcs->hw.tiger.s_tot += s_cnt;
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel,
- (u_int)buf, (u_int)p, s_cnt, cnt, bcs->hw.tiger.sendcnt,
- bcs->cs->hw.njet.irqstat0);
- debugl1(bcs->cs,tmp);
- }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger write_raw: c%d %x-%x %d/%d %d %x", bcs->channel,
+ (u_int)buf, (u_int)p, s_cnt, cnt,
+ bcs->hw.tiger.sendcnt, bcs->cs->hw.njet.irqstat0);
if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd");
bcs->hw.tiger.sp += s_cnt;
bcs->hw.tiger.sendp = p;
if (!bcs->hw.tiger.sendcnt) {
- if (!bcs->hw.tiger.tx_skb) {
- sprintf(tmp,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
- debugl1(bcs->cs, tmp);
+ if (!bcs->tx_skb) {
+ debugl1(bcs->cs,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
} else {
if (bcs->st->lli.l1writewakeup &&
- (PACKET_NOACK != bcs->hw.tiger.tx_skb->pkt_type))
- bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.tiger.tx_skb->len);
- dev_kfree_skb(bcs->hw.tiger.tx_skb);
- bcs->hw.tiger.tx_skb = NULL;
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
}
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
bcs->hw.tiger.free = cnt - s_cnt;
test_and_clear_bit(BC_FLG_HALF, &bcs->Flag);
test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
}
- if ((bcs->hw.tiger.tx_skb = skb_dequeue(&bcs->squeue))) {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
fill_dma(bcs);
} else {
mask ^= 0xffffffff;
if (p>bcs->hw.tiger.s_end)
p = bcs->hw.tiger.send;
}
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp, "tiger write_raw: fill rest %d",
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "tiger write_raw: fill rest %d",
cnt - s_cnt);
- debugl1(bcs->cs,tmp);
- }
}
bcs->event |= 1 << B_XMTBUFREADY;
queue_task(&bcs->tqueue, &tq_immediate);
test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
bcs->hw.tiger.free += cnt;
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger write_raw: fill half");
- debugl1(bcs->cs,tmp);
- }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger write_raw: fill half");
} else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
- if (bcs->cs->debug & L1_DEB_HSCX) {
- sprintf(tmp,"tiger write_raw: fill full");
- debugl1(bcs->cs,tmp);
- }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger write_raw: fill full");
}
}
static void write_tiger(struct IsdnCardState *cs) {
u_int *p, cnt = NETJET_DMA_SIZE/2;
- if (cs->hw.njet.irqstat0 & 1)
+ if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) {
+ debugl1(cs,"tiger warn write double dma %x/%x",
+ cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+ return;
+ } else {
+ cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE;
+ cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE);
+ }
+ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE_1)
p = cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1;
else
p = cs->bcs[0].hw.tiger.send + cnt - 1;
write_raw(cs->bcs, p, cnt);
if (cs->bcs[1].mode == L1_MODE_HDLC)
write_raw(cs->bcs + 1, p, cnt);
- cs->hw.njet.irqstat0 &= 0xfc;
+ cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE;
}
static void
long flags;
switch (pr) {
- case (PH_DATA_REQ):
+ case (PH_DATA | REQUEST):
save_flags(flags);
cli();
- if (st->l1.bcs->hw.tiger.tx_skb) {
+ if (st->l1.bcs->tx_skb) {
skb_queue_tail(&st->l1.bcs->squeue, skb);
restore_flags(flags);
} else {
- st->l1.bcs->hw.tiger.tx_skb = skb;
+ st->l1.bcs->tx_skb = skb;
st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
restore_flags(flags);
}
break;
- case (PH_PULL_IND):
- if (st->l1.bcs->hw.tiger.tx_skb) {
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
printk(KERN_WARNING "tiger_l2l1: this shouldn't happen\n");
break;
}
save_flags(flags);
cli();
- st->l1.bcs->hw.tiger.tx_skb = skb;
+ st->l1.bcs->tx_skb = skb;
st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
restore_flags(flags);
break;
- case (PH_PULL_REQ):
- if (!st->l1.bcs->hw.tiger.tx_skb) {
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
- st->l1.l1l2(st, PH_PULL_CNF, NULL);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ mode_tiger(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
}
}
+
void
close_tigerstate(struct BCState *bcs)
{
- struct sk_buff *skb;
-
- mode_tiger(bcs, 0, 0);
+ mode_tiger(bcs, 0, bcs->channel);
if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
if (bcs->hw.tiger.rcvbuf) {
kfree(bcs->hw.tiger.rcvbuf);
kfree(bcs->hw.tiger.sendbuf);
bcs->hw.tiger.sendbuf = NULL;
}
- while ((skb = skb_dequeue(&bcs->rqueue))) {
- dev_kfree_skb(skb);
- }
- while ((skb = skb_dequeue(&bcs->squeue))) {
- dev_kfree_skb(skb);
- }
- if (bcs->hw.tiger.tx_skb) {
- dev_kfree_skb(bcs->hw.tiger.tx_skb);
- bcs->hw.tiger.tx_skb = NULL;
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
}
}
}
static int
-open_tigerstate(struct IsdnCardState *cs, int bc)
+open_tigerstate(struct IsdnCardState *cs, struct BCState *bcs)
{
- struct BCState *bcs = cs->bcs + bc;
-
if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
- if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_KERNEL))) {
+ if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for tiger.rcvbuf\n");
return (1);
}
- if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_KERNEL))) {
+ if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for tiger.sendbuf\n");
return (1);
skb_queue_head_init(&bcs->rqueue);
skb_queue_head_init(&bcs->squeue);
}
- bcs->hw.tiger.tx_skb = NULL;
+ bcs->tx_skb = NULL;
bcs->hw.tiger.sendcnt = 0;
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
bcs->event = 0;
return (0);
}
-static void
-tiger_manl1(struct PStack *st, int pr,
- void *arg)
-{
- switch (pr) {
- case (PH_ACTIVATE_REQ):
- test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- mode_tiger(st->l1.bcs, st->l1.mode, st->l1.bc);
- st->l1.l1man(st, PH_ACTIVATE_CNF, NULL);
- break;
- case (PH_DEACTIVATE_REQ):
- if (!test_bit(BC_FLG_BUSY, &st->l1.bcs->Flag))
- mode_tiger(st->l1.bcs, 0, 0);
- test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
- break;
- }
-}
-
int
setstack_tiger(struct PStack *st, struct BCState *bcs)
{
- if (open_tigerstate(st->l1.hardware, bcs->channel))
+ bcs->channel = st->l1.bc;
+ if (open_tigerstate(st->l1.hardware, bcs))
return (-1);
st->l1.bcs = bcs;
st->l2.l2l1 = tiger_l2l1;
- st->ma.manl1 = tiger_manl1;
setstack_manager(st);
bcs->st = st;
+ setstack_l1_B(st);
return (0);
}
__initfunc(void
inittiger(struct IsdnCardState *cs))
{
- char tmp[128];
-
if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_SIZE * sizeof(unsigned int),
GFP_KERNEL | GFP_DMA))) {
printk(KERN_WARNING
cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end;
memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
- sprintf(tmp, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send,
+ debugl1(cs, "tiger: send buf %x - %x", (u_int)cs->bcs[0].hw.tiger.send,
(u_int)(cs->bcs[0].hw.tiger.send + NETJET_DMA_SIZE - 1));
- debugl1(cs, tmp);
outl(virt_to_bus(cs->bcs[0].hw.tiger.send),
cs->hw.njet.base + NETJET_DMA_READ_START);
outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq),
"HiSax: No memory for tiger.rec\n");
return;
}
- sprintf(tmp, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec,
+ debugl1(cs, "tiger: rec buf %x - %x", (u_int)cs->bcs[0].hw.tiger.rec,
(u_int)(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1));
- debugl1(cs, tmp);
cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec;
memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_SIZE * sizeof(unsigned int));
outl(virt_to_bus(cs->bcs[0].hw.tiger.rec),
cs->hw.njet.base + NETJET_DMA_WRITE_IRQ);
outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_SIZE - 1),
cs->hw.njet.base + NETJET_DMA_WRITE_END);
- sprintf(tmp, "tiger: dmacfg %x/%x pulse=%d",
+ debugl1(cs, "tiger: dmacfg %x/%x pulse=%d",
inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
- debugl1(cs, tmp);
cs->hw.njet.last_is0 = 0;
cs->bcs[0].BC_SetStack = setstack_tiger;
cs->bcs[1].BC_SetStack = setstack_tiger;
netjet_interrupt(int intno, void *dev_id, struct pt_regs *regs)
{
struct IsdnCardState *cs = dev_id;
- u_char val, sval, stat = 1;
- char tmp[128];
+ u_char val, sval;
+ long flags;
if (!cs) {
printk(KERN_WARNING "NETjet: Spurious interrupt!\n");
if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
NETJET_ISACIRQ)) {
val = ReadISAC(cs, ISAC_ISTA);
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "tiger: i1 %x %x", sval, val);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "tiger: i1 %x %x", sval, val);
if (val) {
isac_interrupt(cs, val);
- stat |= 2;
+ WriteISAC(cs, ISAC_MASK, 0xFF);
+ WriteISAC(cs, ISAC_MASK, 0x0);
}
}
- if ((cs->hw.njet.irqstat0 = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
-/* sprintf(tmp, "tiger: ist0 %x %x %x %x/%x pulse=%d",
+ save_flags(flags);
+ cli();
+ if ((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT0))) {
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ restore_flags(flags);
+ return;
+ }
+ cs->hw.njet.irqstat0 = sval;
+ restore_flags(flags);
+/* debugl1(cs, "tiger: ist0 %x %x %x %x/%x pulse=%d",
sval,
bytein(cs->hw.njet.base + NETJET_DMACTRL),
bytein(cs->hw.njet.base + NETJET_IRQMASK0),
inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
- debugl1(cs, tmp);
*/
- if (cs->hw.njet.last_is0 & cs->hw.njet.irqstat0 & 0xf) {
- sprintf(tmp, "tiger: ist0 %x->%x irq lost",
- cs->hw.njet.last_is0, cs->hw.njet.irqstat0);
- debugl1(cs, tmp);
- }
- cs->hw.njet.last_is0 = cs->hw.njet.irqstat0;
/* cs->hw.njet.irqmask0 = ((0x0f & cs->hw.njet.irqstat0) ^ 0x0f) | 0x30;
*/ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, cs->hw.njet.irqstat0);
/* byteout(cs->hw.njet.base + NETJET_IRQMASK0, cs->hw.njet.irqmask0);
-*/ if (cs->hw.njet.irqstat0 & 0x0c)
+*/ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ)
read_tiger(cs);
- if (cs->hw.njet.irqstat0 & 0x03)
+ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE)
write_tiger(cs);
- }
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ restore_flags(flags);
+
/* if (!testcnt--) {
cs->hw.njet.dmactrl = 0;
byteout(cs->hw.njet.base + NETJET_DMACTRL,
cs->hw.njet.dmactrl);
byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
}
-*/ if (stat & 2) {
- WriteISAC(cs, ISAC_MASK, 0xFF);
- WriteISAC(cs, ISAC_MASK, 0x0);
- }
+*/
}
static void
return(0);
case CARD_SETIRQ:
return(request_irq(cs->irq, &netjet_interrupt,
- I4L_IRQ_FLAG, "HiSax", cs));
+ I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs));
case CARD_INIT:
inittiger(cs);
clear_pending_isac_ints(cs);
initisac(cs);
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
return(0);
case CARD_TEST:
return(0);
-static int pci_index __initdata = 0;
+static struct pci_dev *dev_netjet __initdata = NULL;
__initfunc(int
setup_netjet(struct IsdnCard *card))
int bytecnt;
struct IsdnCardState *cs = card->cs;
char tmp[64];
-#if CONFIG_PCI
- u_char pci_bus, pci_device_fn, pci_irq;
- u_int pci_ioaddr, found;
-#endif
strcpy(tmp, NETjet_revision);
printk(KERN_INFO "HiSax: Traverse Tech. NETjet driver Rev. %s\n", HiSax_getrev(tmp));
if (cs->typ != ISDN_CTYPE_NETJET)
return(0);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
#if CONFIG_PCI
- found = 0;
- for (; pci_index < 0xff; pci_index++) {
- if (pcibios_find_device(PCI_VENDOR_TRAVERSE_TECH,
- PCI_NETJET_ID, pci_index, &pci_bus, &pci_device_fn)
- == PCIBIOS_SUCCESSFUL)
- found = 1;
- else
- break;
- /* get IRQ */
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &pci_irq);
-
- /* get IO address */
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &pci_ioaddr);
- if (found)
- break;
- }
- if (!found) {
- printk(KERN_WARNING "NETjet: No PCI card found\n");
+ if (!pci_present()) {
+ printk(KERN_ERR "Netjet: no PCI bus present\n");
return(0);
}
- if (!pci_irq) {
+ if ((dev_netjet = pci_find_device(PCI_VENDOR_TRAVERSE_TECH,
+ PCI_NETJET_ID, dev_netjet))) {
+ cs->irq = dev_netjet->irq;
+ if (!cs->irq) {
printk(KERN_WARNING "NETjet: No IRQ for PCI card found\n");
return(0);
}
- if (!pci_ioaddr) {
+ cs->hw.njet.base = dev_netjet->base_address[0] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ if (!cs->hw.njet.base) {
printk(KERN_WARNING "NETjet: No IO-Adr for PCI card found\n");
return(0);
}
- pci_ioaddr &= ~3; /* remove io/mem flag */
- cs->hw.njet.base = pci_ioaddr;
- cs->hw.njet.auxa = pci_ioaddr + NETJET_AUXDATA;
- cs->hw.njet.isac = pci_ioaddr | NETJET_ISAC_OFF;
- cs->irq = pci_irq;
+ cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+ cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
bytecnt = 256;
+ } else {
+ printk(KERN_WARNING "NETjet: No PCI card found\n");
+ return(0);
+ }
#else
printk(KERN_WARNING "NETjet: NO_PCI_BIOS\n");
printk(KERN_WARNING "NETjet: unable to config NETJET PCI\n");
-/* $Id: niccy.c,v 1.2 1998/02/11 17:31:04 keil Exp $
+/* $Id: niccy.c,v 1.4 1998/04/16 19:16:48 keil Exp $
* niccy.c low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
* compatible (SAGEM cybermodem)
* Thanks to Dr. Neuhaus and SAGEM for informations
*
* $Log: niccy.c,v $
- * Revision 1.2 1998/02/11 17:31:04 keil
- * new file
+ * Revision 1.4 1998/04/16 19:16:48 keil
+ * need config.h
*
+ * Revision 1.3 1998/04/15 16:42:59 keil
+ * new init code
*
+ * Revision 1.2 1998/02/11 17:31:04 keil
+ * new file
*
*/
-#include <linux/config.h>
+
#define __NO_VERSION__
+#include <linux/config.h>
#include "hisax.h"
#include "isac.h"
#include "hscx.h"
#include "isdnl1.h"
#include <linux/pci.h>
-#include <linux/bios32.h>
extern const char *CardType[];
-const char *niccy_revision = "$Revision: 1.2 $";
+const char *niccy_revision = "$Revision: 1.4 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
/* PCI stuff */
#define PCI_VENDOR_DR_NEUHAUS 0x1267
#define PCI_NICCY_ID 0x1016
+#define PCI_IRQ_CTRL_REG 0x38
+#define PCI_IRQ_ENABLE 0x1f00
+#define PCI_IRQ_DISABLE 0xff0000
+#define PCI_IRQ_ASSERT 0x800000
static inline u_char
readreg(unsigned int ale, unsigned int adr, u_char off)
printk(KERN_WARNING "Niccy: Spurious interrupt!\n");
return;
}
+ if (cs->subtyp == NICCY_PCI) {
+ int ival;
+ ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ if (!(ival & PCI_IRQ_ASSERT)) /* IRQ not for us (shared) */
+ return;
+ outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ }
val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_ISTA + 0x40);
Start_HSCX:
if (val) {
void
release_io_niccy(struct IsdnCardState *cs)
{
- if (cs->subtyp == NICCY_PCI)
+ if (cs->subtyp == NICCY_PCI) {
+ int val;
+
+ val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ val &= PCI_IRQ_DISABLE;
+ outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ release_region(cs->hw.niccy.cfg_reg, 0x80);
release_region(cs->hw.niccy.isac, 4);
- else {
+ } else {
release_region(cs->hw.niccy.isac, 2);
release_region(cs->hw.niccy.isac_ale, 2);
}
static void
niccy_reset(struct IsdnCardState *cs)
{
- // No reset procedure known
+ int val, nval;
+
+ val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ nval = val | PCI_IRQ_ENABLE;
+ outl(nval, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+
+ inithscxisac(cs, 3);
}
static int
niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
{
+ int imode;
+
switch (mt) {
case CARD_RESET:
niccy_reset(cs);
release_io_niccy(cs);
return(0);
case CARD_SETIRQ:
+ if (cs->subtyp == NICCY_PCI)
+ imode = I4L_IRQ_FLAG | SA_SHIRQ;
+ else
+ imode = I4L_IRQ_FLAG;
return(request_irq(cs->irq, &niccy_interrupt,
- I4L_IRQ_FLAG, "HiSax", cs));
+ imode, "HiSax", cs));
+ break;
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ niccy_reset(cs);
return(0);
case CARD_TEST:
return(0);
return(0);
}
-static int pci_index __initdata = 0;
+static struct pci_dev *niccy_dev __initdata = NULL;
__initfunc(int
setup_niccy(struct IsdnCard *card))
request_region(cs->hw.niccy.isac_ale, 2, "niccy addr");
} else {
#if CONFIG_PCI
- u_char pci_bus, pci_device_fn, pci_irq;
u_int pci_ioaddr;
+ if (!pci_present()) {
+ printk(KERN_ERR "Niccy: no PCI bus present\n");
+ return(0);
+ }
+
cs->subtyp = 0;
- for (; pci_index < 0xff; pci_index++) {
- if (pcibios_find_device(PCI_VENDOR_DR_NEUHAUS,
- PCI_NICCY_ID, pci_index, &pci_bus, &pci_device_fn)
- == PCIBIOS_SUCCESSFUL)
- cs->subtyp = NICCY_PCI;
- else
- break;
+ if ((niccy_dev = pci_find_device(PCI_VENDOR_DR_NEUHAUS,
+ PCI_NICCY_ID, niccy_dev))) {
/* get IRQ */
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &pci_irq);
-
- /* get IO address */
- /* if it won't work try the other PCI addresses
- * PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_5
- */
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_2, &pci_ioaddr);
- if (cs->subtyp)
- break;
- }
- if (!cs->subtyp) {
- printk(KERN_WARNING "Niccy: No PCI card found\n");
+ if (!niccy_dev->irq) {
+ printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
return(0);
}
- if (!pci_irq) {
- printk(KERN_WARNING "Niccy: No IRQ for PCI card found\n");
+ cs->irq = niccy_dev->irq;
+ if (!niccy_dev->base_address[0]) {
+ printk(KERN_WARNING "Niccy: No IO-Adr for PCI cfg found\n");
return(0);
}
-
- if (!pci_ioaddr) {
+ cs->hw.niccy.cfg_reg = niccy_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+ if (!niccy_dev->base_address[1]) {
printk(KERN_WARNING "Niccy: No IO-Adr for PCI card found\n");
return(0);
}
- pci_ioaddr &= ~3; /* remove io/mem flag */
+ pci_ioaddr = niccy_dev->base_address[1] & PCI_BASE_ADDRESS_IO_MASK;
cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
- cs->irq = pci_irq;
+ cs->subtyp = NICCY_PCI;
+ } else {
+ printk(KERN_WARNING "Niccy: No PCI card found\n");
+ return(0);
+ }
if (check_region((cs->hw.niccy.isac), 4)) {
printk(KERN_WARNING
"HiSax: %s data port %x-%x already in use\n",
return (0);
} else
request_region(cs->hw.niccy.isac, 4, "niccy");
+ if (check_region(cs->hw.niccy.cfg_reg, 0x80)) {
+ printk(KERN_WARNING
+ "HiSax: %s pci port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.niccy.cfg_reg,
+ cs->hw.niccy.cfg_reg + 0x80);
+ release_region(cs->hw.niccy.isac, 4);
+ return (0);
+ } else {
+ request_region(cs->hw.niccy.cfg_reg, 0x80, "niccy pci");
+ }
#else
printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
"HiSax: %s %s config irq:%d data:0x%X ale:0x%X\n",
CardType[cs->typ], (cs->subtyp==1) ? "PnP":"PCI",
cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
- niccy_reset(cs);
cs->readisac = &ReadISAC;
cs->writeisac = &WriteISAC;
cs->readisacfifo = &ReadISACfifo;
-/* $Id: q931.c,v 1.6 1997/07/27 21:09:44 keil Exp $
+/* $Id: q931.c,v 1.7 1998/11/15 23:55:17 keil Exp $
* q931.c code to decode ITU Q.931 call control messages
*
*
*
* $Log: q931.c,v $
+ * Revision 1.7 1998/11/15 23:55:17 keil
+ * changes from 2.0
+ *
* Revision 1.6 1997/07/27 21:09:44 keil
* move functions to isdnl3.c
*
{MT_N0_CLO_ACK, "CLOse ACKnowledge"}
};
-int mt_n0_len = (sizeof(mt_n0) / sizeof(struct MessageType));
+#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType))
static
struct MessageType mt_n1[] =
{MT_N1_STAT, "STATus"}
};
-int mt_n1_len = (sizeof(mt_n1) / sizeof(struct MessageType));
+#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
static struct MessageType fac_1tr6[] =
{
{FAC_Rueckwechsel, "Rueckwechsel"},
{FAC_Umleitung, "Umleitung"}
};
-int fac_1tr6_len = (sizeof(fac_1tr6) / sizeof(struct MessageType));
-
-
+#define FAC_1TR6_LEN (sizeof(fac_1tr6) / sizeof(struct MessageType))
static int
prbits(char *dest, u_char b, int start, int len)
{WE0_userInfo, "User Info", general}
};
-static int we_0_len = (sizeof(we_0) / sizeof(struct InformationElement));
+#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement))
static struct InformationElement we_6[] =
{
{WE6_statusCalled, "Status Called", general},
{WE6_addTransAttr, "Additional Transmission Attributes", general}
};
-static int we_6_len = (sizeof(we_6) / sizeof(struct InformationElement));
+#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement))
int
QuickHex(char *txt, u_char * p, int cnt)
}
void
-LogFrame(struct IsdnCardState *sp, u_char * buf, int size)
+LogFrame(struct IsdnCardState *cs, u_char * buf, int size)
{
char *dp;
if (size < 1)
return;
- dp = sp->dlogspace;
- if (size < 4096 / 3 - 10) {
- dp += sprintf(dp, "HEX:");
+ dp = cs->dlog;
+ if (size < MAX_DLOG_SPACE / 3 - 10) {
+ *dp++ = 'H';
+ *dp++ = 'E';
+ *dp++ = 'X';
+ *dp++ = ':';
dp += QuickHex(dp, buf, size);
dp--;
*dp++ = '\n';
*dp = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
} else
- sprintf(dp, "LogFrame: warning Frame too big (%d)\n",
- size);
- HiSax_putstatus(sp, sp->dlogspace);
+ HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
}
void
-dlogframe(struct IsdnCardState *sp, u_char * buf, int size, char *comment)
+dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
{
- u_char *bend = buf + size;
+ u_char *bend, *buf;
char *dp;
unsigned char pd, cr_l, cr, mt;
- int i, cs = 0, cs_old = 0, cs_fest = 0;
+ unsigned char sapi, tei, ftyp;
+ int i, cset = 0, cs_old = 0, cs_fest = 0;
+ int size, finish = 0;
- if (size < 1)
+ if (skb->len < 3)
return;
/* display header */
- dp = sp->dlogspace;
- dp += sprintf(dp, "%s\n", comment);
-
+ dp = cs->dlog;
+ dp += jiftime(dp, jiffies);
+ *dp++ = ' ';
+ sapi = skb->data[0] >> 2;
+ tei = skb->data[1] >> 1;
+ ftyp = skb->data[2];
+ buf = skb->data;
+ dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
+ size = skb->len;
+
+ if (tei == GROUP_TEI) {
+ if (sapi == CTRL_SAPI) { /* sapi 0 */
+ if (ftyp == 3) {
+ dp += sprintf(dp, "broadcast\n");
+ buf += 3;
+ size -= 3;
+ } else {
+ dp += sprintf(dp, "no UI broadcast\n");
+ finish = 1;
+ }
+ } else if (sapi == TEI_SAPI) {
+ dp += sprintf(dp, "tei managment\n");
+ finish = 1;
+ } else {
+ dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
+ finish = 1;
+ }
+ } else {
+ if (sapi == CTRL_SAPI) {
+ if (!(ftyp & 1)) { /* IFrame */
+ dp += sprintf(dp, "with tei %d\n", tei);
+ buf += 4;
+ size -= 4;
+ } else {
+ dp += sprintf(dp, "SFrame with tei %d\n", tei);
+ finish = 1;
+ }
+ } else {
+ dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
+ finish = 1;
+ }
+ }
+ bend = skb->data + skb->len;
+ if (buf >= bend) {
+ dp += sprintf(dp, "frame too short\n");
+ finish = 1;
+ }
+ if (finish) {
+ *dp = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
+ return;
+ }
if ((0xfe & buf[0]) == PROTO_DIS_N0) { /* 1TR6 */
/* locate message type */
pd = *buf++;
cr = 0;
mt = *buf++;
if (pd == PROTO_DIS_N0) { /* N0 */
- for (i = 0; i < mt_n0_len; i++)
+ for (i = 0; i < MT_N0_LEN; i++)
if (mt_n0[i].nr == mt)
break;
/* display message type if it exists */
- if (i == mt_n0_len)
+ if (i == MT_N0_LEN)
dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
cr & 0x7f, (cr & 0x80) ? "called" : "caller",
size, mt);
cr & 0x7f, (cr & 0x80) ? "called" : "caller",
size, mt_n0[i].descr);
} else { /* N1 */
- for (i = 0; i < mt_n1_len; i++)
+ for (i = 0; i < MT_N1_LEN; i++)
if (mt_n1[i].nr == mt)
break;
/* display message type if it exists */
- if (i == mt_n1_len)
+ if (i == MT_N1_LEN)
dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
cr & 0x7f, (cr & 0x80) ? "called" : "caller",
size, mt);
switch ((*buf >> 4) & 7) {
case 1:
dp += sprintf(dp, " Shift %x\n", *buf & 0xf);
- cs_old = cs;
- cs = *buf & 7;
+ cs_old = cset;
+ cset = *buf & 7;
cs_fest = *buf & 8;
break;
case 3:
continue;
}
/* No, locate it in the table */
- if (cs == 0) {
- for (i = 0; i < we_0_len; i++)
+ if (cset == 0) {
+ for (i = 0; i < WE_0_LEN; i++)
if (*buf == we_0[i].nr)
break;
/* When found, give appropriate msg */
- if (i != we_0_len) {
+ if (i != WE_0_LEN) {
dp += sprintf(dp, " %s\n", we_0[i].descr);
dp += we_0[i].f(dp, buf);
} else
- dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
- } else if (cs == 6) {
- for (i = 0; i < we_6_len; i++)
+ dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+ } else if (cset == 6) {
+ for (i = 0; i < WE_6_LEN; i++)
if (*buf == we_6[i].nr)
break;
/* When found, give appropriate msg */
- if (i != we_6_len) {
+ if (i != WE_6_LEN) {
dp += sprintf(dp, " %s\n", we_6[i].descr);
dp += we_6[i].f(dp, buf);
} else
- dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+ dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
} else
- dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cs, *buf, buf[1]);
+ dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
/* Skip to next element */
if (cs_fest == 8) {
- cs = cs_old;
+ cset = cs_old;
cs_old = 0;
cs_fest = 0;
}
} else {
dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
}
- dp += sprintf(dp, "\n");
- HiSax_putstatus(sp, sp->dlogspace);
+ *dp = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
}
-/* $Id: rawhdlc.c,v 1.2 1998/02/09 10:53:51 keil Exp $
+/* $Id: rawhdlc.c,v 1.3 1998/06/17 19:51:21 he Exp $
* rawhdlc.c support routines for cards that don't support HDLC
*
--- /dev/null
+/* $Id: s0box.c,v 2.1 1998/04/15 16:38:24 keil Exp $
+
+ * s0box.c low level stuff for Creatix S0BOX
+ *
+ * Author S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de)
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *s0box_revision = "$Revision: 2.1 $";
+
+static inline void
+writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x1c,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p((addr+off)&0x7f,padr);
+ outb_p(0x16,padr+2);
+ outb_p(val,padr);
+ outb_p(0x17,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ restore_flags(flags);
+}
+
+static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ;
+
+static inline u_char
+readreg(unsigned int padr, signed int addr, u_char off) {
+ register u_char n1, n2;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x1c,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p((addr+off)|0x80,padr);
+ outb_p(0x16,padr+2);
+ outb_p(0x17,padr+2);
+ n1 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x16,padr+2);
+ n2 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ restore_flags(flags);
+ return nibtab[n1] | (nibtab[n2] << 4);
+}
+
+static inline void
+read_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+ int i;
+ register u_char n1, n2;
+
+ outb_p(0x1c, padr+2);
+ outb_p(0x14, padr+2);
+ outb_p(adr|0x80, padr);
+ outb_p(0x16, padr+2);
+ for (i=0; i<size; i++) {
+ outb_p(0x17, padr+2);
+ n1 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x16,padr+2);
+ n2 = (inb_p(padr+1) >> 3) & 0x17;
+ *(data++)=nibtab[n1] | (nibtab[n2] << 4);
+ }
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ return;
+}
+
+static inline void
+write_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+ int i;
+ outb_p(0x1c, padr+2);
+ outb_p(0x14, padr+2);
+ outb_p(adr&0x7f, padr);
+ for (i=0; i<size; i++) {
+ outb_p(0x16, padr+2);
+ outb_p(*(data++), padr);
+ outb_p(0x17, padr+2);
+ }
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ return;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+ struct IsdnCardState *cs = dev_id;
+ u_char val, stat = 0;
+ int count = 0;
+
+ if (!cs) {
+ printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+ return;
+ }
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+ Start_HSCX:
+ if (val) {
+ hscx_int_main(cs, val);
+ stat |= 1;
+ }
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val) {
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ count++;
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ if (count >= MAXCOUNT)
+ printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count);
+ if (stat & 1) {
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+ }
+ if (stat & 2) {
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0);
+ }
+}
+
+void
+release_io_s0box(struct IsdnCardState *cs)
+{
+ release_region(cs->hw.teles3.cfg_reg, 8);
+}
+
+static int
+S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ switch (mt) {
+ case CARD_RESET:
+ break;
+ case CARD_RELEASE:
+ release_io_s0box(cs);
+ break;
+ case CARD_SETIRQ:
+ return(request_irq(cs->irq, &s0box_interrupt,
+ I4L_IRQ_FLAG, "HiSax", cs));
+ case CARD_INIT:
+ inithscxisac(cs, 3);
+ break;
+ case CARD_TEST:
+ break;
+ }
+ return(0);
+}
+
+__initfunc(int
+setup_s0box(struct IsdnCard *card))
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, s0box_revision);
+ printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_S0BOX)
+ return (0);
+
+ cs->hw.teles3.cfg_reg = card->para[1];
+ cs->hw.teles3.hscx[0] = -0x20;
+ cs->hw.teles3.hscx[1] = 0x0;
+ cs->hw.teles3.isac = 0x20;
+ cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+ cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+ cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+ cs->irq = card->para[0];
+ if (check_region(cs->hw.teles3.cfg_reg,8)) {
+ printk(KERN_WARNING
+ "HiSax: %s ports %x-%x already in use\n",
+ CardType[cs->typ],
+ cs->hw.teles3.cfg_reg,
+ cs->hw.teles3.cfg_reg + 7);
+ return 0;
+ } else
+ request_region(cs->hw.teles3.cfg_reg, 8, "S0Box parallel I/O");
+ printk(KERN_INFO
+ "HiSax: %s config irq:%d isac:0x%x cfg:0x%x\n",
+ CardType[cs->typ], cs->irq,
+ cs->hw.teles3.isac, cs->hw.teles3.cfg_reg);
+ printk(KERN_INFO
+ "HiSax: hscx A:0x%x hscx B:0x%x\n",
+ cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &S0Box_card_msg;
+ ISACVersion(cs, "S0Box:");
+ if (HscxVersion(cs, "S0Box:")) {
+ printk(KERN_WARNING
+ "S0Box: wrong HSCX versions check IO address\n");
+ release_io_s0box(cs);
+ return (0);
+ }
+ return (1);
+}
-/* $Id: sedlbauer.c,v 1.6 1998/02/09 18:46:06 keil Exp $
+/* $Id: sedlbauer.c,v 1.9 1998/11/15 23:55:20 keil Exp $
* sedlbauer.c low level stuff for Sedlbauer cards
- * includes Support for the Sedlbauer Speed Star
- * derived from the original file dynalink.c from Karsten Keil
+ * includes support for the Sedlbauer speed star (speed star II),
+ * support for the Sedlbauer speed fax+,
+ * support for the Sedlbauer ISDN-Controller PC/104 and
+ * support for the Sedlbauer speed pci
+ * derived from the original file asuscom.c from Karsten Keil
*
* Copyright (C) 1997,1998 Marcus Niemann (for the modifications to
- * the original file dynalink.c)
+ * the original file asuscom.c)
*
* Author Marcus Niemann (niemann@www-bib.fh-bielefeld.de)
*
* Edgar Toernig
*
* $Log: sedlbauer.c,v $
+ * Revision 1.9 1998/11/15 23:55:20 keil
+ * changes from 2.0
+ *
+ * Revision 1.8 1998/08/13 23:34:51 keil
+ * starting speedfax+ (ISAR) support
+ *
+ * Revision 1.7 1998/04/15 16:44:33 keil
+ * new init code
+ *
* Revision 1.6 1998/02/09 18:46:06 keil
* Support for Sedlbauer PCMCIA (Marcus Niemann)
*
*
*/
+/* Supported cards:
+ * Card: Chip: Configuration: Comment:
+ * ---------------------------------------------------------------------
+ * Speed Card ISAC_HSCX DIP-SWITCH
+ * Speed Win ISAC_HSCX ISAPNP
+ * Speed Fax+ ISAC_ISAR ISAPNP #HDLC works#
+ * Speed Star ISAC_HSCX CARDMGR
+ * Speed Win2 IPAC ISAPNP
+ * ISDN PC/104 IPAC DIP-SWITCH
+ * Speed Star2 IPAC CARDMGR
+ * Speed PCI IPAC PNP
+ *
+ * Important:
+ * For the sedlbauer speed fax+ to work properly you have to download
+ * the firmware onto the card.
+ * For example: hisaxctrl <DriverID> 9 ISAR.BIN
+*/
+
+#define SEDLBAUER_PCI 1
+
#define __NO_VERSION__
+#include <linux/config.h>
#include "hisax.h"
#include "isac.h"
+#include "ipac.h"
#include "hscx.h"
+#include "isar.h"
#include "isdnl1.h"
+#include <linux/pci.h>
extern const char *CardType[];
-const char *Sedlbauer_revision = "$Revision: 1.6 $";
+const char *Sedlbauer_revision = "$Revision: 1.9 $";
const char *Sedlbauer_Types[] =
-{"None", "Speed Card", "Speed Win", "Speed Star"};
+ {"None", "speed card/win", "speed star", "speed fax+",
+ "speed win II / ISDN PC/104", "speed star II", "speed pci"};
+
+#ifdef SEDLBAUER_PCI
+#define PCI_VENDOR_SEDLBAUER 0xe159
+#define PCI_SPEEDPCI_ID 0x02
+#endif
-#define SEDL_SPEED_CARD 1
-#define SEDL_SPEED_WIN 2
-#define SEDL_SPEED_STAR 3
+#define SEDL_SPEED_CARD_WIN 1
+#define SEDL_SPEED_STAR 2
+#define SEDL_SPEED_FAX 3
+#define SEDL_SPEED_WIN2_PC104 4
+#define SEDL_SPEED_STAR2 5
+#define SEDL_SPEED_PCI 6
+
+#define SEDL_CHIP_TEST 0
+#define SEDL_CHIP_ISAC_HSCX 1
+#define SEDL_CHIP_ISAC_ISAR 2
+#define SEDL_CHIP_IPAC 3
+
+#define SEDL_BUS_ISA 1
+#define SEDL_BUS_PCI 2
+#define SEDL_BUS_PCMCIA 3
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
-#define SEDL_RESET_ON 0
-#define SEDL_RESET_OFF 1
-#define SEDL_ISAC 2
-#define SEDL_HSCX 3
-#define SEDL_ADR 4
+#define SEDL_HSCX_ISA_RESET_ON 0
+#define SEDL_HSCX_ISA_RESET_OFF 1
+#define SEDL_HSCX_ISA_ISAC 2
+#define SEDL_HSCX_ISA_HSCX 3
+#define SEDL_HSCX_ISA_ADR 4
+
+#define SEDL_HSCX_PCMCIA_RESET 0
+#define SEDL_HSCX_PCMCIA_ISAC 1
+#define SEDL_HSCX_PCMCIA_HSCX 2
+#define SEDL_HSCX_PCMCIA_ADR 4
+
+#define SEDL_ISAR_ISA_ISAC 4
+#define SEDL_ISAR_ISA_ISAR 6
+#define SEDL_ISAR_ISA_ADR 8
+#define SEDL_ISAR_ISA_ISAR_RESET_ON 10
+#define SEDL_ISAR_ISA_ISAR_RESET_OFF 12
-#define SEDL_PCMCIA_RESET 0
-#define SEDL_PCMCIA_ISAC 1
-#define SEDL_PCMCIA_HSCX 2
-#define SEDL_PCMCIA_ADR 4
+#define SEDL_IPAC_ANY_ADR 0
+#define SEDL_IPAC_ANY_IPAC 2
+
+#define SEDL_IPAC_PCI_BASE 0
+#define SEDL_IPAC_PCI_ADR 0xc0
+#define SEDL_IPAC_PCI_IPAC 0xc8
#define SEDL_RESET 0x3 /* same as DOS driver */
writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
}
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
static u_char
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
{
cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value);
}
+/* ISAR access routines
+ * mode = 0 access with IRQ on
+ * mode = 1 access with IRQ off
+ * mode = 2 access with IRQ off and using last offset
+ */
+
+static u_char
+ReadISAR(struct IsdnCardState *cs, int mode, u_char offset)
+{
+ if (mode == 0)
+ return (readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset));
+ else if (mode == 1)
+ byteout(cs->hw.sedl.adr, offset);
+ return(bytein(cs->hw.sedl.hscx));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+ if (mode == 0)
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset, value);
+ else {
+ if (mode == 1)
+ byteout(cs->hw.sedl.adr, offset);
+ byteout(cs->hw.sedl.hscx, value);
+ }
+}
+
/*
* fast interrupt HSCX stuff goes here
*/
return;
}
- if ((cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) && (*cs->busy_flag == 1)) {
+ if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) {
/* The card tends to generate interrupts while being removed
causing us to just crash the kernel. bad. */
printk(KERN_WARNING "Sedlbauer: card not available!\n");
}
}
+static void
+sedlbauer_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista, val, icnt = 20;
+
+ if (!cs) {
+ printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+ return;
+ }
+ ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+Start_IPAC:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA | 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPAC;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0);
+}
+
+static void
+sedlbauer_interrupt_isar(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ int cnt = 20;
+
+ if (!cs) {
+ printk(KERN_WARNING "Sedlbauer: Spurious interrupt!\n");
+ return;
+ }
+
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+ Start_ISAR:
+ if (val & ISAR_IRQSTA)
+ isar_int_main(cs);
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+ if ((val & ISAR_IRQSTA) && --cnt) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "ISAR IntStat after IntRoutine");
+ goto Start_ISAR;
+ }
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+ if (val && --cnt) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ if (!cnt)
+ printk(KERN_WARNING "Sedlbauer IRQ LOOP\n");
+
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, ISAR_IRQMSK);
+}
+
void
release_io_sedlbauer(struct IsdnCardState *cs)
{
- int bytecnt = 8;
+ int bytecnt = (cs->subtyp == SEDL_SPEED_FAX) ? 16 : 8;
+ if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+ bytecnt = 256;
+ }
if (cs->hw.sedl.cfg_reg)
release_region(cs->hw.sedl.cfg_reg, bytecnt);
}
{
long flags;
- if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA) {
- byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */
- save_flags(flags);
- sti();
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
- byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(1);
- restore_flags(flags);
+ printk(KERN_INFO "Sedlbauer: resetting card\n");
+
+ if (!((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) &&
+ (cs->hw.sedl.chip == SEDL_CHIP_ISAC_HSCX))) {
+ if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
+ save_flags(flags);
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12);
+ restore_flags(flags);
+ } else {
+ byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */
+ save_flags(flags);
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+ byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(1);
+ restore_flags(flags);
+ }
}
}
release_io_sedlbauer(cs);
return(0);
case CARD_SETIRQ:
- return(request_irq(cs->irq, &sedlbauer_interrupt,
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ return(request_irq(cs->irq, &sedlbauer_interrupt_isar,
I4L_IRQ_FLAG, "HiSax", cs));
+ } else if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+ return(request_irq(cs->irq, &sedlbauer_interrupt_ipac,
+ I4L_IRQ_FLAG, "HiSax", cs));
+ } else {
+ return(request_irq(cs->irq, &sedlbauer_interrupt,
+ I4L_IRQ_FLAG, "HiSax", cs));
+ }
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ clear_pending_isac_ints(cs);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+ ISAR_IRQBIT, 0);
+ initisac(cs);
+ initisar(cs);
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ /* RESET Receiver and Transmitter */
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
+ } else {
+ inithscxisac(cs, 3);
+ }
return(0);
case CARD_TEST:
return(0);
+ case CARD_LOAD_FIRM:
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ if (isar_load_firmware(cs, arg))
+ return(1);
+ else
+ ll_run(cs);
+ }
+ return(0);
}
return(0);
}
+
+#ifdef SEDLBAUER_PCI
+static int pci_index __initdata = 0;
+#endif
+
__initfunc(int
setup_sedlbauer(struct IsdnCard *card))
{
- int bytecnt;
+ int bytecnt, ver, val;
struct IsdnCardState *cs = card->cs;
char tmp[64];
strcpy(tmp, Sedlbauer_revision);
printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
+
if (cs->typ == ISDN_CTYPE_SEDLBAUER) {
- cs->subtyp = SEDL_SPEED_CARD;
+ cs->subtyp = SEDL_SPEED_CARD_WIN;
+ cs->hw.sedl.bus = SEDL_BUS_ISA;
+ cs->hw.sedl.chip = SEDL_CHIP_TEST;
} else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) {
cs->subtyp = SEDL_SPEED_STAR;
+ cs->hw.sedl.bus = SEDL_BUS_PCMCIA;
+ cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ } else if (cs->typ == ISDN_CTYPE_SEDLBAUER_FAX) {
+ cs->subtyp = SEDL_SPEED_FAX;
+ cs->hw.sedl.bus = SEDL_BUS_ISA;
+ cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
} else
return (0);
bytecnt = 8;
- cs->hw.sedl.cfg_reg = card->para[1];
- cs->irq = card->para[0];
- if (cs->subtyp == SEDL_SPEED_STAR) {
- cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ADR;
- cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_ISAC;
- cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_HSCX;
- cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET;
- cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_PCMCIA_RESET;
+ if (card->para[1]) {
+ cs->hw.sedl.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ bytecnt = 16;
+ }
} else {
- cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ADR;
- cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAC;
- cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX;
- cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_RESET_ON;
- cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_RESET_OFF;
- }
-
- /* In case of the sedlbauer pcmcia card, this region is in use,
+/* Probe for Sedlbauer speed pci */
+#if SEDLBAUER_PCI
+#if CONFIG_PCI
+ for (; pci_index < 255; pci_index++) {
+ unsigned char pci_bus, pci_device_fn;
+ unsigned int ioaddr;
+ unsigned char irq;
+
+ if (pcibios_find_device (PCI_VENDOR_SEDLBAUER,
+ PCI_SPEEDPCI_ID, pci_index,
+ &pci_bus, &pci_device_fn) != 0) {
+ continue;
+ }
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &ioaddr);
+ cs->irq = irq;
+ cs->hw.sedl.cfg_reg = ioaddr & PCI_BASE_ADDRESS_IO_MASK;
+ if (!cs->hw.sedl.cfg_reg) {
+ printk(KERN_WARNING "Sedlbauer: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+ cs->hw.sedl.bus = SEDL_BUS_PCI;
+ cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+ cs->subtyp = SEDL_SPEED_PCI;
+ bytecnt = 256;
+ byteout(cs->hw.sedl.cfg_reg, 0xff);
+ byteout(cs->hw.sedl.cfg_reg, 0x00);
+ byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd);
+ byteout(cs->hw.sedl.cfg_reg+ 5, 0x02);
+ break;
+ }
+ if (pci_index == 255) {
+ printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
+ return(0);
+ }
+ pci_index++;
+#else
+ printk(KERN_WARNING "Sedlbauer: NO_PCI_BIOS\n");
+ return (0);
+#endif /* CONFIG_PCI */
+#endif /* SEDLBAUER_PCI */
+ }
+
+ /* In case of the sedlbauer pcmcia card, this region is in use,
reserved for us by the card manager. So we do not check it
here, it would fail. */
- if (cs->typ != ISDN_CTYPE_SEDLBAUER_PCMCIA &&
- check_region((cs->hw.sedl.cfg_reg), bytecnt)) {
+ if (cs->hw.sedl.bus != SEDL_BUS_PCMCIA &&
+ check_region((cs->hw.sedl.cfg_reg), bytecnt)) {
printk(KERN_WARNING
- "HiSax: %s config port %x-%x already in use\n",
- CardType[card->typ],
- cs->hw.sedl.cfg_reg,
- cs->hw.sedl.cfg_reg + bytecnt);
- return (0);
+ "HiSax: %s config port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.sedl.cfg_reg,
+ cs->hw.sedl.cfg_reg + bytecnt);
+ return (0);
} else {
request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn");
}
printk(KERN_INFO
- "Sedlbauer: defined at 0x%x IRQ %d\n",
+ "Sedlbauer: defined at 0x%x-0x%x IRQ %d\n",
cs->hw.sedl.cfg_reg,
+ cs->hw.sedl.cfg_reg + bytecnt,
cs->irq);
- printk(KERN_WARNING
- "Sedlbauer %s uses ports 0x%x-0x%x\n",
- Sedlbauer_Types[cs->subtyp],
- cs->hw.sedl.cfg_reg,
- cs->hw.sedl.cfg_reg + bytecnt);
- printk(KERN_INFO "Sedlbauer: resetting card\n");
- reset_sedlbauer(cs);
- cs->readisac = &ReadISAC;
- cs->writeisac = &WriteISAC;
- cs->readisacfifo = &ReadISACfifo;
- cs->writeisacfifo = &WriteISACfifo;
cs->BC_Read_Reg = &ReadHSCX;
cs->BC_Write_Reg = &WriteHSCX;
cs->BC_Send_Data = &hscx_fill_fifo;
cs->cardmsg = &Sedl_card_msg;
- ISACVersion(cs, "Sedlbauer:");
- if (HscxVersion(cs, "Sedlbauer:")) {
- printk(KERN_WARNING
- "Sedlbauer: wrong HSCX versions check IO address\n");
- release_io_sedlbauer(cs);
- return (0);
+
+/*
+ * testing ISA and PCMCIA Cards for IPAC, default is ISAC
+ * do not test for PCI card, because ports are different
+ * and PCI card uses only IPAC (for the moment)
+ */
+ if (cs->hw.sedl.bus != SEDL_BUS_PCI) {
+ val = readreg(cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR,
+ cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC, IPAC_ID);
+ if (val == 1) {
+ /* IPAC */
+ cs->subtyp = SEDL_SPEED_WIN2_PC104;
+ if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+ cs->subtyp = SEDL_SPEED_STAR2;
+ }
+ cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+ } else {
+ /* ISAC_HSCX oder ISAC_ISAR */
+ if (cs->hw.sedl.chip == SEDL_CHIP_TEST) {
+ cs->hw.sedl.chip = SEDL_CHIP_ISAC_HSCX;
+ }
+ }
+ }
+
+/*
+ * hw.sedl.chip is now properly set
+ */
+ printk(KERN_INFO "Sedlbauer: %s detected\n",
+ Sedlbauer_Types[cs->subtyp]);
+
+
+ if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+ /* IPAC */
+ if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+ } else {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+ }
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ cs->readisac = &ReadISAC_IPAC;
+ cs->writeisac = &WriteISAC_IPAC;
+ cs->readisacfifo = &ReadISACfifo_IPAC;
+ cs->writeisacfifo = &WriteISACfifo_IPAC;
+
+ val = readreg(cs->hw.sedl.adr,cs->hw.sedl.isac, IPAC_ID);
+ printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val);
+ reset_sedlbauer(cs);
+ } else {
+ /* ISAC_HSCX oder ISAC_ISAR */
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR;
+ cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_ON;
+ cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_ISAR_ISA_ISAR_RESET_OFF;
+ cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar;
+ cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar;
+ test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+
+ ISACVersion(cs, "Sedlbauer:");
+
+ cs->BC_Read_Reg = &ReadISAR;
+ cs->BC_Write_Reg = &WriteISAR;
+ cs->BC_Send_Data = &isar_fill_fifo;
+ ver = ISARVersion(cs, "Sedlbauer:");
+ if (ver < 0) {
+ printk(KERN_WARNING
+ "Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
+ release_io_sedlbauer(cs);
+ return (0);
+ }
+ } else {
+ if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ISAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_HSCX;
+ cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+ cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+ } else {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ISAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_HSCX;
+ cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON;
+ cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF;
+ }
+ ISACVersion(cs, "Sedlbauer:");
+
+ if (HscxVersion(cs, "Sedlbauer:")) {
+ printk(KERN_WARNING
+ "Sedlbauer: wrong HSCX versions check IO address\n");
+ release_io_sedlbauer(cs);
+ return (0);
+ }
+ reset_sedlbauer(cs);
+ }
}
return (1);
}
-/* $Id: sportster.c,v 1.5 1998/02/02 13:29:46 keil Exp $
+/* $Id: sportster.c,v 1.7 1998/11/15 23:55:22 keil Exp $
* sportster.c low level stuff for USR Sportster internal TA
*
* Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
*
* $Log: sportster.c,v $
+ * Revision 1.7 1998/11/15 23:55:22 keil
+ * changes from 2.0
+ *
+ * Revision 1.6 1998/04/15 16:44:35 keil
+ * new init code
+ *
* Revision 1.5 1998/02/02 13:29:46 keil
* fast io
*
#include "isdnl1.h"
extern const char *CardType[];
-const char *sportster_revision = "$Revision: 1.5 $";
+const char *sportster_revision = "$Revision: 1.7 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
return(request_irq(cs->irq, &sportster_interrupt,
I4L_IRQ_FLAG, "HiSax", cs));
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ inithscxisac(cs, 1);
cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+ inithscxisac(cs, 2);
return(0);
case CARD_TEST:
return(0);
-/* $Id: tei.c,v 2.7 1998/02/12 23:08:11 keil Exp $
+/* $Id: tei.c,v 2.11 1998/11/15 23:55:24 keil Exp $
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
* based on the teles driver from Jan den Ouden
*
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
+ *
* Thanks to Jan den Ouden
* Fritz Elfert
*
* $Log: tei.c,v $
+ * Revision 2.11 1998/11/15 23:55:24 keil
+ * changes from 2.0
+ *
+ * Revision 2.10 1998/05/25 14:08:10 keil
+ * HiSax 3.0
+ * fixed X.75 and leased line to work again
+ * Point2Point and fixed TEI are runtime options now:
+ * hisaxctrl <id> 7 1 set PTP
+ * hisaxctrl <id> 8 <TEIVALUE *2 >
+ * set fixed TEI to TEIVALUE (0-63)
+ *
+ * Revision 2.9 1998/05/25 12:58:23 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.8 1998/03/07 22:57:07 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 2.7 1998/02/12 23:08:11 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#include "isdnl2.h"
#include <linux/random.h>
-const char *tei_revision = "$Revision: 2.7 $";
+const char *tei_revision = "$Revision: 2.11 $";
#define ID_REQUEST 1
#define ID_ASSIGNED 2
bp[2] = ri & 0xff;
bp[3] = m_id;
bp[4] = (tei << 1) | 1;
- st->l2.l2l1(st, PH_DATA_REQ, skb);
+ st->l2.l2l1(st, PH_DATA | REQUEST, skb);
}
static void
tei_id_request(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- char tmp[64];
if (st->l2.tei != -1) {
- sprintf(tmp, "assign request for allready asigned tei %d",
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "assign request for allready asigned tei %d",
st->l2.tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
return;
}
st->ma.ri = random_ri();
- if (st->ma.debug) {
- sprintf(tmp, "assign request ri %d", st->ma.ri);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "assign request ri %d", st->ma.ri);
put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ);
FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1);
struct sk_buff *skb = arg;
struct IsdnCardState *cs;
int ri, tei;
- char tmp[64];
ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
tei = skb->data[4] >> 1;
- if (st->ma.debug) {
- sprintf(tmp, "identity assign ri %d tei %d", ri, tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity assign ri %d tei %d", ri, tei);
if ((ost = findtei(st, tei))) { /* same tei is in use */
if (ri != ost->ma.ri) {
- sprintf(tmp, "possible duplicate assignment tei %d", tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- ost->l2.l2tei(ost, MDL_ERROR_REQ, NULL);
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "possible duplicate assignment tei %d", tei);
+ ost->l2.l2tei(ost, MDL_ERROR | RESPONSE, NULL);
}
} else if (ri == st->ma.ri) {
FsmDelTimer(&st->ma.t202, 1);
FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
- st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) tei);
+ st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
cs = (struct IsdnCardState *) st->l1.hardware;
- cs->cardmsg(cs, MDL_ASSIGN_REQ, NULL);
+ cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
}
}
struct PStack *st = fi->userdata;
struct sk_buff *skb = arg;
int ri, tei;
- char tmp[64];
ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
tei = skb->data[4] >> 1;
- if (st->ma.debug) {
- sprintf(tmp, "identity denied ri %d tei %d", ri, tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity denied ri %d tei %d", ri, tei);
}
static void
struct PStack *st = fi->userdata;
struct sk_buff *skb = arg;
int tei;
- char tmp[64];
tei = skb->data[4] >> 1;
- if (st->ma.debug) {
- sprintf(tmp, "identity check req tei %d", tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity check req tei %d", tei);
if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
FsmDelTimer(&st->ma.t202, 4);
FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
struct sk_buff *skb = arg;
struct IsdnCardState *cs;
int tei;
- char tmp[64];
tei = skb->data[4] >> 1;
- if (st->ma.debug) {
- sprintf(tmp, "identity remove tei %d", tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity remove tei %d", tei);
if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
FsmDelTimer(&st->ma.t202, 5);
FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
- st->ma.manl2(st, MDL_REMOVE_REQ, 0);
+ st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
cs = (struct IsdnCardState *) st->l1.hardware;
- cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+ cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
}
}
tei_id_verify(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- char tmp[64];
- if (st->ma.debug) {
- sprintf(tmp, "id verify request for tei %d", st->l2.tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "id verify request for tei %d", st->l2.tei);
put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY);
FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2);
tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- char tmp[64];
struct IsdnCardState *cs;
if (--st->ma.N202) {
st->ma.ri = random_ri();
- if (st->ma.debug) {
- sprintf(tmp, "assign req(%d) ri %d",
- 4 - st->ma.N202, st->ma.ri);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "assign req(%d) ri %d", 4 - st->ma.N202,
+ st->ma.ri);
put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
} else {
- sprintf(tmp, "assign req failed");
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- st->ma.manl2(st, MDL_ERROR_IND, 0);
+ st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
+ st->l3.l3l2(st, MDL_ERROR | RESPONSE, 0);
cs = (struct IsdnCardState *) st->l1.hardware;
- cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+ cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
FsmChangeState(fi, ST_TEI_NOP);
}
}
tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
{
struct PStack *st = fi->userdata;
- char tmp[64];
struct IsdnCardState *cs;
if (--st->ma.N202) {
- if (st->ma.debug) {
- sprintf(tmp, "id verify req(%d) for tei %d",
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "id verify req(%d) for tei %d",
3 - st->ma.N202, st->l2.tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4);
} else {
- sprintf(tmp, "verify req for tei %d failed", st->l2.tei);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- st->ma.manl2(st, MDL_REMOVE_REQ, 0);
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "verify req for tei %d failed", st->l2.tei);
+ st->l3.l3l2(st, MDL_REMOVE | REQUEST, 0);
cs = (struct IsdnCardState *) st->l1.hardware;
- cs->cardmsg(cs, MDL_REMOVE_REQ, NULL);
+ cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
FsmChangeState(fi, ST_TEI_NOP);
}
}
{
struct sk_buff *skb = arg;
int mt;
- char tmp[64];
- if (pr == PH_DATA_IND) {
+ if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (pr == (PH_DATA | INDICATION)) {
if (skb->len < 3) {
- sprintf(tmp, "short mgr frame %d/3", skb->len);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "short mgr frame %ld/3", skb->len);
} else if (((skb->data[0] >> 2) != TEI_SAPI) ||
((skb->data[1] >> 1) != GROUP_TEI)) {
- sprintf(tmp, "wrong mgr sapi/tei %x/%x",
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "wrong mgr sapi/tei %x/%x",
skb->data[0], skb->data[1]);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
} else if ((skb->data[2] & 0xef) != UI) {
- sprintf(tmp, "mgr frame is not ui %x",
- skb->data[2]);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "mgr frame is not ui %x", skb->data[2]);
} else {
skb_pull(skb, 3);
if (skb->len < 5) {
- sprintf(tmp, "short mgr frame %d/5", skb->len);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "short mgr frame %ld/5", skb->len);
} else if (skb->data[0] != TEI_ENTITY_ID) {
/* wrong management entity identifier, ignore */
- sprintf(tmp, "tei handler wrong entity id %x\n",
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "tei handler wrong entity id %x",
skb->data[0]);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
} else {
mt = skb->data[3];
if (mt == ID_ASSIGNED)
else if (mt == ID_REMOVE)
FsmEvent(&st->ma.tei_m, EV_REMOVE, skb);
else {
- sprintf(tmp, "tei handler wrong mt %x\n",
- mt);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "tei handler wrong mt %x\n", mt);
}
}
}
} else {
- sprintf(tmp, "tei handler wrong pr %x\n", pr);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "tei handler wrong pr %x\n", pr);
}
dev_kfree_skb(skb);
}
static void
tei_l2tei(struct PStack *st, int pr, void *arg)
{
+ struct IsdnCardState *cs;
+
+ if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+ if (pr == (MDL_ASSIGN | INDICATION)) {
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "fixed assign tei %d", st->l2.tei);
+ st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+ cs = (struct IsdnCardState *) st->l1.hardware;
+ cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+ }
+ return;
+ }
switch (pr) {
- case (MDL_ASSIGN_IND):
-#ifdef TEI_FIXED
- if (st->ma.debug) {
- char tmp[64];
- sprintf(tmp, "fixed assign tei %d", TEI_FIXED);
- st->ma.tei_m.printdebug(&st->ma.tei_m, tmp);
- }
- st->ma.manl2(st, MDL_ASSIGN_REQ, (void *) (int) TEI_FIXED);
-#else
+ case (MDL_ASSIGN | INDICATION):
FsmEvent(&st->ma.tei_m, EV_IDREQ, arg);
-#endif
break;
- case (MDL_ERROR_REQ):
+ case (MDL_ERROR | REQUEST):
FsmEvent(&st->ma.tei_m, EV_VERIFY, arg);
break;
default:
}
static void
-tei_debug(struct FsmInst *fi, char *s)
+tei_debug(struct FsmInst *fi, char *fmt, ...)
{
+ va_list args;
struct PStack *st = fi->userdata;
- char tm[32], str[256];
- jiftime(tm, jiffies);
- sprintf(str, "%s Tei %s\n", tm, s);
- HiSax_putstatus(st->l1.hardware, str);
+ va_start(args, fmt);
+ VHiSax_putstatus(st->l1.hardware, "tei ", fmt, args);
+ va_end(args);
}
void
}
void
-init_tei(struct IsdnCardState *sp, int protocol)
+init_tei(struct IsdnCardState *cs, int protocol)
{
-
}
void
-/* $Id: teleint.c,v 1.5 1998/02/02 13:40:47 keil Exp $
+/* $Id: teleint.c,v 1.7 1998/11/15 23:55:26 keil Exp $
* teleint.c low level stuff for TeleInt isdn cards
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
*
* $Log: teleint.c,v $
+ * Revision 1.7 1998/11/15 23:55:26 keil
+ * changes from 2.0
+ *
+ * Revision 1.6 1998/04/15 16:45:31 keil
+ * new init code
+ *
* Revision 1.5 1998/02/02 13:40:47 keil
* fast io
*
extern const char *CardType[];
-const char *TeleInt_revision = "$Revision: 1.5 $";
+const char *TeleInt_revision = "$Revision: 1.7 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
{
register u_char ret;
- int max_delay = 2000;
+ register int max_delay = 20000;
+ register int i;
+
byteout(ale, off);
-
- ret = HFC_BUSY & bytein(ale);
- while (ret && --max_delay)
+ for (i = 0; i<size; i++) {
ret = HFC_BUSY & bytein(ale);
- if (!max_delay) {
- printk(KERN_WARNING "TeleInt Busy not inaktive\n");
- return;
+ while (ret && --max_delay)
+ ret = HFC_BUSY & bytein(ale);
+ if (!max_delay) {
+ printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+ return;
+ }
+ data[i] = bytein(adr);
}
- insb(adr, data, size);
}
writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
{
register u_char ret;
- int max_delay = 2000;
-
+ register int max_delay = 20000;
+ register int i;
+
/* fifo write without cli because it's allready done */
byteout(ale, off);
- ret = HFC_BUSY & bytein(ale);
- while (ret && --max_delay)
+ for (i = 0; i<size; i++) {
ret = HFC_BUSY & bytein(ale);
- if (!max_delay) {
- printk(KERN_WARNING "TeleInt Busy not inaktive\n");
- return;
+ while (ret && --max_delay)
+ ret = HFC_BUSY & bytein(ale);
+ if (!max_delay) {
+ printk(KERN_WARNING "TeleInt Busy not inaktive\n");
+ return;
+ }
+ byteout(adr, data[i]);
}
- outsb(adr, data, size);
}
/* Interface functions */
cs->hw.hfc.cip = reg;
byteout(cs->hw.hfc.addr | 1, reg);
ret = bytein(cs->hw.hfc.addr);
- if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
- char tmp[32];
- sprintf(tmp, "hfc RD %02x %02x", reg, ret);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+ debugl1(cs, "hfc RD %02x %02x", reg, ret);
} else
ret = bytein(cs->hw.hfc.addr | 1);
return (ret);
cs->hw.hfc.cip = reg;
if (data)
byteout(cs->hw.hfc.addr, value);
- if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2)) {
- char tmp[32];
- sprintf(tmp, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+ debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
}
static void
inithfc(cs);
clear_pending_isac_ints(cs);
initisac(cs);
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
cs->hw.hfc.timer.expires = jiffies + 1;
add_timer(&cs->hw.hfc.timer);
return(0);
-/* $Id: teles0.c,v 2.6 1998/02/03 23:27:47 keil Exp $
+/* $Id: teles0.c,v 2.8 1998/04/15 16:44:28 keil Exp $
* teles0.c low level stuff for Teles Memory IO isdn cards
* based on the teles driver from Jan den Ouden
* Beat Doebeli
*
* $Log: teles0.c,v $
+ * Revision 2.8 1998/04/15 16:44:28 keil
+ * new init code
+ *
+ * Revision 2.7 1998/03/07 22:57:08 tsbogend
+ * made HiSax working on Linux/Alpha
+ *
* Revision 2.6 1998/02/03 23:27:47 keil
* IRQ 9
*
extern const char *CardType[];
-const char *teles0_revision = "$Revision: 2.6 $";
+const char *teles0_revision = "$Revision: 2.8 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
static inline void
writeisac(unsigned int adr, u_char off, u_char data)
{
- writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off);
+ writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
}
writehscx(unsigned int adr, int hscx, u_char off, u_char data)
{
writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
- ((off & 1) ? 0x1ff : 0) + off);
+ ((off & 1) ? 0x1ff : 0) + off); mb();
}
static inline void
read_fifo_isac(unsigned int adr, u_char * data, int size)
{
register int i;
- register u_char *ad = (u_char *) (adr + 0x100);
+ register u_char *ad = (u_char *) ((long)adr + 0x100);
for (i = 0; i < size; i++)
data[i] = readb(ad);
}
write_fifo_isac(unsigned int adr, u_char * data, int size)
{
register int i;
- register u_char *ad = (u_char *) (adr + 0x100);
- for (i = 0; i < size; i++)
- writeb(data[i], ad);
+ register u_char *ad = (u_char *) ((long)adr + 0x100);
+ for (i = 0; i < size; i++) {
+ writeb(data[i], ad); mb();
+ }
}
static inline void
read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
{
register int i;
- register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
+ register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
for (i = 0; i < size; i++)
data[i] = readb(ad);
}
write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
{
int i;
- register u_char *ad = (u_char *) (adr + (hscx ? 0x1c0 : 0x180));
- for (i = 0; i < size; i++)
- writeb(data[i], ad);
+ register u_char *ad = (u_char *) ((long)adr + (hscx ? 0x1c0 : 0x180));
+ for (i = 0; i < size; i++) {
+ writeb(data[i], ad); mb();
+ }
}
/* Interface functions */
byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
HZDELAY(HZ / 10 + 1);
}
- writeb(0, cs->hw.teles0.membase + 0x80);
+ writeb(0, cs->hw.teles0.membase + 0x80); mb();
HZDELAY(HZ / 5 + 1);
- writeb(1, cs->hw.teles0.membase + 0x80);
+ writeb(1, cs->hw.teles0.membase + 0x80); mb();
HZDELAY(HZ / 5 + 1);
restore_flags(flags);
return(0);
return(request_irq(cs->irq, &teles0_interrupt,
I4L_IRQ_FLAG, "HiSax", cs));
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ inithscxisac(cs, 3);
return(0);
case CARD_TEST:
return(0);
-/* $Id: teles3.c,v 2.7 1998/02/02 13:29:48 keil Exp $
+/* $Id: teles3.c,v 2.10 1999/02/15 14:37:15 cpetig Exp $
* teles3.c low level stuff for Teles 16.3 & PNP isdn cards
*
* Beat Doebeli
*
* $Log: teles3.c,v $
+ * Revision 2.10 1999/02/15 14:37:15 cpetig
+ * oops, missed something in last commit
+ *
+ * Revision 2.9 1999/02/15 14:11:02 cpetig
+ * fixed a bug with Teles PCMCIA, it doesn't have a config register
+ *
+ * Revision 2.8 1998/04/15 16:44:30 keil
+ * new init code
+ *
* Revision 2.7 1998/02/02 13:29:48 keil
* fast io
*
#include "isdnl1.h"
extern const char *CardType[];
-const char *teles3_revision = "$Revision: 2.7 $";
+const char *teles3_revision = "$Revision: 2.10 $";
#define byteout(addr,val) outb(val,addr)
#define bytein(addr) inb(addr)
release_io_teles3(struct IsdnCardState *cs)
{
if (cs->typ == ISDN_CTYPE_TELESPCMCIA)
- release_region(cs->hw.teles3.cfg_reg, 97);
+ release_region(cs->hw.teles3.hscx[0], 97);
else {
- if (cs->hw.teles3.cfg_reg) {
+ if (cs->hw.teles3.cfg_reg)
if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
release_region(cs->hw.teles3.cfg_reg, 1);
} else {
release_region(cs->hw.teles3.cfg_reg, 8);
}
- }
release_ioregs(cs, 0x7);
}
}
return(request_irq(cs->irq, &teles3_interrupt,
I4L_IRQ_FLAG, "HiSax", cs));
case CARD_INIT:
- clear_pending_isac_ints(cs);
- clear_pending_hscx_ints(cs);
- initisac(cs);
- inithscx(cs);
+ inithscxisac(cs, 3);
return(0);
case CARD_TEST:
return(0);
cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20;
cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820;
} else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
- cs->hw.teles3.cfg_reg = card->para[1];
+ cs->hw.teles3.cfg_reg = 0;
cs->hw.teles3.hscx[0] = card->para[1] - 0x20;
cs->hw.teles3.hscx[1] = card->para[1];
cs->hw.teles3.isac = card->para[1] + 0x20;
cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
- if (check_region((cs->hw.teles3.cfg_reg), 97)) {
+ if (check_region((cs->hw.teles3.hscx[0]), 97)) {
printk(KERN_WARNING
"HiSax: %s ports %x-%x already in use\n",
CardType[cs->typ],
- cs->hw.teles3.cfg_reg,
- cs->hw.teles3.cfg_reg + 96);
+ cs->hw.teles3.hscx[0],
+ cs->hw.teles3.hscx[0] + 96);
return (0);
} else
request_region(cs->hw.teles3.hscx[0], 97, "HiSax Teles PCMCIA");
CardType[cs->typ],
cs->hw.teles3.isac + 32,
cs->hw.teles3.isac + 64);
- if (cs->hw.teles3.cfg_reg) {
+ if (cs->hw.teles3.cfg_reg)
if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
release_region(cs->hw.teles3.cfg_reg, 1);
} else {
release_region(cs->hw.teles3.cfg_reg, 8);
}
- }
return (0);
} else
request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac");
CardType[cs->typ],
cs->hw.teles3.hscx[0] + 32,
cs->hw.teles3.hscx[0] + 64);
- if (cs->hw.teles3.cfg_reg) {
+ if (cs->hw.teles3.cfg_reg)
if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
release_region(cs->hw.teles3.cfg_reg, 1);
} else {
release_region(cs->hw.teles3.cfg_reg, 8);
}
- }
release_ioregs(cs, 1);
return (0);
} else
CardType[cs->typ],
cs->hw.teles3.hscx[1] + 32,
cs->hw.teles3.hscx[1] + 64);
- if (cs->hw.teles3.cfg_reg) {
+ if (cs->hw.teles3.cfg_reg)
if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
release_region(cs->hw.teles3.cfg_reg, 1);
} else {
release_region(cs->hw.teles3.cfg_reg, 8);
}
- }
release_ioregs(cs, 3);
return (0);
} else
-/* $Id: teles3c.c,v 1.2 1998/02/02 13:27:07 keil Exp $
+/* $Id: teles3c.c,v 1.3 1998/11/15 23:55:27 keil Exp $
* teles3c.c low level stuff for teles 16.3c
*
- * Author Karsten Keil (keil@temic-ech.spacenet.de)
+ * Author Karsten Keil (keil@isdn4linux.de)
*
*
* $Log: teles3c.c,v $
+ * Revision 1.3 1998/11/15 23:55:27 keil
+ * changes from 2.0
+ *
* Revision 1.2 1998/02/02 13:27:07 keil
* New
*
extern const char *CardType[];
-const char *teles163c_revision = "$Revision: 1.2 $";
+const char *teles163c_revision = "$Revision: 1.3 $";
static void
t163c_interrupt(int intno, void *dev_id, struct pt_regs *regs)
{
struct IsdnCardState *cs = dev_id;
u_char val, stat;
- char tmp[32];
if (!cs) {
printk(KERN_WARNING "teles3c: Spurious interrupt!\n");
if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) &
(stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) {
val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1);
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "teles3c: stat(%02x) s1(%02x)", stat, val);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "teles3c: stat(%02x) s1(%02x)", stat, val);
hfc2bds0_interrupt(cs, val);
} else {
- if (cs->debug & L1_DEB_ISAC) {
- sprintf(tmp, "teles3c: irq_no_irq stat(%02x)", stat);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "teles3c: irq_no_irq stat(%02x)", stat);
}
}
t163c_card_msg(struct IsdnCardState *cs, int mt, void *arg)
{
long flags;
- char tmp[32];
- if (cs->debug & L1_DEB_ISAC) {
-
- sprintf(tmp, "teles3c: card_msg %x", mt);
- debugl1(cs, tmp);
- }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "teles3c: card_msg %x", mt);
switch (mt) {
case CARD_RESET:
reset_t163c(cs);
--- /dev/null
+/* $Id: telespci.c,v 2.5 1998/11/15 23:55:28 keil Exp $
+
+ * telespci.c low level stuff for Teles PCI isdn cards
+ *
+ * Author Ton van Rosmalen
+ * Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: telespci.c,v $
+ * Revision 2.5 1998/11/15 23:55:28 keil
+ * changes from 2.0
+ *
+ * Revision 2.4 1998/10/05 09:38:08 keil
+ * Fix register addressing
+ *
+ * Revision 2.3 1998/05/25 12:58:26 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.1 1998/04/15 16:38:23 keil
+ * Add S0Box and Teles PCI support
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+
+extern const char *CardType[];
+
+const char *telespci_revision = "$Revision: 2.5 $";
+
+#define ZORAN_PO_RQ_PEN 0x02000000
+#define ZORAN_PO_WR 0x00800000
+#define ZORAN_PO_GID0 0x00000000
+#define ZORAN_PO_GID1 0x00100000
+#define ZORAN_PO_GREG0 0x00000000
+#define ZORAN_PO_GREG1 0x00010000
+#define ZORAN_PO_DMASK 0xFF
+
+#define WRITE_ADDR_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
+#define READ_DATA_ISAC (ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_DATA_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_ADDR_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0)
+#define READ_DATA_HSCX (ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+#define WRITE_DATA_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+
+#define ZORAN_WAIT_NOBUSY do { \
+ portdata = readl(adr + 0x200); \
+ } while (portdata & ZORAN_PO_RQ_PEN)
+
+static inline u_char
+readisac(unsigned int adr, u_char off)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+
+ /* set address for ISAC */
+ writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* read data from ISAC */
+ writel(READ_DATA_ISAC, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ return((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writeisac(unsigned int adr, u_char off, u_char data)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+
+ /* set address for ISAC */
+ writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* write data to ISAC */
+ writel(WRITE_DATA_ISAC | data, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+}
+
+static inline u_char
+readhscx(unsigned int adr, int hscx, u_char off)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+ /* set address for HSCX */
+ writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* read data from HSCX */
+ writel(READ_DATA_HSCX, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ return ((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+ /* set address for HSCX */
+ writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* write data to HSCX */
+ writel(WRITE_DATA_HSCX | data, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* read data from ISAC */
+ for (i = 0; i < size; i++) {
+ /* set address for ISAC fifo */
+ writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(READ_DATA_ISAC, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ data[i] = (u_char)(portdata & ZORAN_PO_DMASK);
+ }
+}
+
+static void
+write_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* write data to ISAC */
+ for (i = 0; i < size; i++) {
+ /* set address for ISAC fifo */
+ writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(WRITE_DATA_ISAC | data[i], adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ }
+}
+
+static inline void
+read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* read data from HSCX */
+ for (i = 0; i < size; i++) {
+ /* set address for HSCX fifo */
+ writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(READ_DATA_HSCX, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ data[i] = (u_char) (portdata & ZORAN_PO_DMASK);
+ }
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+ unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* write data to HSCX */
+ for (i = 0; i < size; i++) {
+ /* set address for HSCX fifo */
+ writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(WRITE_DATA_HSCX | data[i], adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ udelay(10);
+ }
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+ struct IsdnCardState *cs = dev_id;
+ u_char val, stat = 0;
+
+ if (!cs) {
+ printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n");
+ return;
+ }
+ val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+ if (val) {
+ hscx_int_main(cs, val);
+ stat |= 1;
+ }
+ val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+ if (val) {
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ /* Clear interrupt register for Zoran PCI controller */
+ writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+
+ if (stat & 1) {
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+ }
+ if (stat & 2) {
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+ }
+}
+
+void
+release_io_telespci(struct IsdnCardState *cs)
+{
+ iounmap((void *)cs->hw.teles0.membase);
+}
+
+static int
+TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ switch (mt) {
+ case CARD_RESET:
+ return(0);
+ case CARD_RELEASE:
+ release_io_telespci(cs);
+ return(0);
+ case CARD_SETIRQ:
+ return(request_irq(cs->irq, &telespci_interrupt,
+ I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs));
+ case CARD_INIT:
+ inithscxisac(cs, 3);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static struct pci_dev *dev_tel __initdata = NULL;
+
+__initfunc(int
+setup_telespci(struct IsdnCard *card))
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, telespci_revision);
+ printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_TELESPCI)
+ return (0);
+
+#if CONFIG_PCI
+ if (!pci_present()) {
+ printk(KERN_ERR "TelesPCI: no PCI bus present\n");
+ return(0);
+ }
+ if ((dev_tel = pci_find_device (0x11DE, 0x6120, dev_tel))) {
+ cs->irq = dev_tel->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[0],
+ PAGE_SIZE);
+ printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
+ dev_tel->base_address[0], dev_tel->irq);
+ } else {
+ printk(KERN_WARNING "TelesPCI: No PCI card found\n");
+ return(0);
+ }
+#else
+ printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n");
+ printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n");
+ return (0);
+#endif /* CONFIG_PCI */
+
+ /* Initialize Zoran PCI controller */
+ writel(0x00000000, cs->hw.teles0.membase + 0x28);
+ writel(0x01000000, cs->hw.teles0.membase + 0x28);
+ writel(0x01000000, cs->hw.teles0.membase + 0x28);
+ writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C);
+ writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+ writel(0x61000000, cs->hw.teles0.membase + 0x40);
+ /* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
+
+ printk(KERN_INFO
+ "HiSax: %s config irq:%d mem:%x\n",
+ CardType[cs->typ], cs->irq,
+ cs->hw.teles0.membase);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &TelesPCI_card_msg;
+ ISACVersion(cs, "TelesPCI:");
+ if (HscxVersion(cs, "TelesPCI:")) {
+ printk(KERN_WARNING
+ "TelesPCI: wrong HSCX versions check IO/MEM addresses\n");
+ release_io_telespci(cs);
+ return (0);
+ }
+ return (1);
+}
-/* $Id: icn.c,v 1.49 1998/02/13 11:14:15 keil Exp $
+/* $Id: icn.c,v 1.56 1999/04/12 13:15:07 fritz Exp $
* ISDN low-level module for the ICN active ISDN-Card.
*
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: icn.c,v $
+ * Revision 1.56 1999/04/12 13:15:07 fritz
+ * Fixed a cast.
+ *
+ * Revision 1.55 1999/04/12 12:34:02 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.54 1999/01/05 18:29:39 he
+ * merged remaining schedule_timeout() changes from 2.1.127
+ *
+ * Revision 1.53 1998/06/17 19:51:28 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.52 1998/05/20 19:29:58 tsbogend
+ * fixed bug introduced by changes for new BSENT callback
+ *
+ * Revision 1.51 1998/03/07 22:29:55 fritz
+ * Adapted Detlef's chenges for 2.1.
+ *
+ * Revision 1.50 1998/03/07 17:41:54 detabc
+ * add d-channel connect and disconnect support statcallback
+ * from icn low-level to link->level
+ *
* Revision 1.49 1998/02/13 11:14:15 keil
* change for 2.1.86 (removing FREE_READ/FREE_WRITE from [dev]_kfree_skb()
*
#undef MAP_DEBUG
static char
-*revision = "$Revision: 1.49 $";
+*revision = "$Revision: 1.56 $";
static int icn_addcard(int, char *, char *);
cli();
card->xlen[channel] = 0;
card->sndcount[channel] = 0;
- if (card->xskb[channel]) {
+ if ((skb = card->xskb[channel])) {
card->xskb[channel] = NULL;
restore_flags(flags);
- dev_kfree_skb(card->xskb[channel]);
+ dev_kfree_skb(skb);
} else
restore_flags(flags);
}
cmd.parm.length = card->xlen[channel];
card->interface.statcallb(&cmd);
}
+ } else {
+ save_flags(flags);
+ cli();
+ card->xskb[channel] = skb;
+ restore_flags(flags);
}
card->xmit_lock[channel] = 0;
if (!icn_trymaplock_channel(card, mch))
{
{"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */
{"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */
- {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */
- {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */
+ /*
+ ** add d-channel connect and disconnect support to link-level
+ */
+ {"DCON_", ISDN_STAT_DCONN, 10}, /* D-Channel connected */
+ {"DDIS_", ISDN_STAT_DHUP, 11}, /* D-Channel disconnected */
{"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */
{"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */
{"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */
cmd.driver = card->myid;
cmd.arg = channel;
switch (action) {
+ case 11:
+ save_flags(flags);
+ cli();
+ icn_free_queue(card,channel);
+ card->rcvidx[channel] = 0;
+
+ if (card->flags &
+ ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) {
+
+ isdn_ctrl ncmd;
+
+ card->flags &= ~((channel)?
+ ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
+
+ memset(&ncmd, 0, sizeof(ncmd));
+
+ ncmd.driver = card->myid;
+ ncmd.arg = channel;
+ ncmd.command = ISDN_STAT_BHUP;
+ restore_flags(flags);
+ card->interface.statcallb(&cmd);
+ } else
+ restore_flags(flags);
+
+ break;
case 1:
+ icn_free_queue(card,channel);
card->flags |= (channel) ?
ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;
break;
c->parm.num[0] ? "N" : "ALL", c->parm.num);
} else
sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
- c->parm.num[0] ? c->parm.num : "0123456789");
+ c->parm.num[0] ? (char *)(c->parm.num) : "0123456789");
i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
}
break;
#ifdef MODULE
MODULE_AUTHOR("Fritz Elfert");
MODULE_PARM(portbase, "i");
-MODULE_PARM_DESC(portbase, "Port address of first card");
+MODULE_PARM_DESC(portbase, "Port adress of first card");
MODULE_PARM(membase, "i");
-MODULE_PARM_DESC(membase, "Shared memory address of all cards");
+MODULE_PARM_DESC(membase, "Shared memory adress of all cards");
MODULE_PARM(icn_id, "s");
MODULE_PARM_DESC(icn_id, "ID-String of first card");
MODULE_PARM(icn_id2, "s");
-/* $Id: isdn_audio.c,v 1.10 1998/02/20 17:09:40 fritz Exp $
+/* $Id: isdn_audio.c,v 1.13 1999/04/12 12:33:09 fritz Exp $
* Linux ISDN subsystem, audio conversion and compression (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
* DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at)
+ * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de)
*
* 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
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_audio.c,v $
+ * Revision 1.13 1999/04/12 12:33:09 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.12 1998/07/26 18:48:43 armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.11 1998/04/10 10:35:10 paul
+ * fixed (silly?) warnings from egcs on Alpha.
+ *
* Revision 1.10 1998/02/20 17:09:40 fritz
* Changes for recent kernels.
*
#include "isdn_audio.h"
#include "isdn_common.h"
-char *isdn_audio_revision = "$Revision: 1.10 $";
+char *isdn_audio_revision = "$Revision: 1.13 $";
/*
* Misc. lookup-tables.
isdn_audio_tlookup(const char *table, char *buff, unsigned long n)
{
while (n--)
- *buff++ = table[*buff];
+ *buff++ = table[*(unsigned char *)buff];
}
#endif
len -= c;
}
}
+
+silence_state *
+isdn_audio_silence_init(silence_state * s)
+{
+ if (!s)
+ s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC);
+ if (s) {
+ s->idx = 0;
+ s->state = 0;
+ }
+ return s;
+}
+
+void
+isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+ silence_state *s = info->silence_state;
+ int i;
+ signed char c;
+
+ if (!info->emu.vpar[1]) return;
+
+ for (i = 0; i < len; i++) {
+ if (fmt)
+ c = isdn_audio_alaw_to_ulaw[*buf++];
+ else
+ c = *buf++;
+
+ if (c > 0) c -= 128;
+ c = abs(c);
+
+ if (c > (info->emu.vpar[1] * 4)) {
+ s->idx = 0;
+ s->state = 1;
+ } else {
+ if (s->idx < 210000) s->idx++;
+ }
+ }
+}
+
+void
+isdn_audio_eval_silence(modem_info * info)
+{
+ silence_state *s = info->silence_state;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int di;
+ int ch;
+ char what;
+ char *p;
+
+ what = ' ';
+
+ if (s->idx > (info->emu.vpar[2] * 800)) {
+ s->idx = 0;
+ if (!s->state) { /* silence from beginning of rec */
+ what = 's';
+ } else {
+ what = 'q';
+ }
+ }
+ if ((what == 's') || (what == 'q')) {
+ printk(KERN_DEBUG "ttyI%d: %s\n", info->line,
+ (what=='s') ? "silence":"quiet");
+ skb = dev_alloc_skb(2);
+ p = (char *) skb_put(skb, 2);
+ p[0] = 0x10;
+ p[1] = what;
+ if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
+ printk(KERN_WARNING
+ "isdn_audio: insufficient skb_headroom, dropping\n");
+ kfree_skb(skb);
+ return;
+ }
+ ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+ save_flags(flags);
+ cli();
+ di = info->isdn_driver;
+ ch = info->isdn_channel;
+ __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+ dev->drv[di]->rcvcount[ch] += 2;
+ restore_flags(flags);
+ /* Schedule dequeuing */
+ if ((dev->modempoll) && (info->rcvsched))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+ wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+ }
+}
-/* $Id: isdn_audio.h,v 1.5 1997/02/03 22:45:21 fritz Exp $
+/* $Id: isdn_audio.h,v 1.7 1999/04/12 12:33:11 fritz Exp $
* Linux ISDN subsystem, audio conversion and compression (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
*
* 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
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_audio.h,v $
+ * Revision 1.7 1999/04/12 12:33:11 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.6 1998/07/26 18:48:44 armin
+ * Added silence detection in voice receive mode.
+ *
* Revision 1.5 1997/02/03 22:45:21 fritz
* Reformatted according CodingStyle
*
int buf[DTMF_NPOINTS];
} dtmf_state;
+typedef struct silence_state {
+ int state;
+ unsigned int idx;
+} silence_state;
+
extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
extern void isdn_audio_eval_dtmf(modem_info *);
dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
+extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_silence(modem_info *);
+silence_state *isdn_audio_silence_init(silence_state *);
--- /dev/null
+/*
+ * BSD compression module
+ *
+ * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
+ * The whole module is now SKB based.
+ *
+ * Compile with:
+ * gcc -O2 -I/usr/src/linux/include -D__KERNEL__ -DMODULE -c isdn_bsdcomp.c
+ */
+
+/*
+ * Original copyright notice:
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MODULE
+#error This file must be compiled as a module.
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/sched.h> /* to get the struct task_struct */
+#include <linux/string.h> /* used in new tty drivers */
+#include <linux/signal.h> /* used in new tty drivers */
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+
+#include <linux/if.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+
+#include <linux/ppp_defs.h>
+
+#include <linux/isdn.h>
+#include <linux/isdn_ppp.h>
+/* #include <linux/netprotocol.h> */
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <linux/ppp-comp.h>
+
+#include "isdn_ppp.h"
+
+#define BSD_VERSION(x) ((x) >> 5)
+#define BSD_NBITS(x) ((x) & 0x1F)
+
+#define BSD_CURRENT_VERSION 1
+
+#define DEBUG 1
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+
+struct bsd_dict {
+ u32 fcode;
+ u16 codem1; /* output of hash table -1 */
+ u16 cptr; /* map code to hash table entry */
+};
+
+struct bsd_db {
+ int totlen; /* length of this structure */
+ unsigned int hsize; /* size of the hash table */
+ unsigned char hshift; /* used in hash function */
+ unsigned char n_bits; /* current bits/code */
+ unsigned char maxbits; /* maximum bits/code */
+ unsigned char debug; /* non-zero if debug desired */
+ unsigned char unit; /* ppp unit number */
+ u16 seqno; /* sequence # of next packet */
+ unsigned int mru; /* size of receive (decompress) bufr */
+ unsigned int maxmaxcode; /* largest valid code */
+ unsigned int max_ent; /* largest code in use */
+ unsigned int in_count; /* uncompressed bytes, aged */
+ unsigned int bytes_out; /* compressed bytes, aged */
+ unsigned int ratio; /* recent compression ratio */
+ unsigned int checkpoint; /* when to next check the ratio */
+ unsigned int clear_count; /* times dictionary cleared */
+ unsigned int incomp_count; /* incompressible packets */
+ unsigned int incomp_bytes; /* incompressible bytes */
+ unsigned int uncomp_count; /* uncompressed packets */
+ unsigned int uncomp_bytes; /* uncompressed bytes */
+ unsigned int comp_count; /* compressed packets */
+ unsigned int comp_bytes; /* compressed bytes */
+ unsigned short *lens; /* array of lengths of codes */
+ struct bsd_dict *dict; /* dictionary */
+ int xmit;
+};
+
+#define BSD_OVHD 2 /* BSD compress overhead/packet */
+#define MIN_BSD_BITS 9
+#define BSD_INIT_BITS MIN_BSD_BITS
+#define MAX_BSD_BITS 15
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR 256 /* table clear output code */
+#define FIRST 257 /* first free entry */
+#define LAST 255
+
+#define MAXCODE(b) ((1 << (b)) - 1)
+#define BADCODEM1 MAXCODE(MAX_BSD_BITS);
+
+#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
+ ^ (unsigned long)(prefix))
+#define BSD_KEY(prefix,suffix) ((((unsigned long)(suffix)) << 16) \
+ + (unsigned long)(prefix))
+
+#define CHECK_GAP 10000 /* Ratio check interval */
+
+#define RATIO_SCALE_LOG 8
+#define RATIO_SCALE (1<<RATIO_SCALE_LOG)
+#define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+
+static void bsd_clear(struct bsd_db *db)
+{
+ db->clear_count++;
+ db->max_ent = FIRST-1;
+ db->n_bits = BSD_INIT_BITS;
+ db->bytes_out = 0;
+ db->in_count = 0;
+ db->incomp_count = 0;
+ db->ratio = 0;
+ db->checkpoint = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int bsd_check (struct bsd_db *db) /* 1=output CLEAR */
+{
+ unsigned int new_ratio;
+
+ if (db->in_count >= db->checkpoint)
+ {
+ /* age the ratio by limiting the size of the counts */
+ if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
+ {
+ db->in_count -= (db->in_count >> 2);
+ db->bytes_out -= (db->bytes_out >> 2);
+ }
+
+ db->checkpoint = db->in_count + CHECK_GAP;
+
+ if (db->max_ent >= db->maxmaxcode)
+ {
+ /* Reset the dictionary only if the ratio is worse,
+ * or if it looks as if it has been poisoned
+ * by incompressible data.
+ *
+ * This does not overflow, because
+ * db->in_count <= RATIO_MAX.
+ */
+
+ new_ratio = db->in_count << RATIO_SCALE_LOG;
+ if (db->bytes_out != 0)
+ {
+ new_ratio /= db->bytes_out;
+ }
+
+ if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
+ {
+ bsd_clear (db);
+ return 1;
+ }
+ db->ratio = new_ratio;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return statistics.
+ */
+
+static void bsd_stats (void *state, struct compstat *stats)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ stats->unc_bytes = db->uncomp_bytes;
+ stats->unc_packets = db->uncomp_count;
+ stats->comp_bytes = db->comp_bytes;
+ stats->comp_packets = db->comp_count;
+ stats->inc_bytes = db->incomp_bytes;
+ stats->inc_packets = db->incomp_count;
+ stats->in_count = db->in_count;
+ stats->bytes_out = db->bytes_out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void bsd_reset (void *state,unsigned char code, unsigned char id,
+ unsigned char *data, unsigned len,
+ struct isdn_ppp_resetparams *rsparm)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ bsd_clear(db);
+ db->seqno = 0;
+ db->clear_count = 0;
+}
+
+/*
+ * Release the compression structure
+ */
+static void bsd_free (void *state)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ if (db) {
+ /*
+ * Release the dictionary
+ */
+ if (db->dict) {
+ vfree (db->dict);
+ db->dict = NULL;
+ }
+
+ /*
+ * Release the string buffer
+ */
+ if (db->lens) {
+ vfree (db->lens);
+ db->lens = NULL;
+ }
+
+ /*
+ * Finally release the structure itself.
+ */
+ kfree (db);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *bsd_alloc (struct isdn_ppp_comp_data *data)
+{
+ int bits;
+ unsigned int hsize, hshift, maxmaxcode;
+ struct bsd_db *db;
+ int decomp;
+
+ static unsigned int htab[][2] = {
+ { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } ,
+ { 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 }
+ };
+
+ if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+ || BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+ return NULL;
+
+ bits = BSD_NBITS(data->options[0]);
+
+ if(bits < 9 || bits > 15)
+ return NULL;
+
+ hsize = htab[bits-9][0];
+ hshift = htab[bits-9][1];
+
+ /*
+ * Allocate the main control structure for this instance.
+ */
+ maxmaxcode = MAXCODE(bits);
+ db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),GFP_KERNEL);
+ if (!db)
+ return NULL;
+
+ memset (db, 0, sizeof(struct bsd_db));
+
+ db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
+ decomp = db->xmit ? 0 : 1;
+
+ /*
+ * Allocate space for the dictionary. This may be more than one page in
+ * length.
+ */
+ db->dict = (struct bsd_dict *) vmalloc (hsize * sizeof (struct bsd_dict));
+ if (!db->dict) {
+ bsd_free (db);
+ return NULL;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ /*
+ * If this is the compression buffer then there is no length data.
+ * For decompression, the length information is needed as well.
+ */
+ if (!decomp)
+ db->lens = NULL;
+ else {
+ db->lens = (unsigned short *) vmalloc ((maxmaxcode + 1) *
+ sizeof (db->lens[0]));
+ if (!db->lens) {
+ bsd_free (db); /* calls MOD_DEC_USE_COUNT; */
+ return (NULL);
+ }
+ }
+
+ /*
+ * Initialize the data information for the compression code
+ */
+ db->totlen = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
+ db->hsize = hsize;
+ db->hshift = hshift;
+ db->maxmaxcode = maxmaxcode;
+ db->maxbits = bits;
+
+ return (void *) db;
+}
+
+/*
+ * Initialize the database.
+ */
+static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
+{
+ struct bsd_db *db = state;
+ int indx;
+ int decomp;
+
+ if(!state || !data) {
+ printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
+ return 0;
+ }
+
+ decomp = db->xmit ? 0 : 1;
+
+ if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+ || (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+ || (BSD_NBITS(data->options[0]) != db->maxbits)
+ || (decomp && db->lens == NULL)) {
+ printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
+ return 0;
+ }
+
+ if (decomp)
+ for(indx=LAST;indx>=0;indx--)
+ db->lens[indx] = 1;
+
+ indx = db->hsize;
+ while (indx-- != 0) {
+ db->dict[indx].codem1 = BADCODEM1;
+ db->dict[indx].cptr = 0;
+ }
+
+ db->unit = unit;
+ db->mru = 0;
+
+ db->debug = 1;
+
+ bsd_reset(db,0,0,NULL,0,NULL);
+
+ return 1;
+}
+
+/*
+ * Obtain pointers to the various structures in the compression tables
+ */
+
+#define dict_ptrx(p,idx) &(p->dict[idx])
+#define lens_ptrx(p,idx) &(p->lens[idx])
+
+#ifdef DEBUG
+static unsigned short *lens_ptr(struct bsd_db *db, int idx)
+{
+ if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
+ printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
+ idx = 0;
+ }
+ return lens_ptrx (db, idx);
+}
+
+static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
+{
+ if ((unsigned int) idx >= (unsigned int) db->hsize) {
+ printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
+ idx = 0;
+ }
+ return dict_ptrx (db, idx);
+}
+
+#else
+#define lens_ptr(db,idx) lens_ptrx(db,idx)
+#define dict_ptr(db,idx) dict_ptrx(db,idx)
+#endif
+
+/*
+ * compress a packet
+ */
+static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
+{
+ struct bsd_db *db;
+ int hshift;
+ unsigned int max_ent;
+ unsigned int n_bits;
+ unsigned int bitno;
+ unsigned long accm;
+ int ent;
+ unsigned long fcode;
+ struct bsd_dict *dictp;
+ unsigned char c;
+ int hval,disp,ilen,mxcode;
+ unsigned char *rptr = skb_in->data;
+ int isize = skb_in->len;
+
+#define OUTPUT(ent) \
+ { \
+ bitno -= n_bits; \
+ accm |= ((ent) << bitno); \
+ do { \
+ if(skb_out && skb_tailroom(skb_out) > 0) \
+ *(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
+ accm <<= 8; \
+ bitno += 8; \
+ } while (bitno <= 24); \
+ }
+
+ /*
+ * If the protocol is not in the range we're interested in,
+ * just return without compressing the packet. If it is,
+ * the protocol becomes the first byte to compress.
+ */
+ printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
+
+ ent = proto;
+ if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
+ return 0;
+
+ db = (struct bsd_db *) state;
+ hshift = db->hshift;
+ max_ent = db->max_ent;
+ n_bits = db->n_bits;
+ bitno = 32;
+ accm = 0;
+ mxcode = MAXCODE (n_bits);
+
+ /* This is the PPP header information */
+ if(skb_out && skb_tailroom(skb_out) >= 2) {
+ char *v = skb_put(skb_out,2);
+ /* we only push our own data on the header,
+ AC,PC and protos is pushed by caller */
+ v[0] = db->seqno >> 8;
+ v[1] = db->seqno;
+ }
+
+ ilen = ++isize; /* This is off by one, but that is what is in draft! */
+
+ while (--ilen > 0) {
+ c = *rptr++;
+ fcode = BSD_KEY (ent, c);
+ hval = BSD_HASH (ent, c, hshift);
+ dictp = dict_ptr (db, hval);
+
+ /* Validate and then check the entry. */
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+
+ if (dictp->fcode == fcode) {
+ ent = dictp->codem1 + 1;
+ continue; /* found (prefix,suffix) */
+ }
+
+ /* continue probing until a match or invalid entry */
+ disp = (hval == 0) ? 1 : hval;
+
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = dict_ptr (db, hval);
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ } while (dictp->fcode != fcode);
+
+ ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */
+ continue;
+
+nomatch:
+ OUTPUT(ent); /* output the prefix */
+
+ /* code -> hashtable */
+ if (max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ struct bsd_dict *dictp3;
+ int indx;
+
+ /* expand code size if needed */
+ if (max_ent >= mxcode) {
+ db->n_bits = ++n_bits;
+ mxcode = MAXCODE (n_bits);
+ }
+
+ /*
+ * Invalidate old hash table entry using
+ * this code, and then take it over.
+ */
+ dictp2 = dict_ptr (db, max_ent + 1);
+ indx = dictp2->cptr;
+ dictp3 = dict_ptr (db, indx);
+
+ if (dictp3->codem1 == max_ent)
+ dictp3->codem1 = BADCODEM1;
+
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->fcode = fcode;
+ db->max_ent = ++max_ent;
+
+ if (db->lens) {
+ unsigned short *len1 = lens_ptr (db, max_ent);
+ unsigned short *len2 = lens_ptr (db, ent);
+ *len1 = *len2 + 1;
+ }
+ }
+ ent = c;
+ }
+
+ OUTPUT(ent); /* output the last code */
+
+ if(skb_out)
+ db->bytes_out += skb_out->len; /* Do not count bytes from here */
+ db->uncomp_bytes += isize;
+ db->in_count += isize;
+ ++db->uncomp_count;
+ ++db->seqno;
+
+ if (bitno < 32)
+ ++db->bytes_out; /* must be set before calling bsd_check */
+
+ /*
+ * Generate the clear command if needed
+ */
+
+ if (bsd_check(db))
+ OUTPUT (CLEAR);
+
+ /*
+ * Pad dribble bits of last code with ones.
+ * Do not emit a completely useless byte of ones.
+ */
+ if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0)
+ *(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
+
+ /*
+ * Increase code size if we would have without the packet
+ * boundary because the decompressor will do so.
+ */
+ if (max_ent >= mxcode && max_ent < db->maxmaxcode)
+ db->n_bits++;
+
+ /* If output length is too large then this is an incompressible frame. */
+ if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
+ ++db->incomp_count;
+ db->incomp_bytes += isize;
+ return 0;
+ }
+
+ /* Count the number of compressed frames */
+ ++db->comp_count;
+ db->comp_bytes += skb_out->len;
+ return skb_out->len;
+
+#undef OUTPUT
+}
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
+{
+ bsd_compress (state, skb_in, NULL, proto);
+}
+
+/*
+ * Decompress "BSD Compress".
+ */
+static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
+ struct isdn_ppp_resetparams *rsparm)
+{
+ struct bsd_db *db;
+ unsigned int max_ent;
+ unsigned long accm;
+ unsigned int bitno; /* 1st valid bit in accm */
+ unsigned int n_bits;
+ unsigned int tgtbitno; /* bitno when we have a code */
+ struct bsd_dict *dictp;
+ int seq;
+ unsigned int incode;
+ unsigned int oldcode;
+ unsigned int finchar;
+ unsigned char *p,*ibuf;
+ int ilen;
+ int codelen;
+ int extra;
+
+ db = (struct bsd_db *) state;
+ max_ent = db->max_ent;
+ accm = 0;
+ bitno = 32; /* 1st valid bit in accm */
+ n_bits = db->n_bits;
+ tgtbitno = 32 - n_bits; /* bitno when we have a code */
+
+ printk(KERN_DEBUG "bsd_decompress called\n");
+
+ if(!skb_in || !skb_out) {
+ printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
+ return DECOMP_ERROR;
+ }
+
+ /*
+ * Get the sequence number.
+ */
+ if( (p = skb_pull(skb_in,2)) == NULL) {
+ return DECOMP_ERROR;
+ }
+ p-=2;
+ seq = (p[0] << 8) + p[1];
+ ilen = skb_in->len;
+ ibuf = skb_in->data;
+
+ /*
+ * Check the sequence number and give up if it differs from
+ * the value we're expecting.
+ */
+ if (seq != db->seqno) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
+ db->unit, seq, db->seqno - 1);
+ }
+ return DECOMP_ERROR;
+ }
+
+ ++db->seqno;
+ db->bytes_out += ilen;
+
+ if(skb_tailroom(skb_out) > 0)
+ *(skb_put(skb_out,1)) = 0;
+ else
+ return DECOMP_ERR_NOMEM;
+
+ oldcode = CLEAR;
+
+ /*
+ * Keep the checkpoint correctly so that incompressible packets
+ * clear the dictionary at the proper times.
+ */
+
+ for (;;) {
+ if (ilen-- <= 0) {
+ db->in_count += (skb_out->len - 1); /* don't count the header */
+ break;
+ }
+
+ /*
+ * Accumulate bytes until we have a complete code.
+ * Then get the next code, relying on the 32-bit,
+ * unsigned accm to mask the result.
+ */
+
+ bitno -= 8;
+ accm |= *ibuf++ << bitno;
+ if (tgtbitno < bitno)
+ continue;
+
+ incode = accm >> tgtbitno;
+ accm <<= n_bits;
+ bitno += n_bits;
+
+ /*
+ * The dictionary must only be cleared at the end of a packet.
+ */
+
+ if (incode == CLEAR) {
+ if (ilen > 0) {
+ if (db->debug)
+ printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+ bsd_clear(db);
+ break;
+ }
+
+ if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
+ || (incode > max_ent && oldcode == CLEAR)) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+ db->unit, incode, oldcode);
+ printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
+ max_ent, skb_out->len, db->seqno);
+ }
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+
+ /* Special case for KwKwK string. */
+ if (incode > max_ent) {
+ finchar = oldcode;
+ extra = 1;
+ } else {
+ finchar = incode;
+ extra = 0;
+ }
+
+ codelen = *(lens_ptr (db, finchar));
+ if( skb_tailroom(skb_out) < codelen + extra) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
+#ifdef DEBUG
+ printk(KERN_DEBUG " len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
+ ilen, finchar, codelen, skb_out->len);
+#endif
+ }
+ return DECOMP_FATALERROR;
+ }
+
+ /*
+ * Decode this code and install it in the decompressed buffer.
+ */
+
+ p = skb_put(skb_out,codelen);
+ p += codelen;
+ while (finchar > LAST) {
+ struct bsd_dict *dictp2 = dict_ptr (db, finchar);
+
+ dictp = dict_ptr (db, dictp2->cptr);
+
+#ifdef DEBUG
+ if (--codelen <= 0 || dictp->codem1 != finchar-1) {
+ if (codelen <= 0) {
+ printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
+ printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
+ } else {
+ if (dictp->codem1 != finchar-1) {
+ printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
+ printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
+ }
+ }
+ return DECOMP_FATALERROR;
+ }
+#endif
+
+ {
+ u32 fcode = dictp->fcode;
+ *--p = (fcode >> 16) & 0xff;
+ finchar = fcode & 0xffff;
+ }
+ }
+ *--p = finchar;
+
+#ifdef DEBUG
+ if (--codelen != 0)
+ printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
+#endif
+
+ if (extra) /* the KwKwK case again */
+ *(skb_put(skb_out,1)) = finchar;
+
+ /*
+ * If not first code in a packet, and
+ * if not out of code space, then allocate a new code.
+ *
+ * Keep the hash table correct so it can be used
+ * with uncompressed packets.
+ */
+ if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2, *dictp3;
+ u16 *lens1, *lens2;
+ unsigned long fcode;
+ int hval, disp, indx;
+
+ fcode = BSD_KEY(oldcode,finchar);
+ hval = BSD_HASH(oldcode,finchar,db->hshift);
+ dictp = dict_ptr (db, hval);
+
+ /* look for a free hash table entry */
+ if (dictp->codem1 < max_ent) {
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = dict_ptr (db, hval);
+ } while (dictp->codem1 < max_ent);
+ }
+
+ /*
+ * Invalidate previous hash table entry
+ * assigned this code, and then take it over
+ */
+
+ dictp2 = dict_ptr (db, max_ent + 1);
+ indx = dictp2->cptr;
+ dictp3 = dict_ptr (db, indx);
+
+ if (dictp3->codem1 == max_ent)
+ dictp3->codem1 = BADCODEM1;
+
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->fcode = fcode;
+ db->max_ent = ++max_ent;
+
+ /* Update the length of this string. */
+ lens1 = lens_ptr (db, max_ent);
+ lens2 = lens_ptr (db, oldcode);
+ *lens1 = *lens2 + 1;
+
+ /* Expand code size if needed. */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+ db->n_bits = ++n_bits;
+ tgtbitno = 32-n_bits;
+ }
+ }
+ oldcode = incode;
+ }
+
+ ++db->comp_count;
+ ++db->uncomp_count;
+ db->comp_bytes += skb_in->len - BSD_OVHD;
+ db->uncomp_bytes += skb_out->len;
+
+ if (bsd_check(db)) {
+ if (db->debug)
+ printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
+ db->unit, db->seqno - 1);
+ }
+ return skb_out->len;
+}
+
+/*************************************************************
+ * Table of addresses for the BSD compression module
+ *************************************************************/
+
+static struct isdn_ppp_compressor ippp_bsd_compress = {
+ NULL,NULL, /* prev,next: overwritten by isdn_ppp */
+ CI_BSD_COMPRESS, /* compress_proto */
+ bsd_alloc, /* alloc */
+ bsd_free, /* free */
+ bsd_init, /* init */
+ bsd_reset, /* reset */
+ bsd_compress, /* compress */
+ bsd_decompress, /* decompress */
+ bsd_incomp, /* incomp */
+ bsd_stats /* comp_stat */
+};
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+int init_module(void)
+{
+ int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
+ if (answer == 0)
+ printk (KERN_INFO "PPP BSD Compression module registered\n");
+ return answer;
+}
+
+void cleanup_module(void)
+{
+ isdn_ppp_unregister_compressor (&ippp_bsd_compress);
+}
--- /dev/null
+/* $Id: isdn_budget.c,v 1.3 1998/10/23 10:18:39 paul Exp $
+ *
+ * Linux ISDN subsystem, budget-accounting for network interfaces.
+ *
+ * Copyright 1997 by Christian Lademann <cal@zls.de>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn_budget.c,v $
+ * Revision 1.3 1998/10/23 10:18:39 paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.2 1998/03/07 23:17:30 fritz
+ * Added RCS keywords
+ * Bugfix: Did not compile without isdn_dumppkt beeing enabled.
+ *
+ */
+
+/*
+30.06.97:cal:angelegt
+04.11.97:cal:budget.period: int --> time_t
+*/
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+
+#ifdef CONFIG_ISDN_BUDGET
+
+#define VERBOSE_PRINTK(v, l, p...) { \
+ if(dev->net_verbose >= (v)) { \
+ printk(l ## p); \
+ } else { ; } \
+}
+
+
+int
+isdn_net_budget(int type, struct device *ndev) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ int i, ret = 0;
+
+ switch(type) {
+ case ISDN_BUDGET_INIT:
+ for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+ lp->budget [i] .amount = -1;
+ lp->budget [i] .used = 0;
+ lp->budget [i] .period = (time_t)0;
+ lp->budget [i] .period_started = (time_t)0;
+ lp->budget [i] .last_check = CURRENT_TIME;
+ lp->budget [i] .notified = 0;
+ }
+
+ return(0);
+ break;
+
+ case ISDN_BUDGET_CHECK_DIAL:
+ case ISDN_BUDGET_CHECK_CHARGE:
+ case ISDN_BUDGET_CHECK_ONLINE:
+ ret = 0;
+
+ for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+ if(lp->budget [i] .amount < 0)
+ continue;
+
+ if(lp->budget [i] .period_started + lp->budget [i] .period < CURRENT_TIME) {
+ lp->budget [i] .used = 0;
+ lp->budget [i] .period_started = CURRENT_TIME;
+ lp->budget [i] .notified = 0;
+ }
+
+ if(lp->budget [i] .used >= lp->budget [i] .amount)
+ ret |= (1 << i);
+ }
+
+ switch(type) {
+ case ISDN_BUDGET_CHECK_DIAL:
+ if(! ret) {
+ lp->budget [ISDN_BUDGET_DIAL] .used++;
+ lp->budget [ISDN_BUDGET_DIAL] .last_check = CURRENT_TIME;
+ }
+ break;
+
+ case ISDN_BUDGET_CHECK_CHARGE:
+ lp->budget [ISDN_BUDGET_CHARGE] .used++;
+ lp->budget [ISDN_BUDGET_CHARGE] .last_check = CURRENT_TIME;
+ break;
+
+ case ISDN_BUDGET_CHECK_ONLINE:
+ if(lp->budget [ISDN_BUDGET_ONLINE] .last_check) {
+ lp->budget [ISDN_BUDGET_ONLINE] .used += (CURRENT_TIME - lp->budget [ISDN_BUDGET_ONLINE] .last_check);
+ }
+
+ lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME;
+ break;
+ }
+
+/*
+ if(ret)
+ lp->flags |= ISDN_NET_DM_OFF;
+*/
+ for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+ if(ret & (1 << i) && ! lp->budget [i] .notified) {
+ switch(i) {
+ case ISDN_BUDGET_DIAL:
+ printk(KERN_WARNING "isdn_budget: dial budget used up.\n");
+ break;
+
+ case ISDN_BUDGET_CHARGE:
+ printk(KERN_WARNING "isdn_budget: charge budget used up.\n");
+ break;
+
+ case ISDN_BUDGET_ONLINE:
+ printk(KERN_WARNING "isdn_budget: online budget used up.\n");
+ break;
+
+ default:
+ printk(KERN_WARNING "isdn_budget: budget #%d used up.\n", i);
+ break;
+ }
+
+ lp->budget [i] .notified = 1;
+ }
+ }
+
+ return(ret);
+ break;
+
+ case ISDN_BUDGET_START_ONLINE:
+ lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME;
+ return(0);
+
+ break;
+ }
+
+ return(-1);
+}
+
+
+int
+isdn_budget_ioctl(isdn_ioctl_budget *iocmd) {
+ isdn_net_dev *p = isdn_net_findif(iocmd->name);
+
+ if(p) {
+ switch(iocmd->command) {
+ case ISDN_BUDGET_SET_BUDGET:
+ if(! suser())
+ return(-EPERM);
+
+ if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET)
+ return(-EINVAL);
+
+ if(iocmd->amount < 0)
+ iocmd->amount = -1;
+
+ p->local->budget [iocmd->budget] .amount = iocmd->amount;
+ p->local->budget [iocmd->budget] .period = iocmd->period;
+
+ if(iocmd->used <= 0)
+ p->local->budget [iocmd->budget] .used = 0;
+ else
+ p->local->budget [iocmd->budget] .used = iocmd->used;
+
+ if(iocmd->period_started == (time_t)0)
+ p->local->budget [iocmd->budget] .period_started = CURRENT_TIME;
+ else
+ p->local->budget [iocmd->budget] .period_started = iocmd->period_started;
+
+ return(0);
+ break;
+
+ case ISDN_BUDGET_GET_BUDGET:
+ if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET)
+ return(-EINVAL);
+
+ iocmd->amount = p->local->budget [iocmd->budget] .amount;
+ iocmd->used = p->local->budget [iocmd->budget] .used;
+ iocmd->period = p->local->budget [iocmd->budget] .period;
+ iocmd->period_started = p->local->budget [iocmd->budget] .period_started;
+
+ return(0);
+ break;
+
+ default:
+ return(-EINVAL);
+ break;
+ }
+ }
+ return(-ENODEV);
+}
+#endif
-/* $Id: isdn_cards.c,v 1.7 1998/02/20 17:24:28 fritz Exp $
+/* $Id: isdn_cards.c,v 1.9 1999/04/12 12:33:11 fritz Exp $
* Linux ISDN subsystem, initialization for non-modularized drivers.
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de)
*
* 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
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_cards.c,v $
+ * Revision 1.9 1999/04/12 12:33:11 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.8 1999/03/29 11:13:23 armin
+ * Added eicon driver init.
+ *
* Revision 1.7 1998/02/20 17:24:28 fritz
* Added ACT2000 init.
*
extern void pcbit_init(void);
#endif
+#ifdef CONFIG_ISDN_DRV_EICON
+extern void eicon_init(void);
+#endif
+
#ifdef CONFIG_ISDN_DRV_AVMB1
extern void avmb1_init(void);
extern void capi_init(void);
#if CONFIG_ISDN_DRV_ACT2000
act2000_init();
#endif
+#if CONFIG_ISDN_DRV_EICON
+ eicon_init();
+#endif
}
-/* $Id: isdn_cards.h,v 1.2 1997/02/03 23:31:55 fritz Exp $
+/* $Id: isdn_cards.h,v 1.3 1999/04/12 12:33:13 fritz Exp $
* Linux ISDN subsystem, initialization for non-modularized drivers.
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
*
* 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
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_cards.h,v $
+ * Revision 1.3 1999/04/12 12:33:13 fritz
+ * Changes from 2.0 tree.
+ *
* Revision 1.2 1997/02/03 23:31:55 fritz
* Reformatted according CodingStyle
*
-/* $Id: isdn_common.c,v 1.55 1998/02/23 23:35:32 fritz Exp $
+/* $Id: isdn_common.c,v 1.75 1999/04/18 14:06:47 fritz Exp $
* Linux ISDN subsystem, common used functions (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg
* Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
* $Log: isdn_common.c,v $
+ * Revision 1.75 1999/04/18 14:06:47 fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.74 1999/04/12 13:16:45 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.73 1999/04/12 12:33:15 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.72 1999/03/02 12:04:44 armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ * register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ * which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ * establishment and register S7.
+ *
+ * Revision 1.71 1999/01/28 09:10:43 armin
+ * Fixed bad while-loop in isdn_readbch().
+ *
+ * Revision 1.70 1999/01/15 19:58:54 he
+ * removed compatibiltity macro
+ *
+ * Revision 1.69 1998/09/07 21:59:58 he
+ * flush method for 2.1.118 and above
+ * updated IIOCTLNETGPN
+ *
+ * Revision 1.68 1998/08/31 21:09:45 he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ * peer phone number)
+ *
+ * Revision 1.67 1998/06/26 15:12:21 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.66 1998/06/17 19:50:41 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.65 1998/06/07 00:20:00 fritz
+ * abc cleanup.
+ *
+ * Revision 1.64 1998/06/02 12:10:03 detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.63 1998/05/03 17:40:38 detabc
+ * Include abc-extension-support for >= 2.1.x Kernels in
+ * isdn_net.c and isdn_common.c. alpha-test OK and running !
+ *
+ * Revision 1.62 1998/04/14 16:28:43 he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.61 1998/03/22 18:50:46 hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.60 1998/03/19 13:18:18 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 1.59 1998/03/09 17:46:23 he
+ * merged in 2.1.89 changes
+ *
+ * Revision 1.58 1998/03/07 22:35:24 fritz
+ * Starting generic module support (Nothing usable yet).
+ *
+ * Revision 1.57 1998/03/07 18:21:01 cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.56 1998/02/25 17:49:38 he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
* Revision 1.55 1998/02/23 23:35:32 fritz
* Eliminated some compiler warnings.
*
isdn_dev *dev = (isdn_dev *) 0;
-static char *isdn_revision = "$Revision: 1.55 $";
+static char *isdn_revision = "$Revision: 1.75 $";
extern char *isdn_net_revision;
extern char *isdn_tty_revision;
void
isdn_MOD_INC_USE_COUNT(void)
{
+ int i;
+
MOD_INC_USE_COUNT;
+ for (i = 0; i < dev->drivers; i++) {
+ isdn_ctrl cmd;
+
+ cmd.driver = i;
+ cmd.arg = 0;
+ cmd.command = ISDN_CMD_LOCK;
+ isdn_command(&cmd);
+ dev->drv[i]->locks++;
+ }
}
void
isdn_MOD_DEC_USE_COUNT(void)
{
+ int i;
+
MOD_DEC_USE_COUNT;
+ for (i = 0; i < dev->drivers; i++)
+ if (dev->drv[i]->locks > 0) {
+ isdn_ctrl cmd;
+
+ cmd.driver = i;
+ cmd.arg = 0;
+ cmd.command = ISDN_CMD_UNLOCK;
+ isdn_command(&cmd);
+ dev->drv[i]->locks--;
+ }
}
#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
}
#endif
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+static int
+isdn_star(char *s, char *p)
+{
+ while (isdn_wildmat(s, p)) {
+ if (*++s == '\0')
+ return (2);
+ }
+ return (0);
+}
+
+/*
+ * Shell-type Pattern-matching for incoming caller-Ids
+ * This function gets a string in s and checks, if it matches the pattern
+ * given in p.
+ *
+ * Return:
+ * 0 = match.
+ * 1 = no match.
+ * 2 = no match. Would eventually match, if s would be longer.
+ *
+ * Possible Patterns:
+ *
+ * '?' matches one character
+ * '*' matches zero or more characters
+ * [xyz] matches the set of characters in brackets.
+ * [^xyz] matches any single character not in the set of characters
+ */
+
+int
+isdn_wildmat(char *s, char *p)
+{
+ register int last;
+ register int matched;
+ register int reverse;
+ register int nostar = 1;
+
+ for (; *p; s++, p++)
+ switch (*p) {
+ case '\\':
+ /*
+ * Literal match with following character,
+ * fall through.
+ */
+ p++;
+ default:
+ if (*s != *p)
+ return (*s == '\0')?2:1;
+ continue;
+ case '?':
+ /* Match anything. */
+ if (*s == '\0')
+ return (2);
+ continue;
+ case '*':
+ nostar = 0;
+ /* Trailing star matches everything. */
+ return (*++p ? isdn_star(s, p) : 0);
+ case '[':
+ /* [^....] means inverse character class. */
+ if ((reverse = (p[1] == '^')))
+ p++;
+ for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+ /* This next line requires a good C compiler. */
+ if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+ matched = 1;
+ if (matched == reverse)
+ return (1);
+ continue;
+ }
+ return (*s == '\0')?0:nostar;
+}
+
static void
isdn_free_queue(struct sk_buff_head *queue)
{
isdn_timer_funct(ulong dummy)
{
int tf = dev->tflags;
-
if (tf & ISDN_TIMER_FAST) {
if (tf & ISDN_TIMER_MODEMREAD)
isdn_tty_readmodem();
if (tf & ISDN_TIMER_KEEPALIVE)
isdn_net_slarp_out();
}
+ if (tf & ISDN_TIMER_CARRIER)
+ isdn_tty_carrier_timeout();
#if (defined CONFIG_ISDN_PPP) && (defined CONFIG_ISDN_MPP)
if (tf & ISDN_TIMER_IPPP)
isdn_ppp_timer_timeout();
#endif
}
}
- if (tf) {
+ if (tf)
+ {
int flags;
save_flags(flags);
isdn_command(&cmd);
}
+/*
+ * Begin of a CAPI like LL<->HL interface, currently used only for
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include "avmb1/capicmd.h" /* this should be moved in a common place */
+
+int
+isdn_capi_rec_hl_msg(capi_msg *cm) {
+
+ int di;
+ int ch;
+
+ di = (cm->adr.Controller & 0x7f) -1;
+ ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f);
+ switch(cm->Command) {
+ case CAPI_FACILITY:
+ /* in the moment only handled in tty */
+ return(isdn_tty_capi_facility(cm));
+ default:
+ return(-1);
+ }
+}
+
static int
isdn_status_callback(isdn_ctrl * c)
{
wake_up_interruptible(&dev->drv[di]->st_waitq);
break;
case ISDN_STAT_RUN:
- dev->drv[di]->running = 1;
+ dev->drv[di]->flags |= DRV_FLAG_RUNNING;
for (i = 0; i < ISDN_MAX_CHANNELS; i++)
if (dev->drvmap[i] == di)
isdn_all_eaz(di, dev->chanmap[i]);
break;
case ISDN_STAT_STOP:
- dev->drv[di]->running = 0;
+ dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
break;
case ISDN_STAT_ICALL:
if (i < 0)
return 0;
}
/* Try to find a network-interface which will accept incoming call */
- cmd.driver = di;
- cmd.arg = c->arg;
- cmd.command = ISDN_CMD_LOCK;
- isdn_command(&cmd);
r = isdn_net_find_icall(di, c->arg, i, c->parm.setup);
switch (r) {
case 0:
/* No network-device replies.
- * Try ttyI's
+ * Try ttyI's.
+ * These return 0 on no match, 1 on match and
+ * 3 on eventually match, if CID is longer.
*/
- if (isdn_tty_find_icall(di, c->arg, c->parm.setup) >= 0)
- retval = 1;
- else if (dev->drv[di]->reject_bus) {
+ retval = isdn_tty_find_icall(di, c->arg, c->parm.setup);
+ if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) {
+ /* No tty responding */
cmd.driver = di;
cmd.arg = c->arg;
cmd.command = ISDN_CMD_HANGUP;
/* ... then start callback. */
isdn_net_dial();
break;
+ case 5:
+ /* Number would eventually match, if longer */
+ retval = 3;
+ break;
}
- if (retval != 1) {
- cmd.driver = di;
- cmd.arg = c->arg;
- cmd.command = ISDN_CMD_UNLOCK;
- isdn_command(&cmd);
- }
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "ICALL: ret=%d\n", retval);
+#endif
return retval;
break;
case ISDN_STAT_CINF:
#endif
if (dev->global_flags & ISDN_GLOBAL_STOPPED)
return 0;
- dev->drv[di]->flags &= ~(1 << (c->arg));
+ dev->drv[di]->online &= ~(1 << (c->arg));
isdn_info_update();
/* Signal hangup to network-devices */
if (isdn_net_stat_callback(i, c))
/* Signal B-channel-connect to network-devices */
if (dev->global_flags & ISDN_GLOBAL_STOPPED)
return 0;
- dev->drv[di]->flags |= (1 << (c->arg));
+ dev->drv[di]->online |= (1 << (c->arg));
isdn_info_update();
if (isdn_net_stat_callback(i, c))
break;
#endif
if (dev->global_flags & ISDN_GLOBAL_STOPPED)
return 0;
- dev->drv[di]->flags &= ~(1 << (c->arg));
+ dev->drv[di]->online &= ~(1 << (c->arg));
isdn_info_update();
#ifdef CONFIG_ISDN_X25
/* Signal hangup to network-devices */
break;
break;
case ISDN_STAT_ADDCH:
+ if (isdn_add_channels(dev->drv[di], di, c->arg, 1))
+ return -1;
+ isdn_info_update();
break;
case ISDN_STAT_UNLOAD:
save_flags(flags);
isdn_free_queue(&dev->drv[di]->rpqueue[i]);
kfree(dev->drv[di]->rpqueue);
kfree(dev->drv[di]->rcv_waitq);
+#if LINUX_VERSION_CODE < 131841
+ kfree(dev->drv[di]->snd_waitq);
+#endif
kfree(dev->drv[di]);
dev->drv[di] = NULL;
dev->drvid[di][0] = '\0';
return 0;
case ISDN_STAT_L1ERR:
break;
+ case CAPI_PUT_MESSAGE:
+ return(isdn_capi_rec_hl_msg(&c->parm.cmsg));
default:
return -1;
}
* of the mapping (di,ch)<->minor, happen during the sleep? --he
*/
int
+#if LINUX_VERSION_CODE < 131841
+isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, struct wait_queue **sleep)
+#else
isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep)
+#endif
{
int left;
int count;
dflag = 0;
count_pull = count_put = 0;
- while ((count_pull < skb->len) && (left-- > 0)) {
+ while ((count_pull < skb->len) && (left > 0)) {
+ left--;
if (dev->drv[di]->DLEflag & DLEmask) {
*cp++ = DLE;
dev->drv[di]->DLEflag &= ~DLEmask;
return (dev->chanmap[minor]);
}
-#define INF_DV 0x01 /* Data version for /dev/isdninfo */
-
static char *
isdn_statstr(void)
{
p = istatbuf + strlen(istatbuf);
for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
if (dev->drv[i]) {
- sprintf(p, "%ld ", dev->drv[i]->flags);
+ sprintf(p, "%ld ", dev->drv[i]->online);
p = istatbuf + strlen(istatbuf);
} else {
sprintf(p, "? ");
drvidx = isdn_minor2drv(minor);
if (drvidx < 0)
return -ENODEV;
- if (!dev->drv[drvidx]->running)
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
return -ENODEV;
chidx = isdn_minor2chan(minor);
if( ! (p = kmalloc(count,GFP_KERNEL)) ) return -ENOMEM;
drvidx = isdn_minor2drv(minor);
if (drvidx < 0)
return -ENODEV;
- if (!dev->drv[drvidx]->running)
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
return -ENODEV;
chidx = isdn_minor2chan(minor);
while (isdn_writebuf_stub(drvidx, chidx, buf, count, 1) != count)
/*
* We want to use the isdnctrl device to load the firmware
*
- if (!dev->drv[drvidx]->running)
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
return -ENODEV;
*/
if (dev->drv[drvidx]->interface->writecmd)
return POLLERR;
}
-/*
- * This accesses user space with interrupts off, but is not needed by
- * any of the isdn4k-util programs anyway. Thus, in contrast to your
- * first impression after looking at the code, fixing is trival!*/
-#if 0
-static int
-isdn_set_allcfg(char *src)
-{
- int ret;
- int i;
- ulong flags;
- isdn_net_ioctl_cfg cfg;
- isdn_net_ioctl_phone phone;
-
- if ((ret = isdn_net_rmall()))
- return ret;
- if (copy_from_user((char *) &i, src, sizeof(int))) return -EFAULT;
- save_flags(flags);
- cli();
- src += sizeof(int);
- while (i) {
- int phone_len;
- int out_flag;
-
- if (copy_from_user((char *) &cfg, src, sizeof(cfg))) {
- restore_flags(flags);
- return -EFAULT;
- }
- src += sizeof(cfg);
- if (!isdn_net_new(cfg.name, NULL)) {
- restore_flags(flags);
- return -EIO;
- }
- if ((ret = isdn_net_setcfg(&cfg))) {
- restore_flags(flags);
- return ret;
- }
- phone_len = out_flag = 0;
- while (out_flag < 2) {
- if ((ret = verify_area(VERIFY_READ, src, 1))) {
- restore_flags(flags);
- return ret;
- }
- get_user(phone.phone[phone_len], src++);
- if ((phone.phone[phone_len] == ' ') ||
- (phone.phone[phone_len] == '\0')) {
- if (phone_len) {
- phone.phone[phone_len] = '\0';
- strcpy(phone.name, cfg.name);
- phone.outgoing = out_flag;
- if ((ret = isdn_net_addphone(&phone))) {
- restore_flags(flags);
- return ret;
- }
- } else
- out_flag++;
- phone_len = 0;
- }
- if (++phone_len >= sizeof(phone.phone))
- printk(KERN_WARNING
- "%s: IIOCSETSET phone number too long, ignored\n",
- cfg.name);
- }
- i--;
- }
- restore_flags(flags);
- return 0;
-}
-
-static int
-isdn_get_allcfg(char *dest)
-{
- isdn_net_ioctl_cfg cfg;
- isdn_net_ioctl_phone phone;
- isdn_net_dev *p;
- ulong flags;
- int ret;
-
- /* Walk through netdev-chain */
- save_flags(flags);
- cli();
- p = dev->netdev;
- while (p) {
- isdn_net_local *lp = p->local;
-
- if ((ret = verify_area(VERIFY_WRITE, (void *) dest, sizeof(cfg) + 200))) {
- restore_flags(flags);
- return ret;
- }
- strcpy(cfg.eaz, lp->msn);
- cfg.exclusive = lp->exclusive;
- if (lp->pre_device >= 0) {
- sprintf(cfg.drvid, "%s,%d", dev->drvid[lp->pre_device],
- lp->pre_channel);
- } else
- cfg.drvid[0] = '\0';
- cfg.onhtime = lp->onhtime;
- cfg.charge = lp->charge;
- cfg.l2_proto = lp->l2_proto;
- cfg.l3_proto = lp->l3_proto;
- cfg.p_encap = lp->p_encap;
- cfg.secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
- cfg.callback = (lp->flags & ISDN_NET_CALLBACK) ? 1 : 0;
- cfg.chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
- cfg.ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0;
- cfg.chargeint = lp->chargeint;
- if (copy_to_user(dest, lp->name, 10)) {
- restore_flags(flags);
- return -EFAULT;
- }
- dest += 10;
- if (copy_to_user(dest, (char *) &cfg, sizeof(cfg))) {
- restore_flags(flags);
- return -EFAULT;
- }
- dest += sizeof(cfg);
- strcpy(phone.name, lp->name);
- phone.outgoing = 0;
- if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
- restore_flags(flags);
- return ret;
- } else
- dest += ret;
- strcpy(phone.name, lp->name);
- phone.outgoing = 1;
- if ((ret = isdn_net_getphones(&phone, dest)) < 0) {
- restore_flags(flags);
- return ret;
- } else
- dest += ret;
- put_user(0, dest);
- p = p->next;
- }
- restore_flags(flags);
- return 0;
-}
-#endif
static int
isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
} else
return -EINVAL;
break;
+#ifdef CONFIG_NETDEVICES
+ case IIOCNETGPN:
+ /* Get peer phone number of a connected
+ * isdn network interface */
+ if (arg) {
+ if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
+ return -EFAULT;
+ return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
+ } else
+ return -EINVAL;
+#endif
default:
return -EINVAL;
}
if (drvidx < 0)
return -ENODEV;
chidx = isdn_minor2chan(minor);
- if (!dev->drv[drvidx]->running)
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
return -ENODEV;
return 0;
}
}
if (drvidx == -1)
return -ENODEV;
- dev->drv[drvidx]->reject_bus = iocts.arg;
- return 0;
-#if 0
- case IIOCGETSET:
- /* Get complete setup (all network-interfaces and profile-
- settings of all tty-devices */
- if (arg)
- return (isdn_get_allcfg((char *) arg));
+ if (iocts.arg)
+ dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
else
- return -EINVAL;
- break;
- case IIOCSETSET:
- /* Set complete setup (all network-interfaces and profile-
- settings of all tty-devices */
- if (arg)
- return (isdn_set_allcfg((char *) arg));
- else
- return -EINVAL;
- break;
-#endif
+ dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
+ return 0;
case IIOCSIGPRF:
dev->profd = current;
return 0;
int i;
if ((ret = verify_area(VERIFY_WRITE, (void *) arg,
- (ISDN_MODEM_ANZREG + ISDN_MSNLEN)
+ (ISDN_MODEM_ANZREG + ISDN_MSNLEN + ISDN_LMSNLEN)
* ISDN_MAX_CHANNELS)))
return ret;
if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
return -EFAULT;
p += ISDN_MSNLEN;
+ if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN))
+ return -EFAULT;
+ p += ISDN_LMSNLEN;
}
- return (ISDN_MODEM_ANZREG + ISDN_MSNLEN) * ISDN_MAX_CHANNELS;
+ return (ISDN_MODEM_ANZREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS;
} else
return -EINVAL;
break;
uint minor = MINOR(ino->i_rdev);
int drvidx;
int chidx;
- isdn_ctrl c;
if (minor == ISDN_MINOR_STATUS) {
infostruct *p;
if (drvidx < 0)
return -ENODEV;
chidx = isdn_minor2chan(minor);
- if (!dev->drv[drvidx]->running)
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
return -ENODEV;
- if (!(dev->drv[drvidx]->flags & (1 << chidx)))
+ if (!(dev->drv[drvidx]->online & (1 << chidx)))
return -ENODEV;
- c.command = ISDN_CMD_LOCK;
- c.driver = drvidx;
- isdn_command(&c);
- MOD_INC_USE_COUNT;
+ isdn_MOD_INC_USE_COUNT();
return 0;
}
if (minor <= ISDN_MINOR_CTRLMAX) {
drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
if (drvidx < 0)
return -ENODEV;
- c.command = ISDN_CMD_LOCK;
- c.driver = drvidx;
- MOD_INC_USE_COUNT;
- isdn_command(&c);
+ isdn_MOD_INC_USE_COUNT();
return 0;
}
#ifdef CONFIG_ISDN_PPP
if (minor <= ISDN_MINOR_PPPMAX) {
int ret;
if (!(ret = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep)))
- MOD_INC_USE_COUNT;
+ isdn_MOD_INC_USE_COUNT();
return ret;
}
#endif
isdn_close(struct inode *ino, struct file *filep)
{
uint minor = MINOR(ino->i_rdev);
- int drvidx;
- isdn_ctrl c;
- MOD_DEC_USE_COUNT;
if (minor == ISDN_MINOR_STATUS) {
infostruct *p = dev->infochain;
infostruct *q = NULL;
+
+ MOD_DEC_USE_COUNT;
while (p) {
if (p->private == (char *) &(filep->private_data)) {
if (q)
printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
return 0;
}
- if (minor < ISDN_MINOR_CTRL) {
- drvidx = isdn_minor2drv(minor);
- if (drvidx < 0)
- return 0;
- c.command = ISDN_CMD_UNLOCK;
- c.driver = drvidx;
- isdn_command(&c);
+ isdn_MOD_DEC_USE_COUNT();
+ if (minor < ISDN_MINOR_CTRL)
return 0;
- }
if (minor <= ISDN_MINOR_CTRLMAX) {
- drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
- if (drvidx < 0)
- return 0;
if (dev->profd == current)
dev->profd = NULL;
- c.command = ISDN_CMD_UNLOCK;
- c.driver = drvidx;
- isdn_command(&c);
return 0;
}
#ifdef CONFIG_ISDN_PPP
ulong flags;
ulong features;
ulong vfeatures;
- isdn_ctrl cmd;
save_flags(flags);
cli();
if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
((pre_dev != d) || (pre_chan != dev->chanmap[i])))
continue;
- if ((dev->drv[d]->running)) {
+ if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
if (((dev->drv[d]->interface->features & features) == features) ||
(((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
(dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
dev->usage[i] |= usage;
isdn_info_update();
- cmd.driver = d;
- cmd.arg = 0;
- cmd.command = ISDN_CMD_LOCK;
- isdn_command(&cmd);
restore_flags(flags);
return i;
} else {
dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
dev->usage[i] |= usage;
isdn_info_update();
- cmd.driver = d;
- cmd.arg = 0;
- cmd.command = ISDN_CMD_LOCK;
- isdn_command(&cmd);
restore_flags(flags);
return i;
}
{
int i;
ulong flags;
- isdn_ctrl cmd;
save_flags(flags);
cli();
dev->obytes[i] = 0;
isdn_info_update();
isdn_free_queue(&dev->drv[di]->rpqueue[ch]);
- cmd.driver = di;
- cmd.arg = ch;
- cmd.command = ISDN_CMD_UNLOCK;
- restore_flags(flags);
- isdn_command(&cmd);
- return;
}
restore_flags(flags);
}
copy_from_user(skb_put(skb, len), buf, len);
else
memcpy(skb_put(skb, len), buf, len);
-
ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb);
if (ret <= 0)
dev_kfree_skb(skb);
return ret;
}
+int
+register_isdn_module(isdn_module *m) {
+#if 0
+ isdn_module_list **pp = &dev->modules;
+ isdn_module *new = kmalloc(sizeof(isdn_module_list), GFP_KERNEL);
+
+ if (!new) {
+ printk(KERN_WARNING "isdn: Out of memory in register_isdn_module\n");
+ return -1;
+ }
+ while (*pp && (*pp)->orig != m)
+ pp = &(*pp)->next;
+ if (*pp != NULL) {
+ printk(KERN_WARNING "isdn: Module %s already registered\n", m->name);
+ return -1;
+ }
+ while (*pp && ((*pp)->module.priority < m->priority))
+ pp = &(*pp)->next;
+ new->next = *pp;
+ new->orig = m;
+ new->module = *m;
+
+ *pp = new;
+#endif
+ return 0;
+}
+
+int
+unregister_isdn_module(isdn_module *m) {
+#if 0
+ isdn_module_list **pp = &dev->modules;
+
+ while (*pp && *pp != m)
+ pp = &(*pp)->next;
+ if (*pp == NULL) {
+ printk(KERN_WARNING "isdn: Module %s not found\n", m->name);
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int
+isdn_add_channels(driver *d, int drvidx, int n, int adding)
+{
+ int j, k, m;
+ ulong flags;
+
+#if LINUX_VERSION_CODE >= 131841
+ init_waitqueue_head(&d->st_waitq);
+#endif
+ if (d->flags & DRV_FLAG_RUNNING)
+ return -1;
+ if (n < 1)
+ return 0;
+
+ m = (adding) ? d->channels + n : n;
+
+ if (dev->channels + n > ISDN_MAX_CHANNELS) {
+ printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+ ISDN_MAX_CHANNELS);
+ return -1;
+ }
+
+ if ((adding) && (d->rcverr))
+ kfree(d->rcverr);
+ if (!(d->rcverr = (int *) kmalloc(sizeof(int) * m, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+ return -1;
+ }
+ memset((char *) d->rcverr, 0, sizeof(int) * m);
+
+ if ((adding) && (d->rcvcount))
+ kfree(d->rcvcount);
+ if (!(d->rcvcount = (int *) kmalloc(sizeof(int) * m, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+ if (!adding) kfree(d->rcverr);
+ return -1;
+ }
+ memset((char *) d->rcvcount, 0, sizeof(int) * m);
+
+ if ((adding) && (d->rpqueue)) {
+ for (j = 0; j < d->channels; j++)
+ isdn_free_queue(&d->rpqueue[j]);
+ kfree(d->rpqueue);
+ }
+ if (!(d->rpqueue =
+ (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * m, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+ if (!adding) {
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ }
+ return -1;
+ }
+ for (j = 0; j < m; j++) {
+ skb_queue_head_init(&d->rpqueue[j]);
+ }
+
+ if ((adding) && (d->rcv_waitq))
+ kfree(d->rcv_waitq);
+#if LINUX_VERSION_CODE < 131841
+ if (!(d->rcv_waitq = (struct wait_queue **)
+ kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) {
+#else
+ d->rcv_waitq = (wait_queue_head_t *)
+ kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_KERNEL);
+ if (!d->rcv_waitq) {
+#endif
+ printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+ if (!adding) {
+ kfree(d->rpqueue);
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ }
+ return -1;
+ }
+#if LINUX_VERSION_CODE < 131841
+ memset((char *) d->rcv_waitq, 0, sizeof(struct wait_queue *) * m);
+
+ if ((adding) && (d->snd_waitq))
+ kfree(d->snd_waitq);
+ if (!(d->snd_waitq = (struct wait_queue **)
+ kmalloc(sizeof(struct wait_queue *) * m, GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc snd_waitq\n");
+ if (!adding) {
+ kfree(d->rcv_waitq);
+ kfree(d->rpqueue);
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ }
+ return -1;
+ }
+ memset((char *) d->snd_waitq, 0, sizeof(struct wait_queue *) * m);
+#else
+ d->snd_waitq = d->rcv_waitq + m;
+ for (j = 0; j < m; j++) {
+ init_waitqueue_head(&d->rcv_waitq[m]);
+ init_waitqueue_head(&d->snd_waitq[m]);
+ }
+#endif
+
+ dev->channels += n;
+ save_flags(flags);
+ cli();
+ for (j = d->channels; j < m; j++)
+ for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+ if (dev->chanmap[k] < 0) {
+ dev->chanmap[k] = j;
+ dev->drvmap[k] = drvidx;
+ break;
+ }
+ restore_flags(flags);
+ d->channels = m;
+ return 0;
+}
+
/*
* Low-level-driver registration
*/
EXPORT_SYMBOL(register_isdn);
+EXPORT_SYMBOL(register_isdn_module);
+EXPORT_SYMBOL(unregister_isdn_module);
+#ifdef CONFIG_ISDN_PPP
+EXPORT_SYMBOL(isdn_ppp_register_compressor);
+EXPORT_SYMBOL(isdn_ppp_unregister_compressor);
+#endif
int
register_isdn(isdn_if * i)
{
driver *d;
- int n,
- j,
- k;
+ int j;
ulong flags;
int drvidx;
ISDN_MAX_DRIVERS);
return 0;
}
- n = i->channels;
- if (dev->channels + n > ISDN_MAX_CHANNELS) {
- printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
- ISDN_MAX_CHANNELS);
- return 0;
- }
if (!i->writebuf_skb) {
printk(KERN_WARNING "register_isdn: No write routine given.\n");
return 0;
return 0;
}
memset((char *) d, 0, sizeof(driver));
- init_waitqueue_head(&d->st_waitq);
- if (!(d->rcverr = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
- printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
- kfree(d);
- return 0;
- }
- memset((char *) d->rcverr, 0, sizeof(int) * n);
- if (!(d->rcvcount = (int *) kmalloc(sizeof(int) * n, GFP_KERNEL))) {
- printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
- kfree(d->rcverr);
- kfree(d);
- return 0;
- }
- memset((char *) d->rcvcount, 0, sizeof(int) * n);
- if (!(d->rpqueue =
- (struct sk_buff_head *) kmalloc(sizeof(struct sk_buff_head) * n, GFP_KERNEL))) {
- printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
- kfree(d->rcvcount);
- kfree(d->rcverr);
- kfree(d);
- return 0;
- }
- for (j = 0; j < n; j++) {
- skb_queue_head_init(&d->rpqueue[j]);
- }
- d->rcv_waitq = (wait_queue_head_t *)
- kmalloc(sizeof(wait_queue_head_t) * 2 * n, GFP_KERNEL);
- if (!d->rcv_waitq) {
- printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
- kfree(d->rpqueue);
- kfree(d->rcvcount);
- kfree(d->rcverr);
- kfree(d);
- return 0;
- }
- d->snd_waitq = d->rcv_waitq + n;
- for (j = 0; j < n; j++) {
- init_waitqueue_head(&d->rcv_waitq[n]);
- init_waitqueue_head(&d->snd_waitq[n]);
- }
- d->channels = n;
- d->loaded = 1;
+
d->maxbufsize = i->maxbufsize;
d->pktcount = 0;
d->stavail = 0;
- d->running = 0;
- d->flags = 0;
+ d->flags = DRV_FLAG_LOADED;
+ d->online = 0;
d->interface = i;
+ d->channels = 0;
for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
if (!dev->drv[drvidx])
break;
+ if (isdn_add_channels(d, drvidx, i->channels, 0)) {
+ kfree(d);
+ return 0;
+ }
i->channels = drvidx;
-
i->rcvcallb_skb = isdn_receive_skb_callback;
i->statcallb = isdn_status_callback;
if (!strlen(i->id))
for (j = 0; j < drvidx; j++)
if (!strcmp(i->id, dev->drvid[j]))
sprintf(i->id, "line%d", drvidx);
- for (j = 0; j < n; j++)
- for (k = 0; k < ISDN_MAX_CHANNELS; k++)
- if (dev->chanmap[k] < 0) {
- dev->chanmap[k] = j;
- dev->drvmap[k] = drvidx;
- break;
- }
dev->drv[drvidx] = d;
- dev->channels += n;
strcpy(dev->drvid[drvidx], i->id);
isdn_info_update();
dev->drivers++;
memset((char *) dev, 0, sizeof(isdn_dev));
init_timer(&dev->timer);
dev->timer.function = isdn_timer_funct;
+#if LINUX_VERSION_CODE < 131841
+ dev->sem = MUTEX;
+#else
init_MUTEX(&dev->sem);
- init_waitqueue_head(&dev->info_waitq);
+ init_waitqueue_head(&dev->info_waitq);
+#endif
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
dev->drvmap[i] = -1;
dev->chanmap[i] = -1;
dev->m_idx[i] = -1;
strcpy(dev->num[i], "???");
+#if LINUX_VERSION_CODE >= 131841
init_waitqueue_head(&dev->mdm.info[i].open_wait);
init_waitqueue_head(&dev->mdm.info[i].close_wait);
+#endif
}
if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
printk(KERN_WARNING "isdn: Could not register control devices\n");
-/* $Id: isdn_common.h,v 1.9 1998/02/20 17:19:01 fritz Exp $
+/* $Id: isdn_common.h,v 1.15 1999/04/18 14:06:50 fritz Exp $
* header for Linux ISDN subsystem, common used functions and debugging-switches (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
* Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
* $Log: isdn_common.h,v $
+ * Revision 1.15 1999/04/18 14:06:50 fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.14 1999/04/12 12:33:18 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.13 1999/03/02 12:04:47 armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ * register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ * which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ * establishment and register S7.
+ *
+ * Revision 1.12 1998/06/26 15:12:27 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.11 1998/04/14 16:28:47 he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.10 1998/03/07 18:21:03 cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
* Revision 1.9 1998/02/20 17:19:01 fritz
* Added common stub for sending commands to lowlevel.
*
extern void isdn_timer_ctrl(int tf, int onoff);
extern void isdn_unexclusive_channel(int di, int ch);
extern int isdn_getnum(char **);
+#if LINUX_VERSION_CODE < 131841
+extern int isdn_readbchan(int, int, u_char *, u_char *, int, struct wait_queue**);
+#else
extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *);
+#endif
extern int isdn_get_free_channel(int, int, int, int, int);
extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
extern int register_isdn(isdn_if * i);
+extern int isdn_wildmat(char *, char *);
+extern int isdn_add_channels(driver *, int, int, int);
#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
extern void isdn_dumppkt(char *, u_char *, int, int);
#endif
-/* $Id: isdn_concap.c,v 1.2 1998/01/31 22:49:21 keil Exp $
+/* $Id: isdn_concap.c,v 1.5 1998/10/30 18:44:48 he Exp $
* Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
* stuff goes here. Stuff that depends only on the concap protocol goes to
* another -- protocol specific -- source file.
*
* $Log: isdn_concap.c,v $
+ * Revision 1.5 1998/10/30 18:44:48 he
+ * pass return value from isdn_net_dial_req for dialmode change
+ *
+ * Revision 1.4 1998/10/30 17:55:24 he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.3 1998/05/26 22:39:22 he
+ * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
+ * concap typo
+ * cleared dev.tbusy in isdn_net BCONN status callback
+ *
* Revision 1.2 1998/01/31 22:49:21 keil
* correct comments
*
#include <linux/concap.h>
#include "isdn_concap.h"
-/* The declaration of this (or a plublic variant thereof) should really go
- in linux/isdn.h. But we really need it here (and isdn_ppp, like us, also
- refers to that private function currently owned by isdn_net.c) */
-extern int isdn_net_force_dial_lp(isdn_net_local *);
-
/* The following set of device service operations are for encapsulation
- protocols that require for reliable datalink sematics. That means:
+ protocols that require for reliable datalink semantics. That means:
- before any data is to be submitted the connection must explicitly
be set up.
IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
/* dial ... */
- ret = isdn_net_force_dial_lp( lp );
+ ret = isdn_net_dial_req( lp );
if ( ret ) IX25DEBUG("dialing failed\n");
- return 0;
+ return ret;
}
int isdn_concap_dl_disconn_req(struct concap_proto *concap)
-/* $Id: isdn_net.c,v 1.55 1998/02/23 19:38:22 fritz Exp $
+/* $Id: isdn_net.c,v 1.84 1999/04/18 14:06:55 fritz Exp $
* Linux ISDN subsystem, network interfaces and related functions (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
* Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
* $Log: isdn_net.c,v $
+ * Revision 1.84 1999/04/18 14:06:55 fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.83 1999/04/12 12:33:23 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.82 1999/01/17 00:55:58 he
+ * added mark_bh in BCONN statcallb and cleaned up some dead code
+ *
+ * Revision 1.81 1999/01/15 16:36:52 he
+ * replaced icmp_send() by dst_link_failure()
+ *
+ * Revision 1.80 1998/12/01 13:06:22 paul
+ * Also huptimeout with dialmode == manual
+ *
+ * Revision 1.79 1998/10/30 17:55:27 he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.78 1998/10/26 18:20:46 he
+ * re-inserted p=p->next in isdn_net_find_icall() (fixes kernel lock up
+ * on incoming call not matching the first interface)
+ *
+ * Revision 1.77 1998/10/23 10:18:44 paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.76 1998/09/07 22:00:05 he
+ * flush method for 2.1.118 and above
+ * updated IIOCTLNETGPN
+ *
+ * Revision 1.75 1998/08/31 21:09:50 he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ * peer phone number)
+ *
+ * Revision 1.74 1998/07/30 11:28:32 paul
+ * printk message only appeared when status is off and interface is rawIP,
+ * which is confusing for people who don't know about "isdnctrl status <if> on".
+ *
+ * Revision 1.73 1998/06/26 22:01:37 keil
+ * tx_queue_len = 5 was too small
+ *
+ * Revision 1.72 1998/06/26 15:12:31 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.71 1998/06/18 22:43:08 fritz
+ * Bugfix: Setting ndev->do_ioctl had beed accidetly removed at abc-cleanup.
+ *
+ * Revision 1.70 1998/06/17 19:50:49 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.69 1998/06/09 12:27:37 cal
+ * Changed default of local netdev flags: ISDN_NET_STOPPED is default now,
+ * so autodial is suppressed for that device until it is switched on using
+ * 'isdnctrl status dev-name on'.
+ *
+ * Revision 1.68 1998/06/07 00:20:05 fritz
+ * abc cleanup.
+ *
+ * Revision 1.67 1998/06/02 12:10:08 detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.66 1998/05/26 22:39:24 he
+ * sync'ed with 2.1.102 where appropriate (CAPABILITY changes)
+ * concap typo
+ * cleared dev.tbusy in isdn_net BCONN status callback
+ *
+ * Revision 1.65 1998/05/22 10:01:11 detabc
+ * in case of a icmp-unreach condition the tcp-keepalive-entrys
+ * will be dropped from the internal double-link-list (only abc-extension).
+ * send icmp unreach only if the skb->protocol == ETH_P_IP
+ * speedup abc-no-dchan redial
+ *
+ * Revision 1.64 1998/05/07 19:58:39 detabc
+ * bugfix in abc_delayed_hangup
+ * optimize keepalive-tests for abc_rawip
+ *
+ * Revision 1.63 1998/05/05 23:23:36 detabc
+ * change ICMP_HOST_UNREACH to ICMP_NET_UNREACH (only abc-ext.)
+ * set dev->tbusy to zero in isdn_net_unreachable() (only abc-ext.)
+ * drop all new packets and send ICMP_NET_UNREACH for
+ * min. dialwait to max. dialwait * 6 time. (only abc-ext.)
+ * change random-deliver of packets (small first) from all emcapsulation
+ * to only rawip with ABC-Router-Flag enabled.
+ *
+ * Revision 1.62 1998/05/03 17:40:42 detabc
+ * Include abc-extension-support for >= 2.1.x Kernels in
+ * isdn_net.c and isdn_common.c. alpha-test OK and running !
+ *
+ * Revision 1.61 1998/04/16 19:19:42 keil
+ * Fix from vger (tx max qlength)
+ *
+ * Revision 1.60 1998/04/14 16:28:49 he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.59 1998/03/07 22:37:33 fritz
+ * Bugfix: restore_flags missing.
+ *
+ * Revision 1.58 1998/03/07 18:21:05 cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.57 1998/02/25 18:31:13 fritz
+ * Added debugging output in adjust_header.
+ *
+ * Revision 1.56 1998/02/25 17:49:42 he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
* Revision 1.55 1998/02/23 19:38:22 fritz
* Corrected check for modified feature-flags.
*
#include <linux/isdn.h>
#include <net/arp.h>
#include <net/dst.h>
-#ifndef DEV_NUMBUFFS
#include <net/pkt_sched.h>
-#endif
#include <linux/inetdevice.h>
#include "isdn_common.h"
#include "isdn_net.h"
/* Prototypes */
int isdn_net_force_dial_lp(isdn_net_local *);
-static int isdn_net_wildmat(char *s, char *p);
static int isdn_net_start_xmit(struct sk_buff *, struct device *);
static int isdn_net_xmit(struct device *, isdn_net_local *, struct sk_buff *);
-#ifdef DEV_NUMBUFFS
-static void dev_purge_queues(struct device *dev); /* move this to net/core/dev.c */
-#endif
-char *isdn_net_revision = "$Revision: 1.55 $";
+char *isdn_net_revision = "$Revision: 1.84 $";
/*
* Code for raw-networking over ISDN
dst_link_failure(skb);
}
+ else { /* dial not triggered by rawIP packet */
+ printk(KERN_DEBUG "isdn_net: %s: %s\n",
+ dev->name,
+ (reason != NULL) ? reason : "reason unknown");
+ }
}
static void
isdn_net_reset(struct device *dev)
{
#ifdef CONFIG_ISDN_X25
- struct concap_device_ops * dops =
+ struct concap_device_ops * dops =
( (isdn_net_local *) dev->priv ) -> dops;
- struct concap_proto * cprot =
- ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+ struct concap_proto * cprot =
+ ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
#endif
ulong flags;
dev->interrupt = 0;
dev->tbusy = 0;
#ifdef CONFIG_ISDN_X25
- if( cprot && cprot -> pops && dops )
+ if( cprot && cprot -> pops && dops )
cprot -> pops -> restart ( cprot, dev, dops );
#endif
restore_flags(flags);
if (ifa != NULL)
memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
}
-
+
/* If this interface has slaves, start them also */
if ((p = (((isdn_net_local *) dev->priv)->slave))) {
save_flags(flags);
cli();
+ lp->flags |= ISDN_NET_CONNECTED;
lp->isdn_device = dev->drvmap[idx];
lp->isdn_channel = dev->chanmap[idx];
dev->rx_netdev[idx] = lp->netdev;
dev_kfree_skb(lp->sav_skb);
lp->sav_skb = NULL;
}
-#ifdef DEV_NUMBUFFS
- if (!lp->master) /* purge only for master device */
- dev_purge_queues(&lp->netdev->dev);
-#else
if (!lp->master) { /* reset only master device */
/* Moral equivalent of dev_purge_queues():
BEWARE! This chunk of code cannot be called from hardware
*/
qdisc_reset(lp->netdev->dev.qdisc);
}
-#endif
lp->dialstate = 0;
dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
anymore = 1;
l->huptimer++;
- if ((l->onhtime) && (l->huptimer > l->onhtime)) {
+ /*
+ * if there is some dialmode where timeout-hangup
+ * should _not_ be done, check for that here
+ */
+ if ((l->onhtime) &&
+ (l->huptimer > l->onhtime))
+ {
if (l->hupflags & ISDN_MANCHARGE &&
l->hupflags & ISDN_CHARGEHUP) {
while (jiffies - l->chargetime > l->chargeint)
} else if (l->hupflags & ISDN_INHUP)
isdn_net_hangup(&p->dev);
}
+
+ if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) {
+ isdn_net_hangup(&p->dev);
+ break;
+ }
}
p = (isdn_net_dev *) p->next;
}
{
isdn_net_dev *p = dev->st_netdev[idx];
int cmd = c->command;
-
+
if (p) {
isdn_net_local *lp = p->local;
#ifdef CONFIG_ISDN_X25
failed. If there are generic encap protocol
receiver routines signal the closure of
the link*/
-
- if( !(lp->flags & ISDN_NET_CONNECTED)
+
+ if( !(lp->flags & ISDN_NET_CONNECTED)
&& pops && pops -> disconn_ind )
pops -> disconn_ind(cprot);
#endif /* CONFIG_ISDN_X25 */
if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
- lp->flags &= ~ISDN_NET_CONNECTED;
- if (lp->first_skb) {
- dev_kfree_skb(lp->first_skb);
- lp->first_skb = NULL;
- }
- if (lp->sav_skb) {
- dev_kfree_skb(lp->sav_skb);
- lp->sav_skb = NULL;
- }
- isdn_free_channel(lp->isdn_device, lp->isdn_channel,
- ISDN_USAGE_NET);
#ifdef CONFIG_ISDN_PPP
isdn_ppp_free(lp);
#endif
printk(KERN_INFO "%s: remote hangup\n", lp->name);
printk(KERN_INFO "%s: Chargesum is %d\n", lp->name,
lp->charge);
- lp->isdn_device = -1;
- lp->isdn_channel = -1;
- dev->st_netdev[idx] = NULL;
- dev->rx_netdev[idx] = NULL;
+ isdn_net_unbind_channel(lp);
return 1;
}
break;
lp->chargetime = jiffies;
printk(KERN_DEBUG "isdn_net: chargetime of %s now %d\n",
lp->name, lp->chargetime);
+
+ /* reset dial-timeout */
+ lp->dialstarted = 0;
+ lp->dialwait_timer = 0;
+
/* Immediately send first skb to speed up arp */
#ifdef CONFIG_ISDN_PPP
if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
if( pops )
if( pops->connect_ind)
pops->connect_ind(cprot);
-
#endif /* CONFIG_ISDN_X25 */
if (lp->first_skb) {
-
+
if (!(isdn_net_xmit(&p->dev, lp, lp->first_skb)))
lp->first_skb = NULL;
- } else {
+ }
+ else {
/*
* dev.tbusy is usually cleared implicitly by isdn_net_xmit(,,lp->first_skb).
* With an empty lp->first_skb, we need to do this ourselves
break;
}
anymore = 1;
+
+ if(lp->dialtimeout > 0)
+ if(lp->dialstarted == 0 || jiffies > (lp->dialstarted + lp->dialtimeout + lp->dialwait)) {
+ lp->dialstarted = jiffies;
+ lp->dialwait_timer = 0;
+ }
+
lp->dialstate++;
/* Fall through */
case 2:
lp->dialretry = 0;
anymore = 1;
lp->dialstate++;
- /* Falls through */
+ /* Fall through */
case 3:
/* Setup interface, dial current phone-number, switch to next number.
* If list of phone-numbers is exhausted, increment
* retry-counter.
*/
+ if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) {
+ char *s;
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ s = "dial suppressed: isdn system stopped";
+ else
+ s = "dial suppressed: dialmode `off'";
+ isdn_net_unreachable(&p->dev, lp->first_skb, s);
+ isdn_net_hangup(&p->dev);
+ break;
+ }
cmd.driver = lp->isdn_device;
cmd.command = ISDN_CMD_SETL2;
cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
lp->dialstate = 4;
printk(KERN_INFO "%s: Open leased line ...\n", lp->name);
} else {
+ if(lp->dialtimeout > 0)
+ if(jiffies > (lp->dialstarted + lp->dialtimeout)) {
+ restore_flags(flags);
+ lp->dialwait_timer = jiffies + lp->dialwait;
+ lp->dialstarted = 0;
+ isdn_net_unreachable(&p->dev, lp->first_skb, "dial: timed out");
+ isdn_net_hangup(&p->dev);
+ break;
+ }
+
sprintf(cmd.parm.setup.phone, "%s", lp->dial->num);
/*
* Switch to next number or back to start if at end of list.
if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
lp->dial = lp->phone[1];
lp->dialretry++;
+
+ if (lp->dialretry > lp->dialmax) {
+ restore_flags(flags);
+ if (lp->dialtimeout == 0) {
+ lp->dialwait_timer = jiffies + lp->dialwait;
+ lp->dialstarted = 0;
+ isdn_net_unreachable(&p->dev, lp->first_skb, "dial: tried all numbers dialmax times");
+ }
+ isdn_net_hangup(&p->dev);
+ break;
+ }
}
restore_flags(flags);
cmd.driver = lp->isdn_device;
isdn_info_update();
}
printk(KERN_INFO "%s: dialing %d %s...\n", lp->name,
- lp->dialretry - 1, cmd.parm.setup.phone);
+ lp->dialretry, cmd.parm.setup.phone);
lp->dtimer = 0;
#ifdef ISDN_DEBUG_NET_DIAL
printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device,
break;
case 4:
/* Wait for D-Channel-connect.
- * If timeout and max retries not
- * reached, switch back to state 3.
+ * If timeout, switch back to state 3.
+ * Dialmax-handling moved to state 3.
*/
- if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) {
- if (lp->dialretry < lp->dialmax) {
- lp->dialstate = 3;
- } else
- isdn_net_hangup(&p->dev);
- }
+ if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+ lp->dialstate = 3;
anymore = 1;
break;
case 5:
/* Remote does callback. Hangup after cbdelay, then wait for incoming
* call (in state 4).
*/
- if (lp->dtimer++ > lp->cbdelay) {
+ if (lp->dtimer++ > lp->cbdelay)
+ {
printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name);
lp->dtimer = 0;
lp->dialstate = 4;
#endif
if (lp->flags & ISDN_NET_CONNECTED) {
- lp->flags &= ~ISDN_NET_CONNECTED;
printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name);
#ifdef CONFIG_ISDN_PPP
isdn_ppp_free(lp);
#ifdef CONFIG_ISDN_X25
/* try if there are generic encap protocol
receiver routines and signal the closure of
- the link */
+ the link */
if( pops && pops -> disconn_ind )
pops -> disconn_ind(cprot);
#endif /* CONFIG_ISDN_X25 */
char addinfo[100];
addinfo[0] = '\0';
- /* This check stolen from 2.1.72 dev_queue_xmit_nit() */
+ /* This check stolen from 2.1.72 dev_queue_xmit_nit() */
if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
/* fall back to old isdn_net_log_packet method() */
char * buf = skb->data;
if (ret == len) {
lp->transcount += len;
clear_bit(0, (void *) &(ndev->tbusy));
- mark_bh(NET_BH);
return 0;
}
if (ret < 0) {
dev_kfree_skb(skb);
lp->stats.tx_errors++;
clear_bit(0, (void *) &(ndev->tbusy));
- mark_bh(NET_BH);
return 0;
}
return 1;
}
-
/*
* Helper function for isdn_net_start_xmit.
* When called, the connection is already established.
if (!skb)
return;
if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
- ulong pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
- if (pullsize)
+ int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
+ if (pullsize > 0) {
+ printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
skb_pull(skb, pullsize);
+ }
}
}
{
isdn_net_local *lp = (isdn_net_local *) ndev->priv;
#ifdef CONFIG_ISDN_X25
- struct concap_proto * cprot = lp -> netdev -> cprot;
+ struct concap_proto * cprot = lp -> netdev -> cprot;
#endif
if (ndev->tbusy) {
ndev->tbusy = 1; /* left instead of obsolete test_and_set_bit() */
#ifdef CONFIG_ISDN_X25
/* At this point hard_start_xmit() passes control to the encapsulation
- protocol (if present).
+ protocol (if present).
For X.25 auto-dialing is completly bypassed because:
- It does not conform with the semantics of a reliable datalink
service as needed by X.25 PLP.
#endif
if (!(lp->flags & ISDN_NET_CONNECTED)) {
int chi;
+ /* only do autodial if allowed by config */
+ if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
+ isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
+ dev_kfree_skb(skb);
+ ndev->tbusy = 0;
+ return 0;
+ }
if (lp->phone[1]) {
ulong flags;
save_flags(flags);
cli();
+
+ if(lp->dialwait_timer <= 0)
+ if(lp->dialstarted > 0 && lp->dialtimeout > 0 && jiffies < lp->dialstarted + lp->dialtimeout + lp->dialwait)
+ lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
+
+ if(lp->dialwait_timer > 0) {
+ if(jiffies < lp->dialwait_timer) {
+ isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+ dev_kfree_skb(skb);
+ ndev->tbusy = 0;
+ restore_flags(flags);
+ return 0;
+ } else
+ lp->dialwait_timer = 0;
+ }
+
/* Grab a free ISDN-Channel */
- if ((chi =
+ if (((chi =
isdn_get_free_channel(ISDN_USAGE_NET,
lp->l2_proto,
lp->l3_proto,
lp->pre_device,
- lp->pre_channel)) < 0) {
+ lp->pre_channel)) < 0) &&
+ ((chi =
+ isdn_get_free_channel(ISDN_USAGE_NET,
+ lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel^1)) < 0)) {
restore_flags(flags);
isdn_net_unreachable(ndev, skb,
"No channel");
if (dev->net_verbose)
isdn_net_log_skb(skb, lp);
lp->dialstate = 1;
- lp->flags |= ISDN_NET_CONNECTED;
/* Connect interface with channel */
isdn_net_bind_channel(lp, chi);
#ifdef CONFIG_ISDN_PPP
{
struct device *p;
#ifdef CONFIG_ISDN_X25
- struct concap_proto * cprot =
- ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+ struct concap_proto * cprot =
+ ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
/* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
#endif
/* If this interface has slaves, stop them also */
while (p) {
#ifdef CONFIG_ISDN_X25
- cprot = ( (isdn_net_local *) p->priv )
- -> netdev -> cprot;
- if( cprot && cprot -> pops )
+ cprot = ( (isdn_net_local *) p->priv )
+ -> netdev -> cprot;
+ if( cprot && cprot -> pops )
cprot -> pops -> close( cprot );
#endif
isdn_net_hangup(p);
int len;
cisco_hdr *ch;
cisco_slarp *s;
-
+
if (!skb) {
printk(KERN_WARNING
"%s: Could not allocate SLARP reply\n", lp->name);
s = (cisco_slarp *)skb_put(skb, sizeof(cisco_slarp));
if (is_reply) {
s->code = htonl(CISCO_SLARP_REPLY);
- memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
+ memset(&s->slarp.reply.ifaddr, 0, sizeof(__u32));
memset(&s->slarp.reply.netmask, 0, sizeof(__u32));
} else {
lp->cisco_myseq++;
default:
#ifdef CONFIG_ISDN_X25
/* try if there are generic sync_device receiver routines */
- if(cprot) if(cprot -> pops)
+ if(cprot) if(cprot -> pops)
if( cprot -> pops -> data_ind){
cprot -> pops -> data_ind(cprot,skb);
return;
kfree_skb(skb);
return;
}
+
netif_rx(skb);
return;
}
ndev->type = ARPHRD_ETHER;
ndev->addr_len = ETH_ALEN;
- ndev->tx_queue_len = 10; /* for clients without MPPP 5 is better. */
+ /* for clients with MPPP maybe higher values better */
+ ndev->tx_queue_len = 30;
for (i = 0; i < ETH_ALEN; i++)
ndev->broadcast[i] = 0xff;
-#ifdef DEV_NUMBUFFS
- for (i = 0; i < DEV_NUMBUFFS; i++)
- skb_queue_head_init(&ndev->buffs[i]);
-#endif
-
/* The ISDN-specific entries in the device structure. */
ndev->open = &isdn_net_open;
ndev->hard_start_xmit = &isdn_net_start_xmit;
max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
-
ndev->stop = &isdn_net_close;
ndev->get_stats = &isdn_net_get_stats;
ndev->rebuild_header = &isdn_net_rebuild_header;
-
#ifdef CONFIG_ISDN_PPP
ndev->do_ioctl = isdn_ppp_dev_ioctl;
#endif
return 0;
}
-/*
- * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
- * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
- */
-
-static int
-isdn_net_Star(char *s, char *p)
-{
- while (isdn_net_wildmat(s, p) == 0)
- if (*++s == '\0')
- return (0);
- return (1);
-}
-
-/*
- * Shell-type Pattern-matching for incoming caller-Ids
- * This function gets a string in s and checks, if it matches the pattern
- * given in p. It returns 1 on success, 0 otherwise.
- *
- * Possible Patterns:
- *
- * '?' matches one character
- * '*' matches zero or more characters
- * [xyz] matches the set of characters in brackets.
- * [^xyz] matches any single character not in the set of characters
- */
-
-static int
-isdn_net_wildmat(char *s, char *p)
-{
- register int last;
- register int matched;
- register int reverse;
-
- for (; *p; s++, p++)
- switch (*p) {
- case '\\':
- /*
- * Literal match with following character,
- * fall through.
- */
- p++;
- default:
- if (*s != *p)
- return (0);
- continue;
- case '?':
- /* Match anything. */
- if (*s == '\0')
- return (0);
- continue;
- case '*':
- /* Trailing star matches everything. */
- return (*++p ? isdn_net_Star(s, p) : 1);
- case '[':
- /* [^....] means inverse character class. */
- if ((reverse = (p[1] == '^')))
- p++;
- for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
- /* This next line requires a good C compiler. */
- if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
- matched = 1;
- if (matched == reverse)
- return (0);
- continue;
- }
- return (*s == '\0');
-}
-
static void
isdn_net_swapbind(int drvidx)
{
* 2 = Reject call, wait cbdelay, then call back
* 3 = Reject call
* 4 = Wait cbdelay, then call back
+ * 5 = No appropriate interface for this call,
+ * would eventually match if CID was longer.
*/
int
isdn_net_find_icall(int di, int ch, int idx, setup_parm setup)
int si1;
int si2;
int ematch;
+ int wret;
int swapped;
int sidx = 0;
isdn_net_dev *p;
}
n = (isdn_net_phone *) 0;
p = dev->netdev;
- ematch = 0;
+ ematch = wret = swapped = 0;
#ifdef ISDN_DEBUG_NET_ICALL
printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
dev->usage[idx]);
#endif
- swapped = 0;
while (p) {
+ int matchret;
isdn_net_local *lp = p->local;
/* If last check has triggered as binding-swap, revert it */
break;
}
swapped = 0;
- if (!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz))
+ if (!(matchret = isdn_wildmat(eaz, isdn_map_eaz2msn(lp->msn, di))))
ematch = 1;
+ /* Remember if more numbers eventually can match */
+ if (matchret > wret)
+ wret = matchret;
#ifdef ISDN_DEBUG_NET_ICALL
printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
lp->name, lp->msn, lp->flags, lp->dialstate);
#endif
- if ((!strcmp(isdn_map_eaz2msn(lp->msn, di), eaz)) && /* EAZ is matching */
- (((!(lp->flags & ISDN_NET_CONNECTED)) && /* but not connected */
- (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */
- ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing */
- (!(lp->flags & ISDN_NET_CALLBACK))) /* but no callback */
- ))) {
+ if ((!matchret) && /* EAZ is matching */
+ (((!(lp->flags & ISDN_NET_CONNECTED)) && /* but not connected */
+ (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */
+ ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing */
+ (!(lp->flags & ISDN_NET_CALLBACK))) /* but no callback */
+ )))
+ {
#ifdef ISDN_DEBUG_NET_ICALL
printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
lp->pre_device, lp->pre_channel);
#ifdef ISDN_DEBUG_NET_ICALL
printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
#endif
- p = (isdn_net_dev *) p->next;
- continue;
}
}
}
n = lp->phone[0];
if (lp->flags & ISDN_NET_SECURE) {
while (n) {
- if (isdn_net_wildmat(nr, n->num))
+ if (!isdn_wildmat(nr, n->num))
break;
n = (isdn_net_phone *) n->next;
}
#ifdef ISDN_DEBUG_NET_ICALL
printk(KERN_DEBUG "n_fi: match3\n");
#endif
- /* Here we got an interface matched, now see if it is up.
+ /* matching interface found */
+
+ /*
+ * Is the state STOPPED?
+ * If so, no dialin is allowed,
+ * so reject actively.
+ * */
+ if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+ restore_flags(flags);
+ printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
+ lp->name);
+ return 3;
+ }
+ /*
+ * Is the interface up?
* If not, reject the call actively.
*/
if (!p->dev.start) {
}
if (lp->flags & ISDN_NET_CALLBACK) {
int chi;
+ /*
+ * Is the state MANUAL?
+ * If so, no callback can be made,
+ * so reject actively.
+ * */
+ if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+ restore_flags(flags);
+ printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
+ lp->name);
+ return 3;
+ }
printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
lp->name, nr, eaz);
if (lp->phone[1]) {
/* Setup dialstate. */
lp->dtimer = 0;
lp->dialstate = 11;
- lp->flags |= ISDN_NET_CONNECTED;
/* Connect interface with channel */
isdn_net_bind_channel(lp, chi);
#ifdef CONFIG_ISDN_PPP
p = (isdn_net_dev *) p->next;
}
/* If none of configured EAZ/MSN matched and not verbose, be silent */
- if (ematch || dev->net_verbose)
+ if (!ematch || dev->net_verbose)
printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
restore_flags(flags);
- return 0;
+ return (wret == 2)?5:0;
}
/*
ulong flags;
save_flags(flags);
cli();
+
/* Grab a free ISDN-Channel */
if ((chi = isdn_get_free_channel(ISDN_USAGE_NET, lp->l2_proto,
lp->l3_proto,
return -EAGAIN;
}
lp->dialstate = 1;
- lp->flags |= ISDN_NET_CONNECTED;
/* Connect interface with channel */
isdn_net_bind_channel(lp, chi);
#ifdef CONFIG_ISDN_PPP
return -EBUSY;
}
+/*
+ * This is called from certain upper protocol layers (multilink ppp
+ * and x25iface encapsulation module) that want to initiate dialing
+ * themselves.
+ */
+int
+isdn_net_dial_req(isdn_net_local * lp)
+{
+ /* is there a better error code? */
+ if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
+
+ return isdn_net_force_dial_lp(lp);
+}
+
/*
* Force a net-interface to dial out.
* This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
netdev->local->onhtime = 10; /* Default hangup-time for saving costs
of those who forget configuring this */
netdev->local->dialmax = 1;
- netdev->local->flags = ISDN_NET_CBHUP; /* Hangup before Callback */
+ netdev->local->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL; /* Hangup before Callback, manual dial */
netdev->local->cbdelay = 25; /* Wait 5 secs before Callback */
+ netdev->local->dialtimeout = -1; /* Infinite Dial-Timeout */
+ netdev->local->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
+ netdev->local->dialstarted = 0; /* Jiffies of last dial-start */
+ netdev->local->dialwait_timer = 0; /* Jiffies of earliest next dial-start */
+
/* Put into to netdev-chain */
netdev->next = (void *) dev->netdev;
dev->netdev = netdev;
save_flags(flags);
cli(); /* avoid races with incoming events trying to
call cprot->pops methods */
- if( cprot && cprot -> pops )
+ if( cprot && cprot -> pops )
cprot -> pops -> proto_del ( cprot );
p -> cprot = NULL;
lp -> dops = NULL;
p -> cprot = isdn_concap_new( cfg -> p_encap );
/* p -> cprot == NULL now if p_encap is not supported
by means of the concap_proto mechanism */
- /* the protocol is not configured yet; this will
+ /* the protocol is not configured yet; this will
happen later when isdn_net_reset() is called */
#endif
}
if( cfg->p_encap >= 0 &&
cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
break;
- printk(KERN_WARNING
+ printk(KERN_WARNING
"%s: encapsulation protocol %d not supported\n",
p->local->name, cfg->p_encap);
return -EINVAL;
lp->triggercps = cfg->triggercps;
lp->slavedelay = cfg->slavedelay * HZ;
lp->pppbind = cfg->pppbind;
+ lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+ lp->dialwait = cfg->dialwait * HZ;
if (cfg->secure)
lp->flags |= ISDN_NET_SECURE;
else
lp->flags &= ~ISDN_NET_CALLBACK;
break;
}
+ lp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */
+ if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+ /* old isdnctrl version, where only 0 or 1 is given */
+ printk(KERN_WARNING
+ "Old isdnctrl version detected! Please update.\n");
+ lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */
+ }
+ else {
+ lp->flags |= cfg->dialmode; /* turn on selected bits */
+ }
if (cfg->chargehup)
lp->hupflags |= ISDN_CHARGEHUP;
else
if (lp->flags & ISDN_NET_CBOUT)
cfg->callback = 2;
cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+ cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
cfg->cbdelay = lp->cbdelay;
cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
(lp->chargeint / HZ) : 0;
cfg->pppbind = lp->pppbind;
+ cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
+ cfg->dialwait = lp->dialwait / HZ;
if (lp->slave)
strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name);
else
}
/*
- * Delete a phone-number from an interface.
+ * Copy a string containing the peer's phone number of a connected interface
+ * to user space.
*/
+int
+isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ int ch, dv, idx;
+ if (!p) return -ENODEV;
+ /*
+ * Theoretical race: while this executes, the remote number might
+ * become invalid (hang up) or change (new connection), resulting
+ * in (partially) wrong number copied to user. This race
+ * currently ignored.
+ */
+ ch = p->local->isdn_channel;
+ dv = p->local->isdn_device;
+ if(ch<0 && dv<0) return -ENOTCONN;
+ idx = isdn_dc2minor(dv, ch);
+ if (idx<0) return -ENODEV;
+ /* for pre-bound channels, we need this extra check */
+ if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN;
+ strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN);
+ phone->outgoing=USG_OUTGOING(idx);
+ if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
+ return 0;
+}
+/*
+ * Delete a phone-number from an interface.
+ */
int
isdn_net_delphone(isdn_net_ioctl_phone * phone)
{
restore_flags(flags);
return 0;
}
-
-#ifdef DEV_NUMBUFFS
-/*
- * helper function to flush device queues
- * the better place would be net/core/dev.c
- */
-static void
-dev_purge_queues(struct device *dev)
-{
- int i;
- for (i = 0; i < DEV_NUMBUFFS; i++) {
- struct sk_buff *skb;
- while ((skb = skb_dequeue(&dev->buffs[i])))
- dev_kfree_skb(skb);
- }
-
-}
-#endif
-/* $Id: isdn_net.h,v 1.6 1997/10/09 21:28:54 fritz Exp $
+/* $Id: isdn_net.h,v 1.9 1999/04/12 12:33:27 fritz Exp $
* header for Linux ISDN subsystem, network related functions (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
* Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
*
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_net.h,v $
+ * Revision 1.9 1999/04/12 12:33:27 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.8 1998/10/30 17:55:33 he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.7 1998/08/31 21:09:55 he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ * peer phone number)
+ *
* Revision 1.6 1997/10/09 21:28:54 fritz
* New HL<->LL interface:
* New BSENT callback with nr. of bytes included.
extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
extern int isdn_net_addphone(isdn_net_ioctl_phone *);
extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
+extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
extern int isdn_net_delphone(isdn_net_ioctl_phone *);
extern int isdn_net_find_icall(int, int, int, setup_parm);
extern void isdn_net_hangup(struct device *);
struct sk_buff *);
extern int isdn_net_rcv_skb(int, struct sk_buff *);
extern void isdn_net_slarp_out(void);
+extern int isdn_net_dial_req(isdn_net_local *);
-/* $Id: isdn_ppp.c,v 1.33 1998/02/20 17:11:54 fritz Exp $
+/* $Id: isdn_ppp.c,v 1.47 1999/04/18 14:06:59 fritz Exp $
*
* Linux ISDN subsystem, functions for synchronous PPP (linklevel).
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
* $Log: isdn_ppp.c,v $
+ * Revision 1.47 1999/04/18 14:06:59 fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.46 1999/04/12 12:33:35 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.45 1998/12/30 17:48:24 paul
+ * fixed syncPPP callback out
+ *
+ * Revision 1.44 1998/10/30 17:55:34 he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.43 1998/10/29 17:23:54 hipp
+ * Minor MPPP fixes, verboser logging.
+ *
+ * Revision 1.42 1998/07/20 11:30:07 hipp
+ * Readded compression check
+ *
+ * Revision 1.41 1998/07/08 16:50:57 hipp
+ * Compression changes
+ *
+ * Revision 1.40 1998/04/06 19:07:27 hipp
+ * added check, whether compression is enabled.
+ *
+ * Revision 1.39 1998/03/25 22:46:53 hipp
+ * Some additional CCP changes.
+ *
+ * Revision 1.38 1998/03/24 16:33:06 hipp
+ * More CCP changes. BSD compression now "works" on a local loopback link.
+ * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
+ *
+ * Revision 1.37 1998/03/22 18:50:49 hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.36 1998/03/09 17:46:30 he
+ * merged in 2.1.89 changes
+ *
+ * Revision 1.35 1998/03/07 18:21:11 cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
+ * Revision 1.34 1998/02/25 17:49:48 he
+ * Changed return codes caused be failing copy_{to,from}_user to -EFAULT
+ *
* Revision 1.33 1998/02/20 17:11:54 fritz
* Changes for recent kernels.
*
* experimental for dynamic addressing: readdress IP frames
*/
#undef ISDN_SYNCPPP_READDRESS
+#define CONFIG_ISDN_CCP 1
#include <linux/config.h>
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/version.h>
-#include <linux/isdn.h>
#include <linux/poll.h>
+#include <linux/isdn.h>
+#include <linux/ppp-comp.h>
+
#include "isdn_common.h"
#include "isdn_ppp.h"
#include "isdn_net.h"
static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
struct sk_buff *skb, int proto);
static int isdn_ppp_if_get_unit(char *namebuf);
-static int isdn_ppp_set_compressor(struct ippp_struct *is,int num);
+static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
- struct ippp_struct *,struct ippp_struct *);
+ struct ippp_struct *,struct ippp_struct *,int proto);
static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
- struct sk_buff *skb);
+ struct sk_buff *skb,int proto);
static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
struct ippp_struct *is,struct ippp_struct *master,int type);
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+ struct sk_buff *skb);
+
+/* New CCP stuff */
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is);
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+ unsigned char code, unsigned char id,
+ unsigned char *data, int len);
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+ unsigned char id);
+static void isdn_ppp_ccp_timer_callback(unsigned long closure);
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+ unsigned char id);
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+ struct isdn_ppp_resetparams *rp);
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+ unsigned char id);
+
+
#ifdef CONFIG_ISDN_MPP
static int isdn_ppp_bundle(struct ippp_struct *, int unit);
static void isdn_ppp_free_mpqueue(isdn_net_dev *);
#endif
-char *isdn_ppp_revision = "$Revision: 1.33 $";
+char *isdn_ppp_revision = "$Revision: 1.47 $";
static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
static struct isdn_ppp_compressor *ipc_head = NULL;
-extern int isdn_net_force_dial_lp(isdn_net_local *);
-
/*
* frame log (debug)
*/
static void
-isdn_ppp_frame_log(char *info, char *data, int len, int maxlen)
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
{
int cnt,
j,
for (i = 0, cnt = 0; cnt < maxlen; i++) {
for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
- printk(KERN_DEBUG "%s[%d]: %s\n", info, i, buf);
+ printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
}
}
/*
* unbind isdn_net_local <=> ippp-device
* note: it can happen, that we hangup/free the master before the slaves
+ * in this case we bind another lp to the master device
*/
int
isdn_ppp_free(isdn_net_local * lp)
if ((is->state & IPPP_CONNECT))
isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */
else if (is->state & IPPP_ASSIGNED)
- is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGEND' staet */
-
+ is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGNED' state */
if (is->debug & 0x1)
printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
}
}
} else {
- for (i = 0; i < ISDN_MAX_CHANNELS; i++)
- if (ippp_table[i]->minor == lp->pppbind && ippp_table[i]->state == IPPP_OPEN)
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (ippp_table[i]->minor == lp->pppbind &&
+ (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
break;
+ }
}
if (i >= ISDN_MAX_CHANNELS) {
restore_flags(flags);
- printk(KERN_WARNING "isdn_ppp_bind: Can't find usable ippp device.\n");
+ printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
return -1;
}
unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */
return -1;
}
lp->ppp_slot = i;
+
+ /* reset some values */
+ lp->netdev->ib.bundled = 0;
+ lp->netdev->ib.next_num = 0;
+ lp->netdev->ib.modify = 0;
+ lp->netdev->ib.last = NULL;
+ lp->netdev->ib.min = 0;
+ lp->netdev->ib.sq = NULL;
+
is = ippp_table[i];
is->lp = lp;
is->unit = unit;
ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
- wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
+#if LINUX_VERSION_CODE < 131841
+ if (ippp_table[lp->ppp_slot]->wq)
+#endif
+ wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
}
/*
return 0;
is = ippp_table[slot];
+#if LINUX_VERSION_CODE < 131841
+ if (is->state && is->wq)
+#else
if (is->state)
+#endif
wake_up_interruptible(&is->wq);
is->state = IPPP_CLOSEWAIT;
}
is = file->private_data = ippp_table[slot];
+#if 0
if (is->debug & 0x1)
+#endif
printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", slot, min, is->state);
/* compression stuff */
- is->compressor = NULL;
- is->decomp_stat = is->comp_stat = NULL;
- is->link_compressor = NULL;
- is->link_decomp_stat = is->link_comp_stat = NULL;
+ is->link_compressor = is->compressor = NULL;
+ is->link_decompressor = is->decompressor = NULL;
+ is->link_comp_stat = is->comp_stat = NULL;
+ is->link_decomp_stat = is->decomp_stat = NULL;
+ is->compflags = 0;
+
+ is->reset = isdn_ppp_ccp_reset_alloc(is);
is->lp = NULL;
is->mp_seqno = 0; /* MP sequence number */
is->mru = 1524; /* MRU, default 1524 */
is->maxcid = 16; /* VJ: maxcid */
is->tk = current;
- /* next two are redundant, but be paranoid */
- init_waitqueue_head(&is->wq); /* read() wait queue */
- init_waitqueue_head(&is->wql); /* select() wait queue */
+#if LINUX_VERSION_CODE < 131841
+ is->wq = NULL; /* read() wait queue */
+#else
+ init_waitqueue_head(&is->wq);
+#endif
is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
is->last = is->rq;
is->minor = min;
is->last = is->rq;
#ifdef CONFIG_ISDN_PPP_VJ
+/* TODO: if this was the previous master: link the slcomp to the new master */
slhc_free(is->slcomp);
is->slcomp = NULL;
#endif
+/* TODO: if this was the previous master: link the the stuff to the new master */
+ if(is->comp_stat)
+ is->compressor->free(is->comp_stat);
+ if(is->link_comp_stat)
+ is->link_compressor->free(is->link_comp_stat);
+ if(is->link_decomp_stat)
+ is->link_decompressor->free(is->link_decomp_stat);
+ if(is->decomp_stat)
+ is->decompressor->free(is->decomp_stat);
+ is->compressor = is->link_compressor = NULL;
+ is->decompressor = is->link_decompressor = NULL;
+ is->comp_stat = is->link_comp_stat = NULL;
+ is->decomp_stat = is->link_decomp_stat = NULL;
+
+ if(is->reset)
+ kfree(is->reset);
+ is->reset = NULL;
+
+ /* this slot is ready for new connections */
is->state = 0;
}
get_arg(void *b, void *val, int len)
{
if (len <= 0)
- len = sizeof(unsigned long);
+ len = sizeof(void *);
if (copy_from_user((void *) val, b, len))
return -EFAULT;
return 0;
* set arg .. ioctl helper
*/
static int
-set_arg(void *b, unsigned long val, void *str)
+set_arg(void *b, void *val,int len)
{
- if (!str) {
- if (copy_to_user(b, (void *) &val, 4))
- return -EFAULT;
- } else {
- if (copy_to_user(b, str, val))
- return -EFAULT;
- }
+ if(len <= 0)
+ len = sizeof(void *);
+ if (copy_to_user(b, (void *) val, len))
+ return -EFAULT;
return 0;
}
isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned long val;
- int num,r;
+ int r,i,j;
struct ippp_struct *is;
isdn_net_local *lp;
+ struct isdn_ppp_comp_data data;
is = (struct ippp_struct *) file->private_data;
lp = is->lp;
#ifdef CONFIG_ISDN_MPP
if (!(is->state & IPPP_CONNECT))
return -EINVAL;
- if ((r = get_arg((void *) arg, &val, 0)))
+ if ((r = get_arg((void *) arg, &val, sizeof(val) )))
return r;
printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
(int) min, (int) is->unit, (int) val);
#endif
break;
case PPPIOCGUNIT: /* get ppp/isdn unit number */
- if ((r = set_arg((void *) arg, is->unit, NULL)))
+ if ((r = set_arg((void *) arg, &is->unit, sizeof(is->unit) )))
+ return r;
+ break;
+ case PPPIOCGIFNAME:
+ if(!lp)
+ return -EINVAL;
+ if ((r = set_arg((void *) arg, lp->name,strlen(lp->name))))
return r;
break;
case PPPIOCGMPFLAGS: /* get configuration flags */
- if ((r = set_arg((void *) arg, is->mpppcfg, NULL)))
+ if ((r = set_arg((void *) arg, &is->mpppcfg, sizeof(is->mpppcfg) )))
return r;
break;
case PPPIOCSMPFLAGS: /* set configuration flags */
- if ((r = get_arg((void *) arg, &val, 0)))
+ if ((r = get_arg((void *) arg, &val, sizeof(val) )))
return r;
is->mpppcfg = val;
break;
case PPPIOCGFLAGS: /* get configuration flags */
- if ((r = set_arg((void *) arg, is->pppcfg, NULL)))
+ if ((r = set_arg((void *) arg, &is->pppcfg,sizeof(is->pppcfg) )))
return r;
break;
case PPPIOCSFLAGS: /* set configuration flags */
- if ((r = get_arg((void *) arg, &val, 0))) {
+ if ((r = get_arg((void *) arg, &val, sizeof(val) ))) {
return r;
}
if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
if (lp) {
struct ppp_idle pidle;
pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
- if ((r = set_arg((void *) arg, sizeof(struct ppp_idle), &pidle)))
+ if ((r = set_arg((void *) arg, &pidle,sizeof(struct ppp_idle))))
return r;
}
break;
case PPPIOCSMRU: /* set receive unit size for PPP */
- if ((r = get_arg((void *) arg, &val, 0)))
+ if ((r = get_arg((void *) arg, &val, sizeof(val) )))
return r;
is->mru = val;
break;
case PPPIOCSMPMTU:
break;
case PPPIOCSMAXCID: /* set the maximum compression slot id */
- if ((r = get_arg((void *) arg, &val, 0)))
+ if ((r = get_arg((void *) arg, &val, sizeof(val) )))
return r;
val++;
if (is->maxcid != val) {
}
break;
case PPPIOCGDEBUG:
- if ((r = set_arg((void *) arg, is->debug, 0)))
+ if ((r = set_arg((void *) arg, &is->debug, sizeof(is->debug) )))
return r;
break;
case PPPIOCSDEBUG:
- if ((r = get_arg((void *) arg, &val, 0)))
+ if ((r = get_arg((void *) arg, &val, sizeof(val) )))
return r;
is->debug = val;
break;
case PPPIOCGCOMPRESSORS:
{
- unsigned long protos = 0;
+ unsigned long protos[8] = {0,};
struct isdn_ppp_compressor *ipc = ipc_head;
while(ipc) {
- protos |= (0x1<<ipc->num);
+ j = ipc->num / (sizeof(long)*8);
+ i = ipc->num % (sizeof(long)*8);
+ if(j < 8)
+ protos[j] |= (0x1<<i);
ipc = ipc->next;
}
- if ((r = set_arg((void *) arg, protos, 0)))
+ if ((r = set_arg((void *) arg,protos,8*sizeof(long) )))
return r;
}
break;
case PPPIOCSCOMPRESSOR:
- if ((r = get_arg((void *) arg, &num, sizeof(int))))
+ if ((r = get_arg((void *) arg, &data, sizeof(struct isdn_ppp_comp_data))))
return r;
- return isdn_ppp_set_compressor(is, num);
- break;
+ return isdn_ppp_set_compressor(is, &data);
case PPPIOCGCALLINFO:
{
struct pppcallinfo pci;
if(lp->flags & ISDN_NET_CALLBACK)
pci.calltype |= CALLTYPE_CALLBACK;
}
- return set_arg((void *)arg,sizeof(struct pppcallinfo),&pci);
+ return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
}
default:
break;
printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
MINOR(file->f_dentry->d_inode->i_rdev));
+ /* just registers wait_queue hook. This doesn't really wait. */
poll_wait(file, &is->wq, wait);
if (!(is->state & IPPP_OPEN)) {
+ if(is->state == IPPP_CLOSEWAIT)
+ return POLLHUP;
printk(KERN_DEBUG "isdn_ppp: device not open\n");
return POLLERR;
}
is->last = bl->next;
restore_flags(flags);
- wake_up_interruptible(&is->wq);
+#if LINUX_VERSION_CODE < 131841
+ if (is->wq)
+#endif
+ wake_up_interruptible(&is->wq);
return len;
}
if (lp->isdn_device < 0 || lp->isdn_channel < 0)
return 0;
- if (dev->drv[lp->isdn_device]->running && lp->dialstate == 0 &&
+ if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
+ lp->dialstate == 0 &&
(lp->flags & ISDN_NET_CONNECTED)) {
int cnt;
struct sk_buff *skb;
return -EFAULT;
if (is->debug & 0x40) {
printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
- isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+ isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
}
+
+ isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
+
if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb)) != count) {
if (lp->sav_skb) {
dev_kfree_skb(lp->sav_skb);
return -1;
}
memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
- init_waitqueue_head(&ippp_table[i]->wq);
- init_waitqueue_head(&ippp_table[i]->wql);
ippp_table[i]->state = 0;
ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
ippp_table[i]->last = ippp_table[i]->rq;
is = ippp_table[lp->ppp_slot];
if (is->debug & 0x4) {
- printk(KERN_DEBUG "ippp_receive: len: %d\n", (int) skb->len);
- isdn_ppp_frame_log("receive", skb->data, skb->len, 32);
+ printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
+ (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len);
+ isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
}
if (net_dev->local->master) {
printk(KERN_WARNING "isdn_ppp_receice: net_dev != master\n");
#ifdef CONFIG_ISDN_MPP
if (!(is->mpppcfg & SC_REJ_MP_PROT)) {
int sqno_end;
-
- if(proto == PPP_LINK_COMP) {
- printk(KERN_DEBUG "received single link compressed frame\n");
- skb = isdn_ppp_decompress(skb,is,NULL);
- if(!skb)
- return;
- proto = isdn_ppp_strip_proto(skb);
+
+ if(is->compflags & SC_LINK_DECOMP_ON) {
+ if(proto == PPP_LINK_COMP) {
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "received single link compressed frame\n");
+ skb = isdn_ppp_decompress(skb,is,NULL,proto);
+ if(!skb)
+ return;
+ proto = isdn_ppp_strip_proto(skb);
+ }
+ else
+ isdn_ppp_decompress(skb,is,NULL,proto);
}
if (proto == PPP_MP) {
}
min_sqno &= mask;
for (lpq = net_dev->queue;;) {
- ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
+ if(ippp_table[lpq->ppp_slot]->last_link_seqno >= 0)
+ ippp_table[lpq->ppp_slot]->last_link_seqno &= mask;
lpq = lpq->next;
if (lpq == net_dev->queue)
break;
if (is->debug & 0x10) {
printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
- isdn_ppp_frame_log("rpush", skb->data, skb->len, 32);
+ isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
}
if(proto == PPP_COMP) {
if(!lp->master)
- skb = isdn_ppp_decompress(skb,is,is);
+ skb = isdn_ppp_decompress(skb,is,is,proto);
else
- skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot]);
- if(!skb)
+ skb = isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot],proto);
+
+ if(!skb) {
+ printk(KERN_DEBUG "ippp: compressed frame discarded!\n");
return;
+ }
+
proto = isdn_ppp_strip_proto(skb);
+ if (is->debug & 0x10) {
+ printk(KERN_DEBUG "RPostDecomp, skb %d %04x\n", (int) skb->len, proto);
+ isdn_ppp_frame_log("R-Decomp", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+ }
+ }
+ else if(is->compflags & SC_DECOMP_ON) { /* If decomp is ON */
+ if(!lp->master)
+ isdn_ppp_decompress(skb,is,is,proto);
+ else
+ isdn_ppp_decompress(skb,is,ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot],proto);
}
switch (proto) {
#endif
break;
case PPP_CCP:
- isdn_ppp_receive_ccp(net_dev,lp,skb);
+ case PPP_LINK_CCP:
+ isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
+ /* Dont pop up ResetReq/Ack stuff to the daemon any
+ longer - the job is done already */
+ if(skb->data[0] == CCP_RESETREQ ||
+ skb->data[0] == CCP_RESETACK)
+ break;
/* fall through */
default:
isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot); /* push data to pppd device */
return;
}
+ /* Reset hangup-timer */
+ lp->huptimer = 0;
netif_rx(skb);
/* net_dev->local->stats.rx_packets++; *//* done in isdn_net.c */
- /* Reset hangup-timer */
- lp->huptimer = 0;
return;
}
lp = nlp;
}
ipt = ippp_table[lp->ppp_slot];
-
lp->huptimer = 0;
/*
if (ipt->debug & 0x4)
printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
+ if (ipts->debug & 0x40)
+ isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot);
#ifdef CONFIG_ISDN_PPP_VJ
if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */
}
#endif
- /*
- * normal or bundle compression
- */
- skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+ /*
+ * normal (single link) or bundle compression
+ */
+ if(ipts->compflags & SC_COMP_ON)
+ skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
if (ipt->debug & 0x24)
printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
#endif
/*
- * 'link' compression
+ * 'link in bundle' compression ...
*/
- skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+ if(ipt->compflags & SC_LINK_COMP_ON)
+ skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
unsigned char *data = isdn_ppp_skb_push(&skb,1);
if (ipts->debug & 0x40) {
printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
- isdn_ppp_frame_log("xmit", skb->data, skb->len, 32);
+ isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
}
if (isdn_net_send_skb(dev, lp, skb)) {
if (lp->sav_skb) { /* whole sav_skb processing with disabled IRQs ?? */
#ifdef CONFIG_ISDN_MPP
+/*
+ * free SQ queue
+ * -------------
+ * Note: We need two queues for MPPP. The SQ queue holds fully (re)assembled frames,
+ * that can't be delivered, because there is an outstanding earlier frame
+ */
static void
isdn_ppp_free_sqqueue(isdn_net_dev * p)
{
}
+/*
+ * free MP queue
+ * -------------
+ * Note: The MP queue holds all frame fragments of frames, that can't be
+ * reassembled, because there is at least one missing fragment.
+ */
static void
isdn_ppp_free_mpqueue(isdn_net_dev * p)
{
return 0;
}
-
+/*
+ * Mask sequence numbers in MP queue
+ */
static void
isdn_ppp_mask_queue(isdn_net_dev * dev, long mask)
{
}
}
+/*
+ * put a fragment at the right place into the MP queue
+ * Also checks, whether this fragment completes a frame. In this case
+ * the fragments are copied together into one SKB
+ */
static int
isdn_ppp_fill_mpqueue(isdn_net_dev * dev, struct sk_buff **skb, int BEbyte, long *sqnop, int min_sqno)
{
slhc_toss(ippp_table[dev->local->ppp_slot]->slcomp);
#endif
}
+#endif
/*
* a buffered packet timed-out?
*/
-
-#endif
-
void
isdn_ppp_timer_timeout(void)
{
if (!sdev)
return 2;
- isdn_net_force_dial_lp((isdn_net_local *) sdev->priv);
+ isdn_net_dial_req((isdn_net_local *) sdev->priv);
return 0;
#else
return -1;
/*
* PPP compression stuff
*/
-static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master)
+
+
+/* Push an empty CCP Data Frame up to the daemon to wake it up and let it
+ generate a CCP Reset-Request or tear down CCP altogether */
+
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is)
+{
+ isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);
+}
+
+/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary,
+ but absolutely nontrivial. The most abstruse problem we are facing is
+ that the generation, reception and all the handling of timeouts and
+ resends including proper request id management should be entirely left
+ to the (de)compressor, but indeed is not covered by the current API to
+ the (de)compressor. The API is a prototype version from PPP where only
+ some (de)compressors have yet been implemented and all of them are
+ rather simple in their reset handling. Especially, their is only one
+ outstanding ResetAck at a time with all of them and ResetReq/-Acks do
+ not have parameters. For this very special case it was sufficient to
+ just return an error code from the decompressor and have a single
+ reset() entry to communicate all the necessary information between
+ the framework and the (de)compressor. Bad enough, LZS is different
+ (and any other compressor may be different, too). It has multiple
+ histories (eventually) and needs to Reset each of them independently
+ and thus uses multiple outstanding Acks and history numbers as an
+ additional parameter to Reqs/Acks.
+ All that makes it harder to port the reset state engine into the
+ kernel because it is not just the same simple one as in (i)pppd but
+ it must be able to pass additional parameters and have multiple out-
+ standing Acks. We are trying to achieve the impossible by handling
+ reset transactions independent by their id. The id MUST change when
+ the data portion changes, thus any (de)compressor who uses more than
+ one resettable state must provide and recognize individual ids for
+ each individual reset transaction. The framework itself does _only_
+ differentiate them by id, because it has no other semantics like the
+ (de)compressor might.
+ This looks like a major redesign of the interface would be nice,
+ but I don't have an idea how to do it better. */
+
+/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is
+ getting that lengthy because there is no simple "send-this-frame-out"
+ function above but every wrapper does a bit different. Hope I guess
+ correct in this hack... */
+
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+ unsigned char code, unsigned char id,
+ unsigned char *data, int len)
+{
+ struct sk_buff *skb;
+ unsigned char *p;
+ int count;
+ int cnt = 0;
+ isdn_net_local *lp = is->lp;
+
+ /* Alloc large enough skb */
+ skb = dev_alloc_skb(len + 16);
+ if(!skb) {
+ printk(KERN_WARNING
+ "ippp: CCP cannot send reset - out of memory\n");
+ return;
+ }
+
+ /* We may need to stuff an address and control field first */
+ if(!(is->pppcfg & SC_COMP_AC)) {
+ p = skb_put(skb, 2);
+ *p++ = 0xff;
+ *p++ = 0x03;
+ }
+
+ /* Stuff proto, code, id and length */
+ p = skb_put(skb, 6);
+ *p++ = (proto >> 8);
+ *p++ = (proto & 0xff);
+ *p++ = code;
+ *p++ = id;
+ cnt = 4 + len;
+ *p++ = (cnt >> 8);
+ *p++ = (cnt & 0xff);
+
+ /* Now stuff remaining bytes */
+ if(len) {
+ p = skb_put(skb, len);
+ memcpy(p, data, len);
+ }
+
+ /* skb is now ready for xmit */
+ printk(KERN_DEBUG "Sending CCP Frame:\n");
+ isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+ /* Just ripped from isdn_ppp_write. Dunno whether it makes sense,
+ especially dunno what the sav_skb stuff is good for. */
+
+ count = skb->len;
+ if ((cnt = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel,
+ 1, skb)) != count) {
+ if (lp->sav_skb) {
+ dev_kfree_skb(lp->sav_skb);
+ printk(KERN_INFO
+ "isdn_ppp_write: freeing sav_skb (%d,%d)!\n",
+ cnt, count);
+ } else
+ printk(KERN_INFO
+ "isdn_ppp_write: Can't write PPP frame to LL (%d,%d)!\n",
+ cnt, count);
+ lp->sav_skb = skb;
+ }
+}
+
+/* Allocate the reset state vector */
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
+{
+ struct ippp_ccp_reset *r;
+ printk(KERN_DEBUG "ippp_ccp: allocating reset data structure\n");
+ r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
+ if(!r)
+ return NULL;
+ memset(r, 0, sizeof(struct ippp_ccp_reset));
+ is->reset = r;
+ return r;
+}
+
+/* Free a given state and clear everything up for later reallocation */
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+ unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs;
+
+ if(is->reset->rs[id]) {
+ printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id);
+ rs = is->reset->rs[id];
+ /* Make sure the kernel will not call back later */
+ if(rs->ta)
+ del_timer(&rs->timer);
+ is->reset->rs[id] = NULL;
+ kfree(rs);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id);
+ }
+}
+
+/* The timer callback function which is called when a ResetReq has timed out,
+ aka has never been answered by a ResetAck */
+static void isdn_ppp_ccp_timer_callback(unsigned long closure)
+{
+ struct ippp_ccp_reset_state *rs =
+ (struct ippp_ccp_reset_state *)closure;
+
+ if(!rs) {
+ printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
+ return;
+ }
+ if(rs->ta && rs->state == CCPResetSentReq) {
+ /* We are correct here */
+ printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
+ rs->id);
+ if(!rs->expra) {
+ /* Hmm, there is no Ack really expected. We can clean
+ up the state now, it will be reallocated if the
+ decompressor insists on another reset */
+ rs->ta = 0;
+ isdn_ppp_ccp_reset_free_state(rs->is, rs->id);
+ return;
+ }
+ /* Push it again */
+ isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id,
+ rs->data, rs->dlen);
+ /* Restart timer */
+ rs->timer.expires = jiffies + HZ*5;
+ add_timer(&rs->timer);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
+ rs->state);
+ }
+}
+
+/* Allocate a new reset transaction state */
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+ unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs;
+ if(is->reset->rs[id]) {
+ printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
+ id);
+ return NULL;
+ } else {
+ rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+ if(!rs)
+ return NULL;
+ memset(rs, 0, sizeof(struct ippp_ccp_reset_state));
+ rs->state = CCPResetIdle;
+ rs->is = is;
+ rs->id = id;
+ rs->timer.data = (unsigned long)rs;
+ rs->timer.function = isdn_ppp_ccp_timer_callback;
+ is->reset->rs[id] = rs;
+ }
+ return rs;
+}
+
+
+/* A decompressor wants a reset with a set of parameters - do what is
+ necessary to fulfill it */
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+ struct isdn_ppp_resetparams *rp)
+{
+ struct ippp_ccp_reset_state *rs;
+
+ if(rp->valid) {
+ /* The decompressor defines parameters by itself */
+ if(rp->rsend) {
+ /* And he wants us to send a request */
+ if(!(rp->idval)) {
+ printk(KERN_ERR "ippp_ccp: decompressor must"
+ " specify reset id\n");
+ return;
+ }
+ if(is->reset->rs[rp->id]) {
+ /* There is already a transaction in existence
+ for this id. May be still waiting for a
+ Ack or may be wrong. */
+ rs = is->reset->rs[rp->id];
+ if(rs->state == CCPResetSentReq && rs->ta) {
+ printk(KERN_DEBUG "ippp_ccp: reset"
+ " trans still in progress"
+ " for id %d\n", rp->id);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: reset"
+ " trans in wrong state %d for"
+ " id %d\n", rs->state, rp->id);
+ }
+ } else {
+ /* Ok, this is a new transaction */
+ printk(KERN_DEBUG "ippp_ccp: new trans for id"
+ " %d to be started\n", rp->id);
+ rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id);
+ if(!rs) {
+ printk(KERN_ERR "ippp_ccp: out of mem"
+ " allocing ccp trans\n");
+ return;
+ }
+ rs->state = CCPResetSentReq;
+ rs->expra = rp->expra;
+ if(rp->dtval) {
+ rs->dlen = rp->dlen;
+ memcpy(rs->data, rp->data, rp->dlen);
+ }
+ /* HACK TODO - add link comp here */
+ isdn_ppp_ccp_xmit_reset(is, PPP_CCP,
+ CCP_RESETREQ, rs->id,
+ rs->data, rs->dlen);
+ /* Start the timer */
+ rs->timer.expires = jiffies + 5*HZ;
+ add_timer(&rs->timer);
+ rs->ta = 1;
+ }
+ } else {
+ printk(KERN_DEBUG "ippp_ccp: no reset sent\n");
+ }
+ } else {
+ /* The reset params are invalid. The decompressor does not
+ care about them, so we just send the minimal requests
+ and increase ids only when an Ack is received for a
+ given id */
+ if(is->reset->rs[is->reset->lastid]) {
+ /* There is already a transaction in existence
+ for this id. May be still waiting for a
+ Ack or may be wrong. */
+ rs = is->reset->rs[is->reset->lastid];
+ if(rs->state == CCPResetSentReq && rs->ta) {
+ printk(KERN_DEBUG "ippp_ccp: reset"
+ " trans still in progress"
+ " for id %d\n", rp->id);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: reset"
+ " trans in wrong state %d for"
+ " id %d\n", rs->state, rp->id);
+ }
+ } else {
+ printk(KERN_DEBUG "ippp_ccp: new trans for id"
+ " %d to be started\n", is->reset->lastid);
+ rs = isdn_ppp_ccp_reset_alloc_state(is,
+ is->reset->lastid);
+ if(!rs) {
+ printk(KERN_ERR "ippp_ccp: out of mem"
+ " allocing ccp trans\n");
+ return;
+ }
+ rs->state = CCPResetSentReq;
+ /* We always expect an Ack if the decompressor doesnt
+ know better */
+ rs->expra = 1;
+ rs->dlen = 0;
+ /* HACK TODO - add link comp here */
+ isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ,
+ rs->id, NULL, 0);
+ /* Start the timer */
+ rs->timer.expires = jiffies + 5*HZ;
+ add_timer(&rs->timer);
+ rs->ta = 1;
+ }
+ }
+}
+
+/* An Ack was received for this id. This means we stop the timer and clean
+ up the state prior to calling the decompressors reset routine. */
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+ unsigned char id)
{
-#if 1
- printk(KERN_ERR "compression not included!\n");
- dev_kfree_skb(skb);
- return NULL;
+ struct ippp_ccp_reset_state *rs = is->reset->rs[id];
+
+ if(rs) {
+ if(rs->ta && rs->state == CCPResetSentReq) {
+ /* Great, we are correct */
+ if(!rs->expra)
+ printk(KERN_DEBUG "ippp_ccp: ResetAck received"
+ " for id %d but not expected\n", id);
+ } else {
+ printk(KERN_INFO "ippp_ccp: ResetAck received out of"
+ "sync for id %d\n", id);
+ }
+ if(rs->ta) {
+ rs->ta = 0;
+ del_timer(&rs->timer);
+ }
+ isdn_ppp_ccp_reset_free_state(is, id);
+ } else {
+ printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
+ " %d\n", id);
+ }
+ /* Make sure the simple reset stuff uses a new id next time */
+ is->reset->lastid++;
+}
+
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
+ int proto)
+{
+#ifndef CONFIG_ISDN_CCP
+ if(proto == PPP_COMP || proto == PPP_LINK_COMP) {
+ printk(KERN_ERR "isdn_ppp: Ouch! Compression not included!\n");
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ return skb;
#else
+ void *stat = NULL;
+ struct isdn_ppp_compressor *ipc = NULL;
+ struct sk_buff *skb_out;
+ int len;
+ struct ippp_struct *ri;
+ struct isdn_ppp_resetparams rsparm;
+ unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
if(!master) {
/*
- * single link compression
+ * single link decompression
*/
- if(!is->link_compressor) {
- printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+ if(!is->link_decompressor) {
+ printk(KERN_ERR "ippp: no link decompressor defined!\n");
dev_kfree_skb(skb);
return NULL;
}
if(!is->link_decomp_stat) {
- printk(KERN_DEBUG "ippp: initialize link compressor\n");
+ printk(KERN_DEBUG "ippp: no link decompressor data allocated\n");
+ dev_kfree_skb(skb);
+ return NULL;
}
-/*
- -> decompress link
-*/
- }
+ stat = is->link_decomp_stat;
+ ipc = is->link_decompressor;
+ ri = is;
+ }
else {
/*
* 'normal' or bundle-compression
*/
- if(!master->compressor) {
- printk(KERN_ERR "ippp: no (link) compressor defined!\n");
+ if(!master->decompressor) {
+ printk(KERN_ERR "ippp: no decompressor defined!\n");
dev_kfree_skb(skb);
return NULL;
}
if(!master->decomp_stat) {
-#if 0
- master->decomp_stat = (master->compressor->decomp_alloc)( .. );
-#endif
- printk(KERN_DEBUG "ippp: initialize compressor\n");
+ printk(KERN_DEBUG "ippp: no decompressor data allocated\n");
+ dev_kfree_skb(skb);
+ return NULL;
}
+ stat = master->decomp_stat;
+ ipc = master->decompressor;
+ ri = master;
+ }
+
+ /*
+ printk(KERN_DEBUG "ippp: Decompress valid!\n");
+ */
+
+ if((master && proto == PPP_COMP) || (!master && proto == PPP_LINK_COMP) ) {
+ /* Set up reset params for the decompressor */
+ memset(&rsparm, 0, sizeof(rsparm));
+ rsparm.data = rsdata;
+ rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+
+/* !!!HACK,HACK,HACK!!! 2048 is only assumed */
+ skb_out = dev_alloc_skb(2048);
+ len = ipc->decompress(stat,skb,skb_out, &rsparm);
+ dev_kfree_skb(skb);
+ if(len <= 0) {
+ /* Ok, some error */
+ switch(len) {
+ case DECOMP_ERROR:
+ ri->pppcfg |= SC_DC_ERROR;
+ printk(KERN_INFO "ippp: decomp wants reset %s params\n",
+ rsparm.valid ? "with" : "without");
+
+ isdn_ppp_ccp_reset_trans(ri, &rsparm);
+
+ break;
+ case DECOMP_FATALERROR:
+ ri->pppcfg |= SC_DC_FERROR;
+ /* Kick ipppd to recognize the error */
+ isdn_ppp_ccp_kickup(ri);
+ break;
+ }
+ /* Did I see a leak here ? */
+ dev_kfree_skb(skb_out);
+ return NULL;
+ }
+ return skb_out;
+ }
+ else {
+ /*
+ printk(KERN_DEBUG "isdn_ppp: [%d] Calling incomp with this frame!\n",is->unit);
+ */
+ ipc->incomp(stat,skb,proto);
+ return skb;
}
-
- return skb;
#endif
}
static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
struct ippp_struct *is,struct ippp_struct *master,int type)
{
-#if 1
- return skb_in;
-#else
int ret;
int new_proto;
struct isdn_ppp_compressor *compressor;
void *stat;
struct sk_buff *skb_out;
+#ifdef CONFIG_ISDN_CCP
+ /* we do not compress control protocols */
+ if(*proto < 0 || *proto > 0x3fff) {
+#else
+ {
+#endif
+ return skb_in;
+ }
+
if(type) { /* type=1 => Link compression */
+#if 0
compressor = is->link_compressor;
stat = is->link_comp_stat;
new_proto = PPP_LINK_COMP;
+#else
+ return skb_in;
+#endif
}
else {
if(!master) {
}
if(!compressor) {
- printk(KERN_ERR "No compressor set!\n");
+ printk(KERN_ERR "isdn_ppp: No compressor set!\n");
return skb_in;
}
if(!stat) {
- /* init here ? */
+ printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
return skb_in;
}
- skb_out = dev_alloc_skb(skb_in->len);
+ /* Allow for at least 150 % expansion (for now) */
+ skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32);
if(!skb_out)
return skb_in;
dev_kfree_skb(skb_in);
*proto = new_proto;
return skb_out;
-#endif
-
}
/*
* we received a CCP frame ..
- * not a clean solution, but we SHOULD handle a few cased in the kernel
+ * not a clean solution, but we MUST handle a few cases in the kernel
*/
static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
- struct sk_buff *skb)
+ struct sk_buff *skb,int proto)
{
-#if 0
- printk(KERN_DEBUG "isdn_ppp_receive_cpp: %02x %02x %02x %02x %02x %02x %02x %02x\n",
- skb->data[0],skb->data[1],skb->data[2],skb->data[3],
- skb->data[4],skb->data[5],skb->data[6],skb->data[7] );
-#endif
+ struct ippp_struct *is = ippp_table[lp->ppp_slot];
+ struct ippp_struct *mis;
+ int len;
+ struct isdn_ppp_resetparams rsparm;
+ unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
+ printk(KERN_DEBUG "Received CCP frame from peer\n");
+ isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+ if(lp->master)
+ mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+ else
+ mis = is;
+
+ switch(skb->data[0]) {
+ case CCP_CONFREQ:
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Disable (de)compression here!\n");
+ if(proto == PPP_CCP)
+ mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+ else
+ is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+ break;
+ case CCP_CONFACK:
+ /* if we RECEIVE an ackowledge we enable the decompressor */
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Enable decompression here!\n");
+ if(proto == PPP_CCP)
+ mis->compflags |= SC_DECOMP_ON;
+ else
+ is->compflags |= SC_LINK_DECOMP_ON;
+ break;
+
+ case CCP_RESETACK:
+ printk(KERN_DEBUG "Received ResetAck from peer\n");
+ len = (skb->data[2] << 8) | skb->data[3];
+ len -= 4;
+
+ if(proto == PPP_CCP) {
+ /* If a reset Ack was outstanding for this id, then
+ clean up the state engine */
+ isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]);
+ if(mis->decompressor && mis->decomp_stat)
+ mis->decompressor->
+ reset(mis->decomp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, NULL);
+ /* TODO: This is not easy to decide here */
+ mis->compflags &= ~SC_DECOMP_DISCARD;
+ mis->pppcfg &= ~SC_DC_ERROR;
+ }
+ else {
+ isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]);
+ if(is->link_decompressor && is->link_decomp_stat)
+ is->link_decompressor->
+ reset(is->link_decomp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, NULL);
+ /* TODO: neither here */
+ is->compflags &= ~SC_LINK_DECOMP_DISCARD;
+ is->pppcfg &= ~SC_DC_ERROR;
+ }
+ break;
+
+ case CCP_RESETREQ:
+ printk(KERN_DEBUG "Received ResetReq from peer\n");
+ /* Receiving a ResetReq means we must reset our compressor */
+ /* Set up reset params for the reset entry */
+ memset(&rsparm, 0, sizeof(rsparm));
+ rsparm.data = rsdata;
+ rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+ /* Isolate data length */
+ len = (skb->data[2] << 8) | skb->data[3];
+ len -= 4;
+ if(proto == PPP_CCP) {
+ if(mis->compressor && mis->comp_stat)
+ mis->compressor->
+ reset(mis->comp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, &rsparm);
+ }
+ else {
+ if(is->link_compressor && is->link_comp_stat)
+ is->link_compressor->
+ reset(is->link_comp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, &rsparm);
+ }
+ /* Ack the Req as specified by rsparm */
+ if(rsparm.valid) {
+ /* Compressor reset handler decided how to answer */
+ if(rsparm.rsend) {
+ /* We should send a Frame */
+ isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+ rsparm.idval ? rsparm.id
+ : skb->data[1],
+ rsparm.dtval ?
+ rsparm.data : NULL,
+ rsparm.dtval ?
+ rsparm.dlen : 0);
+ } else {
+ printk(KERN_DEBUG "ResetAck suppressed\n");
+ }
+ } else {
+ /* We answer with a straight reflected Ack */
+ isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len);
+ }
+ break;
+ }
+}
+
+
+/*
+ * Daemon sends a CCP frame ...
+ */
+
+/* TODO: Clean this up with new Reset semantics */
+
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
+{
+ struct ippp_struct *mis,*is = ippp_table[lp->ppp_slot];
+ int proto;
+ unsigned char *data;
+
+ if(!skb || skb->len < 3)
+ return;
+
+ /* Daemon may send with or without address and control field comp */
+ data = skb->data;
+ if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
+ data += 2;
+ if(skb->len < 5)
+ return;
+ }
+
+ proto = ((int)data[0]<<8)+data[1];
+ if(proto != PPP_CCP && proto != PPP_LINK_CCP)
+ return;
+
+ printk(KERN_DEBUG "Received CCP frame from daemon:\n");
+ isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+ if(lp->master)
+ mis = ippp_table[((isdn_net_local *) (lp->master->priv))->ppp_slot];
+ else
+ mis = is;
+
+ if(mis != is)
+ printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
+
+ switch(data[2]) {
+ case CCP_CONFREQ:
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Disable (de)compression here!\n");
+ if(proto == PPP_CCP)
+ is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+ else
+ is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+ break;
+ case CCP_CONFACK:
+ /* if we SEND an ackowledge we can/must enable the compressor */
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Enable compression here!\n");
+ if(proto == PPP_CCP)
+ is->compflags |= SC_COMP_ON;
+ else
+ is->compflags |= SC_LINK_COMP_ON;
+ break;
+ case CCP_RESETACK:
+ /* If we send a ACK we should reset our compressor */
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Reset decompression state here!\n");
+ printk(KERN_DEBUG "ResetAck from daemon passed by\n");
+ if(proto == PPP_CCP) {
+ /* link to master? */
+ if(is->compressor && is->comp_stat)
+ is->compressor->reset(is->comp_stat, 0, 0,
+ NULL, 0, NULL);
+ is->compflags &= ~SC_COMP_DISCARD;
+ }
+ else {
+ if(is->link_compressor && is->link_comp_stat)
+ is->link_compressor->reset(is->link_comp_stat,
+ 0, 0, NULL, 0, NULL);
+ is->compflags &= ~SC_LINK_COMP_DISCARD;
+ }
+ break;
+ case CCP_RESETREQ:
+ /* Just let it pass by */
+ printk(KERN_DEBUG "ResetReq from daemon passed by\n");
+ break;
+ }
}
+
int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
{
ipc->next = ipc_head;
return 0;
}
-static int isdn_ppp_set_compressor(struct ippp_struct *is,int num)
+static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data)
{
struct isdn_ppp_compressor *ipc = ipc_head;
+ int ret;
+ void *stat;
+ int num = data->num;
+
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
+ (data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
while(ipc) {
if(ipc->num == num) {
- return 0;
- is->compressor = ipc;
- is->link_compressor = ipc;
+ stat = ipc->alloc(data);
+ if(stat) {
+ ret = ipc->init(stat,data,is->unit,0);
+ if(!ret) {
+ printk(KERN_ERR "Can't init (de)compression!\n");
+ ipc->free(stat);
+ stat = NULL;
+ break;
+ }
+ }
+ else {
+ printk(KERN_ERR "Can't alloc (de)compression!\n");
+ break;
+ }
+
+ if(data->flags & IPPP_COMP_FLAG_XMIT) {
+ if(data->flags & IPPP_COMP_FLAG_LINK) {
+ if(is->link_comp_stat)
+ is->link_compressor->free(is->link_comp_stat);
+ is->link_comp_stat = stat;
+ is->link_compressor = ipc;
+ }
+ else {
+ if(is->comp_stat)
+ is->compressor->free(is->comp_stat);
+ is->comp_stat = stat;
+ is->compressor = ipc;
+ }
+ }
+ else {
+ if(data->flags & IPPP_COMP_FLAG_LINK) {
+ if(is->link_decomp_stat)
+ is->link_decompressor->free(is->link_decomp_stat);
+ is->link_decomp_stat = stat;
+ is->link_decompressor = ipc;
+ }
+ else {
+ if(is->decomp_stat)
+ is->decompressor->free(is->decomp_stat);
+ is->decomp_stat = stat;
+ is->decompressor = ipc;
+ }
+ }
+ return 0;
}
ipc = ipc->next;
}
}
-#if 0
-static struct symbol_table isdn_ppp_syms =
-{
-#include <linux/symtab_begin.h>
- X(isdn_ppp_register_compressor),
- X(isdn_ppp_unregister_compressor),
-#include <linux/symtab_end.h>
-};
-#endif
-
-
-
-
-/* $Id: isdn_ppp.h,v 1.12 1998/01/31 22:07:48 keil Exp $
+/* $Id: isdn_ppp.h,v 1.13 1998/03/22 18:50:50 hipp Exp $
* header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
*
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_ppp.h,v $
+ * Revision 1.13 1998/03/22 18:50:50 hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
* Revision 1.12 1998/01/31 22:07:48 keil
* changes for newer kernels
*
extern int isdn_ppp_dial_slave(char *);
extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc);
+
#define IPPP_OPEN 0x01
#define IPPP_CONNECT 0x02
#define IPPP_CLOSEWAIT 0x04
--- /dev/null
+/* $Id: isdn_timru.c,v 1.2 1998/03/07 23:17:28 fritz Exp $
+ *
+ * Linux ISDN subsystem, timeout-rules for network interfaces.
+ *
+ * Copyright 1997 by Christian Lademann <cal@zls.de>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn_timru.c,v $
+ * Revision 1.2 1998/03/07 23:17:28 fritz
+ * Added RCS keywords
+ * Bugfix: Did not compile without isdn_dumppkt beeing enabled.
+ *
+ */
+
+/*
+02.06.97:cal:
+ - ISDN_TIMRU_PACKET_NONE = 0 definiert, die anderen ISDN_TIMRU_PACKET_* -
+ Definitionen jeweils inkrementiert
+
+ - isdn_net_recalc_timeout():
+ - In der Schleife zum Finden einer passenden Regel wurde in jedem Fall
+ die Wildcard-Kette durchsucht. Jetzt nicht mehr.
+ - beim Testen einer Bringup-Regel wird der anfaengliche Timeout auf den
+ Hangup-Timeout des Devices gesetzt (lp->onhtime).
+
+10.06.97:cal:
+ - isdn_net_recalc_timeout(): rule->neg-Handling gesaeubert: eine Regel passt
+ genau dann, wenn match(rule) XOR rule->neg und das bei allen Regeltypen.
+ - isdn_net_add_rule(): rule->timeout bei BRINGUP immer 1, sonst > 0.
+ - alle return(-1), die zu ioctl-Calls zurueckgehen --> return(-EINVAL).
+ - div. Leerzeilen geloescht / eingefuegt; alle return's "geklammert".
+
+12.06.97:cal:
+ - isdn_net_recalc_timeout(): Falls IP-Masquerading verwendet wird, kann mit
+ der neuen Option CONFIG_TIMRU_USE_MASQ der Regel-Match auf die ursprueng-
+ lichen Adressen und nicht auf die der Firewall angewendet werden. Dazu ist
+ ein Patch in net/ipv4/ip_masq.c notwendig: ip_masq_in_get_2 muss
+ exportiert werden.
+
+26.06.97:cal:
+ - isdn_net_add_rule(): rule->timeout darf bei BRINGUP >= 0 sein. Damit
+ laesst sich folgende Systax erreichen: "Falls Paket passt, starte die
+ Verbindung NICHT", wie es im Stand 970602 moeglich war.
+ - isdn_net_recalc_timeout(): BRINGUP: initial timeout wird auf den in der
+ passenden Regel gefundenen Timeout gesetzt. Ist dieser 0, so wird die
+ Verbindung nicht aufgebaut.
+
+16.10.97:cal:
+ - isdn_net_recalc_timeout(): beachte Fake-Header, der bei ausgehenden
+ SyncPPP-Paketen eingesetzt wird;
+ TimRu's "recalc timeout:" - Meldungen in einer Zeile
+
+04.11.97:cal:
+ - isdn_net.c, isdn_net_new(): Timeout-Rules nicht mehr automatisch
+ alloziieren;
+ - isdn_net.c, isdn_net_autohup(): AutoHup auch durchfuehren, wenn keine
+ Timeout-Rules alloziiert sind.
+*/
+/*
+TODO:
+
+- Masq-Adressen statt Paketadresse ausgeben, falls die Masq-Adressen verwendet
+ werden.
+
+- Masq-Adressen-Verwendung als Option in den Regeln vorsehen
+
+- TCP-Flags als Regel-Optionen
+
+- weitere Verfeinerungen fuer Nicht-TCP/IP-Pakete
+*/
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <net/icmp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#ifdef CONFIG_ISDN_TIMRU_USE_MASQ
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+#endif
+#include "isdn_common.h"
+#include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
+#include "isdn_ppp.h"
+#endif
+
+#ifdef CONFIG_ISDN_TIMEOUT_RULES
+
+static int
+isdn_timru_match(isdn_timeout_rule *this, isdn_timeout_rule *rule);
+
+
+#define printk_ip(a) printk("%ld.%ld.%ld.%ld",(ntohl(a)>>24)&0xFF,\
+ (ntohl(a)>>16)&0xFF,\
+ (ntohl(a)>>8)&0xFF,\
+ (ntohl(a))&0xFF)
+#define printk_port(a) printk("%d",ntohs(a))
+
+
+#define VERBOSE_PRINTK(v, l, p...) { \
+ if(dev->net_verbose >= (v)) { \
+ printk(l ## p); \
+ } else { ; } \
+}
+
+
+int
+isdn_net_recalc_timeout(int type, int prot, struct device *ndev, void *buf, ulong arg) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ struct sk_buff *skb;
+ struct iphdr *ip;
+ struct icmphdr *icmp;
+ struct tcphdr *tcp;
+ struct udphdr *udp;
+#ifdef CONFIG_ISDN_TIMRU_USE_MASQ
+#ifdef CONFIG_IP_MASQUERADE
+ struct ip_masq *masq;
+ int m_prot;
+ __u32 m_saddr, m_daddr;
+ __u16 m_sport, m_dport;
+ int check_for_masq;
+#endif
+#endif
+
+ isdn_timeout_rule match_rule;
+
+/*
+ char *cbuf;
+*/
+ int ppp_proto, ppp_hdrlen = 0, new_timeout;
+
+
+ match_rule.type = type;
+ match_rule.protfam = ISDN_TIMRU_PROTFAM_WILDCARD;
+
+ if(dev->net_verbose > 4) {
+ printk(KERN_DEBUG "recalc_timeout:");
+ switch(type) {
+ case ISDN_TIMRU_BRINGUP: printk("BRINGUP, "); break;
+ case ISDN_TIMRU_KEEPUP_IN: printk("KEEPUP_IN, "); break;
+ case ISDN_TIMRU_KEEPUP_OUT: printk("KEEPUP_OUT, "); break;
+ default:
+ printk("ERROR\n");
+ return(-1);
+ break;
+ }
+ }
+
+ switch(prot) {
+ case ISDN_TIMRU_PACKET_PPP:
+ case ISDN_TIMRU_PACKET_PPP_NO_HEADER:
+ if(prot == ISDN_TIMRU_PACKET_PPP) {
+ ppp_proto = PPP_PROTOCOL((char *)buf);
+/*
+ cbuf = (char *)(buf + PPP_HDRLEN);
+*/
+ } else {
+ ppp_proto = (int)arg;
+/*
+ cbuf = (char *)buf;
+*/
+ }
+
+ match_rule.protfam = ISDN_TIMRU_PROTFAM_PPP;
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_WILDCARD;
+
+ switch(ppp_proto) {
+ case PPP_IPCP:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_IPCP;
+ VERBOSE_PRINTK(5, "", "PPP/IPCP\n");
+ break;
+
+ case PPP_IPXCP:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_IPXCP;
+ VERBOSE_PRINTK(5, "", "PPP/IPXCP\n");
+ break;
+
+ case PPP_CCP:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_CCP;
+ VERBOSE_PRINTK(5, "", "PPP/CCP\n");
+ break;
+
+ case PPP_LCP:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_LCP;
+ VERBOSE_PRINTK(5, "", "PPP/LCP\n");
+ break;
+
+ case PPP_PAP:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_PAP;
+ VERBOSE_PRINTK(5, "", "PPP/PAP\n");
+ break;
+
+ case PPP_LQR:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_LQR;
+ VERBOSE_PRINTK(5, "", "PPP/LQR\n");
+ break;
+
+ case PPP_CHAP:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_CHAP;
+ VERBOSE_PRINTK(5, "", "PPP/CHAP\n");
+ break;
+
+ default:
+ match_rule.rule.ppp.protocol = ISDN_TIMRU_PPP_WILDCARD;
+
+ if(dev->net_verbose >= 5) {
+ printk("PPP/? (%x)\n", ppp_proto);
+#ifdef ISDN_DEBUG_NET_DUMP
+ isdn_dumppkt("R:", (u_char *)buf, 40, 40);
+#endif
+ }
+ break;
+ }
+ break;
+
+ case ISDN_TIMRU_PACKET_SKB:
+ skb = (struct sk_buff *)buf;
+
+#ifdef CONFIG_ISDN_PPP
+ if((type == ISDN_TIMRU_BRINGUP ||
+ type == ISDN_TIMRU_KEEPUP_OUT) &&
+ lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+ /* jump over fake header. */
+ ppp_hdrlen = IPPP_MAX_HEADER;
+ }
+#endif
+
+
+ switch(ntohs(skb->protocol)) {
+ case ETH_P_IP:
+/*
+ if(!(ip = skb->ip_hdr))
+*/
+ ip = (struct iphdr *)(skb->data + ppp_hdrlen);
+
+ match_rule.protfam = ISDN_TIMRU_PROTFAM_IP;
+ match_rule.rule.ip.saddr.s_addr = ip->saddr;
+ match_rule.rule.ip.daddr.s_addr = ip->daddr;
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
+
+ switch(ip->protocol) {
+ case IPPROTO_ICMP:
+ if(!(icmp = (struct icmphdr *)((unsigned long *)ip+ip->ihl))) {
+ VERBOSE_PRINTK(5, "", "IP/ICMP HDR-ERR\n");
+ } else {
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_ICMP;
+ match_rule.rule.ip.pt.type.from = icmp->type;
+
+ if(dev->net_verbose >= 5) {
+ printk("IP/ICMP ");
+ printk_ip(ip->saddr);
+ printk(" --> ");
+ printk_ip(ip->daddr);
+ printk("/");
+ printk_port(icmp->type);
+ printk("\n");
+ }
+ }
+ break;
+
+ case IPPROTO_IGMP:
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
+ VERBOSE_PRINTK(5, "", "IP/IGMP\n");
+ break;
+
+ case IPPROTO_IPIP:
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
+ VERBOSE_PRINTK(5, "", "IP/IPIP\n");
+ break;
+
+ case IPPROTO_TCP:
+ if(!(tcp = (struct tcphdr *)((unsigned long *)ip+ip->ihl))) {
+ VERBOSE_PRINTK(5, "", "IP/TCP HDR-ERR\n");
+ } else {
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_TCP;
+ match_rule.rule.ip.pt.port.s_from = tcp->source;
+ match_rule.rule.ip.pt.port.d_from = tcp->dest;
+
+ if(dev->net_verbose >= 5) {
+ printk("IP/TCP ");
+ printk_ip(ip->saddr);
+ printk("/");
+ printk_port(tcp->source);
+ printk(" --> ");
+ printk_ip(ip->daddr);
+ printk("/");
+ printk_port(tcp->dest);
+ printk("\n");
+ }
+ }
+ break;
+
+ case IPPROTO_EGP:
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
+ VERBOSE_PRINTK(5, "", "IP/EGP\n");
+ break;
+
+ case IPPROTO_PUP:
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
+ VERBOSE_PRINTK(5, "" "IP/PUP\n");
+ break;
+
+ case IPPROTO_UDP:
+ if(!(udp=(struct udphdr *)((unsigned long *)ip+ip->ihl))) {
+ VERBOSE_PRINTK(5, "", "IP/UDP HDR-ERR\n");
+ } else {
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_UDP;
+ match_rule.rule.ip.pt.port.s_from = udp->source;
+ match_rule.rule.ip.pt.port.d_from = udp->dest;
+
+ if(dev->net_verbose >= 5) {
+ printk("IP/UDP ");
+ printk_ip(ip->saddr);
+ printk("/");
+ printk_port(udp->source);
+ printk(" --> ");
+ printk_ip(ip->daddr);
+ printk("/");
+ printk_port(udp->dest);
+ printk("\n");
+ }
+ }
+ break;
+
+ case IPPROTO_IDP:
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
+ VERBOSE_PRINTK(5, "", "IP/IDP\n");
+ break;
+
+ default:
+ match_rule.rule.ip.protocol = ISDN_TIMRU_IP_WILDCARD;
+ if(dev->net_verbose >= 5) {
+ printk("IP/? (%x)\n", ip->protocol);
+#ifdef ISDN_DEBUG_NET_DUMP
+ isdn_dumppkt("R:", (u_char *)skb, skb->len, 180);
+#endif
+ }
+ break;
+ }
+ break;
+
+ case ETH_P_ARP:
+ VERBOSE_PRINTK(5, "", "ARP/?\n");
+ break;
+
+ case ETH_P_IPX:
+ VERBOSE_PRINTK(5, "", "IPX/?\n");
+ break;
+
+ case ETH_P_802_2:
+ VERBOSE_PRINTK(5, "", "802.2/?\n");
+ break;
+
+ case ETH_P_802_3:
+ VERBOSE_PRINTK(5, "", "802.3/?\n");
+ break;
+
+ default:
+ if(dev->net_verbose >= 5) {
+ printk("?/? (%x)\n", ntohs(skb->protocol));
+#ifdef ISDN_DEBUG_NET_DUMP
+ isdn_dumppkt("R:", (u_char *)skb, skb->len, 1800);
+#endif
+ }
+ break;
+ }
+
+#ifdef CONFIG_ISDN_TIMRU_USE_MASQ
+#ifdef CONFIG_IP_MASQUERADE
+ check_for_masq = 0;
+ m_saddr = m_daddr = (__u32)0;
+ m_sport = m_dport = (__u16)0;
+ m_prot = 0;
+
+ switch(match_rule.protfam) {
+ case ISDN_TIMRU_PROTFAM_IP:
+ m_saddr = match_rule.rule.ip.saddr.s_addr;
+ m_daddr = match_rule.rule.ip.daddr.s_addr;
+
+ switch(match_rule.rule.ip.protocol) {
+ case ISDN_TIMRU_IP_TCP:
+ m_prot = IPPROTO_TCP;
+ m_sport = match_rule.rule.ip.pt.port.s_from;
+ m_dport = match_rule.rule.ip.pt.port.d_from;
+ check_for_masq = 1;
+ break;
+
+ case ISDN_TIMRU_IP_UDP:
+ m_prot = IPPROTO_UDP;
+ m_sport = match_rule.rule.ip.pt.port.s_from;
+ m_dport = match_rule.rule.ip.pt.port.d_from;
+ check_for_masq = 1;
+ break;
+
+#if 0
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+ case ISDN_TIMRU_IP_ICMP:
+ m_sport = match_rule.rule.ip.pt.type.from;
+ m_dport = 0;
+ check_for_masq = 1;
+ break;
+#endif
+#endif
+ }
+ break;
+ }
+
+ if(check_for_masq) {
+ masq = NULL;
+
+ switch(type) {
+ case ISDN_TIMRU_BRINGUP:
+ case ISDN_TIMRU_KEEPUP_OUT:
+ if((masq = ip_masq_in_get_2(m_prot, m_daddr, m_dport, m_saddr, m_sport))) {
+ match_rule.rule.ip.saddr.s_addr = m_saddr;
+ match_rule.rule.ip.daddr.s_addr = m_daddr;
+ switch(m_prot) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ match_rule.rule.ip.pt.port.s_from = m_sport;
+ match_rule.rule.ip.pt.port.d_from = m_dport;
+ break;
+ }
+ }
+ break;
+
+ case ISDN_TIMRU_KEEPUP_IN:
+ if((masq = ip_masq_in_get_2(m_prot, m_saddr, m_sport, m_daddr, m_dport))) {
+ match_rule.rule.ip.saddr.s_addr = m_daddr;
+ match_rule.rule.ip.daddr.s_addr = m_saddr;
+ switch(m_prot) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ match_rule.rule.ip.pt.port.s_from = m_sport;
+ match_rule.rule.ip.pt.port.d_from = m_dport;
+ break;
+ }
+ }
+ break;
+ }
+
+ if(masq && dev->net_verbose >= 5) {
+ printk(KERN_DEBUG "MASQ-TIMRU: ");
+ printk_ip(masq->maddr);
+ printk("/");
+ printk_port(masq->mport);
+ printk(": ");
+ printk_ip(masq->saddr);
+ printk("/");
+ printk_port(masq->sport);
+ printk(" --> ");
+ printk_ip(masq->daddr);
+ printk("/");
+ printk_port(masq->dport);
+ printk("\n");
+ }
+ }
+#endif
+#endif
+ break;
+ }
+
+ new_timeout = lp->onhtime;
+
+ if(prot && lp->timeout_rules) {
+ isdn_timeout_rule *head, *tor;
+ int pf, found_match, i;
+
+ pf = match_rule.protfam;
+ found_match = 0;
+
+ while(1) {
+ head = tor = lp->timeout_rules->timru[type][pf];
+ i = 0;
+ while(tor) {
+ if((isdn_timru_match(&match_rule, tor) > 0) ^ (tor->neg > 0)) {
+ found_match = 1;
+ new_timeout = tor->timeout;
+ }
+
+ if(found_match) {
+#ifdef DEBUG_RULES
+ printk(KERN_DEBUG "Rule %d-%d-%d matches\n", type, pf, i);
+#endif
+ break;
+ }
+
+ if(tor->next == head)
+ tor = NULL;
+ else {
+ tor = tor->next;
+ i++;
+ }
+ }
+
+ if(! found_match && pf != ISDN_TIMRU_PROTFAM_WILDCARD)
+ pf = ISDN_TIMRU_PROTFAM_WILDCARD;
+ else
+ break;
+ }
+
+ if(! found_match) {
+ new_timeout = lp->timeout_rules->defaults[type];
+#ifdef DEBUG_RULES
+ printk("No rule matches: using default\n");
+#endif
+ }
+ }
+
+ if(type == ISDN_TIMRU_BRINGUP) {
+ if(new_timeout > 0) {
+ lp->huptimeout = new_timeout;
+ lp->huptimer = 0;
+ }
+ } else {
+ if(new_timeout > lp->huptimeout
+ || lp->huptimeout - lp->huptimer < new_timeout) {
+ lp->huptimeout = new_timeout;
+ lp->huptimer = 0;
+ }
+ }
+
+ return(new_timeout);
+}
+
+
+static int
+isdn_timru_match(isdn_timeout_rule *this, isdn_timeout_rule *rule) {
+ if(this->protfam != rule->protfam)
+ return(0);
+
+ switch(rule->protfam) {
+ case ISDN_TIMRU_PROTFAM_WILDCARD:
+ return(1);
+ break;
+
+ case ISDN_TIMRU_PROTFAM_PPP:
+ if(rule->rule.ppp.protocol == ISDN_TIMRU_PPP_WILDCARD
+ || rule->rule.ppp.protocol == this->rule.ppp.protocol)
+ return(1);
+ break;
+
+ case ISDN_TIMRU_PROTFAM_IP:
+ if((this->rule.ip.saddr.s_addr & rule->rule.ip.smask.s_addr) != rule->rule.ip.saddr.s_addr
+ || (this->rule.ip.daddr.s_addr & rule->rule.ip.dmask.s_addr) != rule->rule.ip.daddr.s_addr)
+ return(0);
+
+ if(rule->rule.ip.protocol == ISDN_TIMRU_IP_WILDCARD)
+ return(1);
+
+ if(rule->rule.ip.protocol != this->rule.ip.protocol)
+ return(0);
+
+ switch(rule->rule.ip.protocol) {
+ case ISDN_TIMRU_IP_ICMP:
+ if(this->rule.ip.pt.type.from < rule->rule.ip.pt.type.from
+ || this->rule.ip.pt.type.from > rule->rule.ip.pt.type.to)
+ return(0);
+ break;
+
+ case ISDN_TIMRU_IP_TCP:
+ case ISDN_TIMRU_IP_UDP:
+ if(this->rule.ip.pt.port.s_from < rule->rule.ip.pt.port.s_from
+ || this->rule.ip.pt.port.s_from > rule->rule.ip.pt.port.s_to)
+ return(0);
+
+ if(this->rule.ip.pt.port.d_from < rule->rule.ip.pt.port.d_from
+ || this->rule.ip.pt.port.d_from > rule->rule.ip.pt.port.d_to)
+ return(0);
+
+ break;
+ }
+ break;
+ }
+
+ return(1);
+}
+
+
+static int
+isdn_timru_rule_equals(isdn_timeout_rule *this, isdn_timeout_rule *rule) {
+ if(this->neg != rule->neg
+ || this->protfam != rule->protfam)
+ return(0);
+
+ switch(rule->protfam) {
+ case ISDN_TIMRU_PROTFAM_PPP:
+ if(this->rule.ppp.protocol != rule->rule.ppp.protocol)
+ return(0);
+ break;
+
+ case ISDN_TIMRU_PROTFAM_IP:
+ if(this->rule.ip.protocol != rule->rule.ip.protocol
+ || this->rule.ip.saddr.s_addr != rule->rule.ip.saddr.s_addr
+ || this->rule.ip.smask.s_addr != rule->rule.ip.smask.s_addr
+ || this->rule.ip.daddr.s_addr != rule->rule.ip.daddr.s_addr
+ || this->rule.ip.dmask.s_addr != rule->rule.ip.dmask.s_addr)
+ return(0);
+
+ switch(rule->rule.ip.protocol) {
+ case ISDN_TIMRU_IP_ICMP:
+ if(this->rule.ip.pt.type.from != rule->rule.ip.pt.type.from
+ || this->rule.ip.pt.type.to != rule->rule.ip.pt.type.to)
+ return(0);
+ break;
+
+ case ISDN_TIMRU_IP_TCP:
+ case ISDN_TIMRU_IP_UDP:
+ if(this->rule.ip.pt.port.s_from != rule->rule.ip.pt.port.s_from
+ || this->rule.ip.pt.port.s_to != rule->rule.ip.pt.port.s_to
+ || this->rule.ip.pt.port.d_from != rule->rule.ip.pt.port.d_from
+ || this->rule.ip.pt.port.d_to != rule->rule.ip.pt.port.d_to)
+ return(0);
+
+ break;
+ }
+ break;
+ }
+
+ return(1);
+}
+
+
+int
+isdn_timru_alloc_timeout_rules(struct device *ndev) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ int i, j;
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+ if(!(lp->timeout_rules = (struct isdn_timeout_rules *)kmalloc(sizeof(struct isdn_timeout_rules), GFP_KERNEL))) {
+ restore_flags(flags);
+ printk(KERN_WARNING "isdn_timru: failed to allocate memory.\n");
+ return(-ENOMEM);
+ }
+
+ memset((char *)lp->timeout_rules, 0, sizeof(struct isdn_timeout_rules));
+
+ for(i = 0; i < ISDN_TIMRU_NUM_CHECK; i++) {
+ lp->timeout_rules->defaults[i] = lp->onhtime;
+ for(j = 0; j < ISDN_TIMRU_NUM_PROTFAM; j++)
+ lp->timeout_rules->timru[i][j] = NULL;
+ }
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+int
+isdn_timru_free_timeout_rules(struct device *ndev) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ isdn_timeout_rule *head, *this, *next;
+ int i, j;
+ ulong flags;
+
+ if(!lp->timeout_rules)
+ return(-1);
+
+ save_flags(flags);
+ cli();
+ for(i = 0; i < ISDN_TIMRU_NUM_CHECK; i++)
+ for(j = 0; j < ISDN_TIMRU_NUM_CHECK; j++)
+ if((head = lp->timeout_rules->timru[i][j])) {
+ this = head;
+ do {
+ next = this->next;
+ kfree(this);
+ } while(next == head);
+ }
+
+ kfree(lp->timeout_rules);
+ lp->timeout_rules = NULL;
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+int
+isdn_timru_add_rule(int where, struct device *ndev, isdn_timeout_rule *rule) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ isdn_timeout_rule **head;
+ ulong flags;
+ int ret;
+
+ if(!lp->timeout_rules)
+ if((ret = isdn_timru_alloc_timeout_rules(ndev)))
+ return(ret);
+
+ if(rule->timeout < 0)
+ return(-EINVAL);
+
+ save_flags(flags);
+ cli();
+
+ head = &(lp->timeout_rules->timru[rule->type][rule->protfam]);
+
+ if(! *head)
+ rule->next = rule->prev = *head = rule;
+ else {
+ rule->next = *head;
+ rule->prev = (*head)->prev;
+ (*head)->prev->next = rule;
+ (*head)->prev = rule;
+
+ if(where == 0) /* add to head of chain */
+ *head = rule;
+ }
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+int
+isdn_timru_del_rule(struct device *ndev, isdn_timeout_rule *rule) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ isdn_timeout_rule **head, *this;
+ ulong flags;
+
+ if(!lp->timeout_rules)
+ return(-EINVAL);
+
+ save_flags(flags);
+ cli();
+
+ head = &(lp->timeout_rules->timru[rule->type][rule->protfam]);
+
+ if(! *head) {
+ restore_flags(flags);
+ return(-EINVAL);
+ }
+
+ this = *head;
+ do {
+ if(isdn_timru_rule_equals(this, rule)) {
+ if(this->next != this) { /* more than one rule */
+ this->prev->next = this->next;
+ this->next->prev = this->prev;
+
+ if(this == *head)
+ *head = this->next;
+ } else
+ *head = NULL;
+
+ kfree(this);
+ restore_flags(flags);
+ return(0);
+ } else
+ this = this->next;
+ } while(this == *head);
+
+ restore_flags(flags);
+ return(-EINVAL);
+}
+
+
+int
+isdn_timru_set_default(int type, struct device *ndev, int def) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ ulong flags;
+ int ret;
+
+ if(!lp->timeout_rules)
+ if((ret = isdn_timru_alloc_timeout_rules(ndev)))
+ return(ret);
+
+ if(def < 0)
+ return(-EINVAL);
+
+ save_flags(flags);
+ cli();
+
+ lp->timeout_rules->defaults[type] = def;
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+int
+isdn_timru_get_rule(struct device *ndev, isdn_timeout_rule **rule, int i, int j, int k) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ isdn_timeout_rule *head, *this;
+ int l;
+ ulong flags;
+
+ if(!lp->timeout_rules
+ || i < 0 || i > ISDN_TIMRU_NUM_CHECK
+ || j < 0 || j > ISDN_TIMRU_NUM_PROTFAM
+ || k < 0)
+ return(-EINVAL);
+
+ save_flags(flags);
+ cli();
+
+ if(!(this = head = lp->timeout_rules->timru[i][j])) {
+ restore_flags(flags);
+ return(-EINVAL);
+ }
+
+ for(l = 0; l < k; l++) {
+ if(this->next == head) {
+ restore_flags(flags);
+ return(-EINVAL);
+ }
+ this = this->next;
+ }
+
+ *rule = this;
+ restore_flags(flags);
+ return(0);
+}
+
+
+int
+isdn_timru_get_default(int type, struct device *ndev, int *ret) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ ulong flags;
+
+ if(!lp->timeout_rules)
+ return(-EINVAL);
+
+ save_flags(flags);
+ cli();
+
+ *ret = lp->timeout_rules->defaults[type];
+
+ restore_flags(flags);
+ return(0);
+}
+
+
+int
+isdn_timru_ioctl_add_rule(isdn_ioctl_timeout_rule *iorule)
+{
+ isdn_net_dev *p = isdn_net_findif(iorule->name);
+ isdn_timeout_rule *r;
+
+ if(p) {
+ if(iorule->where < 0) { /* set default */
+ return(isdn_timru_set_default(iorule->type, &p->dev, iorule->defval));
+ } else {
+ if(!(r = (isdn_timeout_rule *) kmalloc(sizeof(isdn_timeout_rule), GFP_KERNEL)))
+ return(-ENOMEM);
+ memcpy((char *)r, (char *)&iorule->rule, sizeof(isdn_timeout_rule));
+ return(isdn_timru_add_rule(iorule->where, &p->dev, r));
+ }
+ }
+ return(-ENODEV);
+}
+
+
+int
+isdn_timru_ioctl_del_rule(isdn_ioctl_timeout_rule *iorule)
+{
+ isdn_net_dev *p = isdn_net_findif(iorule->name);
+ isdn_timeout_rule *r;
+
+ if(p) {
+ if(!(r = (isdn_timeout_rule *) kmalloc(sizeof(isdn_timeout_rule), GFP_KERNEL)))
+ return(-ENOMEM);
+ memcpy((char *)r, (char *)&iorule->rule, sizeof(isdn_timeout_rule));
+ return(isdn_timru_del_rule(&p->dev, r));
+ }
+ return(-ENODEV);
+}
+
+
+int
+isdn_timru_ioctl_get_rule(isdn_ioctl_timeout_rule *iorule)
+{
+ int ret, def;
+ isdn_net_dev *p = isdn_net_findif(iorule->name);
+ isdn_timeout_rule *r;
+
+ if(p) {
+ if(iorule->where < 0) { /* get default */
+ if((ret = isdn_timru_get_default(iorule->type, &p->dev, &def)) < 0)
+ return(ret);
+
+ iorule->protfam = p->local->huptimer;
+ iorule->index = p->local->huptimeout;
+ iorule->defval = def;
+ } else {
+ if(isdn_timru_get_rule(&p->dev, &r, iorule->type, iorule->protfam, iorule->index))
+ return(-ENOMEM);
+
+ memcpy((char *)&iorule->rule, (char *)r, sizeof(isdn_timeout_rule));
+ }
+ return(0);
+ }
+ return(-ENODEV);
+}
+
+#endif
-/* $Id: isdn_tty.c,v 1.47 1998/02/22 19:44:14 fritz Exp $
+/* $Id: isdn_tty.c,v 1.63 1999/04/12 12:33:39 fritz Exp $
* Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_tty.c,v $
+ * Revision 1.63 1999/04/12 12:33:39 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.62 1999/03/02 12:04:48 armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ * register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ * which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ * establishment and register S7.
+ *
+ * Revision 1.61 1999/01/27 22:53:11 he
+ * minor updates (spellings, jiffies wrap around in isdn_tty)
+ *
+ * Revision 1.60 1998/11/15 23:57:32 keil
+ * changes for 2.1.127
+ *
+ * Revision 1.59 1998/08/20 13:50:15 keil
+ * More support for hybrid modem (not working yet)
+ *
+ * Revision 1.58 1998/07/26 18:48:45 armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.57 1998/06/26 15:12:36 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.56 1998/06/18 23:31:51 fritz
+ * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
+ * Removed direct-senddown feature in isdn_tty_write because it will
+ * never succeed with locking and is useless anyway.
+ *
+ * Revision 1.55 1998/06/17 19:50:55 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.54 1998/06/07 00:20:13 fritz
+ * abc cleanup.
+ *
+ * Revision 1.53 1998/06/02 12:10:16 detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.52 1998/03/19 13:18:21 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
+ * Revision 1.51 1998/03/08 14:26:11 detabc
+ * change kfree_skb to dev_kfree_skb
+ * remove SET_SKB_FREE
+ *
+ * Revision 1.50 1998/03/08 13:14:28 detabc
+ * abc-extension support for kernels > 2.1.x
+ * first try (sorry experimental)
+ *
+ * Revision 1.49 1998/03/08 00:01:59 fritz
+ * Bugfix: Lowlevel module usage and channel usage were not
+ * reset on NO DCHANNEL.
+ *
+ * Revision 1.48 1998/03/07 12:28:15 tsbogend
+ * fixed kernel unaligned traps on Linux/Alpha
+ *
* Revision 1.47 1998/02/22 19:44:14 fritz
* Bugfixes and improvements regarding V.110, V.110 now running.
*
static int si2bit[8] =
{4, 1, 4, 4, 4, 4, 4, 4};
-char *isdn_tty_revision = "$Revision: 1.47 $";
+char *isdn_tty_revision = "$Revision: 1.63 $";
#define DLE 0x10
#define ETX 0x03
#define REG_LF 4
#define REG_BS 5
+#define REG_WAITC 7
+
#define REG_RESP 12
#define BIT_RESP 1
#define REG_RESPNUM 12
#define REG_CPPP 12
#define BIT_CPPP 128
-#define REG_DELXMT 13
-#define BIT_DELXMT 1
#define REG_T70 13
#define BIT_T70 2
#define BIT_T70_EXT 32
#define BIT_CIDONCE 16
#define REG_RUNG 13
#define BIT_RUNG 64
+#define REG_RESRXT 13
+#define BIT_RESRXT 128
#define REG_L2PROT 14
#define REG_L3PROT 15
r = 0;
#ifdef CONFIG_ISDN_AUDIO
isdn_audio_eval_dtmf(info);
+ if ((info->vonline & 1) && (info->emu.vpar[1]))
+ isdn_audio_eval_silence(info);
#endif
if ((tty = info->tty)) {
if (info->mcr & UART_MCR_RTS) {
if (info->vonline)
isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+ if ((info->vonline & 1) && (info->emu.vpar[1]))
+ isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
#endif
if ((info->online < 2)
#ifdef CONFIG_ISDN_AUDIO
info->isdn_channel, 1, skb)) == len) {
struct tty_struct *tty = info->tty;
info->send_outstanding++;
- info->msr |= UART_MSR_CTS;
- info->lsr |= UART_LSR_TEMT;
+ info->msr &= ~UART_MSR_CTS;
+ info->lsr &= ~UART_LSR_TEMT;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup) (tty);
dev_kfree_skb(skb);
return;
}
- if (slen)
- skb_pull(skb, slen);
skb_queue_head(&info->xmit_queue, skb);
}
int audio_len;
#endif
struct sk_buff *skb;
- unsigned long flags;
#ifdef CONFIG_ISDN_AUDIO
if (info->vonline & 4) {
}
}
#endif
- save_flags(flags);
- cli();
- if (!(buflen = info->xmit_count)) {
- restore_flags(flags);
+ if (!(buflen = info->xmit_count))
return;
- }
- if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
+ if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
info->msr &= ~UART_MSR_CTS;
- info->lsr &= ~UART_LSR_TEMT;
+ info->lsr &= ~UART_LSR_TEMT;
+ /* info->xmit_count is modified here and in isdn_tty_write().
+ * So we return here if isdn_tty_write() is in the
+ * critical section.
+ */
+ atomic_inc(&info->xmit_lock);
+ if (!(atomic_dec_and_test(&info->xmit_lock)))
+ return;
if (info->isdn_driver < 0) {
info->xmit_count = 0;
- restore_flags(flags);
return;
}
skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
skb = dev_alloc_skb(skb_res + buflen);
#endif
if (!skb) {
- restore_flags(flags);
printk(KERN_WARNING
"isdn_tty: Out of memory in ttyI%d senddown\n",
info->line);
skb_reserve(skb, skb_res);
memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
info->xmit_count = 0;
- restore_flags(flags);
#ifdef CONFIG_ISDN_AUDIO
if (info->vonline & 2) {
/* For now, ifmt is fixed to 1 (alaw), since this
break;
}
#ifdef CONFIG_ISDN_AUDIO
- if (si == 1) {
+ if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
l2 = ISDN_PROTO_L2_TRANS;
usg = ISDN_USAGE_VOICE;
}
cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
cmd.command = ISDN_CMD_DIAL;
info->dialing = 1;
+ info->emu.carrierwait = 0;
strcpy(dev->num[i], n);
isdn_info_update();
isdn_command(&cmd);
+ isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
}
}
kfree(info->dtmf_state);
info->dtmf_state = NULL;
}
+ if (info->silence_state) {
+ kfree(info->silence_state);
+ info->silence_state = NULL;
+ }
if (info->adpcms) {
kfree(info->adpcms);
info->adpcms = NULL;
}
isdn_all_eaz(info->isdn_driver, info->isdn_channel);
info->emu.mdmreg[REG_RINGCNT] = 0;
- usage = (info->emu.mdmreg[REG_SI1I] == 1) ?
- ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
+ usage = ((info->emu.mdmreg[REG_SI1I] != 1) ||
+ (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ?
+ ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
isdn_free_channel(info->isdn_driver, info->isdn_channel,
usage);
}
}
}
+/*
+ * Begin of a CAPI like interface, currently used only for
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include "avmb1/capicmd.h" /* this should be moved in a common place */
+
+int
+isdn_tty_capi_facility(capi_msg *cm) {
+ return(-1); /* dummy */
+}
+
+/* isdn_tty_suspend() tries to suspend the current tty connection
+ */
+static void
+isdn_tty_suspend(char *id, modem_info * info, atemu * m)
+{
+ isdn_ctrl cmd;
+
+ int l;
+
+ if (!info)
+ return;
+
+#ifdef ISDN_DEBUG_MODEM_SERVICES
+ printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
+#endif
+ l = strlen(id);
+ if ((info->isdn_driver >= 0) && l) {
+ cmd.parm.cmsg.Length = l+17;
+ cmd.parm.cmsg.Command = CAPI_FACILITY;
+ cmd.parm.cmsg.Subcommand = CAPI_REQ;
+ cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+ cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+ cmd.parm.cmsg.para[1] = 0;
+ cmd.parm.cmsg.para[2] = l + 3;
+ cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
+ cmd.parm.cmsg.para[4] = 0;
+ cmd.parm.cmsg.para[5] = l;
+ strncpy(&cmd.parm.cmsg.para[6], id, l);
+ cmd.command = CAPI_PUT_MESSAGE;
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ isdn_command(&cmd);
+ }
+}
+
+/* isdn_tty_resume() tries to resume a suspended call
+ * setup of the lower levels before that. unfortunatly here is no
+ * checking for compatibility of used protocols implemented by Q931
+ * It does the same things like isdn_tty_dial, the last command
+ * is different, may be we can merge it.
+ */
+
+static void
+isdn_tty_resume(char *id, modem_info * info, atemu * m)
+{
+ int usg = ISDN_USAGE_MODEM;
+ int si = 7;
+ int l2 = m->mdmreg[REG_L2PROT];
+ isdn_ctrl cmd;
+ ulong flags;
+ int i;
+ int j;
+ int l;
+
+ l = strlen(id);
+ if (!l) {
+ isdn_tty_modem_result(4, info);
+ return;
+ }
+ for (j = 7; j >= 0; j--)
+ if (m->mdmreg[REG_SI1] & (1 << j)) {
+ si = bit2si[j];
+ break;
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+ l2 = ISDN_PROTO_L2_TRANS;
+ usg = ISDN_USAGE_VOICE;
+ }
+#endif
+ m->mdmreg[REG_SI1I] = si2bit[si];
+ save_flags(flags);
+ cli();
+ i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+ if (i < 0) {
+ restore_flags(flags);
+ isdn_tty_modem_result(6, info);
+ } else {
+ info->isdn_driver = dev->drvmap[i];
+ info->isdn_channel = dev->chanmap[i];
+ info->drv_index = i;
+ dev->m_idx[i] = info->line;
+ dev->usage[i] |= ISDN_USAGE_OUTGOING;
+ info->last_dir = 1;
+// strcpy(info->last_num, n);
+ isdn_info_update();
+ restore_flags(flags);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ isdn_command(&cmd);
+ strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETEAZ;
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ info->last_l2 = l2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.parm.cmsg.Length = l+17;
+ cmd.parm.cmsg.Command = CAPI_FACILITY;
+ cmd.parm.cmsg.Subcommand = CAPI_REQ;
+ cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+ cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+ cmd.parm.cmsg.para[1] = 0;
+ cmd.parm.cmsg.para[2] = l+3;
+ cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
+ cmd.parm.cmsg.para[4] = 0;
+ cmd.parm.cmsg.para[5] = l;
+ strncpy(&cmd.parm.cmsg.para[6], id, l);
+ cmd.command =CAPI_PUT_MESSAGE;
+/* info->dialing = 1;
+ strcpy(dev->num[i], n);
+ isdn_info_update();
+*/
+ isdn_command(&cmd);
+ }
+}
+
+/* isdn_tty_send_msg() sends a message to a HL driver
+ * This is used for hybrid modem cards to send AT commands to it
+ */
+
+static void
+isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
+{
+ int usg = ISDN_USAGE_MODEM;
+ int si = 7;
+ int l2 = m->mdmreg[REG_L2PROT];
+ isdn_ctrl cmd;
+ ulong flags;
+ int i;
+ int j;
+ int l;
+
+ l = strlen(msg);
+ if (!l) {
+ isdn_tty_modem_result(4, info);
+ return;
+ }
+ for (j = 7; j >= 0; j--)
+ if (m->mdmreg[REG_SI1] & (1 << j)) {
+ si = bit2si[j];
+ break;
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ if ((si == 1) && (l2 != ISDN_PROTO_L2_MODEM)) {
+ l2 = ISDN_PROTO_L2_TRANS;
+ usg = ISDN_USAGE_VOICE;
+ }
+#endif
+ m->mdmreg[REG_SI1I] = si2bit[si];
+ save_flags(flags);
+ cli();
+ i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1);
+ if (i < 0) {
+ restore_flags(flags);
+ isdn_tty_modem_result(6, info);
+ } else {
+ info->isdn_driver = dev->drvmap[i];
+ info->isdn_channel = dev->chanmap[i];
+ info->drv_index = i;
+ dev->m_idx[i] = info->line;
+ dev->usage[i] |= ISDN_USAGE_OUTGOING;
+ info->last_dir = 1;
+ isdn_info_update();
+ restore_flags(flags);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ isdn_command(&cmd);
+ strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETEAZ;
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ info->last_l2 = l2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.parm.cmsg.Length = l+14;
+ cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
+ cmd.parm.cmsg.Subcommand = CAPI_REQ;
+ cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+ cmd.parm.cmsg.para[0] = l+1;
+ strncpy(&cmd.parm.cmsg.para[1], msg, l);
+ cmd.parm.cmsg.para[l+1] = 0xd;
+ cmd.command =CAPI_PUT_MESSAGE;
+/* info->dialing = 1;
+ strcpy(dev->num[i], n);
+ isdn_info_update();
+*/
+ isdn_command(&cmd);
+ }
+}
+
static inline int
isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
{
{
int c;
int total = 0;
- ulong flags;
modem_info *info = (modem_info *) tty->driver_data;
if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
return 0;
if (!tty)
return 0;
- save_flags(flags);
- cli();
+ if (from_user)
+ down(&info->write_sem);
+ /* See isdn_tty_senddown() */
+ atomic_inc(&info->xmit_lock);
while (1) {
c = MIN(count, info->xmit_size - info->xmit_count);
if (info->isdn_driver >= 0)
} else
#endif
info->xmit_count += c;
- if (m->mdmreg[REG_DELXMT] & BIT_DELXMT) {
- isdn_tty_senddown(info);
- isdn_tty_tint(info);
- }
} else {
info->msr |= UART_MSR_CTS;
info->lsr |= UART_LSR_TEMT;
}
if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
- restore_flags(flags);
+ atomic_dec(&info->xmit_lock);
+ if (from_user)
+ up(&info->write_sem);
return total;
}
status = info->lsr;
restore_flags(flags);
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- put_user(result, (ulong *) value);
+ put_user(result, (uint *) value);
return 0;
}
| ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
| ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
| ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
- put_user(result, (ulong *) value);
+ put_user(result, (uint *) value);
return 0;
}
static int
isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
{
- DECLARE_WAITQUEUE(wait, current);
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue wait =
+ {current, NULL};
+#else
+ DECLARE_WAITQUEUE(wait, NULL);
+#endif
int do_clocal = 0;
unsigned long flags;
int retval;
m->profile[19] = 0;
m->profile[20] = 0;
m->pmsn[0] = '\0';
+ m->plmsn[0] = '\0';
}
#ifdef CONFIG_ISDN_AUDIO
if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+ memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
}
#ifdef CONFIG_ISDN_AUDIO
{
memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+ memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
if (dev->profd)
send_sig(SIGIO, dev->profd, 1);
}
}
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
info = &m->info[i];
+#if LINUX_VERSION_CODE < 131841
+ info->write_sem = MUTEX;
+#else
+ init_MUTEX(&info->write_sem);
+#endif
sprintf(info->last_cause, "0000");
sprintf(info->last_num, "none");
info->last_dir = 0;
info->blocked_open = 0;
info->callout_termios = m->cua_modem.init_termios;
info->normal_termios = m->tty_modem.init_termios;
+#if LINUX_VERSION_CODE < 131841
+ info->open_wait = 0;
+ info->close_wait = 0;
+#else
init_waitqueue_head(&info->open_wait);
init_waitqueue_head(&info->close_wait);
+#endif
info->isdn_driver = -1;
info->isdn_channel = -1;
info->drv_index = -1;
return 0;
}
+static int
+isdn_tty_match_icall(char *cid, atemu *emu, int di)
+{
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
+ emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
+ emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
+#endif
+ if (strlen(emu->lmsn)) {
+ char *p = emu->lmsn;
+ char *q;
+ int tmp;
+ int ret = 0;
+
+ while (1) {
+ if ((q = strchr(p, ';')))
+ *q = '\0';
+ if ((tmp = isdn_wildmat(cid, isdn_map_eaz2msn(p, di))) > ret)
+ ret = tmp;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
+ p, isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+ if (q) {
+ *q = ';';
+ p = q;
+ p++;
+ }
+ if (!tmp)
+ return 0;
+ if (!q)
+ break;
+ }
+ return ret;
+ } else
+ return isdn_wildmat(cid, isdn_map_eaz2msn(emu->msn, di));
+}
+
/*
* An incoming call-request has arrived.
* Search the tty-devices for an appropriate device and bind
* it to the ISDN-Channel.
- * Return Index to dev->mdm or -1 if none found.
+ * Return:
+ *
+ * 0 = No matching device found.
+ * 1 = A matching device found.
+ * 3 = No match found, but eventually would match, if
+ * CID is longer.
*/
int
isdn_tty_find_icall(int di, int ch, setup_parm setup)
{
char *eaz;
int i;
+ int wret;
int idx;
int si1;
int si2;
char nr[32];
ulong flags;
- save_flags(flags);
- cli();
if (!setup.phone[0]) {
nr[0] = '0';
nr[1] = '\0';
#ifdef ISDN_DEBUG_MODEM_ICALL
printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
#endif
+ wret = 0;
+ save_flags(flags);
+ cli();
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
modem_info *info = &dev->mdm.info[i];
-#ifdef ISDN_DEBUG_MODEM_ICALL
- printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
- info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
- info->emu.mdmreg[REG_SI1], info->emu.mdmreg[REG_SI2]);
-#endif
- if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
- eaz)) && /* EAZ is matching */
- (info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */
- (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */
+
+ if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */
+ (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */
idx = isdn_dc2minor(di, ch);
#ifdef ISDN_DEBUG_MODEM_ICALL
printk(KERN_DEBUG "m_fi: match1\n");
#ifndef FIX_FILE_TRANSFER
(info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
#endif
- (info->isdn_driver == -1) &&
- (info->isdn_channel == -1) &&
- (USG_NONE(dev->usage[idx]))) {
- info->isdn_driver = di;
- info->isdn_channel = ch;
- info->drv_index = idx;
- dev->m_idx[idx] = info->line;
- dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
- dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
- strcpy(dev->num[idx], nr);
- info->emu.mdmreg[REG_SI1I] = si2bit[si1];
- info->emu.mdmreg[REG_PLAN] = setup.plan;
- info->emu.mdmreg[REG_SCREEN] = setup.screen;
- isdn_info_update();
- restore_flags(flags);
- printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
- info->line);
- info->msr |= UART_MSR_RI;
- isdn_tty_modem_result(2, info);
- isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
- return info->line;
+ (info->isdn_driver == -1) &&
+ (info->isdn_channel == -1) &&
+ (USG_NONE(dev->usage[idx]))) {
+ int matchret;
+
+ if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
+ wret = matchret;
+ if (!matchret) { /* EAZ is matching */
+ info->isdn_driver = di;
+ info->isdn_channel = ch;
+ info->drv_index = idx;
+ dev->m_idx[idx] = info->line;
+ dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[idx] |= ((si1 != 1) || (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) ?
+ ISDN_USAGE_MODEM : ISDN_USAGE_VOICE;
+ strcpy(dev->num[idx], nr);
+ strcpy(info->emu.cpn, eaz);
+ info->emu.mdmreg[REG_SI1I] = si2bit[si1];
+ info->emu.mdmreg[REG_PLAN] = setup.plan;
+ info->emu.mdmreg[REG_SCREEN] = setup.screen;
+ isdn_info_update();
+ restore_flags(flags);
+ printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+ info->line);
+ info->msr |= UART_MSR_RI;
+ isdn_tty_modem_result(2, info);
+ isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+ return 1;
+ }
}
}
}
- printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
- dev->drv[di]->reject_bus ? "rejected" : "ignored");
restore_flags(flags);
- return -1;
+ printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+ ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored");
+ return (wret == 2)?3:0;
}
#define TTY_IS_ACTIVE(info) \
case ISDN_STAT_CINF:
printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
- if (e == c->parm.num)
+ if (e == (char *)c->parm.num)
info->emu.charge = 0;
break;
printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
#endif
if (TTY_IS_ACTIVE(info)) {
- if (info->dialing == 1) {
- info->dialing = 0;
+ if (info->dialing == 1)
isdn_tty_modem_result(7, info);
- }
+ if (info->dialing > 1)
+ isdn_tty_modem_result(3, info);
+ info->dialing = 0;
#ifdef ISDN_DEBUG_MODEM_HUP
printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
#endif
if (TTY_IS_ACTIVE(info)) {
info->msr |= UART_MSR_DCD;
info->emu.charge = 0;
- if (info->dialing) {
- info->dialing = 0;
+ if (info->dialing & 0xf)
info->last_dir = 1;
- } else
+ else
info->last_dir = 0;
+ info->dialing = 0;
info->rcvsched = 1;
- if (USG_MODEM(dev->usage[i]))
- isdn_tty_modem_result(5, info);
+ if (USG_MODEM(dev->usage[i])) {
+ if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
+ strcpy(info->emu.connmsg, c->parm.num);
+ isdn_tty_modem_result(1, info);
+ }
+ else
+ isdn_tty_modem_result(5, info);
+ }
if (USG_VOICE(dev->usage[i]))
isdn_tty_modem_result(11, info);
return 1;
sprintf(info->last_cause, "0000");
isdn_tty_modem_result(6, info);
}
- info->msr &= ~UART_MSR_DCD;
- if (info->online) {
- isdn_tty_modem_result(3, info);
- info->online = 0;
- }
+ isdn_tty_modem_hup(info, 0);
return 1;
}
break;
"CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
"RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
ulong flags;
- char s[10];
+ char s[ISDN_MSNLEN+10];
switch (code) {
case 2:
}
isdn_tty_at_cout(msg[code], info);
switch (code) {
+ case 1:
+ switch (m->mdmreg[REG_L2PROT]) {
+ case ISDN_PROTO_L2_MODEM:
+ isdn_tty_at_cout(" ", info);
+ isdn_tty_at_cout(m->connmsg, info);
+ break;
+ }
+ break;
case 2:
+ /* Append CPN, if enabled */
+ if ((m->mdmreg[REG_RESRXT] & BIT_RESRXT)) {
+ sprintf(s, "/%s", m->cpn);
+ isdn_tty_at_cout(s, info);
+ }
/* Print CID only once, _after_ 1.st RING */
if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
(m->mdmreg[REG_RINGCNT] == 1)) {
static void
isdn_tty_get_msnstr(char *n, char **p)
{
- while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
+ while ((*p[0] >= '0' && *p[0] <= '9') ||
+ /* Why a comma ??? */
+ (*p[0] == ','))
*n++ = *p[0]++;
*n = '\0';
}
case ISDN_PROTO_L2_TRANS:
isdn_tty_at_cout("transparent", info);
break;
+ case ISDN_PROTO_L2_MODEM:
+ isdn_tty_at_cout("modem", info);
+ break;
default:
isdn_tty_at_cout("unknown", info);
break;
int i;
char rb[100];
+#define MAXRB (sizeof(rb) - 1)
+
switch (*p[0]) {
case 'B':
/* &B - Set Buffersize */
isdn_tty_reset_profile(m);
isdn_tty_modem_reset_regs(info, 1);
break;
+ case 'L':
+ /* &L -Set Numbers to listen on */
+ p[0]++;
+ i = 0;
+ while ((strchr("0123456789,*[]?;", *p[0])) &&
+ (i < ISDN_LMSNLEN))
+ m->lmsn[i++] = *p[0]++;
+ m->lmsn[i] = '\0';
+ break;
case 'R':
/* &R - Set V.110 bitrate adaption */
p[0]++;
sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
strlen(m->msn) ? m->msn : "None");
isdn_tty_at_cout(rb, info);
+ if (strlen(m->lmsn)) {
+ isdn_tty_at_cout("\r\nListen: ", info);
+ isdn_tty_at_cout(m->lmsn, info);
+ isdn_tty_at_cout("\r\n", info);
+ }
break;
case 'W':
/* &W - Write Profile */
#ifdef CONFIG_ISDN_AUDIO
/* If more than one bit set in reg18, autoselect Layer2 */
if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
- if (m->mdmreg[REG_SI1I] == 1)
- l2 = ISDN_PROTO_L2_TRANS;
- else
+ if (m->mdmreg[REG_SI1I] == 1) {
+ if (l2 != ISDN_PROTO_L2_MODEM)
+ l2 = ISDN_PROTO_L2_TRANS;
+ } else
l2 = ISDN_PROTO_L2_X75I;
}
#endif
cmd.driver = info->isdn_driver;
cmd.arg = info->isdn_channel;
cmd.command = ISDN_CMD_ACCEPTD;
+ info->dialing = 16;
+ info->emu.carrierwait = 0;
isdn_command(&cmd);
+ isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
} else
isdn_tty_modem_result(8, info);
}
printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
PARSE_ERROR1;
}
+ info->silence_state = isdn_audio_silence_init(info->silence_state);
+ if (!info->silence_state) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
+ PARSE_ERROR1;
+ }
if (m->vpar[3] < 5) {
info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
if (!info->adpcmr) {
break;
case '=':
p[0]++;
- switch (*p[0]) {
- case '0':
- case '1':
- case '2':
- case '3':
- par1 = isdn_getnum(p);
- if ((par1 < 0) || (par1 > 31))
- PARSE_ERROR1;
- if (*p[0] != ',')
- PARSE_ERROR1;
- p[0]++;
- par2 = isdn_getnum(p);
- if ((par2 < 0) || (par2 > 255))
- PARSE_ERROR1;
- m->vpar[1] = par1;
- m->vpar[2] = par2;
- break;
- case '?':
- p[0]++;
- isdn_tty_at_cout("\r\n<0-31>,<0-255>",
- info);
- break;
- default:
+ if ((*p[0]>='0') && (*p[0]<='9')) {
+ par1 = isdn_getnum(p);
+ if ((par1 < 0) || (par1 > 31))
PARSE_ERROR1;
- }
+ if (*p[0] != ',')
+ PARSE_ERROR1;
+ p[0]++;
+ par2 = isdn_getnum(p);
+ if ((par2 < 0) || (par2 > 255))
+ PARSE_ERROR1;
+ m->vpar[1] = par1;
+ m->vpar[2] = par2;
+ break;
+ } else
+ if (*p[0] == '?') {
+ p[0]++;
+ isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+ info);
+ break;
+ } else
+ PARSE_ERROR1;
break;
default:
PARSE_ERROR1;
p++;
if (info->msr & UART_MSR_DCD)
/* if B-Channel is up */
- isdn_tty_modem_result(5, info);
+ isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? 1:5, info);
else
isdn_tty_modem_result(3, info);
return;
case 'Z':
/* Z - Load Registers from Profile */
p++;
+ if (info->msr & UART_MSR_DCD) {
+ info->online = 0;
+ isdn_tty_on_hook(info);
+ }
isdn_tty_modem_reset_regs(info, 1);
break;
-#ifdef CONFIG_ISDN_AUDIO
case '+':
p++;
switch (*p) {
+#ifdef CONFIG_ISDN_AUDIO
case 'F':
p++;
if (isdn_tty_cmd_PLUSF(&p, info))
return;
break;
case 'V':
- if (!(m->mdmreg[REG_SI1] & 1))
+ if ((!(m->mdmreg[REG_SI1] & 1)) ||
+ (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
PARSE_ERROR;
p++;
if (isdn_tty_cmd_PLUSV(&p, info))
return;
break;
+#endif /* CONFIG_ISDN_AUDIO */
+ case 'S': /* SUSPEND */
+ p++;
+ isdn_tty_get_msnstr(ds, &p);
+ isdn_tty_suspend(ds, info, m);
+ break;
+ case 'R': /* RESUME */
+ isdn_tty_get_msnstr(ds, &p);
+ isdn_tty_resume(ds, info, m);
+ case 'M': /* MESSAGE */
+ p++;
+ isdn_tty_send_msg(info, m, p);
+ break;
default:
PARSE_ERROR;
}
break;
-#endif /* CONFIG_ISDN_AUDIO */
case '&':
p++;
if (isdn_tty_cmd_ATand(&p, info))
}
isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
}
+
+/*
+ * Check all channels if we have a 'no carrier' timeout.
+ * Timeout value is set by Register S7.
+ */
+void
+isdn_tty_carrier_timeout(void)
+{
+ int ton = 0;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+ if (info->dialing) {
+ if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
+ info->dialing = 0;
+ isdn_tty_modem_result(3, info);
+ isdn_tty_modem_hup(info, 1);
+ }
+ else
+ ton = 1;
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
+}
-/* $Id: isdn_tty.h,v 1.10 1997/03/02 14:29:26 fritz Exp $
+/* $Id: isdn_tty.h,v 1.13 1999/04/12 12:33:46 fritz Exp $
* header for Linux ISDN subsystem, tty related functions (linklevel).
*
- * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
* Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdn_tty.h,v $
+ * Revision 1.13 1999/04/12 12:33:46 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.12 1999/03/02 12:04:51 armin
+ * -added ISDN_STAT_ADDCH to increase supported channels after
+ * register_isdn().
+ * -ttyI now goes on-hook on ATZ when B-Ch is connected.
+ * -added timer-function for register S7 (Wait for Carrier).
+ * -analog modem (ISDN_PROTO_L2_MODEM) implementations.
+ * -on L2_MODEM a string will be appended to the CONNECT-Message,
+ * which is provided by the HL-Driver in parm.num in ISDN_STAT_BCONN.
+ * -variable "dialing" used for ATA also, for interrupting call
+ * establishment and register S7.
+ *
+ * Revision 1.11 1998/03/19 13:18:27 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
* Revision 1.10 1997/03/02 14:29:26 fritz
* More ttyI related cleanup.
*
extern void isdn_tty_modem_escape(void);
extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_carrier_timeout(void);
extern void isdn_tty_modem_xmit(void);
extern int isdn_tty_modem_init(void);
extern void isdn_tty_readmodem(void);
extern void isdn_tty_cleanup_xmit(modem_info *);
extern int isdn_tty_stat_callback(int, isdn_ctrl *);
extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
+extern int isdn_tty_capi_facility(capi_msg *cm);
-/* $Id: isdn_x25iface.c,v 1.3 1998/02/20 17:25:20 fritz Exp $
+/* $Id: isdn_x25iface.c,v 1.6 1999/01/27 22:53:19 he Exp $
* stuff needed to support the Linux X.25 PLP code on top of devices that
* can provide a lab_b service using the concap_proto mechanism.
* This module supports a network interface wich provides lapb_sematics
* goes to another -- device related -- concap_proto support source file.
*
* $Log: isdn_x25iface.c,v $
+ * Revision 1.6 1999/01/27 22:53:19 he
+ * minor updates (spellings, jiffies wrap around in isdn_tty)
+ *
+ * Revision 1.5 1998/10/30 17:55:39 he
+ * dialmode for x25iface and multulink ppp
+ *
+ * Revision 1.4 1998/06/17 19:51:00 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
* Revision 1.3 1998/02/20 17:25:20 fritz
* Changes for recent kernels.
*
case 0x01: /* dl_connect request */
if( *state == WAN_DISCONNECTED ){
*state = WAN_CONNECTING;
- cprot -> dops -> connect_req(cprot);
+ ret = cprot -> dops -> connect_req(cprot);
+ if(ret){
+ /* reset state and notify upper layer about
+ * immidiatly failed attempts */
+ isdn_x25iface_disconn_ind(cprot);
+ }
} else {
illegal_state_warn( *state, firstbyte );
}
-/* $Id: isdnloop.c,v 1.4 1998/02/24 21:39:05 he Exp $
+/* $Id: isdnloop.c,v 1.8 1998/11/18 18:59:43 armin Exp $
* ISDN low-level module implementing a dummy loop driver.
*
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdnloop.c,v $
+ * Revision 1.8 1998/11/18 18:59:43 armin
+ * changes for 2.1.127
+ *
+ * Revision 1.7 1998/10/30 18:58:03 he
+ * typecast to suppress a compiler warning
+ *
+ * Revision 1.6 1998/06/17 19:51:37 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.5 1998/04/14 20:59:32 he
+ * merged 2.1.94 changes
+ *
* Revision 1.4 1998/02/24 21:39:05 he
* L2_PROT_X25DTE / DCE
* additional state 17 and new internal signal messages "BCON_I"
#include "isdnloop.h"
static char
-*revision = "$Revision: 1.4 $";
+*revision = "$Revision: 1.8 $";
static int isdnloop_addcard(char *);
c->parm.num[0] ? "N" : "ALL", c->parm.num);
} else
sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
- c->parm.num[0] ? c->parm.num : "0123456789");
+ c->parm.num[0] ? c->parm.num : (u_char *) "0123456789");
i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
}
break;
-/* $Id: isdnloop.h,v 1.2 1997/10/01 09:22:07 fritz Exp $
+/* $Id: isdnloop.h,v 1.3 1998/04/14 20:59:35 he Exp $
* Loopback lowlevel module for testing of linklevel.
*
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdnloop.h,v $
+ * Revision 1.3 1998/04/14 20:59:35 he
+ * merged 2.1.94 changes
+ *
* Revision 1.2 1997/10/01 09:22:07 fritz
* Removed old compatibility stuff for 2.0.X kernels.
* From now on, this code is for 2.1.X ONLY!
* callbacks for the FSM
*/
+/*
+ * Fix: 19981230 - Carlos Morgado <chbm@techie.com>
+ * Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN
+ * NULL pointer dereference in cb_in_1 (originally fixed in 2.0)
+ */
+
+
#define __NO_VERSION__
#include <linux/module.h>
* ictl.num >= strlen() + strlen() + 5
*/
+ if (cbdata->data.setup.CallingPN == NULL) {
+ printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n");
+ strcpy(ictl.parm.setup.phone, "0");
+ }
+ else {
strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+ }
+ if (cbdata->data.setup.CalledPN == NULL) {
+ printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n");
+ strcpy(ictl.parm.setup.eazmsn, "0");
+ }
+ else {
strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
- ictl.parm.setup.si1 = 7;
- ictl.parm.setup.si2 = 0;
- ictl.parm.setup.plan = 0;
- ictl.parm.setup.screen = 0;
+ }
+ ictl.parm.setup.si1 = 7;
+ ictl.parm.setup.si2 = 0;
+ ictl.parm.setup.plan = 0;
+ ictl.parm.setup.screen = 0;
#ifdef DEBUG
printk(KERN_DEBUG "statstr: %s\n", ictl.num);
dev_pcbit[board] = dev;
memset(dev, 0, sizeof(struct pcbit_dev));
+#if LINUX_VERSION_CODE >= 131841
+ init_waitqueue_head(&dev->set_running_wq);
+#endif
if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF )
dev->sh_mem = (unsigned char*) mem_base;
struct frame_buf *write_queue;
/* Protocol start */
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue *set_running_wq;
+#else
wait_queue_head_t set_running_wq;
+#endif
struct timer_list set_running_timer;
struct timer_list error_recover_timer;
/*
- * $Id: message.c,v 1.3 1998/01/31 22:10:55 keil Exp $
+ * $Id: message.c,v 1.4 1999/01/05 18:29:47 he Exp $
* Copyright (C) 1996 SpellCaster Telecommunications Inc.
*
* message.c - functions for sending and receiving control messages
static int io[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
static int io_hi[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
static int irq[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_IRQ_PROBEONLY };
-static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_NONE };
+static int dma[PARPORT_MAX] __initdata = { [0 ... PARPORT_MAX-1] = PARPORT_DMA_AUTO };
extern int parport_pc_init(int *io, int *io_hi, int *irq, int *dma);
extern int parport_ax_init(void);
EXPORT_SYMBOL(parport_claim_or_block);
EXPORT_SYMBOL(parport_release);
EXPORT_SYMBOL(parport_register_port);
+EXPORT_SYMBOL(parport_announce_port);
EXPORT_SYMBOL(parport_unregister_port);
EXPORT_SYMBOL(parport_quiesce);
+EXPORT_SYMBOL(parport_register_driver);
+EXPORT_SYMBOL(parport_unregister_driver);
EXPORT_SYMBOL(parport_register_device);
EXPORT_SYMBOL(parport_unregister_device);
EXPORT_SYMBOL(parport_enumerate);
EXPORT_SYMBOL(parport_proc_unregister);
EXPORT_SYMBOL(parport_probe_hook);
EXPORT_SYMBOL(parport_parse_irqs);
+EXPORT_SYMBOL(parport_parse_dmas);
void inc_parport_count(void)
{
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
+#include <linux/pci.h>
#include <asm/io.h>
if (parport_probe_hook)
(*parport_probe_hook)(p);
+ /* Now that we've told the sharing engine about the port, and
+ found out its characteristics, let the high-level drivers
+ know about it. */
+ parport_announce_port (p);
+
return 1;
}
+/* Look for PCI parallel port cards. */
+static int __init parport_pc_init_pci (int irq, int dma)
+{
+ int count = 0;
+#ifdef CONFIG_PCI
+ int i;
+ struct {
+ unsigned int vendor;
+ unsigned int device;
+ unsigned int numports;
+ struct {
+ unsigned int lo;
+ unsigned int hi; /* -ve if not there */
+ } addr[4];
+ } cards[] = {
+ { 0, }
+ };
+
+ if (!pci_present ())
+ return 0;
+
+ for (i = 0; cards[i].vendor; i++) {
+ struct pci_dev *pcidev = NULL;
+ while ((pcidev = pci_find_device (cards[i].vendor,
+ cards[i].device,
+ pcidev)) != NULL) {
+ int n;
+ for (n = 0; n < cards[i].numports; n++) {
+ int lo = cards[i].addr[n].lo;
+ int hi = cards[i].addr[n].hi;
+ int io_lo = pcidev->base_address[lo];
+ int io_hi = ((hi < 0) ? 0 :
+ pcidev->base_address[hi]);
+ io_lo &= PCI_BASE_ADDRESS_IO_MASK;
+ io_hi &= PCI_BASE_ADDRESS_IO_MASK;
+ count += probe_one_port (io_lo, io_hi,
+ irq, dma);
+ }
+ }
+ }
+#endif /* CONFIG_PCI */
+
+ return count;
+}
+
int __init parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
{
int count = 0, i = 0;
count += probe_one_port(0x3bc, 0x7bc, irq[0], dma[0]);
count += probe_one_port(0x378, 0x778, irq[0], dma[0]);
count += probe_one_port(0x278, 0x678, irq[0], dma[0]);
+ count += parport_pc_init_pci (irq[0], dma[0]);
}
return count;
#ifdef MODULE
static int io[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
static int io_hi[PARPORT_PC_MAX_PORTS+1] = { [0 ... PARPORT_PC_MAX_PORTS] = 0 };
-static int dma[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_NONE };
+static int dmaval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_DMA_AUTO };
static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
+static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
+
+MODULE_PARM_DESC(io, "base address");
MODULE_PARM(io, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(io_hi, "base address for ECR");
MODULE_PARM(io_hi, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(irq, "irq line to use (or 'auto' or 'none')");
MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "i");
+
+MODULE_PARM_DESC(dma, "dma channel to use (or 'auto' or 'none')");
+MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
int init_module(void)
{
the irq values. */
unsigned int i;
for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
- parport_parse_irqs(i, irq, irqval);
+ if (i) {
+ if (parport_parse_irqs(i, irq, irqval)) return 1;
+ if (parport_parse_dmas(i, dma, dmaval)) return 1;
+ }
+ else {
+ /* The user can make us use any IRQs or DMAs we find. */
+ int val;
+
+ if (irq[0] && !parport_parse_irqs (1, irq, &val))
+ switch (val) {
+ case PARPORT_IRQ_NONE:
+ case PARPORT_IRQ_AUTO:
+ irqval[0] = val;
+ }
+
+ if (dma[0] && !parport_parse_dmas (1, dma, &val))
+ switch (val) {
+ case PARPORT_DMA_NONE:
+ case PARPORT_DMA_AUTO:
+ dmaval[0] = val;
+ }
+ }
- return (parport_pc_init(io, io_hi, irqval, dma)?0:1);
+ return (parport_pc_init(io, io_hi, irqval, dmaval)?0:1);
}
void cleanup_module(void)
static struct parport *portlist = NULL, *portlist_tail = NULL;
spinlock_t parportlist_lock = SPIN_LOCK_UNLOCKED;
+static struct parport_driver *driver_chain = NULL;
+spinlock_t driverlist_lock = SPIN_LOCK_UNLOCKED;
+
+static void call_driver_chain (int attach, struct parport *port)
+{
+ struct parport_driver *drv;
+
+ for (drv = driver_chain; drv; drv = drv->next) {
+ if (attach)
+ drv->attach (port);
+ else
+ drv->detach (port);
+ }
+}
+
+int parport_register_driver (struct parport_driver *drv)
+{
+ struct parport *port;
+
+ spin_lock (&driverlist_lock);
+ drv->next = driver_chain;
+ driver_chain = drv;
+ spin_unlock (&driverlist_lock);
+
+ for (port = portlist; port; port = port->next)
+ drv->attach (port);
+
+ return 0;
+}
+
+void parport_unregister_driver (struct parport_driver *arg)
+{
+ struct parport_driver *drv = driver_chain, *olddrv = NULL;
+
+ while (drv) {
+ if (drv == arg) {
+ spin_lock (&driverlist_lock);
+ if (olddrv)
+ olddrv->next = drv->next;
+ else
+ driver_chain = drv->next;
+ spin_unlock (&driverlist_lock);
+ return;
+ }
+ olddrv = drv;
+ drv = drv->next;
+ }
+}
+
void (*parport_probe_hook)(struct parport *port) = NULL;
/* Return a list of all the ports we know about. */
return tmp;
}
+void parport_announce_port (struct parport *port)
+{
+ /* Let drivers know that a new port has arrived. */
+ call_driver_chain (1, port);
+}
+
void parport_unregister_port(struct parport *port)
{
struct parport *p;
+ /* Spread the word. */
+ call_driver_chain (0, port);
+
spin_lock(&parportlist_lock);
if (portlist == port) {
if ((portlist = port->next) == NULL)
}
}
-void parport_parse_irqs(int nports, const char *irqstr[], int irqval[])
+static int parport_parse_params (int nports, const char *str[], int val[],
+ int automatic, int none)
{
unsigned int i;
- for (i = 0; i < nports && irqstr[i]; i++) {
- if (!strncmp(irqstr[i], "auto", 4))
- irqval[i] = PARPORT_IRQ_AUTO;
- else if (!strncmp(irqstr[i], "none", 4))
- irqval[i] = PARPORT_IRQ_NONE;
+ for (i = 0; i < nports && str[i]; i++) {
+ if (!strncmp(str[i], "auto", 4))
+ val[i] = automatic;
+ else if (!strncmp(str[i], "none", 4))
+ val[i] = none;
else {
char *ep;
- unsigned long r = simple_strtoul(irqstr[i], &ep, 0);
- if (ep != irqstr[i])
- irqval[i] = r;
+ unsigned long r = simple_strtoul(str[i], &ep, 0);
+ if (ep != str[i])
+ val[i] = r;
else {
- printk("parport: bad irq specifier `%s'\n", irqstr[i]);
- return;
+ printk("parport: bad specifier `%s'\n", str[i]);
+ return -1;
}
}
}
+
+ return 0;
+}
+
+int parport_parse_irqs(int nports, const char *irqstr[], int irqval[])
+{
+ return parport_parse_params (nports, irqstr, irqval, PARPORT_IRQ_AUTO,
+ PARPORT_IRQ_NONE);
+}
+
+int parport_parse_dmas(int nports, const char *dmastr[], int dmaval[])
+{
+ return parport_parse_params (nports, dmastr, dmaval, PARPORT_DMA_AUTO,
+ PARPORT_DMA_NONE);
}
};
struct device *dev_base = &loopback_dev;
+rwlock_t dev_base_lock = RW_LOCK_UNLOCKED;
for (c=0; c< (arcnet_num_devs-1); c++)
arcnet_devs[c].next=&arcnet_devs[c+1];
+ write_lock_bh(&dev_base_lock);
arcnet_devs[c].next=dev_base;
dev_base=&arcnet_devs[0];
+ write_unlock_bh(&dev_base_lock);
/* Give names to those without them */
for (;;)
{
sprintf(device, "arc%d", arcnum);
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev; dev=dev->next)
if (dev->name != device && !strcmp(dev->name, device))
break;
+ read_unlock_bh(&dev_base_lock);
if (!dev)
return;
arcnum++;
});
#endif
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (dev_is_ethdev(dev))
+ if (dev_is_ethdev(dev)) {
+ read_unlock_bh(&dev_base_lock);
bpq_new_device(dev);
+ read_lock_bh(&dev_base_lock);
+ }
}
-
+ read_unlock_bh(&dev_base_lock);
+out:
return 0;
}
printk(KERN_INFO "LAPB Ethernet driver version 0.01\n");
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (dev_is_ethdev(dev))
+ if (dev_is_ethdev(dev)) {
+ read_unlock_bh(&dev_base_lock);
lapbeth_new_device(dev);
+ read_lock_bh(&dev_base_lock);
+ }
}
+ read_unlock_bh(&dev_base_lock);
return 0;
}
for (i = 0; i < MAX_ETH_CARDS; ++i)
if (ethdev_index[i] == NULL) {
sprintf(pname, "eth%d", i);
- for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) {
if (strcmp(pname, cur_dev->name) == 0) {
dev = cur_dev;
+ read_unlock_bh(&dev_base_lock);
dev->init = NULL;
sizeof_priv = (sizeof_priv + 3) & ~3;
dev->priv = sizeof_priv
if (dev->priv) memset(dev->priv, 0, sizeof_priv);
goto found;
}
+ }
+ read_unlock_bh(&dev_base_lock);
}
alloc_size &= ~3; /* Round to dword boundary. */
for (i = 0; i < MAX_HIP_CARDS; ++i)
if (hipdev_index[i] == NULL) {
sprintf(pname, "hip%d", i);
- for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) {
if (strcmp(pname, cur_dev->name) == 0) {
dev = cur_dev;
+ read_unlock_bh(&dev_base_lock);
dev->init = NULL;
sizeof_priv = (sizeof_priv + 3) & ~3;
dev->priv = sizeof_priv
if (dev->priv) memset(dev->priv, 0, sizeof_priv);
goto hipfound;
}
+ }
+ read_unlock_bh(&dev_base_lock);
}
alloc_size &= ~3; /* Round to dword boundary. */
for (i = 0; i < MAX_TR_CARDS; ++i)
if (trdev_index[i] == NULL) {
sprintf(pname, "tr%d", i);
- for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next) {
if (strcmp(pname, cur_dev->name) == 0) {
dev = cur_dev;
+ read_unlock_bh(&dev_base_lock);
dev->init = NULL;
sizeof_priv = (sizeof_priv + 3) & ~3;
dev->priv = sizeof_priv
if (dev->priv) memset(dev->priv, 0, sizeof_priv);
goto trfound;
}
+ }
+ read_unlock_bh(&dev_base_lock);
}
alloc_size &= ~3; /* Round to dword boundary. */
!memcmp(strip_info->dev.dev_addr, zero_address.c, sizeof(zero_address)) &&
memcmp(&strip_info->true_dev_addr, zero_address.c, sizeof(zero_address)))
{
- struct device *dev = dev_base;
+ struct device *dev;
+ read_lock_bh(&dev_base_lock);
+ dev = dev_base;
while (dev)
{
if (dev->type == strip_info->dev.type &&
{
printk(KERN_INFO "%s: Transferred packet ownership to %s.\n",
strip_info->dev.name, dev->name);
+ read_unlock_bh(&dev_base_lock);
return(dev);
}
dev = dev->next;
}
+ read_unlock_bh(&dev_base_lock);
}
return(&strip_info->dev);
}
The driver is currently maintained by Kai M{kisara (email
Kai.Makisara@metla.fi)
-Last modified: Sun Jan 17 10:57:41 1999 by makisara@home
+Last modified: Sun Apr 18 13:24:43 1999 by makisara@home
BASICS
is set if there is no tape in the drive. GMT_EOD means either
end of recorded data or end of tape. GMT_EOT means end of tape.
-The following ioctls use the structure mtlocation that contains both
-the block number and the partition number. These ioctls are available
-only for SCSI-2 tape drives and the block number is the
-device-independent logical block number defined by the standard.
-
-MTGETLOC Returns the current block and partition number.
-MTSETLOC Sets the tape to the block and partition specified by the
- arguments.
-
MISCELLANEOUS COMPILE OPTIONS
Copyright 1992 - 1999 Kai Makisara
email Kai.Makisara@metla.fi
- Last modified: Sun Mar 7 09:03:17 1999 by makisara@home
+ Last modified: Tue May 18 08:32:34 1999 by makisara@home
Some small formal changes - aeb, 950809
*/
SCpnt->request_bufflen);
if (driver_byte(result) & DRIVER_SENSE)
print_sense("st", SCpnt);
- else
- printk("\n");
}
else
#endif
}
else
bp = (STp->buffer)->b_data;
+ SCpnt->cmd_len = 0;
SCpnt->request.sem = &(STp->sem);
SCpnt->request.rq_status = RQ_SCSI_BUSY;
SCpnt->request.rq_dev = STp->devt;
tpnt->devt = MKDEV(SCSI_TAPE_MAJOR, i);
tpnt->dirty = 0;
- tpnt->waiting = NULL;
tpnt->in_use = 0;
tpnt->drv_buffer = 1; /* Try buffering if no mode sense */
tpnt->restr_dma = (SDp->host)->unchecked_isa_dma;
typedef struct {
kdev_t devt;
unsigned capacity;
- wait_queue_head_t waiting;
Scsi_Device* device;
struct semaphore sem;
ST_buffer * buffer;
* to make the card a four channel one: use dsp to output two
* channels to LINE and dac to output the other two channels to
* SPKR. Set the mixer to only output synth to SPKR.
- * micz it looks like this changes the MIC input impedance. I don't know
- * any detail though.
+ * micbias sets the +5V bias to the mic if using an electretmic.
+ *
*
* Note: sync mode is not yet supported (i.e. running dsp and dac from the same
* clock source)
* Alpha fixes reported by Peter Jones <pjones@redhat.com>
* Note: joystick address handling might still be wrong on archs
* other than i386
+ * 10.05.99 0.21 Added support for an electret mic for SB PCI64
+ * to the Linux kernel sound driver. This mod also straighten
+ * out the question marks around the mic impedance setting
+ * (micz). From Kim.Berts@fisub.mail.abb.com
+ * 11.05.99 0.22 Implemented the IMIX call to mute recording monitor.
+ * Guenter Geiger <geiger@epy.co.at>
*
* some important things missing in Ensoniq documentation:
*
* The card uses a 22.5792 MHz crystal.
* The LINEIN jack may be converted to an AOUT jack by
* setting pin 47 (XCTL0) of the ES1370 to high.
- * Pin 48 (XCTL1) of the ES1370 presumably changes the input impedance of the
- * MIC jack.
+ * Pin 48 (XCTL1) of the ES1370 sets the +5V bias for an electretmic
+ *
*
*/
#define DAC2_DIVTOSR(x) (1411200/((x)+2))
#define CTRL_ADC_STOP 0x80000000 /* 1 = ADC stopped */
-#define CTRL_XCTL1 0x40000000 /* ? mic impedance */
+#define CTRL_XCTL1 0x40000000 /* electret mic bias */
#define CTRL_OPEN 0x20000000 /* no function, can be read and written */
#define CTRL_PCLKDIV 0x1fff0000 /* ADC/DAC2 clock divider */
#define CTRL_SH_PCLKDIV 16
unsigned int recsrc;
unsigned int modcnt;
unsigned short micpreamp;
+ unsigned int imix;
} mix;
/* wave stuff */
return put_user(s->mix.recsrc, (int *)arg);
case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
- for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ val = SOUND_MASK_IMIX;
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
if (mixtable[i].avail)
val |= 1 << i;
return put_user(val, (int *)arg);
case SOUND_MIXER_CAPS:
return put_user(0, (int *)arg);
+
+ case SOUND_MIXER_IMIX:
+ return put_user(s->mix.imix, (int *)arg);
default:
i = _IOC_NR(cmd);
return -EINVAL;
s->mix.modcnt++;
switch (_IOC_NR(cmd)) {
+
+ case SOUND_MIXER_IMIX:
+ if (arg == 0)
+ return -EFAULT;
+ get_user_ret(s->mix.imix,(int *)arg, -EFAULT);
+ val = s->mix.recsrc;
+ /* fall through */
+
case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
get_user_ret(val, (int *)arg, -EFAULT);
for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
wrcodec(s, 0x13, j & 0xaa);
wrcodec(s, 0x14, (j >> 8) & 0x17);
wrcodec(s, 0x15, (j >> 8) & 0x0f);
- i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc30;
+ i = (j & 0x37f) | ((j << 1) & 0x3000) | 0xc60;
+ if (!s->mix.imix) {
+ i &= 0xff60; /* mute record and line monitor */
+ }
wrcodec(s, 0x10, i);
wrcodec(s, 0x11, i >> 8);
return 0;
static int joystick[NR_DEVICE] = { 0, };
#endif
static int lineout[NR_DEVICE] = { 0, };
-static int micz[NR_DEVICE] = { 0, };
+static int micbias[NR_DEVICE] = { 0, };
/* --------------------------------------------------------------------- */
if (!pci_present()) /* No PCI bus in this machine! */
return -ENODEV;
- printk(KERN_INFO "es1370: version v0.20 time " __TIME__ " " __DATE__ "\n");
+ printk(KERN_INFO "es1370: version v0.22 time " __TIME__ " " __DATE__ "\n");
while (index < NR_DEVICE &&
(pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) {
if (pcidev->base_address[0] == 0 ||
goto err_irq;
}
/* initialize codec registers */
- s->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL);
- if (joystick[index]) {
+ /* note: setting CTRL_SERR_DIS is reported to break
+ * mic bias setting (by Kim.Berts@fisub.mail.abb.com) */
+ s->ctrl = CTRL_CDC_EN | (DAC2_SRTODIV(8000) << CTRL_SH_PCLKDIV) | (1 << CTRL_SH_WTSRSEL);
+ if (joystick[index]) {
if (check_region(0x200, JOY_EXTENT))
printk(KERN_ERR "es1370: io port 0x200 in use\n");
else
}
if (lineout[index])
s->ctrl |= CTRL_XCTL0;
- if (micz[index])
+ if (micbias[index])
s->ctrl |= CTRL_XCTL1;
s->sctrl = 0;
printk(KERN_INFO "es1370: found adapter at io %#lx irq %u\n"
wrcodec(s, 0x17, 0); /* CODEC ADC and CODEC DAC use {LR,B}CLK2 and run off the LRCLK2 PLL; program DAC_SYNC=0!! */
wrcodec(s, 0x18, 0); /* recording source is mixer */
wrcodec(s, 0x19, s->mix.micpreamp = 1); /* turn on MIC preamp */
+ s->mix.imix = 1;
fs = get_fs();
set_fs(KERNEL_DS);
val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD;
MODULE_PARM_DESC(joystick, "if 1 enables joystick interface (still need separate driver)");
MODULE_PARM(lineout, "1-" __MODULE_STRING(NR_DEVICE) "i");
MODULE_PARM_DESC(lineout, "if 1 the LINE input is converted to LINE out");
-MODULE_PARM(micz, "1-" __MODULE_STRING(NR_DEVICE) "i");
-MODULE_PARM_DESC(micz, "changes (??) the microphone impedance");
+MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone");
MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
MODULE_DESCRIPTION("ES1370 AudioPCI Driver");
bool 'USB keyboard support' CONFIG_USB_KBD
bool 'USB audio parsing support' CONFIG_USB_AUDIO
bool 'USB Abstract Control Model support' CONFIG_USB_ACM
+ bool 'USB printer support' CONFIG_USB_PRINTER
+
fi
endmenu
USBX_OBJS += cpia.o
endif
+ifeq ($(CONFIG_USB_PRINTER),y)
+ USBX_OBJS += printer.o
+endif
+
ifeq ($(CONFIG_USB), y)
L_OBJS += $(USBX_OBJS)
endif
int usb_audio_init(void);
int hub_init(void);
int usb_acm_init(void);
+int usb_printer_init(void);
void hub_cleanup(void);
void usb_mouse_cleanup(void);
0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
- 0x1c, 0x01, 0xd3, 0x0f, 0x39, 0x0c, 0x0d, 0x1a,
+ 0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a,
0x1b, 0x2b, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34,
0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40,
0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46,
- 0x00, 0xd2, 0xc7, 0xc9, 0x63, 0xcf, 0xd1, 0xcd,
+ 0x00, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd,
0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e,
0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47,
# misc fixes
keycode 0 = Pause
keycode 29 = Control
-keycode 99 = Remove
keycode 42 = Shift
keycode 54 = Shift_R
keycode 109 = Application
keycode 208 = Down
keycode 209 = Next
keycode 210 = Insert
-keycode 211 = Delete
+keycode 211 = Remove
keycode 219 = Window
keycode 220 = Window_R
keycode 221 = Menu
buffer++;
retval++;
state = 0;
+ if (!--count)
+ break;
+ }
+
+ /*
+ * SUBTLE:
+ *
+ * The only way to get here is to do a read() of
+ * more than 3 bytes: if you read a byte at a time
+ * you will just ever see states 0-2, for backwards
+ * compatibility.
+ *
+ * So you can think of this as a packet interface,
+ * where you have arbitrary-sized packets, and you
+ * only ever see the first three bytes when you read
+ * them in small chunks.
+ */
+ { /* fallthrough - dz */
+ int dz = mouse->dz;
+ mouse->dz = 0;
+ put_user(dz, buffer);
+ buffer++;
+ retval++;
+ state = 0;
}
break;
}
#endif
-
+static DECLARE_WAIT_QUEUE_HEAD(bulk_wakeup);
static DECLARE_WAIT_QUEUE_HEAD(control_wakeup);
static DECLARE_WAIT_QUEUE_HEAD(root_hub);
OHCI_DEBUG( printk(" ret_status: %x\n", status); })
return 0;
}
-
+
+static int sohci_bulk_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw)
+{
+ *(int * )lw0 = status;
+ wake_up(&bulk_wakeup);
+
+ OHCI_DEBUG( { int i; printk("USB HC BULK<<<: %x:", ep_addr, ctrl_len);)
+ OHCI_DEBUG( printk(" data(%d):", data_len);)
+ OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+ OHCI_DEBUG( printk(" ret_status: %x\n", status); })
+ return 0;
+}
+
static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id)
{
struct ohci * ohci = usb_dev->bus->hcpriv;
return cc_to_status[status & 0x0f];
}
+static int sohci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct ohci * ohci = usb_dev->bus->hcpriv;
+ int status;
+ union ep_addr_ ep_addr;
+
+ ep_addr.iep = 0;
+ ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */
+ | (pipe & 0x80) /* direction */
+ | (11 << 5); /* type = bulk*/
+ ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */
+
+ status = 0xf; /* CC not Accessed */
+ OHCI_DEBUG( { int i; printk("USB HC BULK>>>: %x:", ep_addr.iep);)
+ OHCI_DEBUG( printk(" data(%d):", len);)
+ OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);)
+ OHCI_DEBUG( printk("\n"); })
+
+ usb_ohci_add_ep(ohci, ep_addr.iep, 0, 1, sohci_bulk_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01);
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ add_wait_queue(&bulk_wakeup, &wait);
+
+ ohci_trans_req(ohci, ep_addr.iep, 0, NULL, data, len, (__OHCI_BAG) &status, 0);
+
+ schedule_timeout(HZ/10);
+
+ remove_wait_queue(&bulk_wakeup, &wait);
+
+ OHCI_DEBUG(printk("USB HC status::: %x\n", cc_to_status[status & 0x0f]);)
+
+ return cc_to_status[status & 0x0f];
+}
static int sohci_usb_deallocate(struct usb_device *usb_dev) {
struct ohci_device *dev = usb_to_ohci(usb_dev);
return usb_dev;
}
-/* FIXME! */
-#define sohci_bulk_msg NULL
struct usb_operations sohci_device_operations = {
sohci_usb_allocate,
}
writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */
writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */
- writel_set((0x01<<4), &ohci->regs->control); /* start CTRL u. (BULK list) */
+ writel_set((0x03<<4), &ohci->regs->control); /* start CTRL u. (BULK list) */
spin_unlock_irqrestore(&usb_ed_lock, flags);
{
/* int fminterval; */
unsigned int mask;
+ int port_nr;
/* fminterval = readl(&ohci->regs->fminterval) & 0x3fff;
reset_hc(ohci); */
/* | OHCI_INTR_SO | OHCI_INTR_UE |OHCI_INTR_RHSC |OHCI_INTR_SF|
OHCI_INTR_FNO */
-
+ if(readl(&ohci->regs->roothub.a) & 0x100) /* global power on */
+ writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */
+ else { /* port power on */
+ for(port_nr=0; port_nr < (ohci->regs->roothub.a & 0xff); port_nr++)
+ writel(0x100, &ohci->regs->roothub.portstatus[port_nr]);
+ }
+ wait_ms(50);
writel((0x00), &ohci->regs->control); /* USB Reset BUS */
wait_ms(10);
- writel((0x97), &ohci->regs->control); /* USB Operational */
-
- writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */
- wait_ms(50);
-
+ writel((0xB7), &ohci->regs->control); /* USB Operational */
+
OHCI_DEBUG(printk("USB HC rstart_hc_operational: %x\n", readl(&ohci->regs->control)); )
OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); )
OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); )
--- /dev/null
+
+/* Driver for USB Printers
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/lp.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define NAK_TIMEOUT (HZ) /* stall wait for printer */
+#define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */
+
+#ifndef USB_PRINTER_MAJOR
+#define USB_PRINTER_MAJOR 0
+#endif
+
+static int mymajor = USB_PRINTER_MAJOR;
+
+#define MAX_PRINTERS 8
+
+struct pp_usb_data {
+ struct usb_device *pusb_dev;
+ __u8 isopen; /* nz if open */
+ __u8 noinput; /* nz if no input stream */
+ __u8 minor; /* minor number of device */
+ __u8 status; /* last status from device */
+ int maxin, maxout; /* max transfer size in and out */
+ char *obuf; /* transfer buffer (out only) */
+ wait_queue_head_t wait_q; /* for timeouts */
+ unsigned int last_error; /* save for checking */
+};
+
+static struct pp_usb_data *minor_data[MAX_PRINTERS];
+
+#define PPDATA(x) ((struct pp_usb_data *)(x))
+
+unsigned char printer_read_status(struct pp_usb_data *p)
+{
+ __u8 status;
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 1;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1)) {
+ return 0;
+ }
+ return status;
+}
+
+static int printer_check_status(struct pp_usb_data *p)
+{
+ unsigned int last = p->last_error;
+ unsigned char status = printer_read_status(p);
+
+ if (status & LP_PERRORP)
+ /* No error. */
+ last = 0;
+ else if ((status & LP_POUTPA)) {
+ if (last != LP_POUTPA) {
+ last = LP_POUTPA;
+ printk(KERN_INFO "usblp%d out of paper\n", p->minor);
+ }
+ } else if (!(status & LP_PSELECD)) {
+ if (last != LP_PSELECD) {
+ last = LP_PSELECD;
+ printk(KERN_INFO "usblp%d off-line\n", p->minor);
+ }
+ } else {
+ if (last != LP_PERRORP) {
+ last = LP_PERRORP;
+ printk(KERN_INFO "usblp%d on fire\n", p->minor);
+ }
+ }
+
+ p->last_error = last;
+
+ return status;
+}
+
+void printer_reset(struct pp_usb_data *p)
+{
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RECIP_OTHER;
+ dr.request = 2;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+ dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+static int open_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p;
+
+ if(MINOR(inode->i_rdev) >= MAX_PRINTERS ||
+ !minor_data[MINOR(inode->i_rdev)]) {
+ return -ENODEV;
+ }
+
+ p = minor_data[MINOR(inode->i_rdev)];
+ p->minor = MINOR(inode->i_rdev);
+
+ if (p->isopen++) {
+ return -EBUSY;
+ }
+ if (!(p->obuf = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ printer_check_status(p);
+
+
+ file->private_data = p;
+// printer_reset(p);
+ init_waitqueue_head(&p->wait_q);
+ return 0;
+}
+
+static int close_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p = file->private_data;
+
+ free_page((unsigned long)p->obuf);
+ p->isopen = 0;
+ file->private_data = NULL;
+ if(!p->pusb_dev) {
+ minor_data[p->minor] = NULL;
+ kfree(p);
+
+ MOD_DEC_USE_COUNT;
+
+ }
+ return 0;
+}
+
+static ssize_t write_printer(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ unsigned long copy_size;
+ unsigned long bytes_written = 0;
+ unsigned long partial;
+ int result;
+ int maxretry;
+
+ do {
+ char *obuf = p->obuf;
+ unsigned long thistime;
+
+ thistime = copy_size = (count > p->maxout) ? p->maxout : count;
+ if (copy_from_user(p->obuf, buffer, copy_size))
+ return -EFAULT;
+ maxretry = MAX_RETRY_COUNT;
+ while (thistime) {
+ if (!p->pusb_dev)
+ return -ENODEV;
+ if (signal_pending(current)) {
+ return bytes_written ? bytes_written : -EINTR;
+ }
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial);
+ if (result & 0x08) { /* NAK - so hold for a while */
+ obuf += partial;
+ thistime -= partial;
+ if(!maxretry--)
+ return -ETIME;
+ interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT);
+ continue;
+ } else
+ break;
+ };
+ if (result) {
+ /* whoops - let's reset and fail the request */
+// printk("Whoops - %x\n", result);
+ printer_reset(p);
+ interruptible_sleep_on_timeout(&p->wait_q, 5*HZ); /* let reset do its stuff */
+ return -EIO;
+ }
+ bytes_written += copy_size;
+ count -= copy_size;
+ buffer += copy_size;
+ } while ( count > 0 );
+
+ return bytes_written ? bytes_written : -EIO;
+}
+
+static ssize_t read_printer(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ int read_count;
+ int this_read;
+ char buf[64];
+ unsigned long partial;
+ int result;
+
+ if (p->noinput)
+ return -EINVAL;
+
+ read_count = 0;
+ while (count) {
+ if (signal_pending(current)) {
+ return read_count ? read_count : -EINTR;
+ }
+ if (!p->pusb_dev)
+ return -ENODEV;
+ this_read = (count > sizeof(buf)) ? sizeof(buf) : count;
+
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_rcvbulkpipe(p->pusb_dev, 2), buf, this_read, &partial);
+
+ /* unlike writes, we don't retry a NAK, just stop now */
+ if (result & 0x08)
+ count = this_read = partial;
+ else if (result)
+ return -EIO;
+
+ if (this_read) {
+ if (copy_to_user(buffer, p->obuf, this_read))
+ return -EFAULT;
+ count -= this_read;
+ read_count += this_read;
+ buffer += this_read;
+ }
+ }
+ return read_count;
+}
+
+static int printer_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ int i;
+
+ /*
+ * FIXME - this will not cope with combined printer/scanners
+ */
+ if (dev->descriptor.bDeviceClass != 7 ||
+ dev->descriptor.bNumConfigurations != 1 ||
+ dev->config[0].bNumInterfaces != 1) {
+ return -1;
+ }
+
+ interface = dev->config->interface;
+
+ /* Lets be paranoid (for the moment)*/
+ if (interface->bInterfaceClass != 7 ||
+ interface->bInterfaceSubClass != 1 ||
+ (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1)||
+ interface->bNumEndpoints > 2) {
+ return -1;
+ }
+
+ if (interface->endpoint[0].bEndpointAddress != 0x01 ||
+ interface->endpoint[0].bmAttributes != 0x02 ||
+ (interface->bNumEndpoints > 1 && (
+ interface->endpoint[1].bEndpointAddress != 0x82 ||
+ interface->endpoint[1].bmAttributes != 0x02))) {
+ return -1;
+ }
+
+ for (i=0; i<MAX_PRINTERS; i++) {
+ if (!minor_data[i])
+ break;
+ }
+ if (i >= MAX_PRINTERS) {
+ return -1;
+ }
+
+ printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum);
+
+ if (!(dev->private = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) {
+ printk( KERN_DEBUG "usb_printer: no memory!\n");
+ return -1;
+ }
+
+ memset(dev->private, 0, sizeof(struct pp_usb_data));
+ minor_data[i] = PPDATA(dev->private);
+ minor_data[i]->minor = i;
+ minor_data[i]->pusb_dev = dev;
+ minor_data[i]->maxout = interface->endpoint[0].wMaxPacketSize * 16;
+ if (minor_data[i]->maxout > PAGE_SIZE) {
+ minor_data[i]->maxout = PAGE_SIZE;
+ }
+ if (interface->bInterfaceProtocol != 2)
+ minor_data[i]->noinput = 1;
+ else {
+ minor_data[i]->maxin = interface->endpoint[1].wMaxPacketSize;
+ }
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk(KERN_INFO " Failed to set configuration\n");
+ return -1;
+ }
+#if 0
+ {
+ __u8 status;
+ __u8 ieee_id[64];
+ devrequest dr;
+
+ /* Lets get the device id if possible */
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 0;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = sizeof(ieee_id) - 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1) == 0) {
+ if (ieee_id[1] < sizeof(ieee_id) - 1)
+ ieee_id[ieee_id[1]+2] = '\0';
+ else
+ ieee_id[sizeof(ieee_id)-1] = '\0';
+ printk(KERN_INFO " Printer ID is %s\n", &ieee_id[2]);
+ }
+ status = printer_read_status(PPDATA(dev->private));
+ printk(KERN_INFO " Status is %s,%s,%s\n",
+ (status & 0x10) ? "Selected" : "Not Selected",
+ (status & 0x20) ? "No Paper" : "Paper",
+ (status & 0x08) ? "No Error" : "Error");
+ }
+#endif
+ return 0;
+}
+
+static void printer_disconnect(struct usb_device *dev)
+{
+ struct pp_usb_data *pp = dev->private;
+
+ if (pp->isopen) {
+ /* better let it finish - the release will do whats needed */
+ pp->pusb_dev = NULL;
+ return;
+ }
+ minor_data[pp->minor] = NULL;
+ kfree(pp);
+ dev->private = NULL; /* just in case */
+ MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver printer_driver = {
+ "printer",
+ printer_probe,
+ printer_disconnect,
+ { NULL, NULL }
+};
+
+static struct file_operations usb_printer_fops = {
+ NULL, /* seek */
+ read_printer,
+ write_printer,
+ NULL, /* readdir */
+ NULL, /* poll - out for the moment */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_printer,
+ NULL, /* flush ? */
+ close_printer,
+ NULL,
+ NULL
+};
+
+int usb_printer_init(void)
+{
+ int result;
+
+ MOD_INC_USE_COUNT;
+
+ if ((result = register_chrdev(USB_PRINTER_MAJOR, "usblp", &usb_printer_fops)) < 0) {
+ printk(KERN_WARNING "usbprinter: Cannot register device\n");
+ return result;
+ }
+ if (mymajor == 0) {
+ mymajor = result;
+ }
+ usb_register(&printer_driver);
+ printk(KERN_INFO "USB Printer support registered.\n");
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+ return usb_printer_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned int offset;
+
+ usb_deregister(&printer_driver);
+ unregister_chrdev(mymajor, "usblplp");
+}
+#endif
void show_td(struct uhci_td * td)
{
+ char *spid;
+
printk("%08x ", td->link);
printk("%se%d %s%s%s%s%s%s%s%s%s%sLength=%x ",
((td->status >> 29) & 1) ? "SPD " : "",
((td->status >> 18) & 1) ? "CRC/Timeo " : "",
((td->status >> 17) & 1) ? "BitStuff " : "",
td->status & 0x7ff);
- printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x ",
+ switch (td->info & 0xff) {
+ case 0x2d:
+ spid = "SETUP";
+ break;
+ case 0xe1:
+ spid = "OUT";
+ break;
+ case 0x69:
+ spid = "IN";
+ break;
+ default:
+ spid = "?";
+ break;
+ }
+ printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x(%s) ",
td->info >> 21,
((td->info >> 19) & 1) ? "DT " : "",
(td->info >> 15) & 15,
(td->info >> 8) & 127,
- td->info & 0xff);
+ (td->info & 0xff),
+ spid);
printk("(buf=%08x)\n", td->buffer);
}
*/
/* 4/4/1999 added data toggle for interrupt pipes -keryan */
+/* 5/16/1999 added global toggles for bulk and control */
#include <linux/config.h>
#include <linux/module.h>
/*
* Return the result of a TD..
*/
-static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td)
+static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td, unsigned long *rval)
{
unsigned int status;
+ struct uhci_td *tmp = td->first;
- status = (td->status >> 16) & 0xff;
+ /* locate the first failing td, if any */
+ do {
+ status = (tmp->status >> 16) & 0xff;
+ if (status)
+ break;
+ if ((tmp->link & 1) || (tmp->link & 2))
+ break;
+ tmp = bus_to_virt(tmp->link & ~0xF);
+ } while (1);
+
+ if(rval)
+ *rval = 0;
/* Some debugging code */
- if (status) {
+ if (status && (!usb_pipeendpoint(tmp->info) || !(status & 0x08)) ) {
int i = 10;
- struct uhci_td *tmp = td->first;
- printk("uhci_td_result() failed with status %d\n", status);
+
+ tmp = td->first;
+ printk("uhci_td_result() failed with status %x\n", status);
show_status(dev->uhci);
do {
show_td(tmp);
break;
} while (1);
}
+ if (usb_pipeendpoint(tmp->info) && (status & 0x08)) {
+// printk("uhci_td_result() - NAK\n");
+ /* find total length xferred and reset toggle on failed packets */
+ tmp = td->first;
+ do {
+ /* sum up packets that did not fail */
+ if(rval && !((tmp->status >> 16) & 0xff))
+ *rval += (tmp->status & 0x3ff) + 1;
+
+ /*
+ * Note - only the first to fail will be marked NAK
+ */
+ if (tmp->status & 0xFF0000) {
+ /* must reset the toggle on any error */
+ usb_settoggle(dev->usb, usb_pipeendpoint(tmp->info), (tmp->info >> 19) & 1);
+ break;
+ }
+
+ if ((tmp->link & 1) || (tmp->link & 2))
+ break;
+ tmp = bus_to_virt(tmp->link & ~0xF);
+ } while (1);
+#if 0
+ if (rval) {
+ printk("uhci_td_result returning partial count %d\n", *rval);
+ }
+#endif
+ }
return status;
}
static void uhci_qh_deallocate(struct uhci_qh *qh)
{
- if (qh->element != 1)
- printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element);
+// if (qh->element != 1)
+// printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element);
qh->element = 1;
qh->link = 1;
uhci_qh_deallocate(ctrl_qh);
- return uhci_td_result(dev, last);
+ return uhci_td_result(dev, last, NULL);
}
/*
struct uhci_td *first, *td, *prevtd;
unsigned long destination, status;
int ret;
+ int maxsze = usb_maxpacket(usb_dev, pipe);
- if (len > usb_maxpacket(usb_dev->maxpacketsize) * 29)
+ if (len > maxsze * 29)
printk("Warning, too much data for a control packet, crashing\n");
first = td = uhci_td_allocate(dev);
while (len > 0) {
/* Build the TD for control status */
int pktsze = len;
- int maxsze = usb_maxpacket(pipe);
if (pktsze > maxsze)
pktsze = maxsze;
td->first = first;
td->backptr = &prevtd->link;
+ data += pktsze;
+ len -= pktsze;
+
prevtd = td;
td = uhci_td_allocate(dev);
prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */
- data += maxsze;
- len -= maxsze;
}
/*
} while (1);
}
+ if (ret) {
+ __u8 *p = cmd;
+
+ printk("Failed cmd - %02X %02X %02X %02X %02X %02X %02X %02X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ }
return ret;
}
}
/* td points to the last td in the list, which interrupts on completion */
-static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last)
+static int uhci_run_bulk(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last, unsigned long *rval)
{
DECLARE_WAITQUEUE(wait, current);
struct uhci_qh *bulk_qh = uhci_qh_allocate(dev);
uhci_qh_deallocate(bulk_qh);
- return uhci_td_result(dev, last);
+ return uhci_td_result(dev, last, rval);
}
/*
* 31 TD's is a minimum of 248 bytes worth of bulk
* information.
*/
-static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len)
+static int uhci_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, unsigned long *rval)
{
struct uhci_device *dev = usb_to_uhci(usb_dev);
struct uhci_td *first, *td, *prevtd;
unsigned long destination, status;
int ret;
+ int maxsze = usb_maxpacket(usb_dev, pipe);
- if (len > usb_maxpacket(usb_dev->maxpacketsize) * 31)
- printk("Warning, too much data for a bulk packet, crashing\n");
+ if (len > maxsze * 31)
+ printk("Warning, too much data for a bulk packet, crashing (%d/%d)\n", len, maxsze);
/* The "pipe" thing contains the destination in bits 8--18, 0x69 is IN */
/*
while (len > 0) {
/* Build the TD for control status */
int pktsze = len;
- int maxsze = usb_maxpacket(pipe);
if (pktsze > maxsze)
pktsze = maxsze;
td->status = status; /* Status */
- td->info = destination | ((pktsze-1) << 21); /* pktsze bytes of data */
+ td->info = destination | ((pktsze-1) << 21) |
+ (usb_gettoggle(usb_dev, usb_pipeendpoint(pipe)) << 19); /* pktsze bytes of data */
td->buffer = virt_to_bus(data);
td->backptr = &prevtd->link;
-
- prevtd = td;
- td = uhci_td_allocate(dev);
- prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */
+ td->first = first;
data += maxsze;
len -= maxsze;
+
+ if (len > 0) {
+ prevtd = td;
+ td = uhci_td_allocate(dev);
+ prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */
+ }
+
/* Alternate Data0/1 (start with Data0) */
- destination ^= 1 << 19;
+ usb_dotoggle(usb_dev, usb_pipeendpoint(pipe));
}
td->link = 1; /* Terminate */
/* Start it up.. */
- ret = uhci_run_bulk(dev, first, td);
+ ret = uhci_run_bulk(dev, first, td, rval);
{
int maxcount = 100;
status = inw(io_addr + USBSTS);
outw(status, io_addr + USBSTS);
- if ((status & ~0x21) != 0)
- printk("interrupt: %X\n", status);
+// if ((status & ~0x21) != 0)
+// printk("interrupt: %X\n", status);
/* Walk the list of pending TD's to see which ones completed.. */
uhci_interrupt_notify(uhci);
}
}
+
outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR);
outw(0, io_addr + USBFRNUM);
outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD);
/* Run and mark it configured with a 64-byte max packet */
- outw(USBCMD_RS | USBCMD_CF, io_addr + USBCMD);
+ outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
}
/*
#endif
}
-#define uhci_init init_module
+int init_modules(void)
+{
+ return uhci_init();
+}
#endif
printk("\n");
}
-
+void usb_show_string(struct usb_device* dev, char *id, int index)
+{
+ if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index])
+ printk("%s: %s\n", id, dev->stringindex[index]);
+}
#ifdef CONFIG_USB_CPIA
usb_cpia_init();
#endif
+#ifdef CONFIG_USB_PRINTER
+ usb_printer_init();
+#endif
usb_hub_init();
return 0;
if(dev->config==NULL)
return;
+
for(c=0;c<dev->descriptor.bNumConfigurations;c++)
{
cf=&dev->config[c];
kfree(cf->interface);
}
kfree(dev->config);
+
+ if (dev->stringindex)
+ kfree(dev->stringindex);
+ if (dev->stringtable)
+ kfree(dev->stringtable);
}
void usb_init_root_hub(struct usb_device *dev)
int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size)
{
devrequest dr;
+ int i = 5;
+ int result;
dr.requesttype = 0x80;
dr.request = USB_REQ_GET_DESCRIPTOR;
dr.index = 0;
dr.length = size;
- return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size);
+ while (i--) {
+ if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size)))
+ break;
+ }
+ return result;
+}
+
+int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size)
+{
+ devrequest dr;
+ int i = 5;
+ int result;
+
+ dr.requesttype = 0x80;
+ dr.request = USB_REQ_GET_DESCRIPTOR;
+ dr.value = (USB_DT_STRING << 8) + index;
+ dr.index = langid;
+ dr.length = size;
+
+ while (i--) {
+ if (!(result = dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size)))
+ break;
+ }
+ return result;
}
int usb_get_device_descriptor(struct usb_device *dev)
return 0;
}
+static void usb_set_maxpacket(struct usb_device *dev)
+{
+ struct usb_endpoint_descriptor *ep;
+ struct usb_interface_descriptor *ip = dev->actconfig->interface;
+ int i;
+
+ for (i=0; i<dev->actconfig->bNumInterfaces; i++) {
+ if (dev->actconfig->interface[i].bInterfaceNumber == dev->ifnum) {
+ ip = &dev->actconfig->interface[i];
+ break;
+ }
+ }
+ ep = ip->endpoint;
+ for (i=0; i<ip->bNumEndpoints; i++) {
+ dev->epmaxpacket[ep[i].bEndpointAddress & 0x0f] = ep[i].wMaxPacketSize;
+ }
+}
+
int usb_set_interface(struct usb_device *dev, int interface, int alternate)
{
devrequest dr;
if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
return -1;
+ dev->ifnum = interface;
+ usb_set_maxpacket(dev);
return 0;
}
int usb_set_configuration(struct usb_device *dev, int configuration)
{
devrequest dr;
-
+ int i;
+ struct usb_config_descriptor *cp = NULL;
+
dr.requesttype = 0;
dr.request = USB_REQ_SET_CONFIGURATION;
dr.value = configuration;
dr.index = 0;
dr.length = 0;
+ for (i=0; i<dev->descriptor.bNumConfigurations; i++) {
+ if (dev->config[i].bConfigurationValue == configuration) {
+ cp = &dev->config[i];
+ break;
+ }
+ }
+ if (!cp) {
+ printk(KERN_INFO "usb: selecting invalid configuration %d\n", configuration);
+ return -1;
+ }
if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0))
return -1;
+ dev->actconfig = cp;
+ dev->toggle = 0;
+ usb_set_maxpacket(dev);
return 0;
}
return usb_parse_configuration(dev, buffer, bufptr - buffer);
}
+int usb_get_stringtable(struct usb_device *dev)
+{
+ int i;
+ int maxindex;
+ int langid;
+ unsigned char buffer[256];
+ int totalchars;
+ struct usb_string_descriptor *sd = (struct usb_string_descriptor *)buffer;
+ char *string;
+ __u8 bLengths[USB_MAXSTRINGS+1];
+ int j;
+
+ dev->maxstring = 0;
+ if(usb_get_string(dev, 0, 0, buffer, 2) ||
+ usb_get_string(dev, 0, 0, buffer, sd->bLength))
+ return -1;
+ /* we are going to assume that the first ID is good */
+ langid = sd->wData[0];
+
+ /* whip through and find total length and max index */
+ for (maxindex = 1, totalchars = 0; maxindex<=USB_MAXSTRINGS; maxindex++) {
+ if(usb_get_string(dev, langid, maxindex, buffer, 2))
+ break;
+ totalchars += (sd->bLength - 2)/2 + 1;
+ bLengths[maxindex] = sd->bLength;
+ }
+ if (--maxindex <= 0)
+ return -1;
+
+ /* get space for strings and index */
+ dev->stringindex = kmalloc(sizeof(char *)*maxindex, GFP_KERNEL);
+ if (!dev->stringindex)
+ return -1;
+ dev->stringtable = kmalloc(totalchars, GFP_KERNEL);
+ if (!dev->stringtable) {
+ kfree(dev->stringindex);
+ dev->stringindex = NULL;
+ return -1;
+ }
+
+ /* fill them in */
+ memset(dev->stringindex, 0, sizeof(char *)*maxindex);
+ for (i=1, string = dev->stringtable; i <= maxindex; i++) {
+ if (usb_get_string(dev, langid, i, buffer, bLengths[i]))
+ continue;
+ dev->stringindex[i] = string;
+ for (j=0; j < (bLengths[i] - 2)/2; j++) {
+ *string++ = sd->wData[j];
+ }
+ *string++ = '\0';
+ }
+ dev->maxstring = maxindex;
+ return 0;
+}
+
/*
* By the time we get here, the device has gotten a new device ID
* and is in the default state. We need to identify the thing and
dev->devnum);
dev->maxpacketsize = 0; /* Default to 8 byte max packet size */
+ dev->epmaxpacket[0] = 8;
addr = dev->devnum;
dev->devnum = 0;
+#if 1
/* Slow devices */
for (i = 0; i < 5; i++) {
if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8))
printk("giving up\n");
return;
}
+#endif
#if 0
printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0);
#endif
+ dev->epmaxpacket[0] = dev->descriptor.bMaxPacketSize0;
switch (dev->descriptor.bMaxPacketSize0) {
case 8: dev->maxpacketsize = 0; break;
case 16: dev->maxpacketsize = 1; break;
dev->devnum = addr;
+#if 1
if (usb_set_address(dev)) {
printk("Unable to set address\n");
/* FIXME: We should disable the port */
return;
}
+#else
+ usb_set_address(dev);
+#endif
wait_ms(10); /* Let the SET_ADDRESS settle */
return;
}
+ usb_get_stringtable(dev);
+
+ dev->actconfig = dev->config;
+ dev->ifnum = 0;
+ usb_set_maxpacket(dev);
+
+ usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer);
+ usb_show_string(dev, "Product", dev->descriptor.iProduct);
+ usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber);
+
#if 0
printk("Vendor: %X\n", dev->descriptor.idVendor);
printk("Product: %X\n", dev->descriptor.idProduct);
extern int usb_kbd_init(void);
extern int usb_cpia_init(void);
extern int usb_mouse_init(void);
+extern int usb_printer_init(void);
extern void hub_cleanup(void);
extern void usb_mouse_cleanup(void);
#define USB_MAXCONFIG 8
#define USB_MAXINTERFACES 32
#define USB_MAXENDPOINTS 32
+#define USB_MAXSTRINGS 16
struct usb_device_descriptor {
__u8 bLength;
struct usb_string_descriptor {
__u8 bLength;
__u8 bDescriptorType;
+ __u16 wData[1];
};
/* Hub descriptor */
struct usb_device *(*allocate)(struct usb_device *);
int (*deallocate)(struct usb_device *);
int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int);
- int (*bulk_msg)(struct usb_device *, unsigned int, void *, int);
+ int (*bulk_msg)(struct usb_device *, unsigned int, void *, int, unsigned long *);
int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *);
};
int devnum; /* Device number on USB bus */
int slow; /* Slow device? */
int maxpacketsize; /* Maximum packet size */
-
+ __u16 toggle; /* one bit for each endpoint */
+ struct usb_config_descriptor *actconfig; /* the active configuration */
+ int epmaxpacket[16]; /* endpoint specific maximums */
+ int ifnum; /* active interface number */
struct usb_bus *bus; /* Bus we're apart of */
struct usb_driver *driver; /* Driver */
struct usb_device_descriptor descriptor; /* Descriptor */
struct usb_config_descriptor *config; /* All of the configs */
struct usb_device *parent;
+ char *stringtable; /* Strings (multiple, null term) */
+ char **stringindex; /* pointers to strings */
+ int maxstring; /* max valid index */
/*
* Child devices - these can be either new devices
* appropriately.
*/
-#define usb_maxpacket(pipe) (8 << ((pipe) & 3))
+#define usb_maxpacket(dev,pipe) ((dev)->epmaxpacket[usb_pipeendpoint(pipe)])
#define usb_packetid(pipe) (((pipe) & 0x80) ? 0x69 : 0xE1)
#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f)
#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff)
+/* The D0/D1 toggle bits */
+#define usb_gettoggle(dev, ep) (((dev)->toggle >> ep) & 1)
+#define usb_dotoggle(dev, ep) ((dev)->toggle ^= (1 << ep))
+#define usb_settoggle(dev, ep, bit) ((dev)->toggle = ((dev)->toggle & (0xfffe << ep)) | (bit << ep))
+
static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint)
{
return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26) | dev->maxpacketsize;
#define usb_rcvctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint) | 0x80)
#define usb_sndisocpipe(dev,endpoint) ((0 << 30) | __create_pipe(dev,endpoint))
#define usb_rcvisocpipe(dev,endpoint) ((0 << 30) | __create_pipe(dev,endpoint) | 0x80)
+#define usb_sndbulkpipe(dev,endpoint) ((3 << 30) | __create_pipe(dev,endpoint))
+#define usb_rcvbulkpipe(dev,endpoint) ((3 << 30) | __create_pipe(dev,endpoint) | 0x80)
#define usb_snddefctrl(dev) ((2 << 30) | __default_pipe(dev))
#define usb_rcvdefctrl(dev) ((2 << 30) | __default_pipe(dev) | 0x80)
void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *);
void usb_show_hub_descriptor(struct usb_hub_descriptor *);
void usb_show_device(struct usb_device *);
+void usb_show_string(struct usb_device* dev, char *id, int index);
/*
* Audio parsing helpers
return fd;
file = fcheck(fd);
- if (!file->f_op || !file->f_op->mmap) {
+ if ((fd_offset & ~PAGE_MASK) != 0) {
+ printk(KERN_WARNING
+ "fd_offset is not page aligned. Please convert program: %s\n",
+ file->f_dentry->d_name.name
+ );
+ }
+
+ if (!file->f_op || !file->f_op->mmap || ((fd_offset & ~PAGE_MASK) != 0)) {
sys_close(fd);
do_mmap(NULL, 0, ex.a_text+ex.a_data,
PROT_READ|PROT_WRITE|PROT_EXEC,
start_addr = ex.a_entry & 0xfffff000;
+ if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
+ printk(KERN_WARNING
+ "N_TXTOFF is not page aligned. Please convert library: %s\n",
+ file->f_dentry->d_name.name
+ );
+
+ do_mmap(NULL, start_addr & PAGE_MASK, ex.a_text + ex.a_data + ex.a_bss,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED| MAP_PRIVATE, 0);
+
+ read_exec(file->f_dentry, N_TXTOFF(ex),
+ (char *)start_addr, ex.a_text + ex.a_data, 0);
+ flush_icache_range((unsigned long) start_addr,
+ (unsigned long) start_addr + ex.a_text + ex.a_data);
+
+ retval = 0;
+ goto out_putf;
+ }
/* Now use mmap to map the library into memory. */
error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
PROT_READ | PROT_WRITE | PROT_EXEC,
padzero(elf_bss);
#if 0
- printk("(start_brk) %x\n" , current->mm->start_brk);
- printk("(end_code) %x\n" , current->mm->end_code);
- printk("(start_code) %x\n" , current->mm->start_code);
- printk("(end_data) %x\n" , current->mm->end_data);
- printk("(start_stack) %x\n" , current->mm->start_stack);
- printk("(brk) %x\n" , current->mm->brk);
+ printk("(start_brk) %lx\n" , (long) current->mm->start_brk);
+ printk("(end_code) %lx\n" , (long) current->mm->end_code);
+ printk("(start_code) %lx\n" , (long) current->mm->start_code);
+ printk("(end_data) %lx\n" , (long) current->mm->end_data);
+ printk("(start_stack) %lx\n" , (long) current->mm->start_stack);
+ printk("(brk) %lx\n" , (long) current->mm->brk);
#endif
if ( current->personality == PER_SVR4 )
+++ /dev/null
-.depend
-.*.flags
return error;
}
-#ifndef __alpha__
+#if !(defined(__alpha__) || defined(__ia64__))
/*
* sys_utime() can be implemented in user-level using sys_utimes().
}
ret = -EINVAL;
- if (n < 0 || n > KFDS_NR)
+ if (n < 0)
goto out_nofds;
+
+ if (n > KFDS_NR)
+ n = KFDS_NR;
+
/*
* We need 6 bitmaps (in/out/ex for both incoming and outgoing),
* since we used fdset we need to allocate memory in units of
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
* into arch/i386 instead?
return error;
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
return error;
}
-#if !defined(__alpha__) && !defined(__sparc__)
+#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__)
/*
* For backward compatibility? Maybe this should be moved
#ifndef __ASM_ALPHA_PROCESSOR_H
#define __ASM_ALPHA_PROCESSOR_H
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
/*
* We have a 42-bit user address space: 4TB user VM...
*/
atomic_t count;
atomic_t waking; /* biased by -1 */
wait_queue_head_t wait;
+#if WAITQUEUE_DEBUG
+ long __magic;
+#endif
};
-#define MUTEX ((struct semaphore) \
- { ATOMIC_INIT(1), ATOMIC_INIT(-1), NULL })
-#define MUTEX_LOCKED ((struct semaphore) \
- { ATOMIC_INIT(0), ATOMIC_INIT(-1), NULL })
+#if WAITQUEUE_DEBUG
+# define __SEM_DEBUG_INIT(name) , (long)&(name).__magic
+#else
+# define __SEM_DEBUG_INIT(name)
+#endif
+
+#define __SEMAPHORE_INITIALIZER(name,count) \
+ { ATOMIC_INIT(count), ATOMIC_INIT(-1), \
+ __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+ __SEM_DEBUG_INIT(name) }
+
+#define __MUTEX_INITIALIZER(name) \
+ __SEMAPHORE_INITIALIZER(name,1)
+
+#define __DECLARE_SEMAPHORE_GENERIC(name,count) \
+ struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
+
+#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
+#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
+
+extern inline void sema_init (struct semaphore *sem, int val)
+{
+ /*
+ * Logically,
+ * *sem = (struct semaphore)__SEMAPHORE_INITIALIZER((*sem),val);
+ * except that gcc produces better initializing by parts yet.
+ */
+
+ atomic_set(&sem->count, val);
+ atomic_set(&sem->waking, -1);
+ init_waitqueue_head(&sem->wait);
+#if WAITQUEUE_DEBUG
+ sem->__magic = (long)&sem->__magic;
+#endif
+}
+
+static inline void init_MUTEX (struct semaphore *sem)
+{
+ sema_init(sem, 1);
+}
+
+static inline void init_MUTEX_LOCKED (struct semaphore *sem)
+{
+ sema_init(sem, 0);
+}
-#define sema_init(sem, val) atomic_set(&((sem)->count), val)
extern void __down(struct semaphore * sem);
extern int __down_interruptible(struct semaphore * sem);
a function that ordinarily wouldn't. Otherwise we could
have it done by the macro directly, which can be optimized
the linker. */
- register void *pv __asm__("$27") = __down_failed;
+ register void *pv __asm__("$27");
+
+#if WAITQUEUE_DEBUG
+ CHECK_MAGIC(sem->__magic);
+#endif
+ pv = __down_failed;
__asm__ __volatile__ (
"/* semaphore down operation */\n"
"1: ldl_l $24,%1\n"
value is in $24. */
register int ret __asm__("$24");
- register void *pv __asm__("$27") = __down_failed_interruptible;
+ register void *pv __asm__("$27");
+#if WAITQUEUE_DEBUG
+ CHECK_MAGIC(sem->__magic);
+#endif
+
+ pv = __down_failed_interruptible;
__asm__ __volatile__ (
"/* semaphore down interruptible operation */\n"
"1: ldl_l $24,%2\n"
} while (tmp == 0);
*/
+#if WAITQUEUE_DEBUG
+ CHECK_MAGIC(sem->__magic);
+#endif
+
__asm__ __volatile__(
"1: ldq_l %1,%4\n"
" lda %3,1\n"
it's return address in $28. The pv is loaded as usual.
The gp is clobbered (in the module case) as usual. */
- register void *pv __asm__("$27") = __up_wakeup;
+ register void *pv __asm__("$27");
+#if WAITQUEUE_DEBUG
+ CHECK_MAGIC(sem->__magic);
+#endif
+
+ pv = __up_wakeup;
__asm__ __volatile__ (
"/* semaphore up operation */\n"
" mb\n"
#ifndef __ASM_ARM_PROCESSOR_H
#define __ASM_ARM_PROCESSOR_H
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
#define FP_SIZE 35
struct fp_hard_struct {
#include <asm/math_emu.h>
#include <asm/segment.h>
#include <asm/page.h>
+#include <asm/types.h>
+
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ void *pc; __asm__("movl $1f,%0\n1:":"=g" (pc)); pc; })
/*
* CPU type and hardware bug flags. Kept separately for each CPU.
} while (0)
/* Forward declaration, a strange C thing */
+struct task_struct;
struct mm_struct;
/* Free all resources held by a thread. */
extern void synchronize_bh(void);
+/*
+ * This is suboptimal. We only need to disable bh's locally
+ * on this CPU...
+ */
+#define local_bh_disable() atomic_inc(&global_bh_lock)
+#define local_bh_enable() atomic_dec(&global_bh_lock)
+
static inline void start_bh_atomic(void)
{
atomic_inc(&global_bh_lock);
#ifndef __ASM_SPINLOCK_H
#define __ASM_SPINLOCK_H
+/*
+ * These are the generic versions of the spinlocks
+ * and read-write locks.. We should actually do a
+ * <linux/spinlock.h> with all of this. Oh, well.
+ */
+#define spin_lock_irqsave(lock, flags) do { local_irq_save(flags); spin_lock(lock); } while (0)
+#define spin_lock_irq(lock) do { local_irq_disable(); spin_lock(lock); } while (0)
+#define spin_lock_bh(lock) do { local_bh_disable(); spin_lock(lock); } while (0)
+
+#define read_lock_irqsave(lock, flags) do { local_irq_save(flags); read_lock(lock); } while (0)
+#define read_lock_irq(lock) do { local_irq_disable(); read_lock(lock); } while (0)
+#define read_lock_bh(lock) do { local_bh_disable(); read_lock(lock); } while (0)
+
+#define write_lock_irqsave(lock, flags) do { local_irq_save(flags); write_lock(lock); } while (0)
+#define write_lock_irq(lock) do { local_irq_disable(); write_lock(lock); } while (0)
+#define write_lock_bh(lock) do { local_bh_disable(); write_lock(lock); } while (0)
+
+#define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock); local_irq_restore(flags); } while (0)
+#define spin_unlock_irq(lock) do { spin_unlock(lock); local_irq_enable(); } while (0)
+#define spin_unlock_bh(lock) do { spin_unlock(lock); local_bh_enable(); } while (0)
+
+#define read_unlock_irqrestore(lock, flags) do { read_unlock(lock); local_irq_restore(flags); } while (0)
+#define read_unlock_irq(lock) do { read_unlock(lock); local_irq_enable(); } while (0)
+#define read_unlock_bh(lock) do { read_unlock(lock); local_bh_enable(); } while (0)
+
+#define write_unlock_irqrestore(lock, flags) do { write_unlock(lock); local_irq_restore(flags); } while (0)
+#define write_unlock_irq(lock) do { write_unlock(lock); local_irq_enable(); } while (0)
+#define write_unlock_bh(lock) do { write_unlock(lock); local_bh_enable(); } while (0)
+
#ifndef __SMP__
#define DEBUG_SPINLOCKS 0 /* 0 == no debugging, 1 == maintain lock state, 2 == full debug */
#define spin_trylock(lock) (1)
#define spin_unlock_wait(lock) do { } while(0)
#define spin_unlock(lock) do { } while(0)
-#define spin_lock_irq(lock) cli()
-#define spin_unlock_irq(lock) sti()
-
-#define spin_lock_irqsave(lock, flags) \
- do { save_flags(flags); cli(); } while (0)
-#define spin_unlock_irqrestore(lock, flags) \
- restore_flags(flags)
#elif (DEBUG_SPINLOCKS < 2)
#define spin_lock(x) do { (x)->lock = 1; } while (0)
#define spin_unlock_wait(x) do { } while (0)
#define spin_unlock(x) do { (x)->lock = 0; } while (0)
-#define spin_lock_irq(x) do { cli(); spin_lock(x); } while (0)
-#define spin_unlock_irq(x) do { spin_unlock(x); sti(); } while (0)
-
-#define spin_lock_irqsave(x, flags) \
- do { save_flags(flags); spin_lock_irq(x); } while (0)
-#define spin_unlock_irqrestore(x, flags) \
- do { spin_unlock(x); restore_flags(flags); } while (0)
#else /* (DEBUG_SPINLOCKS >= 2) */
#define spin_lock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1; restore_flags(__spinflags);} while (0)
#define spin_unlock_wait(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_wait(%s:%p) deadlock\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} restore_flags(__spinflags);} while (0)
#define spin_unlock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0)
-#define spin_lock_irq(x) do {cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock_irq(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1;} while (0)
-#define spin_unlock_irq(x) do {cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; sti();} while (0)
-
-#define spin_lock_irqsave(x,flags) do {save_flags(flags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock_irqsave(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1;} while (0)
-#define spin_unlock_irqrestore(x,flags) do {cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_irqrestore(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(flags);} while (0)
#endif /* DEBUG_SPINLOCKS */
#define read_unlock(lock) do { } while(0)
#define write_lock(lock) do { } while(0)
#define write_unlock(lock) do { } while(0)
-#define read_lock_irq(lock) cli()
-#define read_unlock_irq(lock) sti()
-#define write_lock_irq(lock) cli()
-#define write_unlock_irq(lock) sti()
-
-#define read_lock_irqsave(lock, flags) \
- do { save_flags(flags); cli(); } while (0)
-#define read_unlock_irqrestore(lock, flags) \
- restore_flags(flags)
-#define write_lock_irqsave(lock, flags) \
- do { save_flags(flags); cli(); } while (0)
-#define write_unlock_irqrestore(lock, flags) \
- restore_flags(flags)
#else /* __SMP__ */
#define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
-#define spin_lock_irq(lock) \
- do { __cli(); spin_lock(lock); } while (0)
-
-#define spin_unlock_irq(lock) \
- do { spin_unlock(lock); __sti(); } while (0)
-
-#define spin_lock_irqsave(lock, flags) \
- do { __save_flags(flags); __cli(); spin_lock(lock); } while (0)
-
-#define spin_unlock_irqrestore(lock, flags) \
- do { spin_unlock(lock); __restore_flags(flags); } while (0)
-
/*
* Read-write spinlocks, allowing multiple readers
* but only one writer.
#define write_unlock(rw) \
asm volatile("lock ; btrl $31,%0":"=m" (__dummy_lock(&(rw)->lock)))
-#define read_lock_irq(lock) do { __cli(); read_lock(lock); } while (0)
-#define read_unlock_irq(lock) do { read_unlock(lock); __sti(); } while (0)
-#define write_lock_irq(lock) do { __cli(); write_lock(lock); } while (0)
-#define write_unlock_irq(lock) do { write_unlock(lock); __sti(); } while (0)
-
-#define read_lock_irqsave(lock, flags) \
- do { __save_flags(flags); __cli(); read_lock(lock); } while (0)
-#define read_unlock_irqrestore(lock, flags) \
- do { read_unlock(lock); __restore_flags(flags); } while (0)
-#define write_lock_irqsave(lock, flags) \
- do { __save_flags(flags); __cli(); write_lock(lock); } while (0)
-#define write_unlock_irqrestore(lock, flags) \
- do { write_unlock(lock); __restore_flags(flags); } while (0)
-
#endif /* __SMP__ */
#endif /* __ASM_SPINLOCK_H */
#define __restore_flags(x) \
__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
+/* For spinlocks etc */
+#define local_irq_save(x) __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory")
+#define local_irq_restore(x) __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory")
+#define local_irq_disable() __asm__ __volatile__("cli": : :"memory")
+#define local_irq_enable() __asm__ __volatile__("sti": : :"memory")
#ifdef __SMP__
#ifndef __ASM_M68K_PROCESSOR_H
#define __ASM_M68K_PROCESSOR_H
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
#include <asm/segment.h>
#include <asm/fpu.h>
#ifndef __ASM_MIPS_PROCESSOR_H
#define __ASM_MIPS_PROCESSOR_H
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
#if !defined (_LANGUAGE_ASSEMBLY)
#include <asm/cachectl.h>
#include <asm/mipsregs.h>
static __inline__ int ide_default_irq(ide_ioreg_t base)
{
- return ppc_ide_md.default_irq(base);
+ if ( ppc_ide_md.default_irq )
+ return ppc_ide_md.default_irq(base);
+ else
+ return -1;
}
static __inline__ ide_ioreg_t ide_default_io_base(int index)
{
- return ppc_ide_md.default_io_base(index);
+ if ( ppc_ide_md.default_io_base )
+ return ppc_ide_md.default_io_base(index);
+ else
+ return -1;
}
static __inline__ void ide_init_default_hwifs(void)
static __inline__ int ide_check_region (ide_ioreg_t from, unsigned int extent)
{
- return ppc_ide_md.check_region(from, extent);
+ if ( ppc_ide_md.check_region )
+ return ppc_ide_md.check_region(from, extent);
+ else
+ return -1;
}
static __inline__ void ide_request_region (ide_ioreg_t from, unsigned int extent, const char *name)
{
- ppc_ide_md.request_region(from, extent, name);
+ if ( ppc_ide_md.request_region )
+ ppc_ide_md.request_region(from, extent, name);
}
static __inline__ void ide_release_region (ide_ioreg_t from, unsigned int extent)
{
- ppc_ide_md.release_region(from, extent);
+ if ( ppc_ide_md.release_region )
+ ppc_ide_md.release_region(from, extent);
}
-static __inline__ void ide_fix_driveid (struct hd_driveid *id) {
- ppc_ide_md.fix_driveid(id);
+static __inline__ void ide_fix_driveid (struct hd_driveid *id)
+{
+ if ( ppc_ide_md.fix_driveid )
+ ppc_ide_md.fix_driveid(id);
}
#undef inb
#ifndef __ASSEMBLY__
/* Hardware Page Table Entry */
typedef struct _PTE {
+#ifdef CONFIG_PPC64
+ unsigned long long vsid:52;
+ unsigned long api:5;
+ unsigned long :5;
+ unsigned long h:1;
+ unsigned long v:1;
+ unsigned long long rpn:52;
+#else /* CONFIG_PPC64 */
unsigned long v:1; /* Entry is valid */
unsigned long vsid:24; /* Virtual segment identifier */
unsigned long h:1; /* Hash algorithm indicator */
unsigned long api:6; /* Abbreviated page index */
unsigned long rpn:20; /* Real (physical) page number */
+#endif /* CONFIG_PPC64 */
unsigned long :3; /* Unused */
unsigned long r:1; /* Referenced */
unsigned long c:1; /* Changed */
} P601_BATU;
typedef struct _BATU { /* Upper part of BAT (all except 601) */
+#ifdef CONFIG_PPC64
+ unsigned long long bepi:47;
+#else /* CONFIG_PPC64 */
unsigned long bepi:15; /* Effective page index (virtual address) */
+#endif /* CONFIG_PPC64 */
unsigned long :4; /* Unused */
unsigned long bl:11; /* Block size mask */
unsigned long vs:1; /* Supervisor valid */
} P601_BATL;
typedef struct _BATL { /* Lower part of BAT (all except 601) */
+#ifdef CONFIG_PPC64
+ unsigned long long brpn:47;
+#else /* CONFIG_PPC64 */
unsigned long brpn:15; /* Real page index (physical address) */
+#endif /* CONFIG_PPC64 */
unsigned long :10; /* Unused */
unsigned long w:1; /* Write-thru cache */
unsigned long i:1; /* Cache inhibit */
#define flush_tlb_page local_flush_tlb_page
#define flush_tlb_range local_flush_tlb_range
+extern __inline__ void flush_tlb_pgtables(struct mm_struct *mm, unsigned long start, unsigned long end)
+{
+ /* PPC has hw page tables. */
+}
+
/*
* No cache flushing is required when address mappings are
* changed, because the caches on PowerPCs are physically
#ifndef __ASM_PPC_PROCESSOR_H
#define __ASM_PPC_PROCESSOR_H
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
#include <linux/config.h>
#include <asm/ptrace.h>
#include <asm/residual.h>
/* Bit encodings for Machine State Register (MSR) */
+#ifdef CONFIG_PPC64
+#define MSR_SF (1<<63)
+#define MSR_ISF (1<<61)
+#endif /* CONFIG_PPC64 */
#define MSR_POW (1<<18) /* Enable Power Management */
#define MSR_TGPR (1<<17) /* TLB Update registers in use */
#define MSR_ILE (1<<16) /* Interrupt Little-Endian enable */
*/
#ifndef __ASSEMBLY__
+#ifdef CONFIG_PPC64
+#define REG unsigned long /*long*/
+#else
+#define REG unsigned long
+#endif
struct pt_regs {
- unsigned long gpr[32];
- unsigned long nip;
- unsigned long msr;
- unsigned long orig_gpr3; /* Used for restarting system calls */
- unsigned long ctr;
- unsigned long link;
- unsigned long xer;
- unsigned long ccr;
- unsigned long mq; /* 601 only (not used at present) */
- /* Used on APUS to hold IPL value. */
- unsigned long trap; /* Reason for being here */
- unsigned long dar; /* Fault registers */
- unsigned long dsisr;
- unsigned long result; /* Result of a system call */
+ REG gpr[32];
+ REG nip;
+ REG msr;
+ REG orig_gpr3; /* Used for restarting system calls */
+ REG ctr;
+ REG link;
+ REG xer;
+ REG ccr;
+ REG mq; /* 601 only (not used at present) */
+ /* Used on APUS to hold IPL value. */
+ REG trap; /* Reason for being here */
+ REG dar; /* Fault registers */
+ REG dsisr;
+ REG result; /* Result of a system call */
};
#endif
-#ifndef _PPC_SEMAPHORE_H
-#define _PPC_SEMAPHORE_H
+#ifndef _SPARC_SEMAPHORE_H
+#define _SPARC_SEMAPHORE_H
/*
- * SMP- and interrupt-safe semaphores..
- *
- * (C) Copyright 1996 Linus Torvalds
- * Adapted for PowerPC by Gary Thomas and Paul Mackerras
+ * Swiped from asm-sparc/semaphore.h and modified
+ * -- Cort (cort@cs.nmt.edu)
*/
+#ifdef __KERNEL__
+
#include <asm/atomic.h>
+#include <linux/wait.h>
struct semaphore {
atomic_t count;
atomic_t waking;
wait_queue_head_t wait;
+#if WAITQUEUE_DEBUG
+ long __magic;
+#endif
};
-#define sema_init(sem, val) atomic_set(&((sem)->count), (val))
+#if WAITQUEUE_DEBUG
+# define __SEM_DEBUG_INIT(name) \
+ , (long)&(name).__magic
+#else
+# define __SEM_DEBUG_INIT(name)
+#endif
+
+#define __SEMAPHORE_INITIALIZER(name,count) \
+{ ATOMIC_INIT(count), ATOMIC_INIT(0), __WAIT_QUEUE_HEAD_INITIALIZER((name).wait) \
+ __SEM_DEBUG_INIT(name) }
+
+#define __MUTEX_INITIALIZER(name) \
+ __SEMAPHORE_INITIALIZER(name,1)
+
+#define __DECLARE_SEMAPHORE_GENERIC(name,count) \
+ struct semaphore name = __SEMAPHORE_INITIALIZER(name,count)
+
+#define DECLARE_MUTEX(name) __DECLARE_SEMAPHORE_GENERIC(name,1)
+#define DECLARE_MUTEX_LOCKED(name) __DECLARE_SEMAPHORE_GENERIC(name,0)
+
+extern inline void sema_init (struct semaphore *sem, int val)
+{
+ atomic_set(&sem->count, val);
+ atomic_set(&sem->waking, 0);
+ init_waitqueue_head(&sem->wait);
+#if WAITQUEUE_DEBUG
+ sem->__magic = (long)&sem->__magic;
+#endif
+}
+
+static inline void init_MUTEX (struct semaphore *sem)
+{
+ sema_init(sem, 1);
+}
-#define MUTEX ((struct semaphore) \
- { ATOMIC_INIT(1), ATOMIC_INIT(0), NULL })
-#define MUTEX_LOCKED ((struct semaphore) \
- { ATOMIC_INIT(0), ATOMIC_INIT(0), NULL })
+static inline void init_MUTEX_LOCKED (struct semaphore *sem)
+{
+ sema_init(sem, 0);
+}
extern void __down(struct semaphore * sem);
extern int __down_interruptible(struct semaphore * sem);
__up(sem);
}
-#endif /* !(_PPC_SEMAPHORE_H) */
+#endif /* __KERNEL__ */
+
+#endif /* !(_SPARC_SEMAPHORE_H) */
extern int _disable_interrupts(void);
extern void _enable_interrupts(int);
-extern void instruction_dump(unsigned long *);
extern void print_backtrace(unsigned long *);
extern void show_regs(struct pt_regs * regs);
extern void flush_instruction_cache(void);
#ifndef __ASM_SPARC_PROCESSOR_H
#define __ASM_SPARC_PROCESSOR_H
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
#include <linux/a.out.h>
#include <asm/psr.h>
#ifndef __ASM_SPARC64_PROCESSOR_H
#define __ASM_SPARC64_PROCESSOR_H
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
#include <asm/asi.h>
#include <asm/a.out.h>
#include <asm/pstate.h>
static inline int softirq_trylock(int cpu)
{
if (spin_trylock(&global_bh_count)) {
- if (atomic_read(&global_bh_lock) == 0) {
+ if (atomic_read(&global_bh_lock) == 0 &&
+ cpu_data[cpu].bh_count == 0) {
++(cpu_data[cpu].bh_count);
return 1;
}
#define spin_unlock(lock) do { } while(0)
#define spin_lock_irq(lock) cli()
#define spin_unlock_irq(lock) sti()
+#define spin_lock_bh(lock) \
+do { local_bh_count++; \
+ barrier(); \
+} while(0)
+#define spin_unlock_bh(lock) \
+do { barrier(); \
+ local_bh_count--; \
+} while(0)
#define spin_lock_irqsave(lock, flags) save_and_cli(flags)
#define spin_unlock_irqrestore(lock, flags) restore_flags(flags)
#define write_unlock(lock) do { } while(0)
#define read_lock_irq(lock) cli()
#define read_unlock_irq(lock) sti()
+#define read_lock_bh(lock) \
+do { local_bh_count++; \
+ barrier(); \
+} while(0)
+#define read_unlock_bh(lock) \
+do { barrier(); \
+ local_bh_count--; \
+} while(0)
+
#define write_lock_irq(lock) cli()
#define write_unlock_irq(lock) sti()
+#define write_lock_bh(lock) \
+do { local_bh_count++; \
+ barrier(); \
+} while(0)
+
+#define write_unlock_bh(lock) \
+do { barrier(); \
+ local_bh_count--; \
+} while(0)
+
#define read_lock_irqsave(lock, flags) save_and_cli(flags)
#define read_unlock_irqrestore(lock, flags) restore_flags(flags)
#define write_lock_irqsave(lock, flags) save_and_cli(flags)
: "memory");
}
+#define spin_lock_bh(__lock) \
+do { local_bh_count++; \
+ spin_lock(__lock); \
+} while(0)
+
+#define spin_unlock_bh(__lock) \
+do { spin_unlock(__lock); \
+ local_bh_count--; \
+} while(0)
+
#define spin_lock_irqsave(__lock, flags) \
do { register spinlock_t *__lp asm("g1"); \
__lp = (__lock); \
#define spin_trylock(lp) _spin_trylock(lp)
#define spin_lock(lock) _do_spin_lock(lock, "spin_lock")
#define spin_lock_irq(lock) do { __cli(); _do_spin_lock(lock, "spin_lock_irq"); } while(0)
+#define spin_lock_bh(lock) do { local_bh_count++; _do_spin_lock(lock, "spin_lock_bh"); } while(0)
#define spin_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_spin_lock(lock, "spin_lock_irqsave"); } while(0)
#define spin_unlock(lock) _do_spin_unlock(lock)
#define spin_unlock_irq(lock) do { _do_spin_unlock(lock); __sti(); } while(0)
+#define spin_unlock_bh(lock) do { _do_spin_unlock(lock); local_bh_count--; } while(0)
#define spin_unlock_irqrestore(lock, flags) do { _do_spin_unlock(lock); __restore_flags(flags); } while(0)
#endif /* SPIN_LOCK_DEBUG */
#define read_lock_irq(lock) do { __cli(); read_lock(lock); } while (0)
#define read_unlock_irq(lock) do { read_unlock(lock); __sti(); } while (0)
+#define read_lock_bh(lock) do { local_bh_count++; read_lock(lock); } while (0)
+#define read_unlock_bh(lock) do { read_unlock(lock); local_bh_count--; } while (0)
#define write_lock_irq(lock) do { __cli(); write_lock(lock); } while (0)
#define write_unlock_irq(lock) do { write_unlock(lock); __sti(); } while (0)
+#define write_lock_bh(lock) do { local_bh_count++; write_lock(lock); } while (0)
+#define write_unlock_bh(lock) do { write_unlock(lock); local_bh_count--; } while (0)
#define read_lock_irqsave(lock, flags) \
do { __save_and_cli(flags); read_lock(lock); } while (0)
__restore_flags(flags); \
} while(0)
#define read_lock_irq(lock) do { __cli(); _do_read_lock(lock, "read_lock_irq"); } while(0)
+#define read_lock_bh(lock) do { local_bh_count++; _do_read_lock(lock, "read_lock_bh"); } while(0)
#define read_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_read_lock(lock, "read_lock_irqsave"); } while(0)
#define read_unlock(lock) \
__restore_flags(flags); \
} while(0)
#define read_unlock_irq(lock) do { _do_read_unlock(lock, "read_unlock_irq"); __sti() } while(0)
+#define read_unlock_bh(lock) do { _do_read_unlock(lock, "read_unlock_bh"); local_bh_count--; } while(0)
#define read_unlock_irqrestore(lock, flags) do { _do_read_unlock(lock, "read_unlock_irqrestore"); __restore_flags(flags); } while(0)
#define write_lock(lock) \
__restore_flags(flags); \
} while(0)
#define write_lock_irq(lock) do { __cli(); _do_write_lock(lock, "write_lock_irq"); } while(0)
+#define write_lock_bh(lock) do { local_bh_count++; _do_write_lock(lock, "write_lock_bh"); } while(0)
#define write_lock_irqsave(lock, flags) do { __save_and_cli(flags); _do_write_lock(lock, "write_lock_irqsave"); } while(0)
#define write_unlock(lock) \
__restore_flags(flags); \
} while(0)
#define write_unlock_irq(lock) do { _do_write_unlock(lock); __sti(); } while(0)
+#define write_unlock_bh(lock) do { _do_write_unlock(lock); local_bh_count--; } while(0)
#define write_unlock_irqrestore(lock, flags) do { _do_write_unlock(lock); __restore_flags(flags); } while(0)
#endif /* SPIN_LOCK_DEBUG */
/*
- * $Id: b1lli.h,v 1.3 1998/01/31 10:54:37 calle Exp $
+ * $Id: b1lli.h,v 1.6 1999/04/15 19:49:36 calle Exp $
*
* ISDN lowlevel-module for AVM B1-card.
*
* Copyright 1996 by Carsten Paeth (calle@calle.in-berlin.de)
*
* $Log: b1lli.h,v $
+ * Revision 1.6 1999/04/15 19:49:36 calle
+ * fix fuer die B1-PCI. Jetzt geht z.B. auch IRQ 17 ...
+ *
+ * Revision 1.5 1998/10/25 14:50:28 fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.4 1998/03/29 16:05:02 calle
+ * changes from 2.0 tree merged.
+ *
+ * Revision 1.1.2.9 1998/03/20 14:30:02 calle
+ * added cardnr to detect if you try to add same T1 to different io address.
+ * change number of nccis depending on number of channels.
+ *
+ * Revision 1.1.2.8 1998/03/04 17:32:33 calle
+ * Changes for T1.
+ *
+ * Revision 1.1.2.7 1998/02/27 15:38:29 calle
+ * T1 running with slow link.
+ *
+ * Revision 1.1.2.6 1998/02/24 17:57:36 calle
+ * changes for T1.
+ *
* Revision 1.3 1998/01/31 10:54:37 calle
* include changes for PCMCIA cards from 2.0 version
*
int irq;
} avmb1_carddef;
-#define AVM_CARDTYPE_B1 0
-#define AVM_CARDTYPE_T1 1
-#define AVM_CARDTYPE_M1 2
-#define AVM_CARDTYPE_M2 3
+#define AVM_CARDTYPE_B1 0
+#define AVM_CARDTYPE_T1 1
+#define AVM_CARDTYPE_M1 2
+#define AVM_CARDTYPE_M2 3
+#define AVM_CARDTYPE_B1PCI 4
typedef struct avmb1_extcarddef {
int port;
int irq;
int cardtype;
+ int cardnr; /* for HEMA/T1 */
} avmb1_extcarddef;
#define AVMB1_LOAD 0 /* load image to card */
#define AVMB1_LOAD_AND_CONFIG 3 /* load image and config to card */
#define AVMB1_ADDCARD_WITH_TYPE 4 /* add a new card, with cardtype */
#define AVMB1_GET_CARDINFO 5 /* get cardtype */
+#define AVMB1_REMOVECARD 6 /* remove a card (usefull for T1) */
#ifdef __KERNEL__
-#define AVMB1_PORTLEN 0x1f
+#define AVMB1_PORTLEN 0x1f
-#define AVM_MAXVERSION 8
-#define AVM_NBCHAN 2
+#define AVM_MAXVERSION 8
-#define AVM_NAPPS 30
-#define AVM_NPLCI 5
-#define AVM_NNCCI 6
+#define AVM_NAPPS 30
+#define AVM_NNCCI_PER_CHANNEL 4
/*
* Main driver data
typedef struct avmb1_card {
struct avmb1_card *next;
int cnr;
- unsigned short port;
+ unsigned int port;
unsigned irq;
int cardtype;
+ int cardnr; /* for T1-HEMA */
volatile unsigned short cardstate;
int interrupt;
int blocked;
/* b1lli.c */
-int B1_detect(unsigned short base, int cardtype);
-void B1_reset(unsigned short base);
-int B1_load_t4file(unsigned short base, avmb1_t4file * t4file);
-int B1_load_config(unsigned short base, avmb1_t4file * config);
-int B1_loaded(unsigned short base);
-unsigned char B1_assign_irq(unsigned short base, unsigned irq, int cardtype);
-unsigned char B1_enable_irq(unsigned short base);
-unsigned char B1_disable_irq(unsigned short base);
+int B1_detect(unsigned int base, int cardtype);
+int T1_detectandinit(unsigned int base, unsigned irq, int cardnr);
+void B1_reset(unsigned int base);
+void T1_reset(unsigned int base);
+int B1_load_t4file(unsigned int base, avmb1_t4file * t4file);
+int B1_load_config(unsigned int base, avmb1_t4file * config);
+int B1_loaded(unsigned int base);
+void B1_setinterrupt(unsigned int base, unsigned irq, int cardtype);
+unsigned char B1_disable_irq(unsigned int base);
+void T1_disable_irq(unsigned int base);
int B1_valid_irq(unsigned irq, int cardtype);
+int B1_valid_port(unsigned port, int cardtype);
void B1_handle_interrupt(avmb1_card * card);
-void B1_send_init(unsigned short port,
+void B1_send_init(unsigned int port,
unsigned int napps, unsigned int nncci, unsigned int cardnr);
-void B1_send_register(unsigned short port,
+void B1_send_register(unsigned int port,
__u16 appid, __u32 nmsg,
__u32 nb3conn, __u32 nb3blocks, __u32 b3bsize);
-void B1_send_release(unsigned short port, __u16 appid);
-void B1_send_message(unsigned short port, struct sk_buff *skb);
+void B1_send_release(unsigned int port, __u16 appid);
+void B1_send_message(unsigned int port, struct sk_buff *skb);
/* b1capi.c */
void avmb1_handle_new_ncci(avmb1_card * card,
int flags;
int datalen;
void *data;
-} *p;
+};
/* The subfunctions (for the op field) */
#define BLKPG_ADD_PARTITION 1
unsigned long event;
unsigned long last_active;
int count; /* # of fd on device */
- int x_break;
+ int breakon;
+ int breakoff;
int blocked_open; /* # of blocked opens */
long session; /* Session of opening process */
long pgrp; /* pgrp of opening process */
*
*/
+#include <linux/version.h>
+
#define I2C_BUS_MAX 4 /* max # of bus drivers */
#define I2C_DRIVER_MAX 8 /* max # of chip drivers */
#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */
#define I2C_DRIVERID_VIDEOTEXT 3
#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */
+#define I2C_BUSID_PARPORT 2 /* Bit banging on a parallel port */
/*
* struct for a driver for a i2c chip (tuner, soundprocessor,
-/* $Id: isdn.h,v 1.37 1998/02/22 19:45:24 fritz Exp $
+/* $Id: isdn.h,v 1.64 1999/04/18 14:57:14 fritz Exp $
*
* Main header for the Linux ISDN subsystem (linklevel).
*
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * Note: This file differs from the corresponding revision as present in the
- * isdn4linux CVS repository because some later bug fixes have been extracted
- * from the repository and merged into this file. -- Henner Eisen
- *
* $Log: isdn.h,v $
+ * Revision 1.64 1999/04/18 14:57:14 fritz
+ * Removed TIMRU stuff
+ *
+ * Revision 1.63 1999/04/18 14:07:18 fritz
+ * Removed TIMRU stuff.
+ *
+ * Revision 1.62 1999/04/12 13:16:54 fritz
+ * Changes from 2.0 tree.
+ *
+ * Revision 1.61 1999/03/02 11:43:21 armin
+ * Added variable to store connect-message of Modem.
+ * Added Timer-define for RegS7 (Wait for Carrier).
+ *
+ * Revision 1.60 1998/10/25 14:50:29 fritz
+ * Backported from MIPS (Cobalt).
+ *
+ * Revision 1.59 1998/10/23 10:18:55 paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.58 1998/10/23 10:10:06 fritz
+ * Test-Checkin
+ *
+ * Revision 1.57 1998/08/31 21:10:01 he
+ * new ioctl IIOCNETGPN for /dev/isdninfo (get network interface'
+ * peer phone number)
+ *
+ * Revision 1.56 1998/07/26 18:46:52 armin
+ * Added silence detection in voice receive mode.
+ *
+ * Revision 1.55 1998/06/26 15:13:17 fritz
+ * Added handling of STAT_ICALL with incomplete CPN.
+ * Added AT&L for ttyI emulator.
+ * Added more locking stuff in tty_write.
+ *
+ * Revision 1.54 1998/06/18 23:32:01 fritz
+ * Replaced cli()/restore_flags() in isdn_tty_write() by locking.
+ * Removed direct-senddown feature in isdn_tty_write because it will
+ * never succeed with locking and is useless anyway.
+ *
+ * Revision 1.53 1998/06/17 19:51:51 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.52 1998/06/12 11:42:18 detabc
+ * cleanup abc
+ *
+ * Revision 1.51 1998/06/02 12:10:30 detabc
+ * wegen einer einstweiliger verfuegung gegen DW ist zur zeit
+ * die abc-extension bis zur klaerung der rechtslage nicht verfuegbar
+ *
+ * Revision 1.50 1998/05/05 23:11:51 detabc
+ * add Item to stop icmp-unreach (max. 6 times of dialwait delay)
+ *
+ * Revision 1.49 1998/05/03 17:45:00 detabc
+ * Add Item to send icmp-host-unreach to all packets
+ *
+ * Revision 1.48 1998/04/26 19:58:14 detabc
+ * include the new abc-extension-items from 2.0.xx kernels
+ * remove some unused code
+ *
+ * Revision 1.47 1998/04/21 18:00:25 detabc
+ * Add items for secure-callback (abc-extension only)
+ *
+ * Revision 1.46 1998/04/14 16:28:59 he
+ * Fixed user space access with interrupts off and remaining
+ * copy_{to,from}_user() -> -EFAULT return codes
+ *
+ * Revision 1.45 1998/03/24 16:33:12 hipp
+ * More CCP changes. BSD compression now "works" on a local loopback link.
+ * Moved some isdn_ppp stuff from isdn.h to isdn_ppp.h
+ *
+ * Revision 1.44 1998/03/22 18:50:56 hipp
+ * Added BSD Compression for syncPPP .. UNTESTED at the moment
+ *
+ * Revision 1.43 1998/03/09 17:46:44 he
+ * merged in 2.1.89 changes
+ *
+ * Revision 1.42 1998/03/08 13:53:46 detabc
+ * add ABC-variables in structur isdn_devt
+ *
+ * Revision 1.41 1998/03/08 13:14:37 detabc
+ * abc-extension support for kernels > 2.1.x
+ * first try (sorry experimental)
+ *
+ * Revision 1.40 1998/03/08 01:08:29 fritz
+ * Increased NET_DV because of TIMRU
+ *
+ * Revision 1.39 1998/03/07 22:42:49 fritz
+ * Starting generic module support (Nothing usable yet).
+ *
+ * Revision 1.38 1998/03/07 18:21:29 cal
+ * Dynamic Timeout-Rule-Handling vs. 971110 included
+ *
* Revision 1.37 1998/02/22 19:45:24 fritz
* Some changes regarding V.110
*
#ifndef isdn_h
#define isdn_h
+#include <linux/config.h>
#include <linux/ioctl.h>
#define ISDN_TTY_MAJOR 43
* the correspondent code in isdn.c
*/
+#ifdef CONFIG_COBALT_MICRO_SERVER
+/* Save memory */
+#define ISDN_MAX_DRIVERS 2
+#define ISDN_MAX_CHANNELS 8
+#else
#define ISDN_MAX_DRIVERS 32
#define ISDN_MAX_CHANNELS 64
+#endif
#define ISDN_MINOR_B 0
#define ISDN_MINOR_BMAX (ISDN_MAX_CHANNELS-1)
-#define ISDN_MINOR_CTRL ISDN_MAX_CHANNELS
-#define ISDN_MINOR_CTRLMAX (2*ISDN_MAX_CHANNELS-1)
-#define ISDN_MINOR_PPP (2*ISDN_MAX_CHANNELS)
-#define ISDN_MINOR_PPPMAX (3*ISDN_MAX_CHANNELS-1)
+#define ISDN_MINOR_CTRL 64
+#define ISDN_MINOR_CTRLMAX (64 + (ISDN_MAX_CHANNELS-1))
+#define ISDN_MINOR_PPP 128
+#define ISDN_MINOR_PPPMAX (128 + (ISDN_MAX_CHANNELS-1))
#define ISDN_MINOR_STATUS 255
/* New ioctl-codes */
#define IIOCNETANM _IO('I',5)
#define IIOCNETDNM _IO('I',6)
#define IIOCNETGNM _IO('I',7)
-#define IIOCGETSET _IO('I',8)
-#define IIOCSETSET _IO('I',9)
+#define IIOCGETSET _IO('I',8) /* no longer supported */
+#define IIOCSETSET _IO('I',9) /* no longer supported */
#define IIOCSETVER _IO('I',10)
#define IIOCNETHUP _IO('I',11)
#define IIOCSETGST _IO('I',12)
#define IIOCNETALN _IO('I',32)
#define IIOCNETDLN _IO('I',33)
+#define IIOCNETGPN _IO('I',34)
+
#define IIOCDBGVAR _IO('I',127)
#define IIOCDRVCTL _IO('I',128)
#define ISDN_MODEM_ANZREG 23 /* Number of Modem-Registers */
#define ISDN_MSNLEN 20
+#define ISDN_LMSNLEN 255 /* Length of tty's Listen-MSN string */
+#define ISDN_CMSGLEN 50 /* Length of CONNECT-Message to add for Modem */
typedef struct {
char drvid[25];
int outgoing;
} isdn_net_ioctl_phone;
-#define NET_DV 0x04 /* Data version for net_cfg */
-#define TTY_DV 0x04 /* Data version for iprofd etc. */
+#define NET_DV 0x05 /* Data version for net_cfg */
+#define TTY_DV 0x05 /* Data version for iprofd etc. */
+#define INF_DV 0x01 /* Data version for /dev/isdninfo */
typedef struct {
char name[10]; /* Name of interface */
int pppbind; /* ippp device for bindings */
int chargeint; /* Use fixed charge interval length */
int triggercps; /* BogoCPS needed for triggering slave */
+ int dialtimeout; /* Dial-Timeout */
+ int dialwait; /* Time to wait after failed dial */
+ int dialmode; /* Flag: off / on / auto */
} isdn_net_ioctl_cfg;
+#define ISDN_NET_DIALMODE_MASK 0xC0 /* bits for status */
+#define ISDN_NET_DM_OFF 0x00 /* this interface is stopped */
+#define ISDN_NET_DM_MANUAL 0x40 /* this interface is on (manual) */
+#define ISDN_NET_DM_AUTO 0x80 /* this interface is autodial */
+#define ISDN_NET_DIALMODE(x) ((&(x))->flags & ISDN_NET_DIALMODE_MASK)
+
#ifdef __KERNEL__
#ifndef STANDALONE
#include <linux/isdnif.h>
+
#define ISDN_DRVIOCTL_MASK 0x7f /* Mask for Device-ioctl */
/* Until now unused */
#define ISDN_TIMER_NETHANGUP 32
#define ISDN_TIMER_IPPP 64
#define ISDN_TIMER_KEEPALIVE 128 /* Cisco-Keepalive */
+#define ISDN_TIMER_CARRIER 256 /* Wait for Carrier */
#define ISDN_TIMER_FAST (ISDN_TIMER_MODEMREAD | ISDN_TIMER_MODEMPLUS | \
ISDN_TIMER_MODEMXMIT)
#define ISDN_TIMER_SLOW (ISDN_TIMER_MODEMRING | ISDN_TIMER_NETHANGUP | \
- ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE)
+ ISDN_TIMER_NETDIAL | ISDN_TIMER_KEEPALIVE | \
+ ISDN_TIMER_CARRIER)
/* Timeout-Values for isdn_net_dial() */
#define ISDN_TIMER_DTIMEOUT10 (10*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
#define ISDN_TIMER_DTIMEOUT15 (15*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
+#define ISDN_TIMER_DTIMEOUT60 (60*HZ/(ISDN_TIMER_02SEC*(ISDN_TIMER_RES+1)))
/* GLOBAL_FLAGS */
#define ISDN_GLOBAL_STOPPED 1
#define ISDN_NET_TMP 0x10 /* tmp interface until getting an IP */
#define ISDN_NET_DYNAMIC 0x20 /* this link is dynamically allocated */
#endif
+
#define ISDN_NET_MAGIC 0x49344C02 /* for paranoia-checking */
/* Phone-list-element */
struct device *,
unsigned char *);
int pppbind; /* ippp device for bindings */
+ int dialtimeout; /* How long shall we try on dialing? (jiffies) */
+ int dialwait; /* How long shall we wait after failed attempt? (jiffies) */
+ ulong dialstarted; /* jiffies of first dialing-attempt */
+ ulong dialwait_timer; /* jiffies of earliest next dialing-attempt */
+ int huptimeout; /* How long will the connection be up? (seconds) */
#ifdef CONFIG_ISDN_X25
struct concap_device_ops *dops; /* callbacks used by encapsulator */
#endif
ulong cisco_yourseq; /* Remote keepalive seq. for Cisco */
} isdn_net_local;
-#ifdef CONFIG_ISDN_PPP
-struct ippp_bundle {
- int mp_mrru; /* unused */
- struct mpqueue *last; /* currently defined in isdn_net_dev */
- int min; /* currently calculated 'on the fly' */
- long next_num; /* we wanna see this seq.-number next */
- struct sqqueue *sq;
- int modify:1; /* set to 1 while modifying sqqueue */
- int bundled:1; /* bundle active ? */
-};
-#endif
-
/* the interface itself */
typedef struct isdn_net_dev_s {
isdn_net_local *local;
u_char mdmreg[ISDN_MODEM_ANZREG]; /* Modem-Registers */
char pmsn[ISDN_MSNLEN]; /* EAZ/MSNs Profile 0 */
char msn[ISDN_MSNLEN]; /* EAZ/MSN */
+ char plmsn[ISDN_LMSNLEN]; /* Listening MSNs Profile 0 */
+ char lmsn[ISDN_LMSNLEN]; /* Listening MSNs */
+ char cpn[ISDN_MSNLEN]; /* CalledPartyNumber on incoming call */
+ char connmsg[ISDN_CMSGLEN]; /* CONNECT-Msg from HL-Driver */
#ifdef CONFIG_ISDN_AUDIO
u_char vpar[10]; /* Voice-parameters */
int lastDLE; /* Flag for voice-coding: DLE seen */
int mdmcmdl; /* Length of Modem-Commandbuffer */
int pluscount; /* Counter for +++ sequence */
int lastplus; /* Timestamp of last + */
+ int carrierwait; /* Seconds of carrier waiting */
char mdmcmd[255]; /* Modem-Commandbuffer */
unsigned int charge; /* Charge units of current connection */
} atemu;
long pgrp; /* pgrp of opening process */
int online; /* 1 = B-Channel is up, drop data */
/* 2 = B-Channel is up, deliver d.*/
- int dialing; /* Dial in progress */
+ int dialing; /* Dial in progress or ATA */
int rcvsched; /* Receive needs schedule */
int isdn_driver; /* Index to isdn-driver */
int isdn_channel; /* Index to isdn-channel */
int xmit_count; /* # of chars in xmit_buf */
unsigned char *xmit_buf; /* transmit buffer */
struct sk_buff_head xmit_queue; /* transmit queue */
+ atomic_t xmit_lock; /* Semaphore for isdn_tty_write */
#ifdef CONFIG_ISDN_AUDIO
int vonline; /* Voice-channel status */
/* Bit 0 = recording */
void *adpcms; /* state for adpcm decompression */
void *adpcmr; /* state for adpcm compression */
void *dtmf_state; /* state for dtmf decoder */
+ void *silence_state; /* state for silence detection */
#endif
struct tty_struct *tty; /* Pointer to corresponding tty */
atemu emu; /* AT-emulator data */
struct termios normal_termios; /* For saving termios structs */
struct termios callout_termios;
- wait_queue_head_t open_wait, close_wait;
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+#else
+ wait_queue_head_t open_wait, close_wait;
+#endif
+ struct semaphore write_sem;
} modem_info;
#define ISDN_MODEM_WINSIZE 8
/*======================= End of ISDN-tty stuff ============================*/
-/*======================= Start of sync-ppp stuff ==========================*/
-
-
-#define NUM_RCV_BUFFS 64
-#define PPP_HARD_HDR_LEN 4
-
-#ifdef CONFIG_ISDN_PPP
-
-struct sqqueue {
- struct sqqueue *next;
- long sqno_start;
- long sqno_end;
- struct sk_buff *skb;
- long timer;
-};
-
-struct mpqueue {
- struct mpqueue *next;
- struct mpqueue *last;
- long sqno;
- struct sk_buff *skb;
- int BEbyte;
- unsigned long time;
-};
-
-struct ippp_buf_queue {
- struct ippp_buf_queue *next;
- struct ippp_buf_queue *last;
- char *buf; /* NULL here indicates end of queue */
- int len;
-};
-
-struct ippp_struct {
- struct ippp_struct *next_link;
- int state;
- struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */
- struct ippp_buf_queue *first; /* pointer to (current) first packet */
- struct ippp_buf_queue *last; /* pointer to (current) last used packet in queue */
- wait_queue_head_t wq;
- wait_queue_head_t wql;
- struct task_struct *tk;
- unsigned int mpppcfg;
- unsigned int pppcfg;
- unsigned int mru;
- unsigned int mpmru;
- unsigned int mpmtu;
- unsigned int maxcid;
- isdn_net_local *lp;
- int unit;
- int minor;
- long last_link_seqno;
- long mp_seqno;
- long range;
-#ifdef CONFIG_ISDN_PPP_VJ
- unsigned char *cbuf;
- struct slcompress *slcomp;
-#endif
- unsigned long debug;
- struct isdn_ppp_compressor *compressor,*link_compressor;
- void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
-};
-
-#endif
-
-/*======================== End of sync-ppp stuff ===========================*/
-
/*======================== Start of V.110 stuff ============================*/
#define V110_BUFSIZE 1024
char *private;
} infostruct;
+typedef struct isdn_module {
+ struct isdn_module *prev;
+ struct isdn_module *next;
+ char *name;
+ int (*get_free_channel)(int, int, int, int, int);
+ int (*free_channel)(int, int, int);
+ int (*status_callback)(isdn_ctrl *);
+ int (*command)(isdn_ctrl *);
+ int (*receive_callback)(int, int, struct sk_buff *);
+ int (*writebuf_skb)(int, int, int, struct sk_buff *);
+ int (*net_start_xmit)(struct sk_buff *, struct device *);
+ int (*net_receive)(struct device *, struct sk_buff *);
+ int (*net_open)(struct device *);
+ int (*net_close)(struct device *);
+ int priority;
+} isdn_module;
+
+#define DRV_FLAG_RUNNING 1
+#define DRV_FLAG_REJBUS 2
+#define DRV_FLAG_LOADED 4
+
/* Description of hardware-level-driver */
typedef struct {
- ulong flags; /* Flags */
- int channels; /* Number of channels */
- int reject_bus; /* Flag: Reject rejected call on bus*/
- wait_queue_head_t st_waitq; /* Wait-Queue for status-read's */
- int maxbufsize; /* Maximum Buffersize supported */
- unsigned long pktcount; /* Until now: unused */
- int running; /* Flag: Protocolcode running */
- int loaded; /* Flag: Driver loaded */
- int stavail; /* Chars avail on Status-device */
- isdn_if *interface; /* Interface to driver */
- int *rcverr; /* Error-counters for B-Ch.-receive */
- int *rcvcount; /* Byte-counters for B-Ch.-receive */
+ ulong online; /* Channel-Online flags */
+ ulong flags; /* Misc driver Flags */
+ int locks; /* Number of locks for this driver */
+ int channels; /* Number of channels */
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue *st_waitq; /* Wait-Queue for status-read's */
+#else
+ wait_queue_head_t st_waitq; /* Wait-Queue for status-read's */
+#endif
+ int maxbufsize; /* Maximum Buffersize supported */
+ unsigned long pktcount; /* Until now: unused */
+ int stavail; /* Chars avail on Status-device */
+ isdn_if *interface; /* Interface to driver */
+ int *rcverr; /* Error-counters for B-Ch.-receive */
+ int *rcvcount; /* Byte-counters for B-Ch.-receive */
#ifdef CONFIG_ISDN_AUDIO
- unsigned long DLEflag; /* Flags: Insert DLE at next read */
+ unsigned long DLEflag; /* Flags: Insert DLE at next read */
+#endif
+ struct sk_buff_head *rpqueue; /* Pointers to start of Rcv-Queue */
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue **rcv_waitq; /* Wait-Queues for B-Channel-Reads */
+ struct wait_queue **snd_waitq; /* Wait-Queue for B-Channel-Send's */
+#else
+ wait_queue_head_t *rcv_waitq; /* Wait-Queues for B-Channel-Reads */
+ wait_queue_head_t *snd_waitq; /* Wait-Queue for B-Channel-Send's */
#endif
- struct sk_buff_head *rpqueue; /* Pointers to start of Rcv-Queue */
- wait_queue_head_t *rcv_waitq; /* array of Wait-Queues for B-Channel-Reads */
- wait_queue_head_t *snd_waitq; /* array of Wait-Queue for B-Channel-Sends */
- char msn2eaz[10][ISDN_MSNLEN]; /* Mapping-Table MSN->EAZ */
+ char msn2eaz[10][ISDN_MSNLEN]; /* Mapping-Table MSN->EAZ */
} driver;
/* Main driver-data */
typedef struct isdn_devt {
- unsigned short flags; /* Bitmapped Flags: */
- /* */
- int drivers; /* Current number of drivers */
- int channels; /* Current number of channels */
- int net_verbose; /* Verbose-Flag */
- int modempoll; /* Flag: tty-read active */
- int tflags; /* Timer-Flags: */
- /* see ISDN_TIMER_..defines */
- int global_flags;
- infostruct *infochain; /* List of open info-devs. */
- wait_queue_head_t info_waitq; /* Wait-Queue for isdninfo */
- struct timer_list timer; /* Misc.-function Timer */
- int chanmap[ISDN_MAX_CHANNELS];/* Map minor->device-channel */
- int drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index */
- int usage[ISDN_MAX_CHANNELS]; /* Used by tty/ip/voice */
- char num[ISDN_MAX_CHANNELS][ISDN_MSNLEN];
- /* Remote number of active ch.*/
- int m_idx[ISDN_MAX_CHANNELS]; /* Index for mdm.... */
- driver *drv[ISDN_MAX_DRIVERS]; /* Array of drivers */
- isdn_net_dev *netdev; /* Linked list of net-if's */
- char drvid[ISDN_MAX_DRIVERS][20];/* Driver-ID */
- struct task_struct *profd; /* For iprofd */
- modem mdm; /* tty-driver-data */
- isdn_net_dev *rx_netdev[ISDN_MAX_CHANNELS]; /* rx netdev-pointers */
- isdn_net_dev *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers */
- ulong ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes */
- ulong obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes */
- int v110emu[ISDN_MAX_CHANNELS];/* V.110 emulator-mode 0=none */
- atomic_t v110use[ISDN_MAX_CHANNELS];/* Usage-Semaphore for stream */
- isdn_v110_stream *v110[ISDN_MAX_CHANNELS]; /* V.110 private data */
- struct semaphore sem; /* serialize list access*/
+ unsigned short flags; /* Bitmapped Flags: */
+ /* */
+ int drivers; /* Current number of drivers */
+ int channels; /* Current number of channels */
+ int net_verbose; /* Verbose-Flag */
+ int modempoll; /* Flag: tty-read active */
+ int tflags; /* Timer-Flags: */
+ /* see ISDN_TIMER_..defines */
+ int global_flags;
+ infostruct *infochain; /* List of open info-devs. */
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue *info_waitq; /* Wait-Queue for isdninfo */
+#else
+ wait_queue_head_t info_waitq; /* Wait-Queue for isdninfo */
+#endif
+ struct timer_list timer; /* Misc.-function Timer */
+ int chanmap[ISDN_MAX_CHANNELS];/* Map minor->device-channel */
+ int drvmap[ISDN_MAX_CHANNELS]; /* Map minor->driver-index */
+ int usage[ISDN_MAX_CHANNELS]; /* Used by tty/ip/voice */
+ char num[ISDN_MAX_CHANNELS][ISDN_MSNLEN];
+ /* Remote number of active ch.*/
+ int m_idx[ISDN_MAX_CHANNELS]; /* Index for mdm.... */
+ driver *drv[ISDN_MAX_DRIVERS]; /* Array of drivers */
+ isdn_net_dev *netdev; /* Linked list of net-if's */
+ char drvid[ISDN_MAX_DRIVERS][20];/* Driver-ID */
+ struct task_struct *profd; /* For iprofd */
+ modem mdm; /* tty-driver-data */
+ isdn_net_dev *rx_netdev[ISDN_MAX_CHANNELS]; /* rx netdev-pointers */
+ isdn_net_dev *st_netdev[ISDN_MAX_CHANNELS]; /* stat netdev-pointers */
+ ulong ibytes[ISDN_MAX_CHANNELS]; /* Statistics incoming bytes */
+ ulong obytes[ISDN_MAX_CHANNELS]; /* Statistics outgoing bytes */
+ int v110emu[ISDN_MAX_CHANNELS];/* V.110 emulator-mode 0=none */
+ atomic_t v110use[ISDN_MAX_CHANNELS];/* Usage-Semaphore for stream */
+ isdn_v110_stream *v110[ISDN_MAX_CHANNELS]; /* V.110 private data */
+ struct semaphore sem; /* serialize list access*/
+ isdn_module *modules;
} isdn_dev;
extern isdn_dev *dev;
+
/* Utility-Macros */
#define MIN(a,b) ((a<b)?a:b)
#define MAX(a,b) ((a>b)?a:b)
-
#endif /* __KERNEL__ */
#endif /* isdn_h */
--- /dev/null
+/* isdn_budget.h
+ *
+ * Linux ISDN subsystem, budget-accounting for network interfaces.
+ *
+ * Copyright 1997 by Christian Lademann <cal@zls.de>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+30.06.97:cal:angelegt
+04.11.97:cal:budget.period: int --> time_t
+*/
+
+#ifndef __isdn_budget_h__
+#define __isdn_budget_h__
+
+#include <linux/types.h>
+
+#define ISDN_BUDGET_DIAL 0
+#define ISDN_BUDGET_CHARGE 1
+#define ISDN_BUDGET_ONLINE 2
+#define ISDN_BUDGET_NUM_BUDGET 3
+
+#define ISDN_BUDGET_INIT 0
+#define ISDN_BUDGET_CHECK_DIAL 1
+#define ISDN_BUDGET_CHECK_CHARGE 2
+#define ISDN_BUDGET_CHECK_ONLINE 3
+#define ISDN_BUDGET_START_ONLINE 10
+
+#define ISDN_BUDGET_SET_BUDGET 0
+#define ISDN_BUDGET_GET_BUDGET 1
+
+typedef struct {
+ char name [9]; /* Interface */
+ int command, /* subcommand */
+ budget, /* budget-nr. */
+ amount, /* set/get budget-amount */
+ used; /* set/get used amount */
+ time_t period, /* set/get length of period */
+ period_started; /* set/get startpoint of period */
+} isdn_ioctl_budget;
+
+#ifdef __KERNEL__
+extern int isdn_net_budget(int, struct device *);
+extern int isdn_budget_ioctl(isdn_ioctl_budget *);
+#endif /* __KERNEL__ */
+
+#endif /* __isdn_budget_h__ */
--- /dev/null
+/*
+ * $Id: isdn_lzscomp.h,v 1.1 1998/07/08 16:52:33 hipp Exp $
+ *
+ * Header for isdn_lzscomp.c
+ * Concentrated here to not mess up half a dozen kernel headers with code
+ * snippets
+ *
+ */
+
+#define CI_LZS_COMPRESS 17
+#define CILEN_LZS_COMPRESS 5
+
+#define LZS_CMODE_NONE 0
+#define LZS_CMODE_LCB 1
+#define LZS_CMODE_CRC 2
+#define LZS_CMODE_SEQNO 3 /* MUST be implemented (default) */
+#define LZS_CMODE_EXT 4 /* Seems to be what Win0.95 uses */
+
+#define LZS_COMP_MAX_HISTS 1 /* Don't waste peers ressources */
+#define LZS_COMP_DEF_HISTS 1 /* Most likely to negotiate */
+#define LZS_DECOMP_MAX_HISTS 32 /* More is really nonsense */
+#define LZS_DECOMP_DEF_HISTS 8 /* If we get it, this may be optimal */
+
+#define LZS_HIST_BYTE1(word) (word>>8) /* Just for better reading */
+#define LZS_HIST_BYTE2(word) (word&0xff) /* of this big endian stuff */
+#define LZS_HIST_WORD(b1,b2) ((b1<<8)|b2) /* (network byte order rulez) */
+/* -*- mode: c; c-basic-offset: 2 -*- */
+
#ifndef _LINUX_ISDN_PPP_H
#define _LINUX_ISDN_PPP_H
-extern int isdn_ppp_dial_slave(char *);
-extern int isdn_ppp_hangup_slave(char *);
#define CALLTYPE_INCOMING 0x1
#define CALLTYPE_OUTGOING 0x2
#define CALLTYPE_CALLBACK 0x4
+#define IPPP_VERSION "2.2.0"
+
struct pppcallinfo
{
- int calltype;
- unsigned char local_num[64];
- unsigned char remote_num[64];
- int charge_units;
+ int calltype;
+ unsigned char local_num[64];
+ unsigned char remote_num[64];
+ int charge_units;
};
#define PPPIOCGCALLINFO _IOWR('t',128,struct pppcallinfo)
#define PPPIOCSMPFLAGS _IOW('t',131,int)
#define PPPIOCSMPMTU _IOW('t',132,int)
#define PPPIOCSMPMRU _IOW('t',133,int)
-#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long)
+#define PPPIOCGCOMPRESSORS _IOR('t',134,unsigned long [8])
#define PPPIOCSCOMPRESSOR _IOW('t',135,int)
+#define PPPIOCGIFNAME _IOR('t',136, char [IFNAMSIZ] )
#define PPP_MP 0x003d
#define PPP_LINK_COMP 0x00fb
+#define PPP_LINK_CCP 0x80fb
#define SC_MP_PROT 0x00000200
#define SC_REJ_MP_PROT 0x00000400
#define SC_OUT_SHORT_SEQ 0x00000800
#define SC_IN_SHORT_SEQ 0x00004000
+#define SC_DECOMP_ON 0x01
+#define SC_COMP_ON 0x02
+#define SC_DECOMP_DISCARD 0x04
+#define SC_COMP_DISCARD 0x08
+#define SC_LINK_DECOMP_ON 0x10
+#define SC_LINK_COMP_ON 0x20
+#define SC_LINK_DECOMP_DISCARD 0x40
+#define SC_LINK_COMP_DISCARD 0x80
+
+#define DECOMP_ERR_NOMEM (-10)
+
#define MP_END_FRAG 0x40
#define MP_BEGIN_FRAG 0x80
+#define ISDN_PPP_COMP_MAX_OPTIONS 16
+
+#define IPPP_COMP_FLAG_XMIT 0x1
+#define IPPP_COMP_FLAG_LINK 0x2
+
+struct isdn_ppp_comp_data {
+ int num;
+ unsigned char options[ISDN_PPP_COMP_MAX_OPTIONS];
+ int optlen;
+ int flags;
+};
+
#ifdef __KERNEL__
+
+/*
+ * We need a way for the decompressor to influence the generation of CCP
+ * Reset-Requests in a variety of ways. The decompressor is already returning
+ * a lot of information (generated skb length, error conditions) so we use
+ * another parameter. This parameter is a pointer to a structure which is
+ * to be marked valid by the decompressor and only in this case is ever used.
+ * Furthermore, the only case where this data is used is when the decom-
+ * pressor returns DECOMP_ERROR.
+ *
+ * We use this same struct for the reset entry of the compressor to commu-
+ * nicate to its caller how to deal with sending of a Reset Ack. In this
+ * case, expra is not used, but other options still apply (supressing
+ * sending with rsend, appending arbitrary data, etc).
+ */
+
+#define IPPP_RESET_MAXDATABYTES 32
+
+struct isdn_ppp_resetparams {
+ unsigned char valid:1; /* rw Is this structure filled at all ? */
+ unsigned char rsend:1; /* rw Should we send one at all ? */
+ unsigned char idval:1; /* rw Is the id field valid ? */
+ unsigned char dtval:1; /* rw Is the data field valid ? */
+ unsigned char expra:1; /* rw Is an Ack expected for this Req ? */
+ unsigned char id; /* wo Send CCP ResetReq with this id */
+ unsigned short maxdlen; /* ro Max bytes to be stored in data field */
+ unsigned short dlen; /* rw Bytes stored in data field */
+ unsigned char *data; /* wo Data for ResetReq info field */
+};
+
/*
* this is an 'old friend' from ppp-comp.h under a new name
* check the original include for more information
*/
struct isdn_ppp_compressor {
- struct isdn_ppp_compressor *next,*prev;
- int num; /* CCP compression protocol number */
- void *(*comp_alloc) (unsigned char *options, int opt_len);
- void (*comp_free) (void *state);
- int (*comp_init) (void *state, unsigned char *options, int opt_len,
- int unit, int opthdr, int debug);
- void (*comp_reset) (void *state);
- int (*compress) (void *state,struct sk_buff *in, struct sk_buff *skb_out,
- int proto);
- void (*comp_stat) (void *state, struct compstat *stats);
- void *(*decomp_alloc) (unsigned char *options, int opt_len);
- void (*decomp_free) (void *state);
- int (*decomp_init) (void *state, unsigned char *options,
- int opt_len, int unit, int opthdr, int mru, int debug);
- void (*decomp_reset) (void *state);
- int (*decompress) (void *state, unsigned char *ibuf, int isize, unsigned char *obuf, int osize);
- void (*incomp) (void *state, unsigned char *ibuf, int icnt);
- void (*decomp_stat) (void *state, struct compstat *stats);
+ struct isdn_ppp_compressor *next, *prev;
+ int num; /* CCP compression protocol number */
+
+ void *(*alloc) (struct isdn_ppp_comp_data *);
+ void (*free) (void *state);
+ int (*init) (void *state, struct isdn_ppp_comp_data *,
+ int unit,int debug);
+
+ /* The reset entry needs to get more exact information about the
+ ResetReq or ResetAck it was called with. The parameters are
+ obvious. If reset is called without a Req or Ack frame which
+ could be handed into it, code MUST be set to 0. Using rsparm,
+ the reset entry can control if and how a ResetAck is returned. */
+
+ void (*reset) (void *state, unsigned char code, unsigned char id,
+ unsigned char *data, unsigned len,
+ struct isdn_ppp_resetparams *rsparm);
+
+ int (*compress) (void *state, struct sk_buff *in,
+ struct sk_buff *skb_out, int proto);
+
+ int (*decompress) (void *state,struct sk_buff *in,
+ struct sk_buff *skb_out,
+ struct isdn_ppp_resetparams *rsparm);
+
+ void (*incomp) (void *state, struct sk_buff *in,int proto);
+ void (*stat) (void *state, struct compstat *stats);
};
extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *);
extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *);
+extern int isdn_ppp_dial_slave(char *);
+extern int isdn_ppp_hangup_slave(char *);
+
+struct ippp_bundle {
+ int mp_mrru; /* unused */
+ struct mpqueue *last; /* currently defined in isdn_net_dev */
+ int min; /* currently calculated 'on the fly' */
+ long next_num; /* we wanna see this seq.-number next */
+ struct sqqueue *sq;
+ int modify:1; /* set to 1 while modifying sqqueue */
+ int bundled:1; /* bundle active ? */
+};
+
+#define NUM_RCV_BUFFS 64
+
+struct sqqueue {
+ struct sqqueue *next;
+ long sqno_start;
+ long sqno_end;
+ struct sk_buff *skb;
+ long timer;
+};
+
+struct mpqueue {
+ struct mpqueue *next;
+ struct mpqueue *last;
+ long sqno;
+ struct sk_buff *skb;
+ int BEbyte;
+ unsigned long time;
+};
+
+struct ippp_buf_queue {
+ struct ippp_buf_queue *next;
+ struct ippp_buf_queue *last;
+ char *buf; /* NULL here indicates end of queue */
+ int len;
+};
+
+/* The data structure for one CCP reset transaction */
+enum ippp_ccp_reset_states {
+ CCPResetIdle,
+ CCPResetSentReq,
+ CCPResetRcvdReq,
+ CCPResetSentAck,
+ CCPResetRcvdAck
+};
+
+struct ippp_ccp_reset_state {
+ enum ippp_ccp_reset_states state; /* State of this transaction */
+ struct ippp_struct *is; /* Backlink to device stuff */
+ unsigned char id; /* Backlink id index */
+ unsigned char ta:1; /* The timer is active (flag) */
+ unsigned char expra:1; /* We expect a ResetAck at all */
+ int dlen; /* Databytes stored in data */
+ struct timer_list timer; /* For timeouts/retries */
+ /* This is a hack but seems sufficient for the moment. We do not want
+ to have this be yet another allocation for some bytes, it is more
+ memory management overhead than the whole mess is worth. */
+ unsigned char data[IPPP_RESET_MAXDATABYTES];
+};
+
+/* The data structure keeping track of the currently outstanding CCP Reset
+ transactions. */
+struct ippp_ccp_reset {
+ struct ippp_ccp_reset_state *rs[256]; /* One per possible id */
+ unsigned char lastid; /* Last id allocated by the engine */
+};
+
+struct ippp_struct {
+ struct ippp_struct *next_link;
+ int state;
+ struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */
+ struct ippp_buf_queue *first; /* pointer to (current) first packet */
+ struct ippp_buf_queue *last; /* pointer to (current) last used packet in queue */
+#if LINUX_VERSION_CODE < 131841
+ struct wait_queue *wq;
+#else
+ wait_queue_head_t wq;
+#endif
+ struct task_struct *tk;
+ unsigned int mpppcfg;
+ unsigned int pppcfg;
+ unsigned int mru;
+ unsigned int mpmru;
+ unsigned int mpmtu;
+ unsigned int maxcid;
+ struct isdn_net_local_s *lp;
+ int unit;
+ int minor;
+ long last_link_seqno;
+ long mp_seqno;
+ long range;
+#ifdef CONFIG_ISDN_PPP_VJ
+ unsigned char *cbuf;
+ struct slcompress *slcomp;
+#endif
+ unsigned long debug;
+ struct isdn_ppp_compressor *compressor,*decompressor;
+ struct isdn_ppp_compressor *link_compressor,*link_decompressor;
+ void *decomp_stat,*comp_stat,*link_decomp_stat,*link_comp_stat;
+ struct ippp_ccp_reset *reset; /* Allocated on demand, may never be needed */
+ unsigned long compflags;
+};
#endif /* __KERNEL__ */
--- /dev/null
+/* isdn_timru.h
+ *
+ * Linux ISDN subsystem, timeout-rules for network interfaces.
+ *
+ * Copyright 1997 by Christian Lademann <cal@zls.de>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+02.06.97:cal:ISDN_TIMRU_PACKET_NONE def., ISDN_TIMRU_PACKET_* inkr.
+*/
+
+#ifndef __isdn_timru_h__
+#define __isdn_timru_h__
+
+#define ISDN_TIMRU_PACKET_NONE 0
+#define ISDN_TIMRU_PACKET_SKB 1
+#define ISDN_TIMRU_PACKET_PPP 2
+#define ISDN_TIMRU_PACKET_PPP_NO_HEADER 3
+
+#define ISDN_TIMRU_BRINGUP 0
+#define ISDN_TIMRU_KEEPUP_IN 1
+#define ISDN_TIMRU_KEEPUP_OUT 2
+#define ISDN_TIMRU_BRINGDOWN 3
+#define ISDN_TIMRU_NUM_CHECK 4
+
+#define ISDN_TIMRU_PROTFAM_WILDCARD 0
+#define ISDN_TIMRU_PROTFAM_IP 1
+#define ISDN_TIMRU_PROTFAM_PPP 2
+#define ISDN_TIMRU_PROTFAM_IPX 3
+#define ISDN_TIMRU_NUM_PROTFAM 4
+
+#define ISDN_TIMRU_IP_WILDCARD 0
+#define ISDN_TIMRU_IP_ICMP 1
+#define ISDN_TIMRU_IP_TCP 2
+#define ISDN_TIMRU_IP_UDP 3
+
+#define ISDN_TIMRU_PPP_WILDCARD 0
+#define ISDN_TIMRU_PPP_IPCP 1
+#define ISDN_TIMRU_PPP_IPXCP 2
+#define ISDN_TIMRU_PPP_CCP 3
+#define ISDN_TIMRU_PPP_LCP 4
+#define ISDN_TIMRU_PPP_PAP 5
+#define ISDN_TIMRU_PPP_LQR 6
+#define ISDN_TIMRU_PPP_CHAP 7
+
+typedef struct {
+ struct in_addr saddr, /* Source Address */
+ smask, /* Source Subnetmask */
+ daddr, /* Dest. Address */
+ dmask; /* Dest. Subnetmask */
+ ushort protocol; /* TCP, UDP, ... */
+ union {
+ struct {
+ __u16 s_from, /* Source Port */
+ s_to,
+ d_from,
+ d_to;
+ } port;
+ struct {
+ __u8 from, /* ICMP-Type */
+ to;
+ } type;
+ } pt;
+} isdn_timeout_rule_ip;
+
+
+typedef struct {
+ ushort protocol; /* IPCP, LCP, ... */
+} isdn_timeout_rule_ppp;
+
+
+typedef struct isdn_timeout_rule_s {
+ struct isdn_timeout_rule_s *next, /* Pointer to next rule */
+ *prev; /* Pointer to previous rule */
+ ushort type, /* BRINGUP, KEEPUP_*, ... */
+ neg;
+ int timeout; /* Timeout value */
+ ushort protfam; /* IP, IPX, PPP, ... */
+ union {
+ isdn_timeout_rule_ip ip; /* IP-Rule */
+ isdn_timeout_rule_ppp ppp; /* PPP-Rule */
+ } rule; /* Prot.-specific rule */
+} isdn_timeout_rule;
+
+
+typedef struct {
+ char name [9]; /* Interface */
+ int where, /* 0/1: add to start/end of list, -1: handle default */
+ type,
+ protfam,
+ index,
+ defval;
+ isdn_timeout_rule rule; /* Rule */
+} isdn_ioctl_timeout_rule;
+
+#ifdef __KERNEL__
+extern int isdn_net_recalc_timeout(int, int, struct device *, void *, ulong);
+extern int isdn_timru_alloc_timeout_rules(struct device *);
+extern int isdn_timru_ioctl_add_rule(isdn_ioctl_timeout_rule *);
+extern int isdn_timru_ioctl_del_rule(isdn_ioctl_timeout_rule *);
+extern int isdn_timru_ioctl_get_rule(isdn_ioctl_timeout_rule *);
+#endif /* __KERNEL__ */
+
+#endif /* __isdn_timru_h__ */
-/* X25 changes:
- Added constants ISDN_PROTO_L2_X25DTE/DCE and corresponding ISDN_FEATURE_..
- */
-
-/* $Id: isdnif.h,v 1.23 1998/02/20 17:36:52 fritz Exp $
+/* $Id: isdnif.h,v 1.25 1998/06/17 19:51:55 he Exp $
*
* Linux ISDN subsystem
*
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Log: isdnif.h,v $
+ * Revision 1.25 1998/06/17 19:51:55 he
+ * merged with 2.1.10[34] (cosmetics and udelay() -> mdelay())
+ * brute force fix to avoid Ugh's in isdn_tty_write()
+ * cleaned up some dead code
+ *
+ * Revision 1.24 1998/03/19 13:18:57 keil
+ * Start of a CAPI like interface for supplementary Service
+ * first service: SUSPEND
+ *
* Revision 1.23 1998/02/20 17:36:52 fritz
* Added L2-protocols for V.110, changed FEATURE-Flag-constants.
*
#define ISDN_PROTO_L2_V11096 7 /* V.110 bitrate adaption 9600 Baud */
#define ISDN_PROTO_L2_V11019 8 /* V.110 bitrate adaption 19200 Baud */
#define ISDN_PROTO_L2_V11038 9 /* V.110 bitrate adaption 38400 Baud */
+#define ISDN_PROTO_L2_MODEM 10 /* Analog Modem on Board */
#define ISDN_PROTO_L2_MAX 15 /* Max. 16 Protocols */
/*
#define ISDN_CMD_UNLOCK 15 /* Release usage-lock */
#define ISDN_CMD_SUSPEND 16 /* Suspend connection */
#define ISDN_CMD_RESUME 17 /* Resume connection */
+#define CAPI_PUT_MESSAGE 18 /* CAPI message send down or up */
/*
* Status-Values delivered from lowlevel to linklevel via
#define ISDN_FEATURE_L2_V11096 (0x0001 << ISDN_PROTO_L2_V11096)
#define ISDN_FEATURE_L2_V11019 (0x0001 << ISDN_PROTO_L2_V11019)
#define ISDN_FEATURE_L2_V11038 (0x0001 << ISDN_PROTO_L2_V11038)
+#define ISDN_FEATURE_L2_MODEM (0x0001 << ISDN_PROTO_L2_MODEM)
#define ISDN_FEATURE_L2_MASK (0x0FFFF) /* Max. 16 protocols */
#define ISDN_FEATURE_L2_SHIFT (0)
#define ISDN_FEATURE_P_SHIFT (24)
typedef struct setup_parm {
- char phone[32]; /* Remote Phone-Number */
- char eazmsn[32]; /* Local EAZ or MSN */
+ unsigned char phone[32]; /* Remote Phone-Number */
+ unsigned char eazmsn[32]; /* Local EAZ or MSN */
unsigned char si1; /* Service Indicator 1 */
unsigned char si2; /* Service Indicator 2 */
unsigned char plan; /* Numbering plan */
unsigned char screen; /* Screening info */
} setup_parm;
+/* CAPI structs */
+
+/* this is compatible to the old union size */
+#define MAX_CAPI_PARA_LEN 50
+
+typedef struct {
+ /* Header */
+ __u16 Length;
+ __u16 ApplId;
+ __u8 Command;
+ __u8 Subcommand;
+ __u16 Messagenumber;
+
+ /* Parameter */
+ union {
+ __u32 Controller;
+ __u32 PLCI;
+ __u32 NCCI;
+ } adr;
+ __u8 para[MAX_CAPI_PARA_LEN];
+} capi_msg;
+
+
/*
* Structure for exchanging above infos
*
union {
ulong errcode; /* Type of error with STAT_L1ERR */
int length; /* Amount of bytes sent with STAT_BSENT */
- char num[50]; /* Additional Data */
- setup_parm setup;
+ u_char num[50]; /* Additional Data */
+ setup_parm setup; /* For SETUP msg */
+ capi_msg cmsg; /* For CAPI like messages */
} parm;
} isdn_ctrl;
* Interrupt support added 1993 Nigel Gamble
*/
-/* Magic numbers for defining port-device mappings */
-#define LP_PARPORT_UNSPEC -4
-#define LP_PARPORT_AUTO -3
-#define LP_PARPORT_OFF -2
-#define LP_PARPORT_NONE -1
-
/*
* Per POSIX guidelines, this module reserves the LP and lp prefixes
* These are the lp_table[minor].flags flags...
#define LP_TIMEOUT_INTERRUPT (60 * HZ)
#define LP_TIMEOUT_POLLED (10 * HZ)
+#ifdef __KERNEL__
+
+/* Magic numbers for defining port-device mappings */
+#define LP_PARPORT_UNSPEC -4
+#define LP_PARPORT_AUTO -3
+#define LP_PARPORT_OFF -2
+#define LP_PARPORT_NONE -1
+
#define LP_F(minor) lp_table[(minor)].flags /* flags for busy, etc. */
#define LP_CHAR(minor) lp_table[(minor)].chars /* busy timeout */
#define LP_TIME(minor) lp_table[(minor)].time /* wait time */
extern int lp_init(void);
#endif
+
+#endif
struct Qdisc *qdisc_list;
unsigned long tx_queue_len; /* Max frames per queue allowed */
+ /* hard_start_xmit synchronizer */
+ spinlock_t xmit_lock;
+
/* Pointers to interface service routines. */
int (*open)(struct device *dev);
int (*stop)(struct device *dev);
extern struct device loopback_dev; /* The loopback */
extern struct device *dev_base; /* All devices */
+extern rwlock_t dev_base_lock; /* Device list lock */
extern struct packet_type *ptype_base[16]; /* Hashed types */
extern int netdev_dropping;
extern int net_cpu_congestion;
static inline unsigned long page_address(struct page * page)
{
- return PAGE_OFFSET + PAGE_SIZE * (page - mem_map);
+ return PAGE_OFFSET + ((page - mem_map) << PAGE_SHIFT);
}
/*
rwlock_t cad_lock;
};
+struct parport_driver {
+ const char *name;
+ void (*attach) (struct parport *);
+ void (*detach) (struct parport *);
+ struct parport_driver *next;
+};
+
/* parport_register_port registers a new parallel port at the given address (if
* one does not already exist) and returns a pointer to it. This entails
* claiming the I/O region, IRQ and DMA.
struct parport *parport_register_port(unsigned long base, int irq, int dma,
struct parport_operations *ops);
+/* Once a registered port is ready for high-level drivers to use, the
+ low-level driver that registered it should announce it. This will
+ call the high-level drivers' attach() functions (after things like
+ determining the IEEE 1284.3 topology of the port and collecting
+ DeviceIDs). */
+void parport_announce_port (struct parport *port);
+
/* Unregister a port. */
extern void parport_unregister_port(struct parport *port);
*/
struct parport *parport_enumerate(void);
+/* Register a new high-level driver. */
+extern int parport_register_driver (struct parport_driver *);
+
+/* Unregister a high-level driver. */
+extern void parport_unregister_driver (struct parport_driver *);
+
/* parport_register_device declares that a device is connected to a port, and
* tells the kernel all it needs to know.
* pf is the preemption function (may be NULL for no callback)
#define PARPORT_FLAG_COMA (1<<0)
#define PARPORT_FLAG_EXCL (1<<1) /* EXCL driver registered. */
-extern void parport_parse_irqs(int, const char *[], int irqval[]);
+extern int parport_parse_irqs(int, const char *[], int irqval[]);
+extern int parport_parse_dmas(int, const char *[], int irqval[]);
extern int parport_ieee1284_nibble_mode_ok(struct parport *, unsigned char);
extern int parport_wait_peripheral(struct parport *, unsigned char, unsigned
char);
#include <asm/system.h>
#include <asm/semaphore.h>
#include <asm/page.h>
+#include <asm/ptrace.h>
#include <linux/smp.h>
#include <linux/tty.h>
/* signals */ SPIN_LOCK_UNLOCKED, &init_signals, {{0}}, {{0}}, NULL, &init_task.sigqueue, 0, 0, \
}
+#ifndef INIT_TASK_SIZE
+# define INIT_TASK_SIZE 2048*sizeof(long)
+#endif
+
union task_union {
struct task_struct task;
- unsigned long stack[2048];
+ unsigned long stack[INIT_TASK_SIZE/sizeof(long)];
};
extern union task_union init_task_union;
static inline int on_sig_stack(unsigned long sp)
{
- return (sp >= current->sas_ss_sp
- && sp < current->sas_ss_sp + current->sas_ss_size);
+ return (sp - current->sas_ss_sp < current->sas_ss_size);
}
static inline int sas_ss_flags(unsigned long sp)
#define _LINUX_SKBUFF_H
#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
#include <linux/time.h>
#include <asm/atomic.h>
unsigned char *tmp=skb->tail;
skb->tail+=len;
skb->len+=len;
- if(skb->tail>skb->end)
- {
- __label__ here;
- skb_over_panic(skb, len, &&here);
-here: ;
+ if(skb->tail>skb->end) {
+ skb_over_panic(skb, len, current_text_addr());
}
return tmp;
}
{
skb->data-=len;
skb->len+=len;
- if(skb->data<skb->head)
- {
- __label__ here;
- skb_under_panic(skb, len, &&here);
-here: ;
+ if(skb->data<skb->head) {
+ skb_under_panic(skb, len, current_text_addr());
}
return skb->data;
}
#ifdef __KERNEL__
-#include <asm/page.h>
-#include <asm/spinlock.h>
+#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/stddef.h>
+#include <asm/page.h>
+#include <asm/spinlock.h>
+#include <asm/processor.h>
+
/*
* Temporary debugging help until all code is converted to the new
* waitqueue usage.
static inline void init_waitqueue_head(wait_queue_head_t *q)
{
#if WAITQUEUE_DEBUG
- __label__ __x;
if (!q)
WQ_BUG();
#endif
INIT_LIST_HEAD(&q->task_list);
#if WAITQUEUE_DEBUG
q->__magic = (long)&q->__magic;
- __x: q->__creator = (long)&&__x;
+ q->__creator = (long)current_text_addr();
#endif
}
#endif
};
-extern struct rtable *rt_hash_table[RT_HASH_DIVISOR];
-
struct ip_rt_acct
{
__u32 o_bytes;
struct sock ** list;
struct sock * gc_tree;
int inflight;
+ atomic_t user_count;
};
#ifdef CONFIG_NETLINK
#define SOCK_DEBUG(sk, msg...) do { } while (0)
#endif
+/* This is the per-socket lock. The spinlock provides a synchronization
+ * between user contexts and software interrupt processing, whereas the
+ * mini-semaphore synchronizes multiple users amongst themselves.
+ */
+typedef struct {
+ spinlock_t slock;
+ unsigned int users;
+ wait_queue_head_t wq;
+} socket_lock_t;
+
+#define sock_lock_init(__sk) \
+do { spin_lock_init(&((__sk)->lock.slock)); \
+ (__sk)->lock.users = 0; \
+ init_waitqueue_head(&((__sk)->lock.wq)); \
+} while(0);
+
struct sock {
/* This must be first. */
struct sock *sklist_next;
unsigned char reuse, /* SO_REUSEADDR setting */
nonagle; /* Disable Nagle algorithm? */
- atomic_t sock_readers; /* User count */
+ socket_lock_t lock; /* Synchronizer... */
int rcvbuf; /* Size of receive buffer in bytes */
wait_queue_head_t *sleep; /* Sock wait queue */
int hashent;
struct sock *pair;
- /* Error and backlog packet queues, rarely used. */
- struct sk_buff_head back_log,
- error_queue;
+ /* The backlog queue is special, it is always used with
+ * the per-socket spinlock held and requires low latency
+ * access. Therefore we special case it's implementation.
+ */
+ struct {
+ struct sk_buff *head;
+ struct sk_buff *tail;
+ } backlog;
+
+ /* Error queue, rarely used. */
+ struct sk_buff_head error_queue;
struct proto *prot;
void (*destruct)(struct sock *sk);
};
+/* The per-socket spinlock must be held here. */
+#define sk_add_backlog(__sk, __skb) \
+do { if((__sk)->backlog.tail == NULL) { \
+ (__sk)->backlog.head = \
+ (__sk)->backlog.tail = (__skb); \
+ } else { \
+ ((__sk)->backlog.tail)->next = (__skb); \
+ (__sk)->backlog.tail = (__skb); \
+ } \
+ (__skb)->next = NULL; \
+} while(0)
+
/* IP protocol blocks we attach to sockets.
* socket layer -> transport layer interface
* transport -> network interface is defined by struct inet_proto
/* Per-protocol hash table implementations use this to make sure
* nothing changes.
*/
-#define SOCKHASH_LOCK() start_bh_atomic()
-#define SOCKHASH_UNLOCK() end_bh_atomic()
+extern rwlock_t sockhash_lock;
+#define SOCKHASH_LOCK_READ() read_lock_bh(&sockhash_lock)
+#define SOCKHASH_UNLOCK_READ() read_unlock_bh(&sockhash_lock)
+#define SOCKHASH_LOCK_WRITE() write_lock_bh(&sockhash_lock)
+#define SOCKHASH_UNLOCK_WRITE() write_unlock_bh(&sockhash_lock)
+
+/* The following variants must _only_ be used when you know you
+ * can only be executing in a BH context.
+ */
+#define SOCKHASH_LOCK_READ_BH() read_lock(&sockhash_lock)
+#define SOCKHASH_UNLOCK_READ_BH() read_unlock(&sockhash_lock)
+#define SOCKHASH_LOCK_WRITE_BH() write_lock(&sockhash_lock)
+#define SOCKHASH_UNLOCK_WRITE_BH() write_unlock(&sockhash_lock)
/* Some things in the kernel just want to get at a protocols
* entire socket list commensurate, thus...
*/
static __inline__ void add_to_prot_sklist(struct sock *sk)
{
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
if(!sk->sklist_next) {
struct proto *p = sk->prot;
if(sk->prot->highestinuse < sk->prot->inuse)
sk->prot->highestinuse = sk->prot->inuse;
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static __inline__ void del_from_prot_sklist(struct sock *sk)
{
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
if(sk->sklist_next) {
sk->sklist_next->sklist_prev = sk->sklist_prev;
sk->sklist_prev->sklist_next = sk->sklist_next;
sk->sklist_next = NULL;
sk->prot->inuse--;
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
-/*
- * Used by processes to "lock" a socket state, so that
+/* Used by processes to "lock" a socket state, so that
* interrupts and bottom half handlers won't change it
* from under us. It essentially blocks any incoming
* packets, so that we won't get any new data or any
* packets that change the state of the socket.
*
- * Note the 'barrier()' calls: gcc may not move a lock
- * "downwards" or a unlock "upwards" when optimizing.
+ * While locked, BH processing will add new packets to
+ * the backlog queue. This queue is processed by the
+ * owner of the socket lock right before it is released.
*/
-extern void __release_sock(struct sock *sk);
+extern void lock_sock(struct sock *sk);
+extern void release_sock(struct sock *sk);
-static inline void lock_sock(struct sock *sk)
-{
-#if 0
-/* debugging code: the test isn't even 100% correct, but it can catch bugs */
-/* Note that a double lock is ok in theory - it's just _usually_ a bug */
- if (atomic_read(&sk->sock_readers)) {
- __label__ here;
- printk("double lock on socket at %p\n", &&here);
-here:
- }
-#endif
- atomic_inc(&sk->sock_readers);
- synchronize_bh();
-}
-
-static inline void release_sock(struct sock *sk)
-{
- barrier();
- if (atomic_dec_and_test(&sk->sock_readers))
- __release_sock(sk);
-}
+/* BH context may only use the following locking interface. */
+#define bh_lock_sock(__sk) spin_lock(&((__sk)->lock.slock))
+#define bh_unlock_sock(__sk) spin_unlock(&((__sk)->lock.slock))
/*
* This might not be the most appropriate place for this two
* New scheme, half the table is for TIME_WAIT, the other half is
* for the rest. I'll experiment with dynamic table growth later.
*/
-#define TCP_HTABLE_SIZE 512
+extern int tcp_ehash_size;
+extern struct sock **tcp_ehash;
/* This is for listening sockets, thus all sockets which possess wildcards. */
#define TCP_LHTABLE_SIZE 32 /* Yes, really, this is all you need. */
-/* This is for all sockets, to keep track of the local port allocations. */
-#define TCP_BHTABLE_SIZE 512
-
/* tcp_ipv4.c: These need to be shared by v4 and v6 because the lookup
* and hashing code needs to work with different AF's yet
* the port space is shared.
*/
-extern struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
/* There are a few simple rules, which allow for local port reuse by
struct tcp_bind_bucket **pprev;
};
-extern struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE];
+extern struct tcp_bind_bucket **tcp_bhash;
+extern int tcp_bhash_size;
+
extern kmem_cache_t *tcp_bucket_cachep;
extern struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum);
extern void tcp_bucket_unlock(struct sock *sk);
/* These are AF independent. */
static __inline__ int tcp_bhashfn(__u16 lport)
{
- return (lport & (TCP_BHTABLE_SIZE - 1));
+ return (lport & (tcp_bhash_size - 1));
}
static __inline__ void tcp_sk_bindify(struct sock *sk)
struct tcp_bind_bucket *tb;
unsigned short snum = sk->num;
- for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb->port != snum; tb = tb->next)
+ for(tb = tcp_bhash[tcp_bhashfn(snum)]; tb->port != snum; tb = tb->next)
;
/* Update bucket flags. */
if(tb->owners == NULL) {
EXPORT_SYMBOL(tty_std_termios);
/* block device driver support */
+EXPORT_SYMBOL(blk_ioctl);
EXPORT_SYMBOL(block_read);
EXPORT_SYMBOL(block_write);
EXPORT_SYMBOL(block_fsync);
#endif /* __sparc__ */
#endif
-#if !defined(__alpha__)
+#if !defined(__alpha__) && !defined(__ia64__)
/*
* For backwards compatibility. Functionality superseded by sigprocmask.
*/
return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
}
-#endif /* !alpha */
+#endif /* !alpha && !__ia64__ */
do_get_fast_time(t);
}
-#ifndef __alpha__
+#if !defined(__alpha__) && !defined(__ia64__)
/*
* sys_time() can be implemented in user-level using
* sys_gettimeofday(). Is this for backwards compatibility? If so,
* why not move it into the appropriate arch directory (for those
* architectures that need it).
+ *
+ * XXX This function is NOT 64-bit clean!
*/
asmlinkage int sys_time(int * tloc)
{
struct vm_operations_struct * ops;
struct inode *inode = file->f_dentry->d_inode;
- if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE)) {
+ ops = &file_private_mmap;
+ if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
ops = &file_shared_mmap;
- /* share_page() can only guarantee proper page sharing if
- * the offsets are all page aligned. */
- if (vma->vm_offset & (PAGE_SIZE - 1))
- return -EINVAL;
- } else {
- ops = &file_private_mmap;
- if (inode->i_op && inode->i_op->bmap &&
- (vma->vm_offset & (inode->i_sb->s_blocksize - 1)))
- return -EINVAL;
- }
if (!inode->i_sb || !S_ISREG(inode->i_mode))
return -EACCES;
if (!inode->i_op || !inode->i_op->readpage)
if (len > TASK_SIZE || addr > TASK_SIZE-len)
return -EINVAL;
+ if (off & ~PAGE_MASK)
+ return -EINVAL;
+
/* offset overflow? */
if (off + len < off)
return -EINVAL;
{
struct device *dev;
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if (strcmp(dev->name, name) == 0)
- return(dev);
+ goto out;
}
- return NULL;
+out:
+ read_unlock_bh(&dev_base_lock);
+ return dev;
}
struct device * dev_get_by_index(int ifindex)
{
struct device *dev;
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if (dev->ifindex == ifindex)
- return(dev);
+ goto out;
}
- return NULL;
+out:
+ read_unlock_bh(&dev_base_lock);
+ return dev;
}
struct device *dev_getbyhwaddr(unsigned short type, char *ha)
{
struct device *dev;
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if (dev->type == type &&
memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
- return(dev);
+ goto out;
}
- return(NULL);
+out:
+ read_unlock_bh(&dev_base_lock);
+ return dev;
}
/*
if (dev->flags&IFF_UP)
return 0;
+ /* Setup the lock before we open the faucet. */
+ spin_lock_init(&dev->xmit_lock);
+
/*
* Call device private open method
*/
if (dev) {
dev_do_clear_fastroute(dev);
} else {
- for (dev = dev_base; dev; dev = dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev; dev = dev->next) {
+ read_unlock_bh(&dev_base_lock);
dev_do_clear_fastroute(dev);
+ read_lock_bh(&dev_base_lock);
+ }
+ read_unlock_bh(&dev_base_lock);
}
}
#endif
NET_PROFILE_ENTER(dev_queue_xmit);
#endif
- start_bh_atomic();
+ spin_lock_bh(&dev->xmit_lock);
q = dev->qdisc;
if (q->enqueue) {
q->enqueue(skb, q);
qdisc_wakeup(dev);
- end_bh_atomic();
+ spin_unlock_bh(&dev->xmit_lock);
#ifdef CONFIG_NET_PROFILE
NET_PROFILE_LEAVE(dev_queue_xmit);
if (netdev_nit)
dev_queue_xmit_nit(skb,dev);
if (dev->hard_start_xmit(skb, dev) == 0) {
- end_bh_atomic();
+ spin_unlock_bh(&dev->xmit_lock);
#ifdef CONFIG_NET_PROFILE
NET_PROFILE_LEAVE(dev_queue_xmit);
if (net_ratelimit())
printk(KERN_DEBUG "Virtual device %s asks to queue packet!\n", dev->name);
}
- end_bh_atomic();
+ spin_unlock_bh(&dev->xmit_lock);
kfree_skb(skb);
*/
total = 0;
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
+ read_unlock_bh(&dev_base_lock);
for (i=0; i<NPROTO; i++) {
if (gifconf_list[i]) {
int done;
} else {
done = gifconf_list[i](dev, pos+total, len-total);
}
- if (done<0)
+ if (done<0) {
return -EFAULT;
+ }
total += done;
}
}
+ read_lock_bh(&dev_base_lock);
}
+ read_unlock_bh(&dev_base_lock);
/*
* All done. Write the updated control block back to the caller.
len+=size;
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
size = sprintf_stats(buffer+len, dev);
len+=size;
pos=begin+len;
- if(pos<offset)
- {
+ if(pos<offset) {
len=0;
begin=pos;
}
if(pos>offset+length)
break;
}
+ read_unlock_bh(&dev_base_lock);
*start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Start slop */
pos+=size;
len+=size;
- for(dev = dev_base; dev != NULL; dev = dev->next)
- {
+ read_lock_bh(&dev_base_lock);
+ for(dev = dev_base; dev != NULL; dev = dev->next) {
size = sprintf_wireless_stats(buffer+len, dev);
len+=size;
pos=begin+len;
- if(pos < offset)
- {
+ if(pos < offset) {
len=0;
begin=pos;
}
if(pos > offset + length)
break;
}
+ read_unlock_bh(&dev_base_lock);
*start = buffer + (offset - begin); /* Start of wanted data */
len -= (offset - begin); /* Start slop */
printk(KERN_INFO "early initialization of device %s is deferred\n", dev->name);
/* Check for existence, and append to tail of chain */
+ write_lock_bh(&dev_base_lock);
for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
- if (d == dev || strcmp(d->name, dev->name) == 0)
+ if (d == dev || strcmp(d->name, dev->name) == 0) {
+ write_unlock_bh(&dev_base_lock);
return -EEXIST;
+ }
}
dev->next = NULL;
*dp = dev;
+ write_unlock_bh(&dev_base_lock);
return 0;
}
return -EIO;
/* Check for existence, and append to tail of chain */
+ write_lock_bh(&dev_base_lock);
for (dp=&dev_base; (d=*dp) != NULL; dp=&d->next) {
- if (d == dev || strcmp(d->name, dev->name) == 0)
+ if (d == dev || strcmp(d->name, dev->name) == 0) {
+ write_unlock_bh(&dev_base_lock);
return -EEXIST;
+ }
}
dev->next = NULL;
dev_init_scheduler(dev);
+ *dp = dev;
+ write_unlock_bh(&dev_base_lock);
+
+ dev->ifindex = -1;
dev->ifindex = dev_new_index();
if (dev->iflink == -1)
dev->iflink = dev->ifindex;
- *dp = dev;
/* Notify protocols, that a new device appeared. */
notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev);
}
/* And unlink it from device chain. */
+ write_lock_bh(&dev_base_lock);
for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) {
if (d == dev) {
*dp = d->next;
- synchronize_bh();
d->next = NULL;
+ write_unlock_bh(&dev_base_lock);
if (dev->destructor)
dev->destructor(dev);
return 0;
}
}
+ write_unlock_bh(&dev_base_lock);
return -ENODEV;
}
*/
dp = &dev_base;
- while ((dev = *dp) != NULL)
- {
+ write_lock_bh(&dev_base_lock);
+ while ((dev = *dp) != NULL) {
dev->iflink = -1;
- if (dev->init && dev->init(dev))
- {
+ if (dev->init && dev->init(dev)) {
/*
* It failed to come up. Unhook it.
*/
*dp = dev->next;
- synchronize_bh();
- }
- else
- {
+ } else {
dp = &dev->next;
+ write_unlock_bh(&dev_base_lock);
dev->ifindex = dev_new_index();
+ write_lock_bh(&dev_base_lock);
if (dev->iflink == -1)
dev->iflink = dev->ifindex;
dev_init_scheduler(dev);
}
}
+ write_unlock_bh(&dev_base_lock);
#ifdef CONFIG_PROC_FS
proc_net_register(&proc_net_dev);
int len=0;
struct device *dev;
- start_bh_atomic();
-
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
for (m = dev->mc_list; m; m = m->next) {
int i;
*eof = 1;
done:
- end_bh_atomic();
+ read_unlock_bh(&dev_base_lock);
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
int s_idx = cb->args[0];
struct device *dev;
+ read_lock_bh(&dev_base_lock);
for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
if (idx < s_idx)
continue;
if (rtnetlink_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
break;
}
+ read_unlock_bh(&dev_base_lock);
cb->args[0] = idx;
return skb->len;
{
struct sock *sk = kmem_cache_alloc(sk_cachep, priority);
- if(sk) {
- if (zero_it)
- memset(sk, 0, sizeof(struct sock));
+ if(sk && zero_it) {
+ memset(sk, 0, sizeof(struct sock));
sk->family = family;
+ sock_lock_init(sk);
}
return sk;
return NULL;
}
-
-void __release_sock(struct sock *sk)
+void lock_sock(struct sock *sk)
{
-#ifdef CONFIG_INET
- if (!sk->prot || !sk->backlog_rcv)
- return;
-
- /* See if we have any packets built up. */
- start_bh_atomic();
- while (!skb_queue_empty(&sk->back_log)) {
- struct sk_buff * skb = sk->back_log.next;
- __skb_unlink(skb, &sk->back_log);
- sk->backlog_rcv(sk, skb);
+ spin_lock_bh(&sk->lock.slock);
+ if(sk->lock.users != 0) {
+ DECLARE_WAITQUEUE(wait, current);
+
+ add_wait_queue_exclusive(&sk->lock.wq, &wait);
+ for(;;) {
+ current->state = TASK_EXCLUSIVE | TASK_UNINTERRUPTIBLE;
+ spin_unlock_bh(&sk->lock.slock);
+ schedule();
+ spin_lock_bh(&sk->lock.slock);
+ if(!sk->lock.users)
+ break;
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&sk->lock.wq, &wait);
}
- end_bh_atomic();
-#endif
+ sk->lock.users = 1;
+ spin_unlock_bh(&sk->lock.slock);
}
+void release_sock(struct sock *sk)
+{
+ spin_lock_bh(&sk->lock.slock);
+ sk->lock.users = 0;
+ if(sk->backlog.tail != NULL) {
+ struct sk_buff *skb = sk->backlog.head;
+ do { struct sk_buff *next = skb->next;
+ skb->next = NULL;
+ sk->backlog_rcv(sk, skb);
+ skb = next;
+ } while(skb != NULL);
+ sk->backlog.head = sk->backlog.tail = NULL;
+ }
+ wake_up(&sk->lock.wq);
+ spin_unlock_bh(&sk->lock.slock);
+}
/*
* Generic socket manager library. Most simpler socket families
{
skb_queue_head_init(&sk->receive_queue);
skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
skb_queue_head_init(&sk->error_queue);
init_timer(&sk->timer);
__initfunc(void eth_setup(char *str, int *ints))
{
- struct device *d = dev_base;
+ struct device *d;
if (!str || !*str)
return;
+ read_lock_bh(&dev_base_lock);
+ d = dev_base;
while (d)
{
if (!strcmp(str,d->name))
}
d=d->next;
}
+ read_unlock_bh(&dev_base_lock);
}
while((skb = skb_dequeue(&sk->error_queue)) != NULL)
kfree_skb(skb);
- /* Now the backlog. */
- while((skb=skb_dequeue(&sk->back_log)) != NULL)
- kfree_skb(skb);
+ /* It is _impossible_ for the backlog to contain anything
+ * when we get here. All user references to this socket
+ * have gone away, only the net layer knows can touch it.
+ */
}
static __inline__ void kill_sk_now(struct sock *sk)
sk->destroy = 1;
sk->ack_backlog = 0;
- release_sock(sk);
+ bh_unlock_sock(sk);
net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
}
+/* Callers must hold the BH spinlock.
+ *
+ * At this point, there should be no process reference to this
+ * socket, and thus no user references at all. Therefore we
+ * can assume the socket waitqueue is inactive and nobody will
+ * try to jump onto it.
+ */
void destroy_sock(struct sock *sk)
{
- lock_sock(sk); /* just to be safe. */
-
/* Now we can no longer get new packets or once the
* timers are killed, send them.
*/
in this case. It is importnat that lo is the first interface
in dev_base list.
*/
+ read_lock_bh(&dev_base_lock);
for (dev=dev_base; dev; dev=dev->next) {
if ((in_dev=dev->ip_ptr) == NULL)
continue;
for_primary_ifa(in_dev) {
- if (ifa->ifa_scope <= scope)
+ if (ifa->ifa_scope <= scope) {
+ read_unlock_bh(&dev_base_lock);
return ifa->ifa_local;
+ }
} endfor_ifa(in_dev);
}
+ read_unlock_bh(&dev_base_lock);
return 0;
}
s_idx = cb->args[0];
s_ip_idx = ip_idx = cb->args[1];
+ read_lock_bh(&dev_base_lock);
for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
if (idx < s_idx)
continue;
}
}
done:
+ read_unlock_bh(&dev_base_lock);
cb->args[0] = idx;
cb->args[1] = ip_idx;
ipv4_devconf.accept_redirects = !on;
ipv4_devconf_dflt.forwarding = on;
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
struct in_device *in_dev = dev->ip_ptr;
if (in_dev)
in_dev->cnf.forwarding = on;
}
+ read_unlock_bh(&dev_base_lock);
rt_cache_flush(0);
static struct fib_rule local_rule = { &main_rule, 0, RT_TABLE_LOCAL, RTN_UNICAST, };
static struct fib_rule *fib_rules = &local_rule;
+static rwlock_t fib_rules_lock = RW_LOCK_UNLOCKED;
int inet_rtm_delrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
{
struct rtattr **rta = arg;
struct rtmsg *rtm = NLMSG_DATA(nlh);
struct fib_rule *r, **rp;
+ int err = -ESRCH;
+ write_lock_bh(&fib_rules_lock);
for (rp=&fib_rules; (r=*rp) != NULL; rp=&r->r_next) {
if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 4) == 0) &&
rtm->rtm_src_len == r->r_src_len &&
(!rta[RTA_PRIORITY-1] || memcmp(RTA_DATA(rta[RTA_PRIORITY-1]), &r->r_preference, 4) == 0) &&
(!rta[RTA_IIF-1] || strcmp(RTA_DATA(rta[RTA_IIF-1]), r->r_ifname) == 0) &&
(!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) {
+ err = -EPERM;
if (r == &local_rule)
- return -EPERM;
+ break;
*rp = r->r_next;
- synchronize_bh();
-
if (r != &default_rule && r != &main_rule)
kfree(r);
- return 0;
+ err = 0;
+ break;
}
}
- return -ESRCH;
+ write_unlock_bh(&fib_rules_lock);
+ return err;
}
/* Allocate new unique table id */
memcpy(&new_r->r_tclassid, RTA_DATA(rta[RTA_FLOW-1]), 4);
#endif
+ write_lock_bh(&fib_rules_lock);
rp = &fib_rules;
if (!new_r->r_preference) {
r = fib_rules;
new_r->r_next = r;
*rp = new_r;
+ write_unlock_bh(&fib_rules_lock);
return 0;
}
{
struct fib_rule *r;
+ write_lock_bh(&fib_rules_lock);
for (r=fib_rules; r; r=r->r_next) {
if (r->r_ifindex == dev->ifindex)
r->r_ifindex = -1;
}
+ write_unlock_bh(&fib_rules_lock);
}
static void fib_rules_attach(struct device *dev)
{
struct fib_rule *r;
+ write_lock_bh(&fib_rules_lock);
for (r=fib_rules; r; r=r->r_next) {
if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0)
r->r_ifindex = dev->ifindex;
}
+ write_unlock_bh(&fib_rules_lock);
}
int fib_lookup(const struct rt_key *key, struct fib_result *res)
u32 saddr = key->src;
FRprintk("Lookup: %08x <- %08x ", key->dst, key->src);
+ read_lock_bh(&fib_rules_lock);
for (r = fib_rules; r; r=r->r_next) {
if (((saddr^r->r_src) & r->r_srcmask) ||
((daddr^r->r_dst) & r->r_dstmask) ||
policy = r;
break;
case RTN_UNREACHABLE:
+ read_unlock_bh(&fib_rules_lock);
return -ENETUNREACH;
default:
case RTN_BLACKHOLE:
+ read_unlock_bh(&fib_rules_lock);
return -EINVAL;
case RTN_PROHIBIT:
+ read_unlock_bh(&fib_rules_lock);
return -EACCES;
}
if (err == 0) {
FRprintk("ok\n");
res->r = policy;
+ read_unlock_bh(&fib_rules_lock);
return 0;
}
- if (err < 0 && err != -EAGAIN)
+ if (err < 0 && err != -EAGAIN) {
+ read_unlock_bh(&fib_rules_lock);
return err;
+ }
}
FRprintk("FAILURE\n");
+ read_unlock_bh(&fib_rules_lock);
return -ENETUNREACH;
}
int s_idx = cb->args[0];
struct fib_rule *r;
+ read_lock_bh(&fib_rules_lock);
for (r=fib_rules, idx=0; r; r = r->r_next, idx++) {
if (idx < s_idx)
continue;
if (inet_fill_rule(skb, r, cb) < 0)
break;
}
+ read_unlock_bh(&fib_rules_lock);
cb->args[0] = idx;
return skb->len;
len=sprintf(buffer,"Idx\tDevice : Count Querier\tGroup Users Timer\tReporter\n");
- for(dev = dev_base; dev; dev = dev->next)
- {
+ read_lock_bh(&dev_base_lock);
+ for(dev = dev_base; dev; dev = dev->next) {
struct in_device *in_dev = dev->ip_ptr;
char *querier = "NONE";
}
}
done:
+ read_unlock_bh(&dev_base_lock);
+
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
unsigned short oflags;
last = &ic_first_dev;
- for (dev = dev_base; dev; dev = dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev; dev = dev->next) {
if (user_dev_name[0] ? !strcmp(dev->name, user_dev_name) :
(!(dev->flags & IFF_LOOPBACK) &&
(dev->flags & (IFF_POINTOPOINT|IFF_BROADCAST)) &&
ic_proto_have_if |= able;
DBG(("IP-Config: Opened %s (able=%d)\n", dev->name, able));
}
+ }
+ read_unlock_bh(&dev_base_lock);
+
*last = NULL;
if (!ic_first_dev) {
slot_dist = tcp_tw_death_row_slot - slot_dist;
timer_expires = jiffies + (slot_dist * TCP_TWKILL_PERIOD);
} else {
- timer_active1 = del_timer(&tp->retransmit_timer);
- timer_active2 = del_timer(&sp->timer);
- if (!timer_active1) tp->retransmit_timer.expires=0;
- if (!timer_active2) sp->timer.expires=0;
+ timer_active1 = tp->retransmit_timer.prev != NULL;
+ timer_active2 = sp->timer.prev != NULL;
timer_active = 0;
timer_expires = (unsigned) -1;
}
(!tw_bucket && sp->socket) ? sp->socket->inode->i_uid : 0,
(!tw_bucket && timer_active) ? sp->timeout : 0,
(!tw_bucket && sp->socket) ? sp->socket->inode->i_ino : 0);
-
- if (timer_active1) add_timer(&tp->retransmit_timer);
- if (timer_active2) add_timer(&sp->timer);
}
/*
" sl local_address rem_address st tx_queue "
"rx_queue tr tm->when retrnsmt uid timeout inode");
pos = 128;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
sp = pro->sklist_next;
while(sp != (struct sock *)pro) {
if (format == 0 && sp->state == TCP_LISTEN) {
i++;
}
out:
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
begin = len - (pos - offset);
*start = buffer + begin;
num &= (RAWV4_HTABLE_SIZE - 1);
skp = &raw_v4_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
sk->next = *skp;
*skp = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v4_unhash(struct sock *sk)
num &= (RAWV4_HTABLE_SIZE - 1);
skp = &raw_v4_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
}
skp = &((*skp)->next);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v4_rehash(struct sock *sk)
num &= (RAWV4_HTABLE_SIZE - 1);
skp = &raw_v4_htable[oldnum];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
sk->next = raw_v4_htable[num];
raw_v4_htable[num] = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
/* Grumble... icmp and ip_input want to get at this... */
{
struct sock *s = sk;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
for(s = sk; s; s = s->next) {
if((s->num == num) &&
!(s->dead && (s->state == TCP_CLOSE)) &&
!(s->bound_dev_if && s->bound_dev_if != dif))
break; /* gotcha */
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return s;
}
static void raw_close(struct sock *sk, long timeout)
{
+ bh_lock_sock(sk);
+
/* Observation: when raw_close is called, processes have
no access to socket anymore. But net still has.
Step one, detach it from networking:
* Route cache.
*/
-struct rtable *rt_hash_table[RT_HASH_DIVISOR];
+static struct rtable *rt_hash_table[RT_HASH_DIVISOR];
+static rwlock_t rt_hash_lock = RW_LOCK_UNLOCKED;
static int rt_intern_hash(unsigned hash, struct rtable * rth, struct rtable ** res);
}
- start_bh_atomic();
+ read_lock_bh(&rt_hash_lock);
for (i = 0; i<RT_HASH_DIVISOR; i++) {
for (r = rt_hash_table[i]; r; r = r->u.rt_next) {
}
done:
- end_bh_atomic();
+ read_unlock_bh(&rt_hash_lock);
*start = buffer+len-(pos-offset);
len = pos-offset;
rover = (rover + 1) & (RT_HASH_DIVISOR-1);
rthp = &rt_hash_table[rover];
+ write_lock_bh(&rt_hash_lock);
while ((rth = *rthp) != NULL) {
if (rth->u.dst.expires) {
/* Entrie is expired even if it is in use */
*rthp = rth->u.rt_next;
rt_free(rth);
}
+ write_unlock_bh(&rt_hash_lock);
/* Fallback loop breaker. */
if ((jiffies - now) > 0)
rt_deadline = 0;
- start_bh_atomic();
+ write_lock_bh(&rt_hash_lock);
for (i=0; i<RT_HASH_DIVISOR; i++) {
- if ((rth = xchg(&rt_hash_table[i], NULL)) == NULL)
+ rth = rt_hash_table[i];
+ if(rth == NULL)
continue;
- end_bh_atomic();
+ rt_hash_table[i] = NULL;
+ write_unlock_bh(&rt_hash_lock);
for (; rth; rth=next) {
next = rth->u.rt_next;
rt_free(rth);
}
- start_bh_atomic();
+ write_lock_bh(&rt_hash_lock);
}
- end_bh_atomic();
+ write_unlock_bh(&rt_hash_lock);
}
+static spinlock_t rt_flush_lock = SPIN_LOCK_UNLOCKED;
+
void rt_cache_flush(int delay)
{
unsigned long now = jiffies;
if (delay < 0)
delay = ip_rt_min_delay;
- start_bh_atomic();
+ spin_lock_bh(&rt_flush_lock);
if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) {
long tmo = (long)(rt_deadline - now);
}
if (delay <= 0) {
- end_bh_atomic();
+ spin_unlock_bh(&rt_flush_lock);
rt_run_flush(0);
return;
}
rt_flush_timer.expires = now + delay;
add_timer(&rt_flush_timer);
- end_bh_atomic();
+ spin_unlock_bh(&rt_flush_lock);
}
/*
do {
int i, k;
- start_bh_atomic();
+ write_lock_bh(&rt_hash_lock);
for (i=0, k=rover; i<RT_HASH_DIVISOR; i++) {
unsigned tmo = expire;
break;
}
rover = k;
- end_bh_atomic();
+ write_unlock_bh(&rt_hash_lock);
if (goal <= 0)
goto work_done;
int attempts = !in_interrupt();
restart:
- start_bh_atomic();
-
rthp = &rt_hash_table[hash];
+ write_lock_bh(&rt_hash_lock);
while ((rth = *rthp) != NULL) {
if (memcmp(&rth->key, &rt->key, sizeof(rt->key)) == 0) {
/* Put it first */
atomic_inc(&rth->u.dst.refcnt);
atomic_inc(&rth->u.dst.use);
rth->u.dst.lastuse = now;
- end_bh_atomic();
+ write_unlock_bh(&rt_hash_lock);
rt_drop(rt);
*rp = rth;
*/
if (rt->rt_type == RTN_UNICAST || rt->key.iif == 0) {
if (!arp_bind_neighbour(&rt->u.dst)) {
- end_bh_atomic();
+ write_unlock_bh(&rt_hash_lock);
/* Neighbour tables are full and nothing
can be released. Try to shrink route cache,
}
#endif
rt_hash_table[hash] = rt;
- end_bh_atomic();
*rp = rt;
+ write_unlock_bh(&rt_hash_lock);
return 0;
}
rthp=&rt_hash_table[hash];
+ write_lock_bh(&rt_hash_lock);
while ( (rth = *rthp) != NULL) {
struct rtable *rt;
rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
if (rt == NULL) {
ip_rt_put(rth);
+ write_unlock_bh(&rt_hash_lock);
return;
}
rt_drop(rth);
break;
}
+ write_unlock_bh(&rt_hash_lock);
}
}
return;
#if RT_CACHE_DEBUG >= 1
printk(KERN_DEBUG "ip_rt_advice: redirect to %d.%d.%d.%d/%02x dropped\n", NIPQUAD(rt->rt_dst), rt->key.tos);
#endif
- start_bh_atomic();
ip_rt_put(rt);
+ write_lock_bh(&rt_hash_lock);
for (rthp = &rt_hash_table[hash]; *rthp; rthp = &(*rthp)->u.rt_next) {
if (*rthp == rt) {
*rthp = rt->u.rt_next;
break;
}
}
- end_bh_atomic();
+ write_unlock_bh(&rt_hash_lock);
return NULL;
}
}
for (i=0; i<2; i++) {
unsigned hash = rt_hash_code(daddr, skeys[i], tos);
+ read_lock_bh(&rt_hash_lock);
for (rth = rt_hash_table[hash]; rth; rth = rth->u.rt_next) {
if (rth->key.dst == daddr &&
rth->key.src == skeys[i] &&
}
}
}
+ read_unlock_bh(&rt_hash_lock);
}
return est_mtu ? : new_mtu;
}
tos &= IPTOS_TOS_MASK;
hash = rt_hash_code(daddr, saddr^(iif<<5), tos);
+ read_lock_bh(&rt_hash_lock);
for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
if (rth->key.dst == daddr &&
rth->key.src == saddr &&
rth->u.dst.lastuse = jiffies;
atomic_inc(&rth->u.dst.use);
atomic_inc(&rth->u.dst.refcnt);
+ read_unlock_bh(&rt_hash_lock);
skb->dst = (struct dst_entry*)rth;
return 0;
}
}
+ read_unlock_bh(&rt_hash_lock);
/* Multicast recognition logic is moved from route cache to here.
The problem was that too many Ethernet cards have broken/missing
hash = rt_hash_code(daddr, saddr^(oif<<5), tos);
- start_bh_atomic();
+ read_lock_bh(&rt_hash_lock);
for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
if (rth->key.dst == daddr &&
rth->key.src == saddr &&
rth->u.dst.lastuse = jiffies;
atomic_inc(&rth->u.dst.use);
atomic_inc(&rth->u.dst.refcnt);
- end_bh_atomic();
+ read_unlock_bh(&rt_hash_lock);
*rp = rth;
return 0;
}
}
- end_bh_atomic();
+ read_unlock_bh(&rt_hash_lock);
return ip_route_output_slow(rp, daddr, saddr, tos, oif);
}
if (h < s_h) continue;
if (h > s_h)
s_idx = 0;
- start_bh_atomic();
+ read_lock_bh(&rt_hash_lock);
for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) {
if (idx < s_idx)
continue;
if (rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) {
dst_release(xchg(&skb->dst, NULL));
- end_bh_atomic();
+ read_unlock_bh(&rt_hash_lock);
goto done;
}
dst_release(xchg(&skb->dst, NULL));
}
- end_bh_atomic();
+ read_unlock_bh(&rt_hash_lock);
}
done:
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
+#include <linux/smp_lock.h>
#include <net/icmp.h>
#include <net/tcp.h>
/*
* Wait for a socket to get into the connected state
*
- * Note: must be called with the socket locked.
+ * Note: Must be called with the socket locked, and it
+ * runs with the kernel fully unlocked.
*/
static int wait_for_tcp_connect(struct sock * sk, int flags)
{
/*
* Wait for more memory for a socket
+ *
+ * NOTE: This runs with the kernel fully unlocked.
*/
static void wait_for_tcp_memory(struct sock * sk)
{
int mss_now;
int err, copied;
+ unlock_kernel();
lock_sock(sk);
err = 0;
err = -ERESTARTSYS;
goto do_interrupted;
}
+ tcp_push_pending_frames(sk, tp);
wait_for_tcp_memory(sk);
/* If SACK's were formed or PMTU events happened,
out:
tcp_push_pending_frames(sk, tp);
release_sock(sk);
+ lock_kernel();
return err;
}
if (flags & MSG_WAITALL)
target=len;
+ unlock_kernel();
add_wait_queue(sk->sleep, &wait);
lock_sock(sk);
/* We now will not sleep again until we are finished
* with skb. Sorry if you are doing the SMP port
* but you'll just have to fix it neatly ;)
+ *
+ * Very funny Alan... -DaveM
*/
atomic_dec(&skb->users);
/* Clean up data we have read: This will do ACK frames. */
cleanup_rbuf(sk, copied);
release_sock(sk);
+ lock_kernel();
return copied;
}
struct sk_buff *skb;
int data_was_unread = 0;
- /*
- * Check whether the socket is locked ... supposedly
- * it's impossible to tcp_close() a locked socket.
- */
- if (atomic_read(&sk->sock_readers))
- printk("tcp_close: socket already locked!\n");
-
/* We need to grab some memory, and put together a FIN,
* and then put it into the queue to be sent.
*/
+ unlock_kernel();
lock_sock(sk);
if(sk->state == TCP_LISTEN) {
/* Special case. */
tcp_set_state(sk, TCP_CLOSE);
tcp_close_pending(sk);
release_sock(sk);
+ lock_kernel();
sk->dead = 1;
return;
}
tcp_check_fin_timer(sk);
release_sock(sk);
+ lock_kernel();
sk->dead = 1;
}
/*
* Wait for an incoming connection, avoid race
- * conditions. This must be called with the socket locked.
+ * conditions. This must be called with the socket locked,
+ * and without the kernel lock held.
*/
static struct open_request * wait_for_connect(struct sock * sk,
struct open_request **pprev)
struct sock *newsk = NULL;
int error;
+ unlock_kernel();
lock_sock(sk);
/* We need to make sure that this socket is listening,
tcp_inc_slow_timer(TCP_SLT_KEEPALIVE);
release_sock(sk);
+ lock_kernel();
return newsk;
out:
*/
sk->err = error;
release_sock(sk);
+ lock_kernel();
return newsk;
}
void __init tcp_init(void)
{
struct sk_buff *skb = NULL;
+ unsigned long goal;
+ int order;
if(sizeof(struct tcp_skb_cb) > sizeof(skb->cb))
__skb_cb_too_small_for_tcp(sizeof(struct tcp_skb_cb),
NULL, NULL);
if(!tcp_timewait_cachep)
panic("tcp_init: Cannot alloc tcp_tw_bucket cache.");
+
+ /* Size and allocate the main established and bind bucket
+ * hash tables.
+ *
+ * The methodology is similar to that of the buffer cache.
+ */
+ goal = num_physpages >> (20 - PAGE_SHIFT);
+ for(order = 5; (1UL << order) < goal; order++)
+ ;
+ do {
+ tcp_ehash_size = (1UL << order) * PAGE_SIZE /
+ sizeof(struct sock *);
+ tcp_ehash = (struct sock **)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (tcp_ehash == NULL && --order > 4);
+
+ if (!tcp_ehash)
+ panic("Failed to allocate TCP established hash table\n");
+ memset(tcp_ehash, 0, tcp_ehash_size * sizeof(struct sock *));
+
+ do {
+ tcp_bhash_size = (1UL << order) * PAGE_SIZE /
+ sizeof(struct tcp_bind_bucket *);
+ tcp_bhash = (struct tcp_bind_bucket **)
+ __get_free_pages(GFP_ATOMIC, order);
+ } while (tcp_bhash == NULL && --order > 4);
+
+ if (!tcp_bhash)
+ panic("Failed to allocate TCP bind hash table\n");
+ memset(tcp_bhash, 0, tcp_bhash_size * sizeof(struct tcp_bind_bucket *));
+
+ printk("TCP: Hash tables configured (established %d bind %d)\n",
+ tcp_ehash_size, tcp_bhash_size);
}
extern void tcp_tw_reschedule(struct tcp_tw_bucket *tw);
extern void tcp_tw_deschedule(struct tcp_tw_bucket *tw);
+/* Must be called only from BH context. */
void tcp_timewait_kill(struct tcp_tw_bucket *tw)
{
+ SOCKHASH_LOCK_WRITE_BH();
+
/* Unlink from various places. */
if(tw->bind_next)
tw->bind_next->bind_pprev = tw->bind_pprev;
tw->sklist_next->sklist_prev = tw->sklist_prev;
tw->sklist_prev->sklist_next = tw->sklist_next;
+ SOCKHASH_UNLOCK_WRITE_BH();
+
/* Ok, now free it up. */
kmem_cache_free(tcp_timewait_cachep, tw);
}
struct sock *sk;
struct tcp_func *af_specific = tw->af_specific;
__u32 isn;
+ int ret;
isn = tw->rcv_nxt + 128000;
if(isn == 0)
tcp_timewait_kill(tw);
sk = af_specific->get_sock(skb, th);
if(sk == NULL ||
- !ipsec_sk_policy(sk,skb) ||
- atomic_read(&sk->sock_readers) != 0)
+ !ipsec_sk_policy(sk,skb))
return 0;
+
+ bh_lock_sock(sk);
+
+ /* Default is to discard the frame. */
+ ret = 0;
+
+ if(sk->lock.users)
+ goto out_unlock;
+
skb_set_owner_r(skb, sk);
af_specific = sk->tp_pinfo.af_tcp.af_specific;
+
if(af_specific->conn_request(sk, skb, isn) < 0)
- return 1; /* Toss a reset back. */
- return 0; /* Discard the frame. */
+ ret = 1; /* Toss a reset back. */
+ out_unlock:
+ bh_unlock_sock(sk);
+ return ret;
}
/* Check RST or SYN */
sk->prot->inuse--;
/* Step 4: Hash TW into TIMEWAIT half of established hash table. */
- head = &tcp_established_hash[sk->hashent + (TCP_HTABLE_SIZE/2)];
+ head = &tcp_ehash[sk->hashent + (tcp_ehash_size >> 1)];
sktw = (struct sock *)tw;
if((sktw->next = *head) != NULL)
(*head)->pprev = &sktw->next;
}
#endif
/* Linkage updates. */
+ SOCKHASH_LOCK_WRITE();
tcp_tw_hashdance(sk, tw);
+ SOCKHASH_UNLOCK_WRITE();
/* Get the TIME_WAIT timeout firing. */
tcp_tw_schedule(tw);
* First half of the table is for sockets not in TIME_WAIT, second half
* is for TIME_WAIT sockets only.
*/
-struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
+struct sock **tcp_ehash;
+int tcp_ehash_size;
/* Ok, let's try this, I give up, we do need a local binding
* TCP hash as well as the others for fast bind/connect.
*/
-struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE];
+struct tcp_bind_bucket **tcp_bhash;
+int tcp_bhash_size;
/* All sockets in TCP_LISTEN state will be in here. This is the only table
* where wildcard'd TCP sockets can exist. Hash function here is just local
static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
__u32 faddr, __u16 fport)
{
- return ((laddr ^ lport) ^ (faddr ^ fport)) & ((TCP_HTABLE_SIZE/2) - 1);
+ return ((laddr ^ lport) ^ (faddr ^ fport)) & ((tcp_ehash_size >> 1) - 1);
}
static __inline__ int tcp_sk_hashfn(struct sock *sk)
struct tcp_bind_bucket *tb;
unsigned short snum = sk->num;
- SOCKHASH_LOCK();
- for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb; tb = tb->next) {
+ SOCKHASH_LOCK_WRITE();
+ for(tb = tcp_bhash[tcp_bhashfn(snum)]; tb; tb = tb->next) {
if(tb->port == snum) {
if(tb->owners == NULL &&
(tb->flags & TCPB_FLAG_LOCKED)) {
break;
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
+/* The sockhash lock must be held as a writer here. */
struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum)
{
struct tcp_bind_bucket *tb;
tb = kmem_cache_alloc(tcp_bucket_cachep, SLAB_ATOMIC);
if(tb != NULL) {
struct tcp_bind_bucket **head =
- &tcp_bound_hash[tcp_bhashfn(snum)];
+ &tcp_bhash[tcp_bhashfn(snum)];
tb->port = snum;
tb->flags = TCPB_FLAG_LOCKED;
tb->owners = NULL;
*/
static __inline__ int tcp_bucket_check(unsigned short snum)
{
- struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ struct tcp_bind_bucket *tb;
+ int ret = 0;
+
+ SOCKHASH_LOCK_WRITE();
+ tb = tcp_bhash[tcp_bhashfn(snum)];
for( ; (tb && (tb->port != snum)); tb = tb->next)
;
if(tb == NULL && tcp_bucket_create(snum) == NULL)
- return 1;
- else
- return 0;
+ ret = 1;
+ SOCKHASH_UNLOCK_WRITE();
+
+ return ret;
}
#endif
struct tcp_bind_bucket *tb;
int result = 0;
- SOCKHASH_LOCK();
- for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ SOCKHASH_LOCK_WRITE();
+ for(tb = tcp_bhash[tcp_bhashfn(snum)];
(tb && (tb->port != snum));
tb = tb->next)
;
}
}
go_like_smoke:
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
return result;
}
int remaining = (high - low) + 1;
int rover;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
rover = tcp_port_rover;
do {
rover += 1;
if((rover < low) || (rover > high))
rover = low;
- tb = tcp_bound_hash[tcp_bhashfn(rover)];
+ tb = tcp_bhash[tcp_bhashfn(rover)];
for( ; tb; tb = tb->next) {
if(tb->port == rover)
goto next;
rover = 0;
if (tb != NULL)
tb->flags |= TCPB_FLAG_GOODSOCKNUM;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
return rover;
}
if (sk->state != TCP_CLOSE) {
struct sock **skp;
- SOCKHASH_LOCK();
- skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))];
+ SOCKHASH_LOCK_WRITE();
+ skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))];
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
*skp = sk;
sk->pprev = skp;
tcp_sk_bindify(sk);
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
}
static void tcp_v4_unhash(struct sock *sk)
{
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
if(sk->pprev) {
if(sk->next)
sk->next->pprev = sk->pprev;
tcp_reg_zap(sk);
tcp_sk_unbindify(sk);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void tcp_v4_rehash(struct sock *sk)
{
unsigned char state;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
state = sk->state;
if(sk->pprev != NULL) {
if(sk->next)
if(state == TCP_LISTEN)
skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
else
- skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))];
+ skp = &tcp_ehash[(sk->hashent = tcp_sk_hashfn(sk))];
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
if(state == TCP_LISTEN)
tcp_sk_bindify(sk);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
/* Don't inline this cruft. Here are some nice properties to
/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
- * It is assumed that this code only gets called from within NET_BH.
+ *
+ * The sockhash lock must be held as a reader here.
*/
-static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
- u32 saddr, u16 sport,
+static inline struct sock *__tcp_v4_lookup(u32 saddr, u16 sport,
u32 daddr, u16 dport, int dif)
{
TCP_V4_ADDR_COOKIE(acookie, saddr, daddr)
* have wildcards anyways.
*/
hash = tcp_hashfn(daddr, hnum, saddr, sport);
- for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
+ for(sk = tcp_ehash[hash]; sk; sk = sk->next) {
if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif)) {
if (sk->state == TCP_ESTABLISHED)
TCP_RHASH(sport) = sk;
}
}
/* Must check for a TIME_WAIT'er before going to listener hash. */
- for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+ for(sk = tcp_ehash[hash+(tcp_ehash_size >> 1)]; sk; sk = sk->next)
if(TCP_IPV4_MATCH(sk, acookie, saddr, daddr, ports, dif))
goto hit;
sk = tcp_v4_lookup_listener(daddr, hnum, dif);
__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
{
- return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dif);
+ struct sock *sk;
+
+ SOCKHASH_LOCK_READ();
+ sk = __tcp_v4_lookup(saddr, sport, daddr, dport, dif);
+ SOCKHASH_UNLOCK_READ();
+
+ return sk;
}
#ifdef CONFIG_IP_TRANSPARENT_PROXY
paddr = idev->ifa_list->ifa_local;
}
- /* This code must run only from NET_BH. */
+ /* We must obtain the sockhash lock here, we are always
+ * in BH context.
+ */
+ SOCKHASH_LOCK_READ_BH();
{
- struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hnum)];
+ struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hnum)];
for( ; (tb && tb->port != hnum); tb = tb->next)
;
if(tb == NULL)
}
next:
if(firstpass--) {
- struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hpnum)];
+ struct tcp_bind_bucket *tb = tcp_bhash[tcp_bhashfn(hpnum)];
for( ; (tb && tb->port != hpnum); tb = tb->next)
;
if(tb) {
}
}
gotit:
+ SOCKHASH_UNLOCK_READ_BH();
return result;
}
#endif /* CONFIG_IP_TRANSPARENT_PROXY */
int retval = 1;
/* Freeze the hash while we snoop around. */
- SOCKHASH_LOCK();
- tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ SOCKHASH_LOCK_READ();
+ tb = tcp_bhash[tcp_bhashfn(snum)];
for(; tb; tb = tb->next) {
if(tb->port == snum && tb->owners != NULL) {
/* Almost certainly the re-use port case, search the real hashes
* so it actually scales.
*/
- sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dport,
+ sk = __tcp_v4_lookup(sk->daddr, sk->dport,
sk->rcv_saddr, snum, sk->bound_dev_if);
+ SOCKHASH_UNLOCK_READ();
+
if((sk != NULL) && (sk->state != TCP_LISTEN))
retval = 0;
- break;
+ return retval;
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return retval;
}
{
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
- if (atomic_read(&sk->sock_readers))
- return;
-
- /* Don't interested in TCP_LISTEN and open_requests (SYN-ACKs
+ /* We are not interested in TCP_LISTEN and open_requests (SYN-ACKs
* send out by Linux are always <576bytes so they should go through
* unfragmented).
*/
if (sk->state == TCP_LISTEN)
return;
+ bh_lock_sock(sk);
+ if(sk->lock.users != 0)
+ goto out;
+
/* We don't check in the destentry if pmtu discovery is forbidden
* on this route. We just assume that no packet_to_big packets
* are send back when pmtu discovery is not active.
* route, but I think that's acceptable.
*/
if (sk->dst_cache == NULL)
- return;
+ goto out;
+
ip_rt_update_pmtu(sk->dst_cache, mtu);
if (sk->ip_pmtudisc != IP_PMTUDISC_DONT &&
tp->pmtu_cookie > sk->dst_cache->pmtu) {
*/
tcp_simple_retransmit(sk);
} /* else let the usual retransmit timer handle it */
+out:
+ bh_unlock_sock(sk);
}
/*
switch (sk->state) {
struct open_request *req, *prev;
case TCP_LISTEN:
- /* Prevent race conditions with accept() -
- * ICMP is unreliable.
- */
- if (atomic_read(&sk->sock_readers)) {
- net_statistics.LockDroppedIcmps++;
- /* If too many ICMPs get dropped on busy
- * servers this needs to be solved differently.
- */
- return;
- }
-
/* The final ACK of the handshake should be already
* handled in the new socket context, not here.
* Strictly speaking - an ICMP error for the final
if (!no_flags && !th->syn && !th->ack)
return;
+ /* Prevent race conditions with accept() -
+ * ICMP is unreliable.
+ */
+ bh_lock_sock(sk);
+ if (sk->lock.users != 0) {
+ net_statistics.LockDroppedIcmps++;
+ /* If too many ICMPs get dropped on busy
+ * servers this needs to be solved differently.
+ */
+ goto out_unlock;
+ }
+
req = tcp_v4_search_req(tp, iph, th, &prev);
if (!req)
- return;
+ goto out_unlock;
if (seq != req->snt_isn) {
net_statistics.OutOfWindowIcmps++;
- return;
+ goto out_unlock;
}
if (req->sk) {
/*
* but only with the next operation on the socket after
* accept.
*/
+ bh_unlock_sock(sk);
sk = req->sk;
} else {
/*
tcp_synq_unlink(tp, req, prev);
req->class->destructor(req);
tcp_openreq_free(req);
+ out_unlock:
+ bh_unlock_sock(sk);
return;
}
break;
{
struct iphdr *iph = skb->nh.iph;
struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
- struct sock *sk;
+ struct sock *sk = NULL;
int i;
+ SOCKHASH_LOCK_READ();
for (i=0; i<TCP_LHTABLE_SIZE; i++) {
for(sk = tcp_listening_hash[i]; sk; sk = sk->next) {
struct open_request *dummy;
th, &dummy) &&
(!sk->bound_dev_if ||
sk->bound_dev_if == skb->dev->ifindex))
- return sk;
+ goto out;
}
}
- return NULL;
+out:
+ SOCKHASH_UNLOCK_READ();
+ return sk;
}
/*
/* Clone the TCP header template */
newsk->dport = req->rmt_port;
- atomic_set(&newsk->sock_readers, 0);
+ sock_lock_init(newsk);
+
atomic_set(&newsk->rmem_alloc, 0);
skb_queue_head_init(&newsk->receive_queue);
atomic_set(&newsk->wmem_alloc, 0);
newsk->done = 0;
newsk->proc = 0;
newsk->pair = NULL;
- skb_queue_head_init(&newsk->back_log);
+ newsk->backlog.head = newsk->backlog.tail = NULL;
skb_queue_head_init(&newsk->error_queue);
#ifdef CONFIG_FILTER
if ((filter = newsk->filter) != NULL)
return sk;
}
+/* The socket must have it's spinlock held when we get
+ * here.
+ *
+ * We have a potential double-lock case here, so even when
+ * doing backlog processing we use the BH locking scheme.
+ * This is because we cannot sleep with the original spinlock
+ * held.
+ */
int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
+ int need_unlock = 0;
#ifdef CONFIG_FILTER
struct sk_filter *filter = sk->filter;
if (filter && sk_filter(skb, filter))
return 0;
}
-
if (sk->state == TCP_LISTEN) {
struct sock *nsk;
* otherwise we just shortcircuit this and continue with
* the new socket..
*/
- if (atomic_read(&nsk->sock_readers)) {
- skb_orphan(skb);
- __skb_queue_tail(&nsk->back_log, skb);
- return 0;
+ if (nsk != sk) {
+ bh_lock_sock(nsk);
+ if (nsk->lock.users != 0) {
+ skb_orphan(skb);
+ sk_add_backlog(nsk, skb);
+ bh_unlock_sock(nsk);
+ return 0;
+ }
+ need_unlock = 1;
+ sk = nsk;
}
- sk = nsk;
}
if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
goto reset;
- return 0;
+ goto out_maybe_unlock;
reset:
tcp_v4_send_reset(skb);
* might be destroyed here. This current version compiles correctly,
* but you have been warned.
*/
+out_maybe_unlock:
+ if(need_unlock)
+ bh_unlock_sock(sk);
return 0;
}
{
struct tcphdr *th;
struct sock *sk;
+ int ret;
if (skb->pkt_type!=PACKET_HOST)
goto discard_it;
IPCB(skb)->redirport, skb->dev->ifindex);
else {
#endif
- sk = __tcp_v4_lookup(th, skb->nh.iph->saddr, th->source,
+ SOCKHASH_LOCK_READ_BH();
+ sk = __tcp_v4_lookup(skb->nh.iph->saddr, th->source,
skb->nh.iph->daddr, th->dest, skb->dev->ifindex);
+ SOCKHASH_UNLOCK_READ_BH();
#ifdef CONFIG_IP_TRANSPARENT_PROXY
if (!sk)
sk = tcp_v4_search_proxy_openreq(skb);
if (sk->state == TCP_TIME_WAIT)
goto do_time_wait;
- if (!atomic_read(&sk->sock_readers))
- return tcp_v4_do_rcv(sk, skb);
- __skb_queue_tail(&sk->back_log, skb);
- return 0;
+ bh_lock_sock(sk);
+ ret = 0;
+ if (!sk->lock.users)
+ ret = tcp_v4_do_rcv(sk, skb);
+ else
+ sk_add_backlog(sk, skb);
+ bh_unlock_sock(sk);
+
+ return ret;
no_tcp_socket:
tcp_v4_send_reset(skb);
#include <net/tcp.h>
+#include <linux/smp_lock.h>
+
extern int sysctl_tcp_timestamps;
extern int sysctl_tcp_window_scaling;
extern int sysctl_tcp_sack;
/* Ok, now lock the socket before we make it visible to
* the incoming packet engine.
*/
+ unlock_kernel();
lock_sock(sk);
/* Socket identity change complete, no longer
/* Now, it is safe to release the socket. */
release_sock(sk);
+ lock_kernel();
}
/* Send out a delayed ack, the caller does the policy checking
if(!sk->zapped &&
sk->tp_pinfo.af_tcp.delayed_acks &&
sk->state != TCP_CLOSE) {
- /* If socket is currently locked, defer the ACK. */
- if (!atomic_read(&sk->sock_readers))
+ bh_lock_sock(sk);
+ if (!sk->lock.users)
tcp_send_ack(sk);
else
tcp_send_delayed_ack(&(sk->tp_pinfo.af_tcp), HZ/10);
+ bh_unlock_sock(sk);
}
}
if(sk->zapped)
return;
- if (atomic_read(&sk->sock_readers)) {
+ bh_lock_sock(sk);
+ if (sk->lock.users) {
/* Try again later. */
tcp_reset_xmit_timer(sk, TIME_PROBE0, HZ/5);
+ bh_unlock_sock(sk);
return;
}
/* Only send another probe if we didn't close things up. */
tcp_send_probe0(sk);
}
+ bh_unlock_sock(sk);
}
static __inline__ int tcp_keepopen_proc(struct sock *sk)
{
int i, reaped = 0;;
- for(i = 0; i < TCP_BHTABLE_SIZE; i++) {
- struct tcp_bind_bucket *tb = tcp_bound_hash[i];
+ SOCKHASH_LOCK_WRITE_BH();
+ for(i = 0; i < tcp_bhash_size; i++) {
+ struct tcp_bind_bucket *tb = tcp_bhash[i];
while(tb) {
struct tcp_bind_bucket *next = tb->next;
tb = next;
}
}
+ SOCKHASH_UNLOCK_WRITE_BH();
+
if(reaped != 0) {
struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data;
struct tcp_tw_bucket *tw;
int killed = 0;
+ /* The death-row tw chains are only ever touched
+ * in BH context so no locking is needed.
+ */
tw = tcp_tw_death_row[tcp_tw_death_row_slot];
tcp_tw_death_row[tcp_tw_death_row_slot] = NULL;
+ tcp_tw_death_row_slot =
+ ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1));
+
while(tw != NULL) {
struct tcp_tw_bucket *next = tw->next_death;
struct tcp_sl_timer *slt = (struct tcp_sl_timer *)data;
atomic_sub(killed, &slt->count);
}
- tcp_tw_death_row_slot =
- ((tcp_tw_death_row_slot + 1) & (TCP_TWKILL_SLOTS - 1));
}
/* These are always called from BH context. See callers in
int slot = (tcp_tw_death_row_slot - 1) & (TCP_TWKILL_SLOTS - 1);
struct tcp_tw_bucket **tpp = &tcp_tw_death_row[slot];
+ SOCKHASH_LOCK_WRITE_BH();
if((tw->next_death = *tpp) != NULL)
(*tpp)->pprev_death = &tw->next_death;
*tpp = tw;
tw->pprev_death = tpp;
tw->death_slot = slot;
+ SOCKHASH_UNLOCK_WRITE_BH();
tcp_inc_slow_timer(TCP_SLT_TWKILL);
}
struct tcp_tw_bucket **tpp;
int slot;
+ SOCKHASH_LOCK_WRITE_BH();
if(tw->next_death)
tw->next_death->pprev_death = tw->pprev_death;
*tw->pprev_death = tw->next_death;
tw->pprev_death = tpp;
tw->death_slot = slot;
+ SOCKHASH_UNLOCK_WRITE_BH();
+
/* Timer was incremented when we first entered the table. */
}
/* This is for handling early-kills of TIME_WAIT sockets. */
void tcp_tw_deschedule(struct tcp_tw_bucket *tw)
{
+ SOCKHASH_LOCK_WRITE_BH();
if(tw->next_death)
tw->next_death->pprev_death = tw->pprev_death;
*tw->pprev_death = tw->next_death;
tw->pprev_death = NULL;
+ SOCKHASH_UNLOCK_WRITE_BH();
+
tcp_dec_slow_timer(TCP_SLT_TWKILL);
}
int count = 0;
int i;
- for(i = chain_start; i < (chain_start + ((TCP_HTABLE_SIZE/2) >> 2)); i++) {
- struct sock *sk = tcp_established_hash[i];
+ SOCKHASH_LOCK_READ_BH();
+ for(i = chain_start; i < (chain_start + ((tcp_ehash_size >> 1) >> 2)); i++) {
+ struct sock *sk;
+
+ sk = tcp_ehash[i];
while(sk) {
- if(!atomic_read(&sk->sock_readers) && sk->keepopen) {
+ struct sock *next = sk->next;
+
+ bh_lock_sock(sk);
+ if (sk->keepopen && !sk->lock.users) {
+ SOCKHASH_UNLOCK_READ_BH();
count += tcp_keepopen_proc(sk);
- if(count == sysctl_tcp_max_ka_probes)
- goto out;
+ SOCKHASH_LOCK_READ_BH();
}
- sk = sk->next;
+ bh_unlock_sock(sk);
+ if(count == sysctl_tcp_max_ka_probes)
+ goto out;
+ sk = next;
}
}
out:
- chain_start = ((chain_start + ((TCP_HTABLE_SIZE/2)>>2)) &
- ((TCP_HTABLE_SIZE/2) - 1));
+ SOCKHASH_UNLOCK_READ_BH();
+ chain_start = ((chain_start + ((tcp_ehash_size >> 1)>>2)) &
+ ((tcp_ehash_size >> 1) - 1));
}
/*
return;
}
- if (atomic_read(&sk->sock_readers)) {
+ bh_lock_sock(sk);
+ if (sk->lock.users) {
/* Try again later */
tcp_reset_xmit_timer(sk, TIME_RETRANS, HZ/20);
+ bh_unlock_sock(sk);
return;
}
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
tcp_write_timeout(sk);
+
+ bh_unlock_sock(sk);
}
/*
* Slow timer for SYN-RECV sockets
*/
+static void tcp_do_syn_queue(struct sock *sk, struct tcp_opt *tp, unsigned long now)
+{
+ struct open_request *prev, *req;
+
+ prev = (struct open_request *) &tp->syn_wait_queue;
+ for(req = tp->syn_wait_queue; req; ) {
+ struct open_request *next = req->dl_next;
+
+ if (! req->sk) {
+ tcp_synq_unlink(tp, req, prev);
+ if(req->retrans >= sysctl_tcp_retries1) {
+ (*req->class->destructor)(req);
+ tcp_dec_slow_timer(TCP_SLT_SYNACK);
+ tp->syn_backlog--;
+ tcp_openreq_free(req);
+ if (! tp->syn_wait_queue)
+ break;
+ } else {
+ unsigned long timeo;
+ struct open_request *rp;
+
+ (*req->class->rtx_syn_ack)(sk, req);
+ req->retrans++;
+ timeo = min((TCP_TIMEOUT_INIT << req->retrans),
+ (120 * HZ));
+ req->expires = now + timeo;
+ rp = prev->dl_next;
+ tcp_synq_queue(tp, req);
+ if(rp != prev->dl_next)
+ prev = prev->dl_next;
+ }
+ } else
+ prev = req;
+ req = next;
+ }
+}
+
/* This now scales very nicely. -DaveM */
static void tcp_syn_recv_timer(unsigned long data)
{
unsigned long now = jiffies;
int i;
+ SOCKHASH_LOCK_READ_BH();
for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
sk = tcp_listening_hash[i];
-
while(sk) {
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
/* TCP_LISTEN is implied. */
- if (!atomic_read(&sk->sock_readers) && tp->syn_wait_queue) {
- struct open_request *prev = (struct open_request *)(&tp->syn_wait_queue);
- struct open_request *req = tp->syn_wait_queue;
- do {
- struct open_request *conn;
-
- conn = req;
- req = req->dl_next;
-
- if (conn->sk) {
- prev = conn;
- continue;
- }
-
- tcp_synq_unlink(tp, conn, prev);
- if (conn->retrans >= sysctl_tcp_retries1) {
-#ifdef TCP_DEBUG
- printk(KERN_DEBUG "syn_recv: "
- "too many retransmits\n");
-#endif
- (*conn->class->destructor)(conn);
- tcp_dec_slow_timer(TCP_SLT_SYNACK);
- tp->syn_backlog--;
- tcp_openreq_free(conn);
-
- if (!tp->syn_wait_queue)
- break;
- } else {
- unsigned long timeo;
- struct open_request *op;
-
- (*conn->class->rtx_syn_ack)(sk, conn);
-
- conn->retrans++;
-#ifdef TCP_DEBUG
- printk(KERN_DEBUG "syn_ack rtx %d\n",
- conn->retrans);
-#endif
- timeo = min((TCP_TIMEOUT_INIT
- << conn->retrans),
- 120*HZ);
- conn->expires = now + timeo;
- op = prev->dl_next;
- tcp_synq_queue(tp, conn);
- if (op != prev->dl_next)
- prev = prev->dl_next;
- }
- /* old prev still valid here */
- } while (req);
- }
+ bh_lock_sock(sk);
+ if (!sk->lock.users && tp->syn_wait_queue)
+ tcp_do_syn_queue(sk, tp, now);
+ bh_unlock_sock(sk);
sk = sk->next;
}
}
+ SOCKHASH_UNLOCK_READ_BH();
}
void tcp_sltimer_handler(unsigned long data)
*/
void net_timer (unsigned long data)
{
- struct sock *sk = (struct sock*)data;
+ struct sock *sk = (struct sock *) data;
int why = sk->timeout;
/* Only process if socket is not in use. */
- if (atomic_read(&sk->sock_readers)) {
+ bh_lock_sock(sk);
+ if (sk->lock.users) {
/* Try again later. */
mod_timer(&sk->timer, jiffies+HZ/20);
+ bh_unlock_sock(sk);
return;
}
printk (KERN_DEBUG "non CLOSE socket in time_done\n");
break;
}
- destroy_sock (sk);
- break;
+ destroy_sock(sk);
+ return;
case TIME_DESTROY:
/* We've waited for a while for all the memory associated with
* the socket to be freed.
*/
destroy_sock(sk);
- break;
+ return;
case TIME_CLOSE:
/* We've waited long enough, close the socket. */
printk ("net_timer: timer expired - reason %d is unknown\n", why);
break;
}
+
+ /* We only need to unlock if the socket was not destroyed. */
+ bh_unlock_sock(sk);
}
struct sock *sk2;
int retval = 0, sk_reuse = sk->reuse;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
if((sk2->num == snum) && (sk2 != sk)) {
unsigned char state = sk2->state;
}
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return retval;
}
static int start = 0;
int i, best, best_size_so_far;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0])
start = sysctl_local_port_range[0];
}
out:
start = result;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return result;
}
-/* Last hit UDP socket cache, this is ipv4 specific so make it static. */
-static u32 uh_cache_saddr, uh_cache_daddr;
-static u16 uh_cache_dport, uh_cache_sport;
-static struct sock *uh_cache_sk = NULL;
-
static void udp_v4_hash(struct sock *sk)
{
struct sock **skp;
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
sk->next = *skp;
*skp = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void udp_v4_unhash(struct sock *sk)
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
}
skp = &((*skp)->next);
}
- if(uh_cache_sk == sk)
- uh_cache_sk = NULL;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void udp_v4_rehash(struct sock *sk)
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[oldnum];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
sk->next = udp_hash[num];
udp_hash[num] = sk;
sk->hashent = num;
- if(uh_cache_sk == sk)
- uh_cache_sk = NULL;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
- * harder than this here plus the last hit cache. -DaveM
+ * harder than this. -DaveM
*/
struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
{
{
struct sock *sk;
- if(!dif && uh_cache_sk &&
- uh_cache_saddr == saddr &&
- uh_cache_sport == sport &&
- uh_cache_dport == dport &&
- uh_cache_daddr == daddr)
- return uh_cache_sk;
-
+ SOCKHASH_LOCK_READ();
sk = udp_v4_lookup_longway(saddr, sport, daddr, dport, dif);
- if(!dif) {
- uh_cache_sk = sk;
- uh_cache_saddr = saddr;
- uh_cache_daddr = daddr;
- uh_cache_sport = sport;
- uh_cache_dport = dport;
- }
+ SOCKHASH_UNLOCK_READ();
return sk;
}
paddr = idev->ifa_list->ifa_local;
}
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
s != NULL;
s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
}
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return result;
}
sk->rcv_saddr=INADDR_ANY;
sk->daddr=INADDR_ANY;
sk->state = TCP_CLOSE;
- if(uh_cache_sk == sk)
- uh_cache_sk = NULL;
return 0;
}
sk->dport = usin->sin_port;
sk->state = TCP_ESTABLISHED;
- if(uh_cache_sk == sk)
- uh_cache_sk = NULL;
-
sk->dst_cache = &rt->u.dst;
return(0);
}
static void udp_close(struct sock *sk, long timeout)
{
+ bh_lock_sock(sk);
+
/* See for explanation: raw_close in ipv4/raw.c */
sk->state = TCP_CLOSE;
udp_v4_unhash(sk);
return;
}
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
if (dev->ip_ptr && (dev->flags & IFF_UP)) {
struct in_device * in_dev = dev->ip_ptr;
flag |= IFA_HOST;
}
+ read_unlock_bh(&dev_base_lock);
addrconf_lock();
ifp = ipv6_add_addr(idev, &addr, flag);
if (ifp) {
ipv6_ifa_notify(RTM_NEWADDR, ifp);
}
addrconf_unlock();
+ read_lock_bh(&dev_base_lock);
}
}
}
+ read_unlock_bh(&dev_base_lock);
}
static void init_loopback(struct device *dev)
struct device *dev;
/* This takes sense only during module load. */
-
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
if (!(dev->flags&IFF_UP))
continue;
+ read_unlock_bh(&dev_base_lock);
switch (dev->type) {
case ARPHRD_LOOPBACK:
init_loopback(dev);
default:
/* Ignore all other */
}
+ read_lock_bh(&dev_base_lock);
}
+ read_unlock_bh(&dev_base_lock);
#endif
#ifdef CONFIG_PROC_FS
int len=0;
struct device *dev;
+ read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
struct inet6_dev *idev;
*eof = 1;
done:
+ read_unlock_bh(&dev_base_lock);
+
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
/*144 */
pos = 149;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
sp = pro->sklist_next;
while(sp != (struct sock *)pro) {
struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp;
}
destp = ntohs(sp->dport);
srcp = ntohs(sp->sport);
+
if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
extern int tcp_tw_death_row_slot;
int slot_dist;
slot_dist = tcp_tw_death_row_slot - slot_dist;
timer_expires = jiffies + (slot_dist * TCP_TWKILL_PERIOD);
} else {
- timer_active1 = del_timer(&tp->retransmit_timer);
- timer_active2 = del_timer(&sp->timer);
- if(!timer_active1) tp->retransmit_timer.expires = 0;
- if(!timer_active2) sp->timer.expires = 0;
+ timer_active1 = tp->retransmit_timer.prev != NULL;
+ timer_active2 = sp->timer.prev != NULL;
timer_active = 0;
timer_expires = (unsigned) -1;
}
((!tw_bucket && sp->socket) ?
sp->socket->inode->i_ino : 0));
- if(timer_active1) add_timer(&tp->retransmit_timer);
- if(timer_active2) add_timer(&sp->timer);
len += sprintf(buffer+len, "%-148s\n", tmpbuf);
if(len >= length)
break;
sp = sp->sklist_next;
i++;
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
begin = len - (pos - offset);
*start = buffer + begin;
num &= (RAWV6_HTABLE_SIZE - 1);
skp = &raw_v6_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
sk->next = *skp;
*skp = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v6_unhash(struct sock *sk)
num &= (RAWV6_HTABLE_SIZE - 1);
skp = &raw_v6_htable[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
}
skp = &((*skp)->next);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void raw_v6_rehash(struct sock *sk)
num &= (RAWV6_HTABLE_SIZE - 1);
skp = &raw_v6_htable[oldnum];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
sk->next = raw_v6_htable[num];
raw_v6_htable[num] = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static __inline__ int inet6_mc_check(struct sock *sk, struct in6_addr *addr)
static void rawv6_close(struct sock *sk, long timeout)
{
+ bh_lock_sock(sk);
+
/* See for explanation: raw_close in ipv4/raw.c */
sk->state = TCP_CLOSE;
raw_v6_unhash(sk);
int hashent = (lport ^ fport);
hashent ^= (laddr->s6_addr32[3] ^ faddr->s6_addr32[3]);
- return (hashent & ((TCP_HTABLE_SIZE/2) - 1));
+ return (hashent & ((tcp_ehash_size >> 1) - 1));
}
static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
struct tcp_bind_bucket *tb;
int result = 0;
- SOCKHASH_LOCK();
- for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ SOCKHASH_LOCK_WRITE();
+ for(tb = tcp_bhash[tcp_bhashfn(snum)];
(tb && (tb->port != snum));
tb = tb->next)
;
}
}
go_like_smoke:
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
return result;
}
if(sk->state != TCP_CLOSE) {
struct sock **skp;
- SOCKHASH_LOCK();
- skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+ SOCKHASH_LOCK_WRITE();
+ skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
*skp = sk;
sk->pprev = skp;
tcp_sk_bindify(sk);
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
}
static void tcp_v6_unhash(struct sock *sk)
{
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
if(sk->pprev) {
if(sk->next)
sk->next->pprev = sk->pprev;
tcp_sk_unbindify(sk);
tcp_reg_zap(sk);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void tcp_v6_rehash(struct sock *sk)
{
unsigned char state;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
state = sk->state;
if(sk->pprev != NULL) {
if(sk->next)
if(state == TCP_LISTEN)
skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
else
- skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+ skp = &tcp_ehash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
if((sk->next = *skp) != NULL)
(*skp)->pprev = &sk->next;
if(state == TCP_LISTEN)
tcp_sk_bindify(sk);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum, int dif)
/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
* we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
- * It is assumed that this code only gets called from within NET_BH.
+ *
+ * The sockhash lock must be held as a reader here.
*/
-static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
- struct in6_addr *saddr, u16 sport,
+static inline struct sock *__tcp_v6_lookup(struct in6_addr *saddr, u16 sport,
struct in6_addr *daddr, u16 dport,
int dif)
{
* have wildcards anyways.
*/
hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
- for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
+ for(sk = tcp_ehash[hash]; sk; sk = sk->next) {
/* For IPV6 do the cheaper port and family tests first. */
if(TCP_IPV6_MATCH(sk, saddr, daddr, ports, dif)) {
if (sk->state == TCP_ESTABLISHED)
}
}
/* Must check for a TIME_WAIT'er before going to listener hash. */
- for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
+ for(sk = tcp_ehash[hash+(tcp_ehash_size >> 1)]; sk; sk = sk->next) {
if(*((__u32 *)&(sk->dport)) == ports &&
sk->family == PF_INET6) {
struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
return sk;
}
-#define tcp_v6_lookup(sa, sp, da, dp, dif) __tcp_v6_lookup((0),(sa),(sp),(da),(dp),(dif))
+#define tcp_v6_lookup(sa, sp, da, dp, dif) \
+({ struct sock *___sk; \
+ SOCKHASH_LOCK_READ(); \
+ ___sk = __tcp_v6_lookup((sa),(sp),(da),(dp),(dif)); \
+ SOCKHASH_UNLOCK_READ(); \
+ ___sk; \
+})
static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
struct in6_addr *saddr,
int retval = 1;
/* Freeze the hash while we snoop around. */
- SOCKHASH_LOCK();
- tb = tcp_bound_hash[tcp_bhashfn(snum)];
+ SOCKHASH_LOCK_READ();
+ tb = tcp_bhash[tcp_bhashfn(snum)];
for(; tb; tb = tb->next) {
if(tb->port == snum && tb->owners != NULL) {
/* Almost certainly the re-use port case, search the real hashes
* so it actually scales. (we hope that all ipv6 ftp servers will
* use passive ftp, I just cover this case for completeness)
*/
- sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr,
+ sk = __tcp_v6_lookup(&sk->net_pinfo.af_inet6.daddr,
sk->dport,
&sk->net_pinfo.af_inet6.rcv_saddr, snum,
sk->bound_dev_if);
+ SOCKHASH_UNLOCK_READ();
+
if((sk != NULL) && (sk->state != TCP_LISTEN))
retval = 0;
- break;
+ return retval;
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return retval;
}
failure:
dst_release(xchg(&sk->dst_cache, NULL));
- memcpy(&np->daddr, 0, sizeof(struct in6_addr));
+ memset(&np->daddr, 0, sizeof(struct in6_addr));
sk->daddr = 0;
return err;
}
if (type == ICMPV6_PKT_TOOBIG) {
struct dst_entry *dst = NULL;
- if (atomic_read(&sk->sock_readers))
+ if (sk->state == TCP_LISTEN)
return;
- if (sk->state == TCP_LISTEN)
+ bh_lock_sock(sk);
+ if(sk->lock.users) {
+ bh_unlock_sock(sk);
return;
+ }
/* icmp should have updated the destination cache entry */
if (sk->dst_cache)
tcp_simple_retransmit(sk);
} /* else let the usual retransmit timer handle it */
dst_release(dst);
- return;
+ bh_unlock_sock(sk);
}
icmpv6_err_convert(type, code, &err);
struct open_request *req, *prev;
struct ipv6hdr hd;
case TCP_LISTEN:
- if (atomic_read(&sk->sock_readers)) {
+ bh_lock_sock(sk);
+ if (sk->lock.users) {
net_statistics.LockDroppedIcmps++;
/* If too many ICMPs get dropped on busy
* servers this needs to be solved differently.
*/
+ bh_unlock_sock(sk);
return;
}
ipv6_addr_copy(&hd.saddr, saddr);
ipv6_addr_copy(&hd.daddr, daddr);
req = tcp_v6_search_req(tp, &hd, th, tcp_v6_iif(skb), &prev);
- if (!req)
- return;
- if (seq != req->snt_isn) {
+ if (!req || (seq != req->snt_isn)) {
net_statistics.OutOfWindowIcmps++;
+ bh_unlock_sock(sk);
return;
}
if (req->sk) {
+ bh_unlock_sock(sk);
sk = req->sk; /* report error in accept */
} else {
tp->syn_backlog--;
tcp_synq_unlink(tp, req, prev);
req->class->destructor(req);
tcp_openreq_free(req);
+ bh_unlock_sock(sk);
}
+
/* FALL THROUGH */
case TCP_SYN_SENT:
case TCP_SYN_RECV: /* Cannot happen */
return sk;
}
+/* The socket must have it's spinlock held when we get
+ * here.
+ *
+ * We have a potential double-lock case here, so even when
+ * doing backlog processing we use the BH locking scheme.
+ * This is because we cannot sleep with the original spinlock
+ * held.
+ */
static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
{
#ifdef CONFIG_FILTER
struct sk_filter *filter;
#endif
- int users = 0;
+ int users = 0, need_unlock = 0;
/* Imagine: socket is IPv6. IPv4 packet arrives,
goes to IPv4 receive handler and backlogged.
* otherwise we just shortcircuit this and continue with
* the new socket..
*/
- if (atomic_read(&nsk->sock_readers)) {
- skb_orphan(skb);
- __skb_queue_tail(&nsk->back_log, skb);
- return 0;
+ if(nsk != sk) {
+ bh_lock_sock(nsk);
+ if (nsk->lock.users) {
+ skb_orphan(skb);
+ sk_add_backlog(nsk, skb);
+ bh_unlock_sock(nsk);
+ return 0;
+ }
+ need_unlock = 1;
+ sk = nsk;
}
- sk = nsk;
}
if (tcp_rcv_state_process(sk, skb, skb->h.th, skb->len))
goto reset;
if (users)
goto ipv6_pktoptions;
- return 0;
+ goto out_maybe_unlock;
reset:
tcp_v6_send_reset(skb);
if (users)
kfree_skb(skb);
kfree_skb(skb);
- return 0;
+ goto out_maybe_unlock;
ipv6_pktoptions:
/* Do you ask, what is it?
if (skb)
kfree_skb(skb);
+out_maybe_unlock:
+ if (need_unlock)
+ bh_unlock_sock(sk);
return 0;
}
struct sock *sk;
struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+ int ret;
th = skb->h.th;
/* CHECKSUM_UNNECESSARY */
};
- sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+ SOCKHASH_LOCK_READ_BH();
+ sk = __tcp_v6_lookup(saddr, th->source, daddr, th->dest, tcp_v6_iif(skb));
+ SOCKHASH_UNLOCK_READ_BH();
if (!sk)
goto no_tcp_socket;
if(sk->state == TCP_TIME_WAIT)
goto do_time_wait;
- if (!atomic_read(&sk->sock_readers))
- return tcp_v6_do_rcv(sk, skb);
+ bh_lock_sock(sk);
+ ret = 0;
+ if (!sk->lock.users)
+ ret = tcp_v6_do_rcv(sk, skb);
+ else
+ sk_add_backlog(sk, skb);
+ bh_unlock_sock(sk);
- __skb_queue_tail(&sk->back_log, skb);
- return(0);
+ return ret;
no_tcp_socket:
tcp_v6_send_reset(skb);
int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
int retval = 0, sk_reuse = sk->reuse;
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_READ();
for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
if((sk2->num == snum) && (sk2 != sk)) {
unsigned char state = sk2->state;
}
}
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_READ();
return retval;
}
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
sk->next = *skp;
*skp = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void udp_v6_unhash(struct sock *sk)
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[num];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
}
skp = &((*skp)->next);
}
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static void udp_v6_rehash(struct sock *sk)
num &= (UDP_HTABLE_SIZE - 1);
skp = &udp_hash[oldnum];
- SOCKHASH_LOCK();
+ SOCKHASH_LOCK_WRITE();
while(*skp != NULL) {
if(*skp == sk) {
*skp = sk->next;
sk->next = udp_hash[num];
udp_hash[num] = sk;
sk->hashent = num;
- SOCKHASH_UNLOCK();
+ SOCKHASH_UNLOCK_WRITE();
}
static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
unsigned short hnum = ntohs(dport);
int badness = -1;
+ SOCKHASH_LOCK_READ();
for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
if((sk->num == hnum) &&
(sk->family == PF_INET6) &&
}
}
}
+ SOCKHASH_UNLOCK_READ();
return result;
}
static void udpv6_close(struct sock *sk, long timeout)
{
+ bh_lock_sock(sk);
+
/* See for explanation: raw_close in ipv4/raw.c */
sk->state = TCP_CLOSE;
udp_v6_unhash(sk);
{
struct device *dev, *first = NULL;
- for (dev = dev_base; dev != NULL; dev = dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
first = dev;
+ }
+ read_unlock_bh(&dev_base_lock);
return first;
}
{
struct device *dev;
- for (dev = dev_base; dev != NULL; dev = dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
- return dev;
-
- return NULL;
+ goto out;
+ }
+out:
+ read_unlock_bh(&dev_base_lock);
+ return dev;
}
static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
EXPORT_SYMBOL(skb_over_panic);
EXPORT_SYMBOL(skb_under_panic);
+/* Socket layer global data */
+EXPORT_SYMBOL(sockhash_lock);
+
/* Socket layer registration */
EXPORT_SYMBOL(sock_register);
EXPORT_SYMBOL(sock_unregister);
+/* Socket locking */
+EXPORT_SYMBOL(lock_sock);
+EXPORT_SYMBOL(release_sock);
+
/* Socket layer support routines */
EXPORT_SYMBOL(memcpy_fromiovec);
EXPORT_SYMBOL(memcpy_tokerneliovec);
EXPORT_SYMBOL(__ip_finish_output);
EXPORT_SYMBOL(inet_dgram_ops);
EXPORT_SYMBOL(ip_cmsg_recv);
-EXPORT_SYMBOL(__release_sock);
/* Route manipulation */
EXPORT_SYMBOL(ip_rt_ioctl);
/* Socket demultiplexing. */
EXPORT_SYMBOL(tcp_good_socknum);
-EXPORT_SYMBOL(tcp_established_hash);
+EXPORT_SYMBOL(tcp_ehash);
+EXPORT_SYMBOL(tcp_ehash_size);
EXPORT_SYMBOL(tcp_listening_hash);
-EXPORT_SYMBOL(tcp_bound_hash);
+EXPORT_SYMBOL(tcp_bhash);
+EXPORT_SYMBOL(tcp_bhash_size);
EXPORT_SYMBOL(udp_good_socknum);
EXPORT_SYMBOL(udp_hash);
EXPORT_SYMBOL(netdev_fc_xoff);
#endif
EXPORT_SYMBOL(dev_base);
+EXPORT_SYMBOL(dev_base_lock);
EXPORT_SYMBOL(dev_close);
EXPORT_SYMBOL(dev_mc_add);
EXPORT_SYMBOL(dev_mc_delete);
{
struct device *dev, *first = NULL;
- for (dev = dev_base; dev != NULL; dev = dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE)
if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
first = dev;
+ }
+ read_unlock_bh(&dev_base_lock);
return first;
}
{
struct device *dev;
- for (dev = dev_base; dev != NULL; dev = dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ROSE && rosecmp(addr, (rose_address *)dev->dev_addr) == 0)
- return dev;
-
- return NULL;
+ goto out;
+ }
+out:
+ read_unlock_bh(&dev_base_lock);
+ return dev;
}
struct rose_route *rose_route_free_lci(unsigned int lci, struct rose_neigh *neigh)
s_idx = cb->args[0];
s_q_idx = q_idx = cb->args[1];
+ read_lock_bh(&dev_base_lock);
for (dev=dev_base, idx=0; dev; dev = dev->next, idx++) {
if (idx < s_idx)
continue;
}
done:
+ read_unlock_bh(&dev_base_lock);
+
cb->args[0] = idx;
cb->args[1] = q_idx;
struct Qdisc *q = (struct Qdisc*)h;
struct device *dev = q->dev;
+ spin_lock_bh(&dev->xmit_lock);
while (!dev->tbusy && (res = qdisc_restart(dev)) < 0)
/* NOTHING */;
+ spin_unlock_bh(&dev->xmit_lock);
/* An explanation is necessary here.
qdisc_restart called dev->hard_start_xmit,
for (h = qdisc_head.forw; h != &qdisc_head; h = h->forw) {
struct Qdisc *q = (struct Qdisc*)h;
struct device *dev = q->dev;
+
+ spin_lock_bh(&dev->xmit_lock);
if (dev->tbusy && jiffies - q->tx_last > q->tx_timeo)
qdisc_restart(dev);
+ spin_unlock_bh(&dev->xmit_lock);
}
dev_watchdog.expires = jiffies + 5*HZ;
add_timer(&dev_watchdog);
static int sockets_in_use = 0;
+/*
+ * Socket hashing lock.
+ */
+rwlock_t sockhash_lock = RW_LOCK_UNLOCKED;
+
/*
* Support routines. Move socket addresses back and forth across the kernel/user
* divide and look after the messy bits.
/* fall through */
case 0:
call_kill:
- kill_fasync(sock->fasync_list, SIGIO);
+ if(sock->fasync_list != NULL)
+ kill_fasync(sock->fasync_list, SIGIO);
break;
}
return 0;
return (unix_peer(osk) == NULL || unix_our_peer(sk, osk));
}
+#define ulock(sk) (&(sk->protinfo.af_unix.user_count))
+
extern __inline__ void unix_lock(unix_socket *sk)
{
- atomic_inc(&sk->sock_readers);
+ atomic_inc(ulock(sk));
}
extern __inline__ void unix_unlock(unix_socket *sk)
{
- atomic_dec(&sk->sock_readers);
+ atomic_dec(ulock(sk));
}
extern __inline__ int unix_locked(unix_socket *sk)
{
- return atomic_read(&sk->sock_readers);
+ return (atomic_read(ulock(sk)) != 0);
}
extern __inline__ void unix_release_addr(struct unix_address *addr)
{
len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld",
s,
- atomic_read(&s->sock_readers),
+ atomic_read(ulock(s)),
0,
s->socket ? s->socket->flags : 0,
s->type,
/*
* Register any pre existing devices.
*/
- for (dev = dev_base; dev != NULL; dev = dev->next)
+ read_lock_bh(&dev_base_lock);
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
|| dev->type == ARPHRD_ETHER
#endif
- ))
- x25_link_device_up(dev);
-
+ )) {
+ read_unlock_bh(&dev_base_lock);
+ x25_link_device_up(dev);
+ read_lock_bh(&dev_base_lock);
+ }
+ }
+ read_unlock_bh(&dev_base_lock);
+
return 0;
}