isdn: deprecate non-mISDN drivers
When isdn4linux came up in the context of another patch series, I remembered that we had discussed removing it a while ago. It turns out that the suggestion from Karsten Keil wa to remove I4L in 2018 after the last public ISDN networks are shut down. This has happened now (with a very small number of exceptions), so I guess it's time to try again. We currently have three ISDN stacks in the kernel: the original isdn4linux (with the hisax driver), the newer CAPI (with four drivers), and finally the mISDN stack (supporting roughly the same hardware as hisax). As far as I can tell, anyone using ISDN with mainline kernel drivers in the past few years uses mISDN, and this is typically used for voice-only PBX installations that don't require a public network. The older stacks support additional features for data networks, but those typically make no sense any more if there is no network to connect to. My proposal for this time is to kill off isdn4linux entirely, as it seems to have been unusable for quite a while. This code has been abandoned for many years and it does cause problems for treewide maintenance as it tends to do everything that we try to stop doing. Birger Harzenetter mentioned that is is still using i4l in order to make use of the 'divert' feature that is not part of mISDN, but has otherwise moved on to mISDN for normal operation, like apparently everyone else. CAPI in turn is not quite as obsolete, but two of the drivers (avm and hysdn) don't seem to be used at all, while another one (gigaset) will stop being maintained as Paul Bolle is no longer able to test it after the network gets shut down in September. All three are now moved into drivers/staging to let others speak up in case there are remaining users. This leaves Bluetooth CMTP as the only remaining user of CAPI, but Marcel Holtmann wishes to keep maintaining it. For the discussion on version 1, see [2] Unfortunately, Karsten Keil as the maintainer has not participated in the discussion. Arnd [1] https://patchwork.kernel.org/patch/8484861/#17900371 [2] https://listserv.isdn4linux.de/pipermail/isdn4linux/2019-April/thread.html -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJc8QxgAAoJEJpsee/mABjZriwQAKL4L62VUz9nbLdK1b0wHsT/ ttHYRZwyzTqzzfXEEyTVynHdwW0qjXxmjfOP7gl1ZY62KtuuBC0XlpQ4Ho05h/DZ zqWSS+BunIHShbS/9Cv+/d2biGVa9+XPgbV2XptPC0zm+VuucnKIssPbawD65rQU BtiNFGqGNo5Wkjn5kdkFIIOdL+eNlW/+l8iuewhJfAoFJYMuKHy6jWUfUqDD/9nv EyrNdWgbPZqwLVKYcqQWpJ0Fy8w1uDhbKbL7PdK/O7aHkNOPLBLY28AEVW0gZ7P1 WmlhChyk84Xp3/9A+aV2q/4rdtjJ1hkEGXyPTeR0TJ6OSKbs90xJA92sVnwBM3TH yaRiFDRFMbu39lxngYYKDcq3BCUUuSVZw9zvtIW+T38DeFT2DcNapDGSqpp3Fhbo QhU8iJK2TnUqbQSMCPuAa14bvowPvDIlmYBV/2aMiDEegjj2Zo1G/Y3oA2JuePwU FVhbYYQTBUF9Un/J5EyuejVBKv/N+8P17hzFADqnuMZ5HzfXtUz0Nv8WwLRsflYo 5ZjNkhEqvLuTVaXtwdWh8nEsXmb1dIpQs4Z9fP3GPirOg9aN1MVIZ38qc9wBmGlk 5V+Fk7kGqfvXMVeE91M4A+ojd06/fG7RGHW4FCmI+/jeYNt1ncPR0wEU5K5qKIC4 qMwln0DfduKFLEE2wcX8 =1ISP -----END PGP SIGNATURE----- Merge tag 'isdn-removal' of https://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground Arnd Bergmann says: ==================== isdn: deprecate non-mISDN drivers When isdn4linux came up in the context of another patch series, I remembered that we had discussed removing it a while ago. It turns out that the suggestion from Karsten Keil wa to remove I4L in 2018 after the last public ISDN networks are shut down. This has happened now (with a very small number of exceptions), so I guess it's time to try again. We currently have three ISDN stacks in the kernel: the original isdn4linux (with the hisax driver), the newer CAPI (with four drivers), and finally the mISDN stack (supporting roughly the same hardware as hisax). As far as I can tell, anyone using ISDN with mainline kernel drivers in the past few years uses mISDN, and this is typically used for voice-only PBX installations that don't require a public network. The older stacks support additional features for data networks, but those typically make no sense any more if there is no network to connect to. My proposal for this time is to kill off isdn4linux entirely, as it seems to have been unusable for quite a while. This code has been abandoned for many years and it does cause problems for treewide maintenance as it tends to do everything that we try to stop doing. Birger Harzenetter mentioned that is is still using i4l in order to make use of the 'divert' feature that is not part of mISDN, but has otherwise moved on to mISDN for normal operation, like apparently everyone else. CAPI in turn is not quite as obsolete, but two of the drivers (avm and hysdn) don't seem to be used at all, while another one (gigaset) will stop being maintained as Paul Bolle is no longer able to test it after the network gets shut down in September. All three are now moved into drivers/staging to let others speak up in case there are remaining users. This leaves Bluetooth CMTP as the only remaining user of CAPI, but Marcel Holtmann wishes to keep maintaining it. For the discussion on version 1, see [2] Unfortunately, Karsten Keil as the maintainer has not participated in the discussion. Arnd [1] https://patchwork.kernel.org/patch/8484861/#17900371 [2] https://listserv.isdn4linux.de/pipermail/isdn4linux/2019-April/thread.html ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
8a7e8ff8ce
|
@ -1,96 +0,0 @@
|
|||
-----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
|
||||
and Eicon Technology Diva 2.01 PCI card.
|
||||
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.
|
||||
|
||||
Additional ITU approval tests have been carried out for all generic cards
|
||||
using Colognechip single chip solutions HFC-S PCI A for PCI cards as well
|
||||
as HFC-S USB based USB ISDN ta adapters.
|
||||
These tests included all layers 1-3 and as well all functional tests for
|
||||
the layer 1. Because all hardware based on these chips are complete ISDN
|
||||
solutions in one chip all cards and USB-TAs using these chips are to be
|
||||
regarded as approved for those tests. Some additional electrical tests
|
||||
of the layer 1 which are independent of the driver and related to a
|
||||
special hardware used will be regarded as approved if at least one
|
||||
solution has been tested including those electrical tests. So if cards
|
||||
or tas have been completely approved for any other os, the approval
|
||||
for those electrical tests is valid for linux, too.
|
||||
Please send any questions regarding this drivers or approval abouts to
|
||||
werner@isdn-development.de
|
||||
Additional information and the type approval documents will be found
|
||||
shortly on the Colognechip website www.colognechip.com
|
||||
|
||||
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 <kkeil@suse.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
|
||||
drivers/isdn/hisax/diva.c
|
||||
drivers/isdn/hisax/hfc_pci.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 one of the approved 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
|
||||
|
||||
iQCVAwUBOFAwqTpxHvX/mS9tAQFI2QP9GLDK2iy/KBhwReE3F7LeO+tVhffTVZ3a
|
||||
20q5/z/WcIg/pnH0uTkl2UgDXBFXYl45zJyDGNpAposIFmT+Edd14o7Vj1w/BBdn
|
||||
Y+5rBmJf+gyBu61da5d6bv0lpymwRa/um+ri+ilYnZ/XPfg5JKhdjGSBCJuJAElM
|
||||
d2jFbTrsMYw=
|
||||
=LNf9
|
||||
-----END PGP SIGNATURE-----
|
|
@ -1,759 +0,0 @@
|
|||
$Id: INTERFACE,v 1.15.8.2 2001/03/13 16:17:07 kai Exp $
|
||||
|
||||
Description of the Interface between Linklevel and Hardwarelevel
|
||||
of isdn4linux:
|
||||
|
||||
|
||||
The Communication between Linklevel (LL) and Hardwarelevel (HL)
|
||||
is based on the struct isdn_if (defined in isdnif.h).
|
||||
|
||||
An HL-driver can register itself at LL by calling the function
|
||||
register_isdn() with a pointer to that struct. Prior to that, it has
|
||||
to preset some of the fields of isdn_if. The LL sets the rest of
|
||||
the fields. All further communication is done via callbacks using
|
||||
the function-pointers defined in isdn_if.
|
||||
|
||||
Changes/Version numbering:
|
||||
|
||||
During development of the ISDN subsystem, several changes have been
|
||||
made to the interface. Before it went into kernel, the package
|
||||
had a unique version number. The last version, distributed separately
|
||||
was 0.7.4. When the subsystem went into kernel, every functional unit
|
||||
got a separate version number. These numbers are shown at initialization,
|
||||
separated by slashes:
|
||||
|
||||
c.c/t.t/n.n/p.p/a.a/v.v
|
||||
|
||||
where
|
||||
|
||||
c.c is the revision of the common code.
|
||||
t.t is the revision of the tty related code.
|
||||
n.n is the revision of the network related code.
|
||||
p.p is the revision of the ppp related code.
|
||||
a.a is the revision of the audio related code.
|
||||
v.v is the revision of the V.110 related code.
|
||||
|
||||
Changes in this document are marked with '***CHANGEx' where x representing
|
||||
the version number. If that number starts with 0, it refers to the old,
|
||||
separately distributed package. If it starts with one of the letters
|
||||
above, it refers to the revision of the corresponding module.
|
||||
***CHANGEIx refers to the revision number of the isdnif.h
|
||||
|
||||
1. Description of the fields of isdn_if:
|
||||
|
||||
int channels;
|
||||
|
||||
This field has to be set by the HL-driver to the number of channels
|
||||
supported prior to calling register_isdn(). Upon return of the call,
|
||||
the LL puts an id there, which has to be used by the HL-driver when
|
||||
invoking the other callbacks.
|
||||
|
||||
int maxbufsize;
|
||||
|
||||
***CHANGE0.6: New since this version.
|
||||
|
||||
Also to be preset by the HL-driver. With this value the HL-driver
|
||||
tells the LL the maximum size of a data-packet it will accept.
|
||||
|
||||
unsigned long features;
|
||||
|
||||
To be preset by the HL-driver. Using this field, the HL-driver
|
||||
announces the features supported. At the moment this is limited to
|
||||
report the supported layer2 and layer3-protocols. For setting this
|
||||
field the constants ISDN_FEATURE..., declared in isdnif.h have to be
|
||||
used.
|
||||
|
||||
***CHANGE0.7.1: The line type (1TR6, EDSS1) has to be set.
|
||||
|
||||
unsigned short hl_hdrlen;
|
||||
|
||||
***CHANGE0.7.4: New field.
|
||||
|
||||
To be preset by the HL-driver, if it supports sk_buff's. The driver
|
||||
should put here the amount of additional space needed in sk_buff's for
|
||||
its internal purposes. Drivers not supporting sk_buff's should
|
||||
initialize this field to 0.
|
||||
|
||||
void (*rcvcallb_skb)(int, int, struct sk_buff *)
|
||||
|
||||
***CHANGE0.7.4: New field.
|
||||
|
||||
This field will be set by LL. The HL-driver delivers received data-
|
||||
packets by calling this function. Upon calling, the HL-driver must
|
||||
already have its private data pulled off the head of the sk_buff.
|
||||
|
||||
Parameter:
|
||||
int driver-Id
|
||||
int Channel-number locally to the driver. (starting with 0)
|
||||
struct sk_buff * Pointer to sk_buff, containing received data.
|
||||
|
||||
int (*statcallb)(isdn_ctrl*);
|
||||
|
||||
This field will be set by LL. This function has to be called by the
|
||||
HL-driver for signaling status-changes or other events to the LL.
|
||||
|
||||
Parameter:
|
||||
isdn_ctrl*
|
||||
|
||||
The struct isdn_ctrl also defined in isdn_if. The exact meanings of its
|
||||
fields are described together with the descriptions of the possible
|
||||
events. Here is only a short description of the fields:
|
||||
|
||||
driver = driver Id.
|
||||
command = event-type. (one of the constants ISDN_STAT_...)
|
||||
arg = depends on event-type.
|
||||
num = depends on event-type.
|
||||
|
||||
Returnvalue:
|
||||
0 on success, else -1
|
||||
|
||||
int (*command)(isdn_ctrl*);
|
||||
|
||||
This field has to be preset by the HL-driver. It points to a function,
|
||||
to be called by LL to perform functions like dialing, B-channel
|
||||
setup, etc. The exact meaning of the parameters is described with the
|
||||
descriptions of the possible commands.
|
||||
|
||||
Parameter:
|
||||
isdn_ctrl*
|
||||
driver = driver-Id
|
||||
command = command to perform. (one of the constants ISDN_CMD_...)
|
||||
arg = depends on command.
|
||||
num = depends on command.
|
||||
|
||||
Returnvalue:
|
||||
>=0 on success, else error-code (-ENODEV etc.)
|
||||
|
||||
int (*writebuf_skb)(int, int, int, struct sk_buff *)
|
||||
|
||||
***CHANGE0.7.4: New field.
|
||||
***CHANGEI.1.21: New field.
|
||||
|
||||
This field has to be preset by the HL-driver. The given function will
|
||||
be called by the LL for delivering data to be send via B-Channel.
|
||||
|
||||
|
||||
Parameter:
|
||||
int driver-Id ***CHANGE0.7.4: New parameter.
|
||||
int channel-number locally to the HL-driver. (starts with 0)
|
||||
int ack ***ChangeI1.21: New parameter
|
||||
If this is !0, the driver has to signal the delivery
|
||||
by sending an ISDN_STAT_BSENT. If this is 0, the driver
|
||||
MUST NOT send an ISDN_STAT_BSENT.
|
||||
struct sk_buff * Pointer to sk_buff containing data to be send via
|
||||
B-channel.
|
||||
|
||||
Returnvalue:
|
||||
Length of data accepted on success, else error-code (-EINVAL on
|
||||
oversized packets etc.)
|
||||
|
||||
int (*writecmd)(u_char*, int, int, int, int);
|
||||
|
||||
This field has to be preset by the HL-driver. The given function will be
|
||||
called to perform write-requests on /dev/isdnctrl (i.e. sending commands
|
||||
to the card) The data-format is hardware-specific. This function is
|
||||
intended for debugging only. It is not necessary for normal operation
|
||||
and never will be called by the tty-emulation- or network-code. If
|
||||
this function is not supported, the driver has to set NULL here.
|
||||
|
||||
Parameter:
|
||||
u_char* pointer to data.
|
||||
int length of data.
|
||||
int flag: 0 = call from within kernel-space. (HL-driver must use
|
||||
memcpy, may NOT use schedule())
|
||||
1 = call from user-space. (HL-driver must use
|
||||
memcpy_fromfs, use of schedule() allowed)
|
||||
int driver-Id.
|
||||
int channel-number locally to the HL-driver. (starts with 0)
|
||||
|
||||
***CHANGEI1.14: The driver-Id and channel-number are new since this revision.
|
||||
|
||||
Returnvalue:
|
||||
Length of data accepted on success, else error-code (-EINVAL etc.)
|
||||
|
||||
int (*readstat)(u_char*, int, int, int, int);
|
||||
|
||||
This field has to be preset by the HL-driver. The given function will be
|
||||
called to perform read-requests on /dev/isdnctrl (i.e. reading replies
|
||||
from the card) The data-format is hardware-specific. This function is
|
||||
intended for debugging only. It is not necessary for normal operation
|
||||
and never will be called by the tty-emulation- or network-code. If
|
||||
this function is not supported, the driver has to set NULL here.
|
||||
|
||||
Parameter:
|
||||
u_char* pointer to data.
|
||||
int length of data.
|
||||
int flag: 0 = call from within kernel-space. (HL-driver must use
|
||||
memcpy, may NOT use schedule())
|
||||
1 = call from user-space. (HL-driver must use
|
||||
memcpy_fromfs, use of schedule() allowed)
|
||||
int driver-Id.
|
||||
int channel-number locally to the HL-driver. (starts with 0)
|
||||
|
||||
***CHANGEI1.14: The driver-Id and channel-number are new since this revision.
|
||||
|
||||
Returnvalue:
|
||||
Length of data on success, else error-code (-EINVAL etc.)
|
||||
|
||||
char id[20];
|
||||
***CHANGE0.7: New since this version.
|
||||
|
||||
This string has to be preset by the HL-driver. Its purpose is for
|
||||
identification of the driver by the user. Eg.: it is shown in the
|
||||
status-info of /dev/isdninfo. Furthermore it is used as Id for binding
|
||||
net-interfaces to a specific channel. If a string of length zero is
|
||||
given, upon return, isdn4linux will replace it by a generic name. (line0,
|
||||
line1 etc.) It is recommended to make this string configurable during
|
||||
module-load-time. (copy a global variable to this string.) For doing that,
|
||||
modules 1.2.8 or newer are necessary.
|
||||
|
||||
2. Description of the commands, a HL-driver has to support:
|
||||
|
||||
All commands will be performed by calling the function command() described
|
||||
above from within the LL. The field command of the struct-parameter will
|
||||
contain the desired command, the field driver is always set to the
|
||||
appropriate driver-Id.
|
||||
|
||||
Until now, the following commands are defined:
|
||||
|
||||
***CHANGEI1.34: The parameter "num" has been replaced by a union "parm" containing
|
||||
the old "num" and a new setup_type struct used for ISDN_CMD_DIAL
|
||||
and ISDN_STAT_ICALL callback.
|
||||
|
||||
ISDN_CMD_IOCTL:
|
||||
|
||||
This command is intended for performing ioctl-calls for configuring
|
||||
hardware or similar purposes (setting port-addresses, loading firmware
|
||||
etc.) For this purpose, in the LL all ioctl-calls with an argument
|
||||
>= IIOCDRVCTL (0x100) will be handed transparently to this
|
||||
function after subtracting 0x100 and placing the result in arg.
|
||||
Example:
|
||||
If a userlevel-program calls ioctl(0x101,...) the function gets
|
||||
called with the field command set to 1.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_IOCTL
|
||||
arg = Original ioctl-cmd - IIOCDRVCTL
|
||||
parm.num = first bytes filled with (unsigned long)arg
|
||||
|
||||
Returnvalue:
|
||||
Depending on driver.
|
||||
|
||||
|
||||
ISDN_CMD_DIAL:
|
||||
|
||||
This command is used to tell the HL-driver it should dial a given
|
||||
number.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_DIAL
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
|
||||
parm.setup.phone = An ASCII-String containing the number to dial.
|
||||
parm.setup.eazmsn = An ASCII-Sting containing the own EAZ or MSN.
|
||||
parm.setup.si1 = The Service-Indicator.
|
||||
parm.setup.si2 = Additional Service-Indicator.
|
||||
|
||||
If the Line has been designed as SPV (a special german
|
||||
feature, meaning semi-leased-line) the phone has to
|
||||
start with an "S".
|
||||
***CHANGE0.6: In previous versions the EAZ has been given in the
|
||||
highbyte of arg.
|
||||
***CHANGE0.7.1: New since this version: ServiceIndicator and AddInfo.
|
||||
|
||||
ISDN_CMD_ACCEPTD:
|
||||
|
||||
With this command, the HL-driver is told to accept a D-Channel-setup.
|
||||
(Response to an incoming call)
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_ACCEPTD
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_CMD_ACCEPTB:
|
||||
|
||||
With this command, the HL-driver is told to perform a B-Channel-setup.
|
||||
(after establishing D-Channel-Connection)
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_ACCEPTB
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_CMD_HANGUP:
|
||||
|
||||
With this command, the HL-driver is told to hangup (B-Channel if
|
||||
established first, then D-Channel). This command is also used for
|
||||
actively rejecting an incoming call.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_HANGUP
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_CMD_CLREAZ:
|
||||
|
||||
With this command, the HL-driver is told not to signal incoming
|
||||
calls to the LL.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_CLREAZ
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_CMD_SETEAZ:
|
||||
|
||||
With this command, the HL-driver is told to signal incoming calls for
|
||||
the given EAZs/MSNs to the LL.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_SETEAZ
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm.num = ASCII-String, containing the desired EAZ's/MSN's
|
||||
(comma-separated). If an empty String is given, the
|
||||
HL-driver should respond to ALL incoming calls,
|
||||
regardless of the destination-address.
|
||||
***CHANGE0.6: New since this version the "empty-string"-feature.
|
||||
|
||||
ISDN_CMD_GETEAZ: (currently unused)
|
||||
|
||||
With this command, the HL-driver is told to report the current setting
|
||||
given with ISDN_CMD_SETEAZ.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_GETEAZ
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm.num = ASCII-String, containing the current EAZ's/MSN's
|
||||
|
||||
ISDN_CMD_SETSIL: (currently unused)
|
||||
|
||||
With this command, the HL-driver is told to signal only incoming
|
||||
calls with the given Service-Indicators.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_SETSIL
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm.num = ASCII-String, containing the desired Service-Indicators.
|
||||
|
||||
ISDN_CMD_GETSIL: (currently unused)
|
||||
|
||||
With this command, the HL-driver is told to return the current
|
||||
Service-Indicators it will respond to.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_SETSIL
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm.num = ASCII-String, containing the current Service-Indicators.
|
||||
|
||||
ISDN_CMD_SETL2:
|
||||
|
||||
With this command, the HL-driver is told to select the given Layer-2-
|
||||
protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or
|
||||
ISDN_CMD_ACCEPTD.
|
||||
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_SETL2
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
logical or'ed with (protocol-Id << 8)
|
||||
protocol-Id is one of the constants ISDN_PROTO_L2...
|
||||
parm = unused.
|
||||
|
||||
ISDN_CMD_GETL2: (currently unused)
|
||||
|
||||
With this command, the HL-driver is told to return the current
|
||||
setting of the Layer-2-protocol.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_GETL2
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
Returnvalue:
|
||||
current protocol-Id (one of the constants ISDN_L2_PROTO)
|
||||
|
||||
ISDN_CMD_SETL3:
|
||||
|
||||
With this command, the HL-driver is told to select the given Layer-3-
|
||||
protocol. This command is issued by the LL prior to ISDN_CMD_DIAL or
|
||||
ISDN_CMD_ACCEPTD.
|
||||
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_SETL3
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
logical or'ed with (protocol-Id << 8)
|
||||
protocol-Id is one of the constants ISDN_PROTO_L3...
|
||||
parm.fax = Pointer to T30_s fax struct. (fax usage only)
|
||||
|
||||
ISDN_CMD_GETL2: (currently unused)
|
||||
|
||||
With this command, the HL-driver is told to return the current
|
||||
setting of the Layer-3-protocol.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_GETL3
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
Returnvalue:
|
||||
current protocol-Id (one of the constants ISDN_L3_PROTO)
|
||||
|
||||
ISDN_CMD_PROCEED:
|
||||
|
||||
With this command, the HL-driver is told to proceed with a incoming call.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_PROCEED
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
setup.eazmsn= empty string or string send as uus1 in DSS1 with
|
||||
PROCEED message
|
||||
|
||||
ISDN_CMD_ALERT:
|
||||
|
||||
With this command, the HL-driver is told to alert a proceeding call.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_ALERT
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
setup.eazmsn= empty string or string send as uus1 in DSS1 with
|
||||
ALERT message
|
||||
|
||||
ISDN_CMD_REDIR:
|
||||
|
||||
With this command, the HL-driver is told to redirect a call in proceeding
|
||||
or alerting state.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_REDIR
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
setup.eazmsn= empty string or string send as uus1 in DSS1 protocol
|
||||
setup.screen= screening indicator
|
||||
setup.phone = redirected to party number
|
||||
|
||||
ISDN_CMD_PROT_IO:
|
||||
|
||||
With this call, the LL-driver invokes protocol specific features through
|
||||
the LL.
|
||||
The call is not implicitely bound to a connection.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_CMD_PROT_IO
|
||||
arg = The lower 8 Bits define the addressed protocol as defined
|
||||
in ISDN_PTYPE..., the upper bits are used to differentiate
|
||||
the protocol specific CMD.
|
||||
|
||||
para = protocol and function specific. See isdnif.h for detail.
|
||||
|
||||
|
||||
ISDN_CMD_FAXCMD:
|
||||
|
||||
With this command the HL-driver receives a fax sub-command.
|
||||
For details refer to INTERFACE.fax
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_CMD_FAXCMD
|
||||
arg = channel-number locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
|
||||
3. Description of the events to be signaled by the HL-driver to the LL.
|
||||
|
||||
All status-changes are signaled via calling the previously described
|
||||
function statcallb(). The field command of the struct isdn_cmd has
|
||||
to be set by the HL-driver with the appropriate Status-Id (event-number).
|
||||
The field arg has to be set to the channel-number (locally to the driver,
|
||||
starting with 0) to which this event applies. (Exception: STAVAIL-event)
|
||||
|
||||
Until now, the following Status-Ids are defined:
|
||||
|
||||
ISDN_STAT_AVAIL:
|
||||
|
||||
With this call, the HL-driver signals the availability of new data
|
||||
for readstat(). Used only for debugging-purposes, see description
|
||||
of readstat().
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_STAVAIL
|
||||
arg = length of available data.
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_ICALL:
|
||||
ISDN_STAT_ICALLW:
|
||||
|
||||
With this call, the HL-driver signals an incoming call to the LL.
|
||||
If ICALLW is signalled the incoming call is a waiting call without
|
||||
a available B-chan.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_ICALL
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
para.setup.phone = Callernumber.
|
||||
para.setup.eazmsn = CalledNumber.
|
||||
para.setup.si1 = Service Indicator.
|
||||
para.setup.si2 = Additional Service Indicator.
|
||||
para.setup.plan = octet 3 from Calling party number Information Element.
|
||||
para.setup.screen = octet 3a from Calling party number Information Element.
|
||||
|
||||
Return:
|
||||
0 = No device matching this call.
|
||||
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 = Incoming called party number is currently incomplete.
|
||||
Additional digits are required.
|
||||
Used for signalling with PtP connections.
|
||||
4 = Call will be held in a proceeding state
|
||||
(HL driver sends PROCEEDING)
|
||||
Used when a user space prog needs time to interpret a call
|
||||
para.setup.eazmsn may be filled with an uus1 message of
|
||||
30 octets maximum. Empty string if no uus.
|
||||
5 = Call will be actively deflected to another party
|
||||
Only available in DSS1/EURO protocol
|
||||
para.setup.phone must be set to destination party number
|
||||
para.setup.eazmsn may be filled with an uus1 message of
|
||||
30 octets maximum. Empty string if no uus.
|
||||
-1 = An error happened. (Invalid parameters for example.)
|
||||
The keypad support now is included in the dial command.
|
||||
|
||||
|
||||
ISDN_STAT_RUN:
|
||||
|
||||
With this call, the HL-driver signals availability of the ISDN-card.
|
||||
(after initializing, loading firmware)
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_RUN
|
||||
arg = unused.
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_STOP:
|
||||
|
||||
With this call, the HL-driver signals unavailability of the ISDN-card.
|
||||
(before unloading, while resetting/reconfiguring the card)
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_STOP
|
||||
arg = unused.
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_DCONN:
|
||||
|
||||
With this call, the HL-driver signals the successful establishment of
|
||||
a D-Channel-connection. (Response to ISDN_CMD_ACCEPTD or ISDN_CMD_DIAL)
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_DCONN
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_BCONN:
|
||||
|
||||
With this call, the HL-driver signals the successful establishment of
|
||||
a B-Channel-connection. (Response to ISDN_CMD_ACCEPTB or because the
|
||||
remote-station has initiated establishment)
|
||||
|
||||
The HL driver should call this when the logical l2/l3 protocol
|
||||
connection on top of the physical B-channel is established.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_BCONN
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm.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:
|
||||
|
||||
With this call, the HL-driver signals the shutdown of a
|
||||
D-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP,
|
||||
or caused by a remote-hangup or if the remote-station has actively
|
||||
rejected a call.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_DHUP
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_BHUP:
|
||||
|
||||
With this call, the HL-driver signals the shutdown of a
|
||||
B-Channel-connection. This could be a response to a prior ISDN_CMD_HANGUP,
|
||||
or caused by a remote-hangup.
|
||||
|
||||
The HL driver should call this as soon as the logical l2/l3 protocol
|
||||
connection on top of the physical B-channel is released.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_BHUP
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_CINF:
|
||||
|
||||
With this call, the HL-driver delivers charge-unit information to the
|
||||
LL.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_CINF
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm.num = ASCII string containing charge-units (digits only).
|
||||
|
||||
ISDN_STAT_LOAD: (currently unused)
|
||||
|
||||
ISDN_STAT_UNLOAD:
|
||||
|
||||
With this call, the HL-driver signals that it will be unloaded now. This
|
||||
tells the LL to release all corresponding data-structures.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_UNLOAD
|
||||
arg = unused.
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_BSENT:
|
||||
|
||||
With this call the HL-driver signals the delivery of a data-packet.
|
||||
This callback is used by the network-interfaces only, tty-Emulation
|
||||
does not need this call.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_BSENT
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm.length = ***CHANGEI.1.21: New field.
|
||||
the driver has to set this to the original length
|
||||
of the skb at the time of receiving it from the linklevel.
|
||||
|
||||
ISDN_STAT_NODCH:
|
||||
|
||||
With this call, the driver has to respond to a prior ISDN_CMD_DIAL, if
|
||||
no D-Channel is available.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_NODCH
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_ADDCH:
|
||||
|
||||
This call is for HL-drivers, which are unable to check card-type
|
||||
or numbers of supported channels before they have loaded any firmware
|
||||
using ioctl. Those HL-driver simply set the channel-parameter to a
|
||||
minimum channel-number when registering, and later if they know
|
||||
the real amount, perform this call, allocating additional channels.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_ADDCH
|
||||
arg = number of channels to be added.
|
||||
parm = unused.
|
||||
|
||||
ISDN_STAT_CAUSE:
|
||||
|
||||
With this call, the HL-driver delivers CAUSE-messages to the LL.
|
||||
Currently the LL does not use this messages. Their contents is simply
|
||||
logged via kernel-messages. Therefore, currently the format of the
|
||||
messages is completely free. However they should be printable.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_NODCH
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm.num = ASCII string containing CAUSE-message.
|
||||
|
||||
ISDN_STAT_DISPLAY:
|
||||
|
||||
With this call, the HL-driver delivers DISPLAY-messages to the LL.
|
||||
Currently the LL does not use this messages.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_DISPLAY
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
para.display= string containing DISPLAY-message.
|
||||
|
||||
ISDN_STAT_PROT:
|
||||
|
||||
With this call, the HL-driver delivers protocol specific infos to the LL.
|
||||
The call is not implicitely bound to a connection.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_PROT
|
||||
arg = The lower 8 Bits define the addressed protocol as defined
|
||||
in ISDN_PTYPE..., the upper bits are used to differentiate
|
||||
the protocol specific STAT.
|
||||
|
||||
para = protocol and function specific. See isdnif.h for detail.
|
||||
|
||||
ISDN_STAT_DISCH:
|
||||
|
||||
With this call, the HL-driver signals the LL to disable or enable the
|
||||
use of supplied channel and driver.
|
||||
The call may be used to reduce the available number of B-channels after
|
||||
loading the driver. The LL has to ignore a disabled channel when searching
|
||||
for free channels. The HL driver itself never delivers STAT callbacks for
|
||||
disabled channels.
|
||||
The LL returns a nonzero code if the operation was not successful or the
|
||||
selected channel is actually regarded as busy.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_DISCH
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm.num[0] = 0 if channel shall be disabled, else enabled.
|
||||
|
||||
ISDN_STAT_L1ERR:
|
||||
|
||||
***CHANGEI1.21 new status message.
|
||||
A signal can be sent to the linklevel if an Layer1-error results in
|
||||
packet-loss on receive or send. The field errcode of the cmd.parm
|
||||
union describes the error more precisely.
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id
|
||||
command = ISDN_STAT_L1ERR
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm.errcode= ISDN_STAT_L1ERR_SEND: Packet lost while sending.
|
||||
ISDN_STAT_L1ERR_RECV: Packet lost while receiving.
|
||||
ISDN_STAT_FAXIND:
|
||||
|
||||
With this call the HL-driver signals a fax sub-command to the LL.
|
||||
For details refer to INTERFACE.fax
|
||||
|
||||
Parameter:
|
||||
driver = driver-Id.
|
||||
command = ISDN_STAT_FAXIND
|
||||
arg = channel-number, locally to the driver. (starting with 0)
|
||||
parm = unused.
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
$Id: INTERFACE.fax,v 1.2 2000/08/06 09:22:50 armin Exp $
|
||||
|
||||
|
||||
Description of the fax-subinterface between linklevel and hardwarelevel of
|
||||
isdn4linux.
|
||||
|
||||
The communication between linklevel (LL) and hardwarelevel (HL) for fax
|
||||
is based on the struct T30_s (defined in isdnif.h).
|
||||
This struct is allocated in the LL.
|
||||
In order to use fax, the LL provides the pointer to this struct with the
|
||||
command ISDN_CMD_SETL3 (parm.fax). This pointer expires in case of hangup
|
||||
and when a new channel to a new connection is assigned.
|
||||
|
||||
|
||||
Data handling:
|
||||
In send-mode the HL-driver has to handle the <DLE> codes and the bit-order
|
||||
conversion by itself.
|
||||
In receive-mode the LL-driver takes care of the bit-order conversion
|
||||
(specified by +FBOR)
|
||||
|
||||
Structure T30_s description:
|
||||
|
||||
This structure stores the values (set by AT-commands), the remote-
|
||||
capability-values and the command-codes between LL and HL.
|
||||
|
||||
If the HL-driver receives ISDN_CMD_FAXCMD, all needed information
|
||||
is in this struct set by the LL.
|
||||
To signal information to the LL, the HL-driver has to set the
|
||||
parameters and use ISDN_STAT_FAXIND.
|
||||
(Please refer to INTERFACE)
|
||||
|
||||
Structure T30_s:
|
||||
|
||||
All members are 8-bit unsigned (__u8)
|
||||
|
||||
- resolution
|
||||
- rate
|
||||
- width
|
||||
- length
|
||||
- compression
|
||||
- ecm
|
||||
- binary
|
||||
- scantime
|
||||
- id[]
|
||||
Local faxmachine's parameters, set by +FDIS, +FDCS, +FLID, ...
|
||||
|
||||
- r_resolution
|
||||
- r_rate
|
||||
- r_width
|
||||
- r_length
|
||||
- r_compression
|
||||
- r_ecm
|
||||
- r_binary
|
||||
- r_scantime
|
||||
- r_id[]
|
||||
Remote faxmachine's parameters. To be set by HL-driver.
|
||||
|
||||
- phase
|
||||
Defines the actual state of fax connection. Set by HL or LL
|
||||
depending on progress and type of connection.
|
||||
If the phase changes because of an AT command, the LL driver
|
||||
changes this value. Otherwise the HL-driver takes care of it, but
|
||||
only necessary on call establishment (from IDLE to PHASE_A).
|
||||
(one of the constants ISDN_FAX_PHASE_[IDLE,A,B,C,D,E])
|
||||
|
||||
- direction
|
||||
Defines outgoing/send or incoming/receive connection.
|
||||
(ISDN_TTY_FAX_CONN_[IN,OUT])
|
||||
|
||||
- code
|
||||
Commands from LL to HL; possible constants :
|
||||
ISDN_TTY_FAX_DR signals +FDR command to HL
|
||||
|
||||
ISDN_TTY_FAX_DT signals +FDT command to HL
|
||||
|
||||
ISDN_TTY_FAX_ET signals +FET command to HL
|
||||
|
||||
|
||||
Other than that the "code" is set with the hangup-code value at
|
||||
the end of connection for the +FHNG message.
|
||||
|
||||
- r_code
|
||||
Commands from HL to LL; possible constants :
|
||||
ISDN_TTY_FAX_CFR output of +FCFR message.
|
||||
|
||||
ISDN_TTY_FAX_RID output of remote ID set in r_id[]
|
||||
(+FCSI/+FTSI on send/receive)
|
||||
|
||||
ISDN_TTY_FAX_DCS output of +FDCS and CONNECT message,
|
||||
switching to phase C.
|
||||
|
||||
ISDN_TTY_FAX_ET signals end of data,
|
||||
switching to phase D.
|
||||
|
||||
ISDN_TTY_FAX_FCON signals the established, outgoing connection,
|
||||
switching to phase B.
|
||||
|
||||
ISDN_TTY_FAX_FCON_I signals the established, incoming connection,
|
||||
switching to phase B.
|
||||
|
||||
ISDN_TTY_FAX_DIS output of +FDIS message and values.
|
||||
|
||||
ISDN_TTY_FAX_SENT signals that all data has been sent
|
||||
and <DLE><ETX> is acknowledged,
|
||||
OK message will be sent.
|
||||
|
||||
ISDN_TTY_FAX_PTS signals a msg-confirmation (page sent successful),
|
||||
depending on fet value:
|
||||
0: output OK message (more pages follow)
|
||||
1: switching to phase B (next document)
|
||||
|
||||
ISDN_TTY_FAX_TRAIN_OK output of +FDCS and OK message (for receive mode).
|
||||
|
||||
ISDN_TTY_FAX_EOP signals end of data in receive mode,
|
||||
switching to phase D.
|
||||
|
||||
ISDN_TTY_FAX_HNG output of the +FHNG and value set by code and
|
||||
OK message, switching to phase E.
|
||||
|
||||
|
||||
- badlin
|
||||
Value of +FBADLIN
|
||||
|
||||
- badmul
|
||||
Value of +FBADMUL
|
||||
|
||||
- bor
|
||||
Value of +FBOR
|
||||
|
||||
- fet
|
||||
Value of +FET command in send-mode.
|
||||
Set by HL in receive-mode for +FET message.
|
||||
|
||||
- pollid[]
|
||||
ID-string, set by +FCIG
|
||||
|
||||
- cq
|
||||
Value of +FCQ
|
||||
|
||||
- cr
|
||||
Value of +FCR
|
||||
|
||||
- ctcrty
|
||||
Value of +FCTCRTY
|
||||
|
||||
- minsp
|
||||
Value of +FMINSP
|
||||
|
||||
- phcto
|
||||
Value of +FPHCTO
|
||||
|
||||
- rel
|
||||
Value of +FREL
|
||||
|
||||
- nbc
|
||||
Value of +FNBC (0,1)
|
||||
(+FNBC is not a known class 2 fax command, I added this to change the
|
||||
automatic "best capabilities" connection in the eicon HL-driver)
|
||||
|
||||
|
||||
Armin
|
||||
mac@melware.de
|
||||
|
|
@ -1,599 +0,0 @@
|
|||
README for the ISDN-subsystem
|
||||
|
||||
1. Preface
|
||||
|
||||
1.1 Introduction
|
||||
|
||||
This README describes how to set up and how to use the different parts
|
||||
of the ISDN-subsystem.
|
||||
|
||||
For using the ISDN-subsystem, some additional userlevel programs are
|
||||
necessary. Those programs and some contributed utilities are available
|
||||
at
|
||||
|
||||
ftp.isdn4linux.de
|
||||
|
||||
/pub/isdn4linux/isdn4k-utils-<VersionNumber>.tar.gz
|
||||
|
||||
|
||||
We also have set up a mailing-list:
|
||||
|
||||
The isdn4linux-project originates in Germany, and therefore by historical
|
||||
reasons, the mailing-list's primary language is german. However mails
|
||||
written in english have been welcome all the time.
|
||||
|
||||
to subscribe: write a email to majordomo@listserv.isdn4linux.de,
|
||||
Subject irrelevant, in the message body:
|
||||
subscribe isdn4linux <your_email_address>
|
||||
|
||||
To write to the mailing-list, write to isdn4linux@listserv.isdn4linux.de
|
||||
|
||||
This mailinglist is bidirectionally gated to the newsgroup
|
||||
|
||||
de.alt.comm.isdn4linux
|
||||
|
||||
There is also a well maintained FAQ in English available at
|
||||
https://www.mhessler.de/i4lfaq/
|
||||
It can be viewed online, or downloaded in sgml/text/html format.
|
||||
The FAQ can also be viewed online at
|
||||
https://www.isdn4linux.de/faq/i4lfaq.html
|
||||
or downloaded from
|
||||
ftp://ftp.isdn4linux.de/pub/isdn4linux/FAQ/
|
||||
|
||||
1.1 Technical details
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
The internal handling is nearly identical, so replace the appropriate
|
||||
term to that one, which applies to your local ISDN-environment.
|
||||
|
||||
When the link-level-module isdn.o is loaded, it supports up to 16
|
||||
low-level-modules with up to 64 channels. (The number 64 is arbitrarily
|
||||
chosen and can be configured at compile-time --ISDN_MAX in isdn.h).
|
||||
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
|
||||
the card.
|
||||
|
||||
O N L Y !!! for debugging (no locking against other devices):
|
||||
One raw-data-device with the following functions:
|
||||
write: data to B-channel.
|
||||
read: data from B-channel.
|
||||
|
||||
In addition the following devices are made available:
|
||||
|
||||
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
|
||||
Seyon, minicom, CSLIP (uri-dip) PPP, mgetty, XCept and Hylafax.
|
||||
|
||||
The modem-emulation supports the following:
|
||||
1.3.1 Commands:
|
||||
|
||||
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 line is a SPV (German 1TR6).
|
||||
ATE0 Echo off.
|
||||
ATE1 Echo on (default).
|
||||
ATH Hang-up.
|
||||
ATH1 Off hook (ignored).
|
||||
ATH0 Hang-up.
|
||||
ATI Return "ISDN for Linux...".
|
||||
ATI0 "
|
||||
ATI1 "
|
||||
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.
|
||||
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)
|
||||
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,
|
||||
if you set it to higher values, because at the
|
||||
time of giving this command the corresponding
|
||||
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
|
||||
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&Lx Set list of phone numbers to listen on. x is a
|
||||
list of wildcard patterns separated by semicolon.
|
||||
If this is set, it has precedence over the MSN set
|
||||
by AT&E.
|
||||
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
|
||||
back to default X.75. This command sets the following
|
||||
Registers:
|
||||
Reg 14 (Layer-2 protocol):
|
||||
x = 0: 0
|
||||
x = 9600: 7
|
||||
x = 19200: 8
|
||||
x = 38400: 9
|
||||
Reg 18.2 = 1
|
||||
Reg 19 (Additional Service Indicator):
|
||||
x = 0: 0
|
||||
x = 9600: 197
|
||||
x = 19200: 199
|
||||
x = 38400: 198
|
||||
Note on value in Reg 19:
|
||||
There is _NO_ common convention for 38400 baud.
|
||||
The value 198 is chosen 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&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+Rx Resume a suspended call with CallID x (x = 1,2,3...)
|
||||
AT+Sx Suspend a call with CallID x (x = 1,2,3...)
|
||||
|
||||
For voice-mode commands refer to README.audio
|
||||
|
||||
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
|
||||
register 2).
|
||||
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
|
||||
0 0 Answer on ring number.
|
||||
(no auto-answer if S0=0).
|
||||
1 0 Count of rings.
|
||||
2 43 Escape character.
|
||||
(a value >= 128 disables the escape sequence).
|
||||
3 13 Carriage return character (ASCII).
|
||||
4 10 Line feed character (ASCII).
|
||||
5 8 Backspace character (ASCII).
|
||||
6 3 Delay in seconds before dialing.
|
||||
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).
|
||||
11 70 Touch tone timing (ignored).
|
||||
12 69 Bit coded register:
|
||||
Bit 0: 0 = Suppress response messages.
|
||||
1 = Show response messages.
|
||||
Bit 1: 0 = English response messages.
|
||||
1 = Numeric response messages.
|
||||
Bit 2: 0 = Echo off.
|
||||
1 = Echo on.
|
||||
Bit 3 0 = DCD always on.
|
||||
1 = DCD follows carrier.
|
||||
Bit 4 0 = CTS follows RTS
|
||||
1 = Ignore RTS, CTS always on.
|
||||
Bit 5 0 = return to command mode on DTR low.
|
||||
1 = Same as 0 but also resets all
|
||||
registers.
|
||||
See also register 13, bit 2
|
||||
Bit 6 0 = DSR always on.
|
||||
1 = DSR only on if channel is available.
|
||||
Bit 7 0 = Cisco-PPP-flag-hack off (default).
|
||||
1 = Cisco-PPP-flag-hack on.
|
||||
13 0 Bit coded register:
|
||||
Bit 0: 0 = Use delayed tty-send-algorithm
|
||||
1 = Direct tty-send.
|
||||
Bit 1: 0 = T.70 protocol (Only for BTX!) off
|
||||
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 4: 0 = CALLER NUMBER before every RING.
|
||||
1 = CALLER NUMBER after first RING.
|
||||
Bit 5: 0 = T.70 extended protocol off
|
||||
1 = T.70 extended protocol on
|
||||
Bit 6: 0 = Special RUNG Message off
|
||||
1 = Special RUNG Message on
|
||||
"RUNG" is delivered on a ttyI, if
|
||||
an incoming call happened (RING) and
|
||||
the remote party hung up before any
|
||||
local ATA was given.
|
||||
Bit 7: 0 = Don't show display messages from net
|
||||
1 = Show display messages from net
|
||||
(S12 Bit 1 must be 0 too)
|
||||
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)
|
||||
11 = Fax G3 (only if hardware supports this)
|
||||
15 0 Layer-3 protocol:
|
||||
0 = transparent
|
||||
1 = transparent with audio features (e.g. DSP)
|
||||
2 = Fax G3 Class 2 commands (S14 has to be set to 11)
|
||||
3 = Fax G3 Class 1 commands (S14 has to be set to 11)
|
||||
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:
|
||||
Bit 0: Service 1 (audio) when set.
|
||||
Bit 1: Service 5 (BTX) when set.
|
||||
Bit 2: Service 7 (data) when set.
|
||||
Note: It is possible to set more than one
|
||||
bit. In this case, on incoming calls
|
||||
the selected services are accepted,
|
||||
and if the service is "audio", the
|
||||
Layer-2-protocol is automatically
|
||||
changed to 4 regardless of the setting
|
||||
of register 14. On outgoing calls,
|
||||
the most significant 1-bit is chosen to
|
||||
select the outgoing service octet.
|
||||
19 0 Service-Octet-2
|
||||
20 0 Bit coded register (readonly)
|
||||
Service-Octet-1 of last call.
|
||||
Bit mapping is the same as register 18
|
||||
21 0 Bit coded register (readonly)
|
||||
Set on incoming call (during RING) to
|
||||
octet 3 of calling party number IE (Numbering plan)
|
||||
See section 4.5.10 of ITU Q.931
|
||||
22 0 Bit coded register (readonly)
|
||||
Set on incoming call (during RING) to
|
||||
octet 3a of calling party number IE (Screening info)
|
||||
See section 4.5.10 of ITU Q.931
|
||||
23 0 Bit coded register:
|
||||
Bit 0: 0 = Add CPN to RING message off
|
||||
1 = Add CPN to RING message on
|
||||
Bit 1: 0 = Add CPN to FCON message off
|
||||
1 = Add CPN to FCON message on
|
||||
Bit 2: 0 = Add CDN to RING/FCON message off
|
||||
1 = Add CDN to RING/FCON message on
|
||||
|
||||
Last but not least a (at the moment fairly primitive) device to request
|
||||
the line-status (/dev/isdninfo) is made available.
|
||||
|
||||
Automatic assignment of devices to lines:
|
||||
|
||||
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
|
||||
interface and then for an opened tty which:
|
||||
|
||||
1. is configured for the same EAZ.
|
||||
2. has the same protocol settings for the B-channel.
|
||||
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.
|
||||
(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
|
||||
and the "connection" between the low-level-layer and the link-level-layer
|
||||
is established and kept until the end of the connection.
|
||||
In all other cases no connection is established. Isdn4linux can be
|
||||
configured to either do NOTHING in this case (which is useful, if
|
||||
other, external devices with the same EAZ/MSN are connected to the bus)
|
||||
or to reject the call actively. (isdnctrl busreject ...)
|
||||
|
||||
For an outgoing call, the inactive physical lines are searched.
|
||||
The call is placed on the first physical line, which supports the
|
||||
requested protocols for the B-channel. If a net-interface, however
|
||||
is pre-bound to a channel, this channel is used directly.
|
||||
|
||||
This makes it possible to configure several network interfaces and ttys
|
||||
for one EAZ, if the network interfaces are set to secure operation.
|
||||
If an incoming call matches one network interface, it gets connected to it.
|
||||
If another incoming call for the same EAZ arrives, which does not match
|
||||
a network interface, the first tty gets a "RING" and so on.
|
||||
|
||||
2 System prerequisites:
|
||||
|
||||
ATTENTION!
|
||||
|
||||
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
|
||||
README.<yourDriver> for information on driver-specific setup.
|
||||
|
||||
4. Device-inodes
|
||||
|
||||
The major and minor numbers and their names are described in
|
||||
Documentation/admin-guide/devices.rst. The major numbers are:
|
||||
|
||||
43 for the ISDN-tty's.
|
||||
44 for the ISDN-callout-tty's.
|
||||
45 for control/info/debug devices.
|
||||
|
||||
5. Application
|
||||
|
||||
a) For some card-types, firmware has to be loaded into the cards, before
|
||||
proceeding with device-independent setup. See README.<yourDriver>
|
||||
for how to do that.
|
||||
|
||||
b) If you only intend to use ttys, you are nearly ready now.
|
||||
|
||||
c) If you want to have really permanent "Modem"-settings on disk, you
|
||||
can start the daemon iprofd. Give it a path to a file at the command-
|
||||
line. It will store the profile-settings in this file every time
|
||||
an AT&W0 is performed on any ISDN-tty. If the file already exists,
|
||||
all profiles are initialized from this file. If you want to unload
|
||||
any of the modules, kill iprofd first.
|
||||
|
||||
d) For networking, continue: Create an interface:
|
||||
isdnctrl addif isdn0
|
||||
|
||||
e) Set the EAZ (or MSN for Euro-ISDN):
|
||||
isdnctrl eaz isdn0 2
|
||||
|
||||
(For 1TR6 a single digit is allowed, for Euro-ISDN the number is your
|
||||
real MSN e.g.: Phone-Number)
|
||||
|
||||
f) Set the number for outgoing calls on the interface:
|
||||
isdnctrl addphone isdn0 out 1234567
|
||||
... (this can be executed more than once, all assigned numbers are
|
||||
tried in order)
|
||||
and the number(s) for incoming calls:
|
||||
isdnctrl addphone isdn0 in 1234567
|
||||
|
||||
g) Set the timeout for hang-up:
|
||||
isdnctrl huptimeout isdn0 <timeout_in_seconds>
|
||||
|
||||
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) 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).
|
||||
|
||||
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 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
|
||||
which starts with "eth". The number of "eth"-interfaces is limited by
|
||||
the kernel.
|
||||
|
||||
5. Additional options for isdnctrl:
|
||||
|
||||
"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
|
||||
works similar to the shell mechanism.
|
||||
|
||||
? one arbitrary digit
|
||||
* zero or arbitrary many digits
|
||||
[123] one of the digits in the list
|
||||
[1-5] one digit between '1' and '5'
|
||||
a '^' as the first character in a list inverts the list
|
||||
|
||||
|
||||
"isdnctrl secure <InterfaceName> off"
|
||||
Switch off secure operation (default).
|
||||
|
||||
"isdnctrl ihup <InterfaceName> [on|off]"
|
||||
Switch the hang-up-timer for incoming calls on or off.
|
||||
|
||||
"isdnctrl eaz <InterfaceName>"
|
||||
Returns the EAZ of an interface.
|
||||
|
||||
"isdnctrl delphone <InterfaceName> in|out <number>"
|
||||
Deletes a number from one of the access-lists of the interface.
|
||||
|
||||
"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]"
|
||||
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
|
||||
quickly, so that the callback from isdn4linux may not be recognized.
|
||||
In this case use ping with the option -i <sec> to increase the interval
|
||||
between echo-packets.
|
||||
|
||||
"isdnctrl cbdelay <InterfaceName> [seconds]"
|
||||
Sets the delay (default 5 sec) between an incoming call and start of
|
||||
dialing when callback is enabled.
|
||||
|
||||
"isdnctrl cbhup <InterfaceName> [on|off]"
|
||||
This enables (default) or disables an active hangup (reject) when getting an
|
||||
incoming call for an interface which is configured for callback.
|
||||
|
||||
"isdnctrl encap <InterfaceName> <EncapType>"
|
||||
Selects the type of packet-encapsulation. The encapsulation can be changed
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
The Ethernet-address of the interface is faked, from its
|
||||
IP-address: fc:fc:i1:i2:i3:i4, where i1-4 are the IP-addr.-values.
|
||||
syncppp Synchronous PPP
|
||||
|
||||
uihdlc HDLC with UI-frame-header (for use with DOS ISPA, option -h1)
|
||||
|
||||
|
||||
NOTE: x25iface encapsulation is currently experimental. Please
|
||||
read README.x25 for further details
|
||||
|
||||
|
||||
Watching packets, using standard-tcpdump will fail for all encapsulations
|
||||
except ethernet because tcpdump does not know how to handle packets
|
||||
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.
|
||||
(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>
|
||||
The same for layer-3. (At the moment only "trans" is allowed)
|
||||
|
||||
"isdnctrl list <InterfaceName>"
|
||||
Shows all parameters of an interface and the charge-info.
|
||||
Try "all" as the interface name.
|
||||
|
||||
"isdnctrl hangup <InterfaceName>"
|
||||
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
|
||||
command. Replace <DriverId> by whatever you assigned while loading the
|
||||
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.
|
||||
|
||||
"isdnctrl busreject <DriverId> on|off"
|
||||
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 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.
|
||||
|
||||
"isdnctrl addslave <InterfaceName> <SlaveName>
|
||||
Creates a slave interface for channel-bundling. Slave interfaces are
|
||||
not seen by the kernel, but their ISDN-part can be configured with
|
||||
isdnctrl as usual. (Phone numbers, EAZ/MSN, timeouts etc.) If more
|
||||
than two channels are to be bundled, feel free to create as many as you
|
||||
want. InterfaceName must be a real interface, NOT a slave. Slave interfaces
|
||||
start dialing, if the master interface resp. the previous slave interface
|
||||
has a load of more than 7000 cps. They hangup if the load goes under 7000
|
||||
cps, according to their "huptimeout"-parameter.
|
||||
|
||||
"isdnctrl sdelay <InterfaceName> secs."
|
||||
This sets the minimum time an Interface has to be fully loaded, until
|
||||
it sends a dial-request to its slave.
|
||||
|
||||
"isdnctrl dial <InterfaceName>"
|
||||
Forces an interface to start dialing even if no packets are to be
|
||||
transferred.
|
||||
|
||||
"isdnctrl mapping <DriverId> MSN0,MSN1,MSN2,...MSN9"
|
||||
This installs a mapping table for EAZ<->MSN-mapping for a single line.
|
||||
Missing MSN's have to be given as "-" or can be omitted, if at the end
|
||||
of the commandline.
|
||||
With this command, it's now possible to have an interface listening to
|
||||
mixed 1TR6- and Euro-Type lines. In this case, the interface has to be
|
||||
configured to a 1TR6-type EAZ (one digit). The mapping is also valid
|
||||
for tty-emulation. Seen from the interface/tty-level the mapping
|
||||
CAN be used, however it's possible to use single tty's/interfaces with
|
||||
real MSN's (more digits) also, in which case the mapping will be ignored.
|
||||
Here is an example:
|
||||
|
||||
You have a 1TR6-type line with base-nr. 1234567 and a Euro-line with
|
||||
MSN's 987654, 987655 and 987656. The DriverId for the Euro-line is "EURO".
|
||||
|
||||
isdnctrl mapping EURO -,987654,987655,987656,-,987655
|
||||
...
|
||||
isdnctrl eaz isdn0 1 # listen on 12345671(1tr6) and 987654(euro)
|
||||
...
|
||||
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
|
||||
on your own, send me a mail containing the proposed changes and
|
||||
some reasoning about them.
|
||||
If other drivers will not be affected, I will include the changes
|
||||
in the next release.
|
||||
For developers only, there is a second mailing-list. Write to me
|
||||
(fritz@isdn4linux.de), if you want to join that list.
|
||||
|
||||
Have fun!
|
||||
|
||||
-Fritz
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
The FAQ for isdn4linux
|
||||
======================
|
||||
|
||||
Please note that there is a big FAQ available in the isdn4k-utils.
|
||||
You find it in:
|
||||
isdn4k-utils/FAQ/i4lfaq.sgml
|
||||
|
||||
In case you just want to see the FAQ online, or download the newest version,
|
||||
you can have a look at my website:
|
||||
https://www.mhessler.de/i4lfaq/ (view + download)
|
||||
or:
|
||||
https://www.isdn4linux.de/faq/4lfaq.html (view)
|
||||
|
||||
As the extension tells, the FAQ is in SGML format, and you can convert it
|
||||
into text/html/... format by using the sgml2txt/sgml2html/... tools.
|
||||
Alternatively, you can also do a 'configure; make all' in the FAQ directory.
|
||||
|
||||
|
||||
Please have a look at the FAQ before posting anything in the Mailinglist,
|
||||
or the newsgroup!
|
||||
|
||||
|
||||
Matthias Hessler
|
||||
hessler@isdn4linux.de
|
||||
|
|
@ -1,659 +0,0 @@
|
|||
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
|
||||
written by Fritz Elfert.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Supported cards
|
||||
---------------
|
||||
|
||||
Teles 8.0/16.0/16.3 and compatible ones
|
||||
Teles 16.3c
|
||||
Teles S0/PCMCIA
|
||||
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 Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
|
||||
Eicon Diva 2.01 ISA and PCI
|
||||
Eicon Diva 2.02 PCI
|
||||
Eicon 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, PCI, Fax+)
|
||||
Sedlbauer Speed Star/Speed Star2 (PCMCIA)
|
||||
Sedlbauer ISDN-Controller PC/104
|
||||
USR Sportster internal TA (compatible Stollmann tina-pp V3)
|
||||
USR internal TA PCI
|
||||
ith Kommunikationstechnik GmbH MIC 16 ISA card
|
||||
Traverse Technologie NETjet PCI S0 card and NETspider U card
|
||||
Ovislink ISDN sc100-p card (NETjet driver)
|
||||
Dr. Neuhaus Niccy PnP/PCI
|
||||
Siemens I-Surf 1.0
|
||||
Siemens I-Surf 2.0 (with IPAC, try type 12 asuscom)
|
||||
ACER P10
|
||||
HST Saphir
|
||||
Berkom Telekom A4T
|
||||
Scitel Quadro
|
||||
Gazel ISDN cards
|
||||
HFC-PCI based cards
|
||||
Winbond W6692 based cards
|
||||
HFC-S+, HFC-SP/PCMCIA cards
|
||||
formula-n enternow
|
||||
Gerdes Power ISDN
|
||||
|
||||
Note: PCF, PCF-Pro: up to now, only the ISDN part is supported
|
||||
PCC-8: not tested yet
|
||||
Eicon.Diehl Diva U interface not tested
|
||||
|
||||
If you know other passive cards with the Siemens chipset, please let me know.
|
||||
You can combine any card, if there is no conflict between the resources
|
||||
(io, mem, irq).
|
||||
|
||||
|
||||
Configuring the driver
|
||||
----------------------
|
||||
|
||||
The HiSax driver can either be built directly into the kernel or as a module.
|
||||
It can be configured using the command line feature while loading the kernel
|
||||
with LILO or LOADLIN or, if built as a module, using insmod/modprobe with
|
||||
parameters.
|
||||
There is also some config needed before you compile the kernel and/or
|
||||
modules. It is included in the normal "make [menu]config" target at the
|
||||
kernel. Don't forget it, especially to select the right D-channel protocol.
|
||||
|
||||
Please note: In older versions of the HiSax driver, all PnP cards
|
||||
needed to be configured with isapnp and worked only with the HiSax
|
||||
driver used as a module.
|
||||
|
||||
In the current version, HiSax will automatically use the in-kernel
|
||||
ISAPnP support, provided you selected it during kernel configuration
|
||||
(CONFIG_ISAPNP), if you don't give the io=, irq= command line parameters.
|
||||
|
||||
The affected card types are: 4,7,12,14,19,27-30
|
||||
|
||||
a) when built as a module
|
||||
-------------------------
|
||||
|
||||
insmod/modprobe hisax.o \
|
||||
io=iobase irq=IRQ mem=membase type=card_type \
|
||||
protocol=D_channel_protocol id=idstring
|
||||
|
||||
or, if several cards are installed:
|
||||
|
||||
insmod/modprobe hisax.o \
|
||||
io=iobase1,iobase2,... irq=IRQ1,IRQ2,... mem=membase1,membase2,... \
|
||||
type=card_type1,card_type2,... \
|
||||
protocol=D_channel_protocol1,D_channel_protocol2,... \
|
||||
id=idstring1%idstring2 ...
|
||||
|
||||
where "iobaseN" represents the I/O base address of the Nth card, "membaseN"
|
||||
the memory base address of the Nth card, etc.
|
||||
|
||||
The reason for the delimiter "%" being used in the idstrings is that ","
|
||||
won't work with the current modules package.
|
||||
|
||||
The parameters may be specified in any order. For example, the "io"
|
||||
parameter may precede the "irq" parameter, or vice versa. If several
|
||||
cards are installed, the ordering within the comma separated parameter
|
||||
lists must of course be consistent.
|
||||
|
||||
Only parameters applicable to the card type need to be specified. For
|
||||
example, the Teles 16.3 card is not memory-mapped, so the "mem"
|
||||
parameter may be omitted for this card. Sometimes it may be necessary
|
||||
to specify a dummy parameter, however. This is the case when there is
|
||||
a card of a different type later in the list that needs a parameter
|
||||
which the preceding card does not. For instance, if a Teles 16.0 card
|
||||
is listed after a Teles 16.3 card, a dummy memory base parameter of 0
|
||||
must be specified for the 16.3. Instead of a dummy value, the parameter
|
||||
can also be skipped by simply omitting the value. For example:
|
||||
mem=,0xd0000. See example 6 below.
|
||||
|
||||
The parameter for the D-Channel protocol may be omitted if you selected the
|
||||
correct one during kernel config. Valid values are "1" for German 1TR6,
|
||||
"2" for EDSS1 (Euro ISDN), "3" for leased lines (no D-Channel) and "4"
|
||||
for US NI1.
|
||||
With US NI1 you have to include your SPID into the MSN setting in the form
|
||||
<MSN>:<SPID> for example (your phonenumber is 1234 your SPID 5678):
|
||||
AT&E1234:5678 on ttyI interfaces
|
||||
isdnctrl eaz ippp0 1234:5678 on network devices
|
||||
|
||||
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:
|
||||
|
||||
Type Required parameters (in addition to type and protocol)
|
||||
|
||||
1 Teles 16.0 irq, mem, io
|
||||
2 Teles 8.0 irq, mem
|
||||
3 Teles 16.3 (non PnP) irq, io
|
||||
4 Creatix/Teles PnP irq, io0 (ISAC), io1 (HSCX)
|
||||
5 AVM A1 (Fritz) irq, io
|
||||
6 ELSA PCC/PCF cards io or nothing for autodetect (the iobase is
|
||||
required only if you have more than one ELSA
|
||||
card in your PC)
|
||||
7 ELSA Quickstep 1000 irq, io (from isapnp setup)
|
||||
8 Teles 16.3 PCMCIA irq, io
|
||||
9 ITK ix1-micro Rev.2 irq, io
|
||||
10 ELSA PCMCIA irq, io (set with card manager)
|
||||
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
|
||||
14 Teles 16.3c PnP 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)
|
||||
29 Siemens I-Surf 1.0 irq, io, memory (from isapnp setup)
|
||||
30 ACER P10 irq, io (from isapnp setup)
|
||||
31 HST Saphir irq, io
|
||||
32 Telekom A4T none
|
||||
33 Scitel Quadro subcontroller (4*S0, subctrl 1...4)
|
||||
34 Gazel ISDN cards (ISA) irq,io
|
||||
34 Gazel ISDN cards (PCI) none
|
||||
35 HFC 2BDS0 PCI none
|
||||
36 W6692 based PCI cards none
|
||||
37 HFC 2BDS0 S+, SP irq,io
|
||||
38 NETspider U PCI card none
|
||||
39 HFC 2BDS0 SP/PCMCIA irq,io (set with cardmgr)
|
||||
40 hotplug interface
|
||||
41 Formula-n enter:now PCI none
|
||||
|
||||
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
|
||||
|
||||
1. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 10
|
||||
modprobe hisax type=3 protocol=2 io=0x280 irq=10
|
||||
|
||||
2. Teles 16.0, 1TR6 ISDN, I/O base d80 hex, IRQ 5, Memory d0000 hex
|
||||
modprobe hisax protocol=1 type=1 io=0xd80 mem=0xd0000 irq=5
|
||||
|
||||
3. Fritzcard, Euro ISDN, I/O base 340 hex, IRQ 10 and ELSA PCF, Euro ISDN
|
||||
modprobe hisax type=5,6 protocol=2,2 io=0x340 irq=10 id=Fritz%Elsa
|
||||
|
||||
4. Any ELSA PCC/PCF card, Euro ISDN
|
||||
modprobe hisax type=6 protocol=2
|
||||
|
||||
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))
|
||||
modprobe hisax type=4 protocol=2 irq=10 io0=0x580 io1=0x180
|
||||
|
||||
In the current version of HiSax, you can instead simply use
|
||||
|
||||
modprobe hisax type=4 protocol=2
|
||||
|
||||
if you configured your kernel for ISAPnP. Don't run isapnp in
|
||||
this case!
|
||||
|
||||
6. Teles 16.3, Euro ISDN, I/O base 280 hex, IRQ 12 and
|
||||
Teles 16.0, 1TR6, IRQ 5, Memory d0000 hex
|
||||
modprobe hisax type=3,1 protocol=2,1 io=0x280 mem=0,0xd0000
|
||||
|
||||
Please note the dummy 0 memory address for the Teles 16.3, used as a
|
||||
placeholder as described above, in the last example.
|
||||
|
||||
7. Teles PCMCIA, Euro ISDN, I/O base 180 hex, IRQ 15 (default values)
|
||||
modprobe hisax type=8 protocol=2 io=0x180 irq=15
|
||||
|
||||
|
||||
b) using LILO/LOADLIN, with the driver compiled directly into the kernel
|
||||
------------------------------------------------------------------------
|
||||
|
||||
hisax=typ1,dp1,pa_1,pb_1,pc_1[,typ2,dp2,pa_2 ... \
|
||||
typn,dpn,pa_n,pb_n,pc_n][,idstring1[,idstring2,...,idstringn]]
|
||||
|
||||
where
|
||||
typ1 = type of 1st card (default depends on kernel settings)
|
||||
dp1 = D-Channel protocol of 1st card. 1=1TR6, 2=EDSS1, 3=leased
|
||||
pa_1 = 1st parameter (depending on the type of the card)
|
||||
pb_1 = 2nd parameter ( " " " " " " " )
|
||||
pc_1 = 3rd parameter ( " " " " " " " )
|
||||
|
||||
typ2,dp2,pa_2,pb_2,pc_2 = Parameters of the second card (defaults: none)
|
||||
typn,dpn,pa_n,pb_n,pc_n = Parameters of the n'th card (up to 16 cards are
|
||||
supported)
|
||||
|
||||
idstring = Driver ID for accessing the particular card with utility
|
||||
programs and for identification when using a line monitor
|
||||
(default: "HiSax")
|
||||
|
||||
Note: the ID string must start with an alphabetical character!
|
||||
|
||||
Card types:
|
||||
|
||||
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
|
||||
4 Creatix/Teles PNP ONLY WORKS AS A MODULE !
|
||||
5 AVM A1 (Fritz) pa=irq pb=iobase
|
||||
6 ELSA PCC/PCF cards pa=iobase or nothing for autodetect
|
||||
7 ELSA Quickstep 1000 ONLY WORKS AS A MODULE !
|
||||
8 Teles S0 PCMCIA pa=irq pb=iobase
|
||||
9 ITK ix1-micro Rev.2 pa=irq pb=iobase
|
||||
10 ELSA PCMCIA pa=irq, pb=io (set with card manager)
|
||||
11 Eicon.Diehl Diva ISAPnP ONLY WORKS AS A MODULE !
|
||||
11 Eicon.Diehl Diva PCI no parameter
|
||||
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 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 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 pa=irq, pb=io (of the used lpt port)
|
||||
26 AVM A1 PCMCIA (Fritz!) pa=irq, pb=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 !
|
||||
29 Siemens I-Surf 1.0 ONLY WORKS AS A MODULE !
|
||||
30 ACER P10 ONLY WORKS AS A MODULE !
|
||||
31 HST Saphir pa=irq, pb=io
|
||||
32 Telekom A4T no parameter
|
||||
33 Scitel Quadro subcontroller (4*S0, subctrl 1...4)
|
||||
34 Gazel ISDN cards (ISA) pa=irq, pb=io
|
||||
34 Gazel ISDN cards (PCI) no parameter
|
||||
35 HFC 2BDS0 PCI no parameter
|
||||
36 W6692 based PCI cards none
|
||||
37 HFC 2BDS0 S+,SP/PCMCIA ONLY WORKS AS A MODULE !
|
||||
38 NETspider U PCI card none
|
||||
39 HFC 2BDS0 SP/PCMCIA ONLY WORKS AS A MODULE !
|
||||
40 hotplug interface ONLY WORKS AS A MODULE !
|
||||
41 Formula-n enter:now PCI none
|
||||
|
||||
Running the driver
|
||||
------------------
|
||||
|
||||
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.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: Elsa driver Rev. 1.13
|
||||
...
|
||||
Apr 13 21:01:59 kke01 kernel: Elsa: PCF-Pro found at 0x360 Rev.:C IRQ 10
|
||||
Apr 13 21:01:59 kke01 kernel: Elsa: timer OK; resetting card
|
||||
Apr 13 21:01:59 kke01 kernel: Elsa: HSCX version A: V2.1 B: V2.1
|
||||
Apr 13 21:01:59 kke01 kernel: Elsa: ISAC 2086/2186 V1.1
|
||||
...
|
||||
Apr 13 21:01:59 kke01 kernel: HiSax: DSS1 Rev. 1.14
|
||||
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
|
||||
detect the S0 power.
|
||||
|
||||
Remember that, according to the new strategy for accessing low-level drivers
|
||||
from within isdn4linux, you should also define a driver ID while doing
|
||||
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 the moment, debugging messages are enabled with the hisaxctrl tool:
|
||||
|
||||
hisaxctrl <DriverId> DebugCmd <debugging_flags>
|
||||
|
||||
<DriverId> default is HiSax, if you didn't specify one.
|
||||
|
||||
DebugCmd is 1 for generic debugging
|
||||
11 for layer 1 development debugging
|
||||
13 for layer 3 development debugging
|
||||
|
||||
where <debugging_flags> is the integer sum of the following debugging
|
||||
options you wish enabled:
|
||||
|
||||
With DebugCmd set to 1:
|
||||
|
||||
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:
|
||||
|
||||
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:
|
||||
|
||||
1 Warnings (default: on)
|
||||
2 l3 protocol descriptor errors
|
||||
4 l3 state machine
|
||||
8 charge info debugging (1TR6)
|
||||
|
||||
For example, 'hisaxctrl HiSax 1 0x3ff' enables full generic debugging.
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Bugs
|
||||
----
|
||||
If you find any, please let me know.
|
||||
|
||||
|
||||
Thanks
|
||||
------
|
||||
Special thanks to:
|
||||
|
||||
Emil Stephan for the name HiSax which is a mix of HSCX and ISAC.
|
||||
|
||||
Fritz Elfert, Jan den Ouden, Michael Hipp, Michael Wein,
|
||||
Andreas Kool, Pekka Sarnila, Sim Yskes, Johan Myrre'en,
|
||||
Klaus-Peter Nischke (ITK AG), Christof Petig, Werner Fehn (ELSA GmbH),
|
||||
Volker Schmidt
|
||||
Edgar Toernig and Marcus Niemann for the Sedlbauer driver
|
||||
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
|
||||
Petr Novak <petr.novak@i.cz> for Winbond W6692 support
|
||||
Werner Cornelius <werner@isdn4linux.de> for HFC-PCI, HFC-S(+/P) and supplementary services support
|
||||
and more people who are hunting bugs. (If I forgot somebody, please
|
||||
send me a mail).
|
||||
|
||||
Firma ELSA GmbH
|
||||
Firma Eicon.Diehl GmbH
|
||||
Firma Dynalink NL
|
||||
Firma ASUSCOM NETWORK INC. Taiwan
|
||||
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@isdn4linux.de
|
||||
|
||||
|
||||
Appendix: Teles PCMCIA driver
|
||||
-----------------------------
|
||||
|
||||
See
|
||||
http://www.linux.no/teles_cs.txt
|
||||
for instructions.
|
||||
|
||||
Appendix: Linux and ISDN-leased lines
|
||||
-------------------------------------
|
||||
|
||||
Original from Juergen Quade, new version KKe.
|
||||
|
||||
Attention NEW VERSION, the old leased line syntax won't work !!!
|
||||
|
||||
You can use HiSax to connect your Linux-Box via an ISDN leased line
|
||||
to e.g. the Internet:
|
||||
|
||||
1. Build a kernel which includes the HiSax driver either as a module
|
||||
or as part of the kernel.
|
||||
cd /usr/src/linux
|
||||
make menuconfig
|
||||
<ISDN subsystem - ISDN support -- HiSax>
|
||||
make clean; make zImage; make modules; make modules_install
|
||||
2. Install the new kernel
|
||||
cp /usr/src/linux/arch/x86/boot/zImage /etc/kernel/linux.isdn
|
||||
vi /etc/lilo.conf
|
||||
<add new kernel in the bootable image section>
|
||||
lilo
|
||||
3. in case the hisax driver is a "fixed" part of the kernel, configure
|
||||
the driver with lilo:
|
||||
vi /etc/lilo.conf
|
||||
<add HiSax driver parameter in the global section (see below)>
|
||||
lilo
|
||||
Your lilo.conf _might_ look like the following:
|
||||
|
||||
# LILO configuration-file
|
||||
# global section
|
||||
# teles 16.0 on IRQ=5, MEM=0xd8000, PORT=0xd80
|
||||
append="hisax=1,3,5,0xd8000,0xd80,HiSax"
|
||||
# teles 16.3 (non pnp) on IRQ=15, PORT=0xd80
|
||||
# append="hisax=3,3,5,0xd8000,0xd80,HiSax"
|
||||
boot=/dev/sda
|
||||
compact # faster, but won't work on all systems.
|
||||
linear
|
||||
read-only
|
||||
prompt
|
||||
timeout=100
|
||||
vga = normal # force sane state
|
||||
# Linux bootable partition config begins
|
||||
image = /etc/kernel/linux.isdn
|
||||
root = /dev/sda1
|
||||
label = linux.isdn
|
||||
#
|
||||
image = /etc/kernel/linux-2.0.30
|
||||
root = /dev/sda1
|
||||
label = linux.secure
|
||||
|
||||
In the line starting with "append" you have to adapt the parameters
|
||||
according to your card (see above in this file)
|
||||
|
||||
3. boot the new linux.isdn kernel
|
||||
4. start the ISDN subsystem:
|
||||
a) load - if necessary - the modules (depends, whether you compiled
|
||||
the ISDN driver as module or not)
|
||||
According to the type of card you have to specify the necessary
|
||||
driver parameter (irq, io, mem, type, protocol).
|
||||
For the leased line the protocol is "3". See the table above for
|
||||
the parameters, which you have to specify depending on your card.
|
||||
b) configure i4l
|
||||
/sbin/isdnctrl addif isdn0
|
||||
# EAZ 1 -- B1 channel 2 --B2 channel
|
||||
/sbin/isdnctrl eaz isdn0 1
|
||||
/sbin/isdnctrl secure isdn0 on
|
||||
/sbin/isdnctrl huptimeout isdn0 0
|
||||
/sbin/isdnctrl l2_prot isdn0 hdlc
|
||||
# Attention you must not set an outgoing number !!! This won't work !!!
|
||||
# The incoming number is LEASED0 for the first card, LEASED1 for the
|
||||
# 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:
|
||||
/sbin/isdnctrl encap isdn0 cisco-h
|
||||
d) configure the interface
|
||||
/sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
|
||||
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
|
||||
/sbin/hisaxctrl HiSax 5 1
|
||||
|
||||
Remarks:
|
||||
a) Use state of the art isdn4k-utils
|
||||
|
||||
Here an example script:
|
||||
#!/bin/sh
|
||||
# Start/Stop ISDN leased line connection
|
||||
|
||||
I4L_AS_MODULE=yes
|
||||
I4L_REMOTE_IS_CISCO=no
|
||||
I4L_MODULE_PARAMS="type=16 io=0x268 irq=7 "
|
||||
I4L_DEBUG=no
|
||||
I4L_LEASED_128K=yes
|
||||
LOCAL_IP=192.168.1.1
|
||||
REMOTE_IP=192.168.2.1
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
echo "Starting ISDN ..."
|
||||
if [ ${I4L_AS_MODULE} = "yes" ]; then
|
||||
echo "loading modules..."
|
||||
/sbin/modprobe hisax ${I4L_MODULE_PARAMS}
|
||||
fi
|
||||
# configure interface
|
||||
/sbin/isdnctrl addif isdn0
|
||||
/sbin/isdnctrl secure isdn0 on
|
||||
if [ ${I4L_DEBUG} = "yes" ]; then
|
||||
/sbin/isdnctrl verbose 7
|
||||
/sbin/hisaxctrl HiSax 1 0xffff
|
||||
/sbin/hisaxctrl HiSax 11 0xff
|
||||
cat /dev/isdnctrl >/tmp/lea.log &
|
||||
fi
|
||||
if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then
|
||||
/sbin/isdnctrl encap isdn0 cisco-h
|
||||
fi
|
||||
/sbin/isdnctrl huptimeout isdn0 0
|
||||
# B-CHANNEL 1
|
||||
/sbin/isdnctrl eaz isdn0 1
|
||||
/sbin/isdnctrl l2_prot isdn0 hdlc
|
||||
# 1. card
|
||||
/sbin/isdnctrl addphone isdn0 in LEASED0
|
||||
if [ ${I4L_LEASED_128K} = "yes" ]; then
|
||||
/sbin/isdnctrl addslave isdn0 isdn0s
|
||||
/sbin/isdnctrl secure isdn0s on
|
||||
/sbin/isdnctrl huptimeout isdn0s 0
|
||||
# B-CHANNEL 2
|
||||
/sbin/isdnctrl eaz isdn0s 2
|
||||
/sbin/isdnctrl l2_prot isdn0s hdlc
|
||||
# 1. card
|
||||
/sbin/isdnctrl addphone isdn0s in LEASED0
|
||||
if [ ${I4L_REMOTE_IS_CISCO} = "yes" ]; then
|
||||
/sbin/isdnctrl encap isdn0s cisco-h
|
||||
fi
|
||||
fi
|
||||
/sbin/isdnctrl dialmode isdn0 manual
|
||||
# configure tcp/ip
|
||||
/sbin/ifconfig isdn0 ${LOCAL_IP} pointopoint ${REMOTE_IP}
|
||||
/sbin/route add -host ${REMOTE_IP} isdn0
|
||||
/sbin/route add default gw ${REMOTE_IP}
|
||||
# switch to leased mode
|
||||
# B-CHANNEL 1
|
||||
/sbin/hisaxctrl HiSax 5 1
|
||||
if [ ${I4L_LEASED_128K} = "yes" ]; then
|
||||
# B-CHANNEL 2
|
||||
sleep 10; /* Wait for master */
|
||||
/sbin/hisaxctrl HiSax 5 2
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
/sbin/ifconfig isdn0 down
|
||||
/sbin/isdnctrl delif isdn0
|
||||
if [ ${I4L_DEBUG} = "yes" ]; then
|
||||
killall cat
|
||||
fi
|
||||
if [ ${I4L_AS_MODULE} = "yes" ]; then
|
||||
/sbin/rmmod hisax
|
||||
/sbin/rmmod isdn
|
||||
/sbin/rmmod ppp
|
||||
/sbin/rmmod slhc
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop}"
|
||||
exit 1
|
||||
esac
|
||||
exit 0
|
|
@ -1,138 +0,0 @@
|
|||
$Id: README.audio,v 1.8 1999/07/11 17:17:29 armin Exp $
|
||||
|
||||
ISDN subsystem for Linux.
|
||||
Description of audio mode.
|
||||
|
||||
When enabled during kernel configuration, the tty emulator of the ISDN
|
||||
subsystem is capable of a reduced set of commands to support audio.
|
||||
This document describes the commands supported and the format of
|
||||
audio data.
|
||||
|
||||
Commands for enabling/disabling audio mode:
|
||||
|
||||
AT+FCLASS=8 Enable audio mode.
|
||||
This affects the following registers:
|
||||
S18: Bits 0 and 2 are set.
|
||||
S16: Set to 48 and any further change to
|
||||
larger values is blocked.
|
||||
AT+FCLASS=0 Disable audio mode.
|
||||
Register 18 is set to 4.
|
||||
AT+FCLASS=? Show possible modes.
|
||||
AT+FCLASS? Report current mode (0 or 8).
|
||||
|
||||
Commands supported in audio mode:
|
||||
|
||||
All audio mode commands have one of the following forms:
|
||||
|
||||
AT+Vxx? Show current setting.
|
||||
AT+Vxx=? Show possible settings.
|
||||
AT+Vxx=v Set simple parameter.
|
||||
AT+Vxx=v,v ... Set complex parameter.
|
||||
|
||||
where xx is a two-character code and v are alphanumerical parameters.
|
||||
The following commands are supported:
|
||||
|
||||
AT+VNH=x Auto hangup setting. NO EFFECT, supported
|
||||
for compatibility only.
|
||||
AT+VNH? Always reporting "1"
|
||||
AT+VNH=? Always reporting "1"
|
||||
|
||||
AT+VIP Reset all audio parameters.
|
||||
|
||||
AT+VLS=x Line select. x is one of the following:
|
||||
0 = No device.
|
||||
2 = Phone line.
|
||||
AT+VLS=? Always reporting "0,2"
|
||||
AT+VLS? Show current line.
|
||||
|
||||
AT+VRX Start recording. Emulator responds with
|
||||
CONNECT and starts sending audio data to
|
||||
the application. See below for data format
|
||||
|
||||
AT+VSD=x,y Set silence-detection parameters.
|
||||
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.
|
||||
|
||||
AT+VDD=x,y Set DTMF-detection parameters.
|
||||
Only possible if online and during this connection.
|
||||
Possible parameters:
|
||||
x = 0 ... 15 sensitivity threshold level.
|
||||
(default 0 , I4L soft-decode)
|
||||
(1-15 soft-decode off, hardware on)
|
||||
y = 0 ... 255 tone duration in units of 5ms.
|
||||
Not for I4L soft decode (default 8, 40ms)
|
||||
AT+VDD=? Report possible parameters.
|
||||
AT+VDD? Show current parameters.
|
||||
|
||||
AT+VSM=x Select audio data format.
|
||||
Possible parameters:
|
||||
2 = ADPCM-2
|
||||
3 = ADPCM-3
|
||||
4 = ADPCM-4
|
||||
5 = aLAW
|
||||
6 = uLAW
|
||||
AT+VSM=? Show possible audio formats.
|
||||
|
||||
AT+VTX Start audio playback. Emulator responds
|
||||
with CONNECT and starts sending audio data
|
||||
received from the application via phone line.
|
||||
General behavior and description of data formats/protocol.
|
||||
when a connection is made:
|
||||
|
||||
On incoming calls, if the application responds to a RING
|
||||
with ATA, depending on the calling service, the emulator
|
||||
responds with either CONNECT (data call) or VCON (voice call).
|
||||
|
||||
On outgoing voice calls, the emulator responds with VCON
|
||||
upon connection setup.
|
||||
|
||||
Audio recording.
|
||||
|
||||
When receiving audio data, a kind of bisync protocol is used.
|
||||
Upon AT+VRX command, the emulator responds with CONNECT, and
|
||||
starts sending audio data to the application. There are several
|
||||
escape sequences defined, all using DLE (0x10) as Escape char:
|
||||
|
||||
<DLE><ETX> End of audio data. (i.e. caused by a
|
||||
hangup of the remote side) Emulator stops
|
||||
recording, responding with VCON.
|
||||
<DLE><DC4> Abort recording, (send by appl.) Emulator
|
||||
stops recording, sends DLE,ETX.
|
||||
<DLE><DLE> Escape sequence for DLE in data stream.
|
||||
<DLE>0 Touchtone "0" received.
|
||||
...
|
||||
<DLE>9 Touchtone "9" received.
|
||||
<DLE># Touchtone "#" received.
|
||||
<DLE>* Touchtone "*" received.
|
||||
<DLE>A Touchtone "A" received.
|
||||
<DLE>B Touchtone "B" received.
|
||||
<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.
|
||||
|
||||
Audio playback.
|
||||
|
||||
When sending audio data, upon AT+VTX command, emulator responds with
|
||||
CONNECT, and starts transferring data from application to the phone line.
|
||||
The same DLE sequences apply to this mode.
|
||||
|
||||
Full-Duplex-Audio:
|
||||
|
||||
When _both_ commands for recording and playback are given in _one_
|
||||
AT-command-line (i.e.: "AT+VTX+VRX"), full-duplex-mode is selected.
|
||||
In this mode, the only way to stop recording is sending <DLE><DC4>
|
||||
and the only way to stop playback is to send <DLE><ETX>.
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
Description of the "concap" encapsulation protocol interface
|
||||
============================================================
|
||||
|
||||
The "concap" interface is intended to be used by network device
|
||||
drivers that need to process an encapsulation protocol.
|
||||
It is assumed that the protocol interacts with a linux network device by
|
||||
- data transmission
|
||||
- connection control (establish, release)
|
||||
Thus, the mnemonic: "CONnection CONtrolling eNCAPsulation Protocol".
|
||||
|
||||
This is currently only used inside the isdn subsystem. But it might
|
||||
also be useful to other kinds of network devices. Thus, if you want
|
||||
to suggest changes that improve usability or performance of the
|
||||
interface, please let me know. I'm willing to include them in future
|
||||
releases (even if I needed to adapt the current isdn code to the
|
||||
changed interface).
|
||||
|
||||
|
||||
Why is this useful?
|
||||
===================
|
||||
|
||||
The encapsulation protocol used on top of WAN connections or permanent
|
||||
point-to-point links are frequently chosen upon bilateral agreement.
|
||||
Thus, a device driver for a certain type of hardware must support
|
||||
several different encapsulation protocols at once.
|
||||
|
||||
The isdn device driver did already support several different
|
||||
encapsulation protocols. The encapsulation protocol is configured by a
|
||||
user space utility (isdnctrl). The isdn network interface code then
|
||||
uses several case statements which select appropriate actions
|
||||
depending on the currently configured encapsulation protocol.
|
||||
|
||||
In contrast, LAN network interfaces always used a single encapsulation
|
||||
protocol which is unique to the hardware type of the interface. The LAN
|
||||
encapsulation is usually done by just sticking a header on the data. Thus,
|
||||
traditional linux network device drivers used to process the
|
||||
encapsulation protocol directly (usually by just providing a hard_header()
|
||||
method in the device structure) using some hardware type specific support
|
||||
functions. This is simple, direct and efficient. But it doesn't fit all
|
||||
the requirements for complex WAN encapsulations.
|
||||
|
||||
|
||||
The configurability of the encapsulation protocol to be used
|
||||
makes isdn network interfaces more flexible, but also much more
|
||||
complex than traditional lan network interfaces.
|
||||
|
||||
|
||||
Many Encapsulation protocols used on top of WAN connections will not just
|
||||
stick a header on the data. They also might need to set up or release
|
||||
the WAN connection. They also might want to send other data for their
|
||||
private purpose over the wire, e.g. ppp does a lot of link level
|
||||
negotiation before the first piece of user data can be transmitted.
|
||||
Such encapsulation protocols for WAN devices are typically more complex
|
||||
than encapsulation protocols for lan devices. Thus, network interface
|
||||
code for typical WAN devices also tends to be more complex.
|
||||
|
||||
|
||||
In order to support Linux' x25 PLP implementation on top of
|
||||
isdn network interfaces I could have introduced yet another branch to
|
||||
the various case statements inside drivers/isdn/isdn_net.c.
|
||||
This eventually made isdn_net.c even more complex. In addition, it made
|
||||
isdn_net.c harder to maintain. Thus, by identifying an abstract
|
||||
interface between the network interface code and the encapsulation
|
||||
protocol, complexity could be reduced and maintainability could be
|
||||
increased.
|
||||
|
||||
|
||||
Likewise, a similar encapsulation protocol will frequently be needed by
|
||||
several different interfaces of even different hardware type, e.g. the
|
||||
synchronous ppp implementation used by the isdn driver and the
|
||||
asynchronous ppp implementation used by the ppp driver have a lot of
|
||||
similar code in them. By cleanly separating the encapsulation protocol
|
||||
from the hardware specific interface stuff such code could be shared
|
||||
better in future.
|
||||
|
||||
|
||||
When operating over dial-up-connections (e.g. telephone lines via modem,
|
||||
non-permanent virtual circuits of wide area networks, ISDN) many
|
||||
encapsulation protocols will need to control the connection. Therefore,
|
||||
some basic connection control primitives are supported. The type and
|
||||
semantics of the connection (i.e the ISO layer where connection service
|
||||
is provided) is outside our scope and might be different depending on
|
||||
the encapsulation protocol used, e.g. for a ppp module using our service
|
||||
on top of a modem connection a connect_request will result in dialing
|
||||
a (somewhere else configured) remote phone number. For an X25-interface
|
||||
module (LAPB semantics, as defined in Documentation/networking/x25-iface.txt)
|
||||
a connect_request will ask for establishing a reliable lapb
|
||||
datalink connection.
|
||||
|
||||
|
||||
The encapsulation protocol currently provides the following
|
||||
service primitives to the network device.
|
||||
|
||||
- create a new encapsulation protocol instance
|
||||
- delete encapsulation protocol instance and free all its resources
|
||||
- initialize (open) the encapsulation protocol instance for use.
|
||||
- deactivate (close) an encapsulation protocol instance.
|
||||
- process (xmit) data handed down by upper protocol layer
|
||||
- receive data from lower (hardware) layer
|
||||
- process connect indication from lower (hardware) layer
|
||||
- process disconnect indication from lower (hardware) layer
|
||||
|
||||
|
||||
The network interface driver accesses those primitives via callbacks
|
||||
provided by the encapsulation protocol instance within a
|
||||
struct concap_proto_ops.
|
||||
|
||||
struct concap_proto_ops{
|
||||
|
||||
/* create a new encapsulation protocol instance of same type */
|
||||
struct concap_proto * (*proto_new) (void);
|
||||
|
||||
/* delete encapsulation protocol instance and free all its resources.
|
||||
cprot may no longer be referenced after calling this */
|
||||
void (*proto_del)(struct concap_proto *cprot);
|
||||
|
||||
/* initialize the protocol's data. To be called at interface startup
|
||||
or when the device driver resets the interface. All services of the
|
||||
encapsulation protocol may be used after this*/
|
||||
int (*restart)(struct concap_proto *cprot,
|
||||
struct net_device *ndev,
|
||||
struct concap_device_ops *dops);
|
||||
|
||||
/* deactivate an encapsulation protocol instance. The encapsulation
|
||||
protocol may not call any *dops methods after this. */
|
||||
int (*close)(struct concap_proto *cprot);
|
||||
|
||||
/* process a frame handed down to us by upper layer */
|
||||
int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
|
||||
|
||||
/* to be called for each data entity received from lower layer*/
|
||||
int (*data_ind)(struct concap_proto *cprot, struct sk_buff *skb);
|
||||
|
||||
/* to be called when a connection was set up/down.
|
||||
Protocols that don't process these primitives might fill in
|
||||
dummy methods here */
|
||||
int (*connect_ind)(struct concap_proto *cprot);
|
||||
int (*disconn_ind)(struct concap_proto *cprot);
|
||||
};
|
||||
|
||||
|
||||
The data structures are defined in the header file include/linux/concap.h.
|
||||
|
||||
|
||||
A Network interface using encapsulation protocols must also provide
|
||||
some service primitives to the encapsulation protocol:
|
||||
|
||||
- request data being submitted by lower layer (device hardware)
|
||||
- request a connection being set up by lower layer
|
||||
- request a connection being released by lower layer
|
||||
|
||||
The encapsulation protocol accesses those primitives via callbacks
|
||||
provided by the network interface within a struct concap_device_ops.
|
||||
|
||||
struct concap_device_ops{
|
||||
|
||||
/* to request data be submitted by device */
|
||||
int (*data_req)(struct concap_proto *, struct sk_buff *);
|
||||
|
||||
/* Control methods must be set to NULL by devices which do not
|
||||
support connection control. */
|
||||
/* to request a connection be set up */
|
||||
int (*connect_req)(struct concap_proto *);
|
||||
|
||||
/* to request a connection be released */
|
||||
int (*disconn_req)(struct concap_proto *);
|
||||
};
|
||||
|
||||
The network interface does not explicitly provide a receive service
|
||||
because the encapsulation protocol directly calls netif_rx().
|
||||
|
||||
|
||||
|
||||
|
||||
An encapsulation protocol itself is actually the
|
||||
struct concap_proto{
|
||||
struct net_device *net_dev; /* net device using our service */
|
||||
struct concap_device_ops *dops; /* callbacks provided by device */
|
||||
struct concap_proto_ops *pops; /* callbacks provided by us */
|
||||
int flags;
|
||||
void *proto_data; /* protocol specific private data, to
|
||||
be accessed via *pops methods only*/
|
||||
/*
|
||||
:
|
||||
whatever
|
||||
:
|
||||
*/
|
||||
};
|
||||
|
||||
Most of this is filled in when the device requests the protocol to
|
||||
be reset (opend). The network interface must provide the net_dev and
|
||||
dops pointers. Other concap_proto members should be considered private
|
||||
data that are only accessed by the pops callback functions. Likewise,
|
||||
a concap proto should access the network device's private data
|
||||
only by means of the callbacks referred to by the dops pointer.
|
||||
|
||||
|
||||
A possible extended device structure which uses the connection controlling
|
||||
encapsulation services could look like this:
|
||||
|
||||
struct concap_device{
|
||||
struct net_device net_dev;
|
||||
struct my_priv /* device->local stuff */
|
||||
/* the my_priv struct might contain a
|
||||
struct concap_device_ops *dops;
|
||||
to provide the device specific callbacks
|
||||
*/
|
||||
struct concap_proto *cprot; /* callbacks provided by protocol */
|
||||
};
|
||||
|
||||
|
||||
|
||||
Misc Thoughts
|
||||
=============
|
||||
|
||||
The concept of the concap proto might help to reuse protocol code and
|
||||
reduce the complexity of certain network interface implementations.
|
||||
The trade off is that it introduces yet another procedure call layer
|
||||
when processing the protocol. This has of course some impact on
|
||||
performance. However, typically the concap interface will be used by
|
||||
devices attached to slow lines (like telephone, isdn, leased synchronous
|
||||
lines). For such slow lines, the overhead is probably negligible.
|
||||
This might no longer hold for certain high speed WAN links (like
|
||||
ATM).
|
||||
|
||||
|
||||
If general linux network interfaces explicitly supported concap
|
||||
protocols (e.g. by a member struct concap_proto* in struct net_device)
|
||||
then the interface of the service function could be changed
|
||||
by passing a pointer of type (struct net_device*) instead of
|
||||
type (struct concap_proto*). Doing so would make many of the service
|
||||
functions compatible to network device support functions.
|
||||
|
||||
e.g. instead of the concap protocol's service function
|
||||
|
||||
int (*encap_and_xmit)(struct concap_proto *cprot, struct sk_buff *skb);
|
||||
|
||||
we could have
|
||||
|
||||
int (*encap_and_xmit)(struct net_device *ndev, struct sk_buff *skb);
|
||||
|
||||
As this is compatible to the dev->hard_start_xmit() method, the device
|
||||
driver could directly register the concap protocol's encap_and_xmit()
|
||||
function as its hard_start_xmit() method. This would eliminate one
|
||||
procedure call layer.
|
||||
|
||||
|
||||
The device's data request function could also be defined as
|
||||
|
||||
int (*data_req)(struct net_device *ndev, struct sk_buff *skb);
|
||||
|
||||
This might even allow for some protocol stacking. And the network
|
||||
interface might even register the same data_req() function directly
|
||||
as its hard_start_xmit() method when a zero layer encapsulation
|
||||
protocol is configured. Thus, eliminating the performance penalty
|
||||
of the concap interface when a trivial concap protocol is used.
|
||||
Nevertheless, the device remains able to support encapsulation
|
||||
protocol configuration.
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
The isdn diversion services are a supporting module working together with
|
||||
the isdn4linux and the HiSax module for passive cards.
|
||||
Active cards, TAs and cards using a own or other driver than the HiSax
|
||||
module need to be adapted to the HL<->LL interface described in a separate
|
||||
document. The diversion services may be used with all cards supported by
|
||||
the HiSax driver.
|
||||
The diversion kernel interface and controlling tool divertctrl were written
|
||||
by Werner Cornelius (werner@isdn4linux.de or werner@titro.de) under the
|
||||
GNU General Public License.
|
||||
|
||||
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.
|
||||
|
||||
Table of contents
|
||||
=================
|
||||
|
||||
1. Features of the i4l diversion services
|
||||
(Or what can the i4l diversion services do for me)
|
||||
|
||||
2. Required hard- and software
|
||||
|
||||
3. Compiling, installing and loading/unloading the module
|
||||
Tracing calling and diversion information
|
||||
|
||||
4. Tracing calling and diversion information
|
||||
|
||||
5. Format of the divert device ASCII output
|
||||
|
||||
|
||||
1. Features of the i4l diversion services
|
||||
(Or what can the i4l diversion services do for me)
|
||||
|
||||
The i4l diversion services offers call forwarding and logging normally
|
||||
only supported by isdn phones. Incoming calls may be diverted
|
||||
unconditionally (CFU), when not reachable (CFNR) or on busy condition
|
||||
(CFB).
|
||||
The diversions may be invoked statically in the providers exchange
|
||||
as normally done by isdn phones. In this case all incoming calls
|
||||
with a special (or all) service identifiers are forwarded if the
|
||||
forwarding reason is met. Activated static services may also be
|
||||
interrogated (queried).
|
||||
The i4l diversion services additionally offers a dynamic version of
|
||||
call forwarding which is not preprogrammed inside the providers exchange
|
||||
but dynamically activated by i4l.
|
||||
In this case all incoming calls are checked by rules that may be
|
||||
compared to the mechanism of ipfwadm or ipchains. If a given rule matches
|
||||
the checking process is finished and the rule matching will be applied
|
||||
to the call.
|
||||
The rules include primary and secondary service identifiers, called
|
||||
number and subaddress, callers number and subaddress and whether the rule
|
||||
matches to all filtered calls or only those when all B-channel resources
|
||||
are exhausted.
|
||||
Actions that may be invoked by a rule are ignore, proceed, reject,
|
||||
direct divert or delayed divert of a call.
|
||||
All incoming calls matching a rule except the ignore rule a reported and
|
||||
logged as ASCII via the proc filesystem (/proc/net/isdn/divert). If proceed
|
||||
is selected the call will be held in a proceeding state (without ringing)
|
||||
for a certain amount of time to let an external program or client decide
|
||||
how to handle the call.
|
||||
|
||||
|
||||
2. Required hard- and software
|
||||
|
||||
For using the i4l diversion services the isdn line must be of a EURO/DSS1
|
||||
type. Additionally the i4l services only work together with the HiSax
|
||||
driver for passive isdn cards. All HiSax supported cards may be used for
|
||||
the diversion purposes.
|
||||
The static diversion services require the provider having static services
|
||||
CFU, CFNR, CFB activated on an MSN-line. The static services may not be
|
||||
used on a point-to-point connection. Further the static services are only
|
||||
available in some countries (for example germany). Countries requiring the
|
||||
keypad protocol for activating static diversions (like the netherlands) are
|
||||
not supported but may use the tty devices for this purpose.
|
||||
The dynamic diversion services may be used in all countries if the provider
|
||||
enables the feature CF (call forwarding). This should work on both MSN- and
|
||||
point-to-point lines.
|
||||
To add and delete rules the additional divertctrl program is needed. This
|
||||
program is part of the isdn4kutils package.
|
||||
|
||||
3. Compiling, installing and loading/unloading the module
|
||||
Tracing calling and diversion information
|
||||
|
||||
|
||||
To compile the i4l code with diversion support you need to say yes to the
|
||||
DSS1 diversion services when selecting the i4l options in the kernel
|
||||
config (menuconfig or config).
|
||||
After having properly activated a make modules and make modules_install all
|
||||
required modules will be correctly installed in the needed modules dirs.
|
||||
As the diversion services are currently not included in the scripts of most
|
||||
standard distributions you will have to add a "insmod dss1_divert" after
|
||||
having loaded the global isdn module.
|
||||
The module can be loaded without any command line parameters.
|
||||
If the module is actually loaded and active may be checked with a
|
||||
"cat /proc/modules" or "ls /proc/net/isdn/divert". The divert file is
|
||||
dynamically created by the diversion module and removed when the module is
|
||||
unloaded.
|
||||
|
||||
|
||||
4. Tracing calling and diversion information
|
||||
|
||||
You also may put a "cat /proc/net/isdn/divert" in the background with the
|
||||
output redirected to a file. Then all actions of the module are logged.
|
||||
The divert file in the proc system may be opened more than once, so in
|
||||
conjunction with inetd and a small remote client on other machines inside
|
||||
your network incoming calls and reactions by the module may be shown on
|
||||
every listening machine.
|
||||
If a call is reported as proceeding an external program or client may
|
||||
specify during a certain amount of time (normally 4 to 10 seconds) what
|
||||
to do with that call.
|
||||
To unload the module all open files to the device in the proc system must
|
||||
be closed. Otherwise the module (and isdn.o) may not be unloaded.
|
||||
|
||||
5. Format of the divert device ASCII output
|
||||
|
||||
To be done later
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
|
||||
Fax with isdn4linux
|
||||
===================
|
||||
|
||||
When enabled during kernel configuration, the tty emulator
|
||||
of the ISDN subsystem is capable of the Fax Class 2 commands.
|
||||
|
||||
This only makes sense under the following conditions :
|
||||
|
||||
- You need the commands as dummy, because you are using
|
||||
hylafax (with patch) for AVM capi.
|
||||
- You want to use the fax capabilities of your isdn-card.
|
||||
(supported cards are listed below)
|
||||
|
||||
|
||||
NOTE: This implementation does *not* support fax with passive
|
||||
ISDN-cards (known as softfax). The low-level driver of
|
||||
the ISDN-card and/or the card itself must support this.
|
||||
|
||||
|
||||
Supported ISDN-Cards
|
||||
--------------------
|
||||
|
||||
Eicon DIVA Server BRI/PCI
|
||||
- full support with both B-channels.
|
||||
|
||||
Eicon DIVA Server 4BRI/PCI
|
||||
- full support with all B-channels.
|
||||
|
||||
Eicon DIVA Server PRI/PCI
|
||||
- full support on amount of B-channels
|
||||
depending on DSPs on board.
|
||||
|
||||
|
||||
|
||||
The command set is known as Class 2 (not Class 2.0) and
|
||||
can be activated by AT+FCLASS=2
|
||||
|
||||
|
||||
The interface between the link-level-module and the hardware-level driver
|
||||
is described in the files INTERFACE.fax and INTERFACE.
|
||||
|
||||
Armin
|
||||
mac@melware.de
|
||||
|
|
@ -48,9 +48,8 @@ GigaSet 307x Device Driver
|
|||
|
||||
1.2. Software
|
||||
--------
|
||||
The driver works with the Kernel CAPI subsystem as well as the old
|
||||
ISDN4Linux subsystem, so it can be used with any software which is able
|
||||
to use CAPI 2.0 or ISDN4Linux for ISDN connections (voice or data).
|
||||
The driver works with the Kernel CAPI subsystem and can be used with any
|
||||
software which is able to use CAPI 2.0 for ISDN connections (voice or data).
|
||||
|
||||
There are some user space tools available at
|
||||
https://sourceforge.net/projects/gigaset307x/
|
||||
|
@ -92,7 +91,7 @@ GigaSet 307x Device Driver
|
|||
gigaset debug debug level (see section 3.2.)
|
||||
|
||||
startmode initial operation mode (see section 2.5.):
|
||||
bas_gigaset ) 1=ISDN4linux/CAPI (default), 0=Unimodem
|
||||
bas_gigaset ) 1=CAPI (default), 0=Unimodem
|
||||
ser_gigaset )
|
||||
usb_gigaset ) cidmode initial Call-ID mode setting (see section
|
||||
2.5.): 1=on (default), 0=off
|
||||
|
@ -154,18 +153,10 @@ GigaSet 307x Device Driver
|
|||
|
||||
2.3. CAPI
|
||||
----
|
||||
If the driver is compiled with CAPI support (kernel configuration option
|
||||
GIGASET_CAPI) the devices will show up as CAPI controllers as soon as the
|
||||
corresponding driver module is loaded, and can then be used with CAPI 2.0
|
||||
kernel and user space applications. For user space access, the module
|
||||
capi.ko must be loaded.
|
||||
|
||||
Legacy ISDN4Linux applications are supported via the capidrv
|
||||
compatibility driver. The kernel module capidrv.ko must be loaded
|
||||
explicitly with the command
|
||||
modprobe capidrv
|
||||
if needed, and cannot be unloaded again without unloading the driver
|
||||
first. (These are limitations of capidrv.)
|
||||
The devices will show up as CAPI controllers as soon as the
|
||||
corresponding driver module is loaded, and can then be used with
|
||||
CAPI 2.0 kernel and user space applications. For user space access,
|
||||
the module capi.ko must be loaded.
|
||||
|
||||
Most distributions handle loading and unloading of the various CAPI
|
||||
modules automatically via the command capiinit(1) from the capi4k-utils
|
||||
|
@ -173,16 +164,6 @@ GigaSet 307x Device Driver
|
|||
Gigaset drivers because it doesn't support more than one module per
|
||||
driver.
|
||||
|
||||
2.4. ISDN4Linux
|
||||
----------
|
||||
If the driver is compiled without CAPI support (native ISDN4Linux
|
||||
variant), it registers the device with the legacy ISDN4Linux subsystem
|
||||
after loading the module. It can then be used with ISDN4Linux
|
||||
applications only. Most distributions provide some configuration utility
|
||||
for setting up that subsystem. Otherwise you can use some HOWTOs like
|
||||
http://www.linuxhaven.de/dlhp/HOWTO/DE-ISDN-HOWTO-5.html
|
||||
|
||||
|
||||
2.5. Unimodem mode
|
||||
-------------
|
||||
In this mode the device works like a modem connected to a serial port
|
||||
|
@ -281,8 +262,7 @@ GigaSet 307x Device Driver
|
|||
number. Dialing "***" (three asterisks) calls all extensions
|
||||
simultaneously (global call).
|
||||
|
||||
This holds for both CAPI 2.0 and ISDN4Linux applications. Unimodem mode
|
||||
does not support internal calls.
|
||||
Unimodem mode does not support internal calls.
|
||||
|
||||
2.8. Unregistered Wireless Devices (M101/M105)
|
||||
-----------------------------------------
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
The driver for the HFC-PCI and HFC-PCI-A chips from CCD may be used
|
||||
for many OEM cards using this chips.
|
||||
Additionally the driver has a special feature which makes it possible
|
||||
to read the echo-channel of the isdn bus. So all frames in both directions
|
||||
may be logged.
|
||||
When the echo logging feature is used the number of available B-channels
|
||||
for a HFC-PCI card is reduced to 1. Of course this is only relevant to
|
||||
the card, not to the isdn line.
|
||||
To activate the echo mode the following ioctls must be entered:
|
||||
|
||||
hisaxctrl <driver/cardname> 10 1
|
||||
|
||||
This reduces the available channels to 1. There must not be open connections
|
||||
through this card when entering the command.
|
||||
And then:
|
||||
|
||||
hisaxctrl <driver/cardname> 12 1
|
||||
|
||||
This enables the echo mode. If Hex logging is activated the isdnctrlx
|
||||
devices show a output with a line beginning of HEX: for the providers
|
||||
exchange and ECHO: for isdn devices sending to the provider.
|
||||
|
||||
If more than one HFC-PCI cards are installed, a specific card may be selected
|
||||
at the hisax module load command line. Supply the load command with the desired
|
||||
IO-address of the desired card.
|
||||
Example:
|
||||
There tree cards installed in your machine at IO-base addresses 0xd000, 0xd400
|
||||
and 0xdc00
|
||||
If you want to use the card at 0xd400 standalone you should supply the insmod
|
||||
or depmod with type=35 io=0xd400.
|
||||
If you want to use all three cards, but the order needs to be at 0xdc00,0xd400,
|
||||
0xd000 you may give the parameters type=35,35,35 io=0xdc00,0xd400,0xd00
|
||||
Then the desired card will be the initialised in the desired order.
|
||||
If the io parameter is used the io addresses of all used cards should be
|
||||
supplied else the parameter is assumed 0 and a auto search for a free card is
|
||||
invoked which may not give the wanted result.
|
||||
|
||||
Comments and reports to werner@isdn4linux.de or werner@isdn-development.de
|
||||
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
Some additional information for setting up a syncPPP
|
||||
connection using network interfaces.
|
||||
---------------------------------------------------------------
|
||||
|
||||
You need one thing beside the isdn4linux package:
|
||||
|
||||
a patched pppd .. (I called it ipppd to show the difference)
|
||||
|
||||
Compiling isdn4linux with sync PPP:
|
||||
-----------------------------------
|
||||
To compile isdn4linux with the sync PPP part, you have
|
||||
to answer the appropriate question when doing a "make config"
|
||||
Don't forget to load the slhc.o
|
||||
module before the isdn.o module, if VJ-compression support
|
||||
is not compiled into your kernel. (e.g if you have no PPP or
|
||||
CSLIP in the kernel)
|
||||
|
||||
Using isdn4linux with sync PPP:
|
||||
-------------------------------
|
||||
Sync PPP is just another encapsulation for isdn4linux. The
|
||||
name to enable sync PPP encapsulation is 'syncppp' .. e.g:
|
||||
|
||||
/sbin/isdnctrl encap ippp0 syncppp
|
||||
|
||||
The name of the interface is here 'ippp0'. You need
|
||||
one interface with the name 'ippp0' to saturate the
|
||||
ipppd, which checks the ppp version via this interface.
|
||||
Currently, all devices must have the name ipppX where
|
||||
'X' is a decimal value.
|
||||
|
||||
To set up a PPP connection you need the ipppd .. You must start
|
||||
the ipppd once after installing the modules. The ipppd
|
||||
communicates with the isdn4linux link-level driver using the
|
||||
/dev/ippp0 to /dev/ippp15 devices. One ipppd can handle
|
||||
all devices at once. If you want to use two PPP connections
|
||||
at the same time, you have to connect the ipppd to two
|
||||
devices .. and so on.
|
||||
I've implemented one additional option for the ipppd:
|
||||
'useifip' will get (if set to not 0.0.0.0) the IP address
|
||||
for the negotiation from the attached network-interface.
|
||||
(also: ipppd will try to negotiate pointopoint IP as remote IP)
|
||||
You must disable BSD-compression, this implementation can't
|
||||
handle compressed packets.
|
||||
|
||||
Check the etc/rc.isdn.syncppp in the isdn4kernel-util package
|
||||
for an example setup script.
|
||||
|
||||
To use the MPPP stuff, you must configure a slave device
|
||||
with isdn4linux. Now call the ipppd with the '+mp' option.
|
||||
To increase the number of links, you must use the
|
||||
'addlink' option of the isdnctrl tool. (rc.isdn.syncppp.MPPP is
|
||||
an example script)
|
||||
|
||||
enjoy it,
|
||||
michael
|
||||
|
||||
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
|
||||
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 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 X.25 neighbour not operated by yourself, ASK the
|
||||
other side first. Be prepared that bugs in the protocol implementation
|
||||
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.
|
||||
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
|
||||
useful to run X.25 on top of ISDN. If you want to try it, select
|
||||
|
||||
"CCITT X.25 Packet Layer"
|
||||
|
||||
from the networking options as well as
|
||||
|
||||
"ISDN Support" and "X.25 PLP on Top of ISDN"
|
||||
|
||||
from the ISDN subsystem options when you configure your kernel for
|
||||
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".
|
||||
|
||||
For local testing it is also recommended to enable 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?
|
||||
=============================
|
||||
|
||||
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 B-channel (leased line as well as dial up connection should
|
||||
work).
|
||||
|
||||
This corresponds to ITU-T recommendation X.31 Case A (circuit-mode
|
||||
access to PSPDN [packet switched public data network]).
|
||||
|
||||
NOTE: X.31 also covers a Case B (access to PSPDN via virtual
|
||||
circuit / packet mode service). The latter mode (which in theory
|
||||
also allows using the D-channel) is not supported by isdn4linux.
|
||||
It should however be possible to establish such packet mode connections
|
||||
with certain active isdn cards provided that the firmware supports X.31
|
||||
and the driver exports this functionality to the user. Currently,
|
||||
the AVM B1 driver is the only driver which does so. (It should be
|
||||
possible to access D-channel X.31 with active AVM cards using the
|
||||
CAPI interface of the AVM-B1 driver).
|
||||
|
||||
- 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.
|
||||
|
||||
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,
|
||||
and choose x25iface encapsulation by
|
||||
|
||||
isdnctrl encap <iface-name> x25iface.
|
||||
|
||||
Once encap is set like this, the device can be used by the X.25 packet layer.
|
||||
|
||||
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 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 (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
|
||||
specify the proper l2_prot parameter:
|
||||
|
||||
To operate in ISO-8208 X.25 DTE-DTE mode, use
|
||||
|
||||
isdnctrl l2_prot <iface-name> x75i
|
||||
|
||||
To access an X.25 network switch via isdn (your linux box is the DTE), use
|
||||
|
||||
isdnctrl l2_prot <iface-name> x25dte
|
||||
|
||||
To mimic an X.25 network switch (DCE side of the connection), use
|
||||
|
||||
isdnctrl l2_prot <iface-name> x25dce
|
||||
|
||||
However, x25dte or x25dce is currently not supported by any real HL
|
||||
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 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 set up a test installation?
|
||||
==================================
|
||||
|
||||
To test X.25 on top of isdn, you need to get
|
||||
|
||||
- a recent version of the "isdnctrl" program that supports setting the new
|
||||
X.25 specific parameters.
|
||||
|
||||
- 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)
|
||||
|
||||
- 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 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 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 X.25 routes. I.e.
|
||||
|
||||
x25route add 01 <iface-name>
|
||||
|
||||
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 X.25 applications available. However, for
|
||||
tests, the x25-utils package contains a modified version of telnet
|
||||
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 X.25 packets received by the network interfaces.
|
||||
The /proc/net/x25* files also contain useful information.
|
||||
|
||||
- Henner
|
|
@ -1,224 +0,0 @@
|
|||
simple isdn4linux PPP FAQ .. to be continued .. not 'debugged'
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Q01: what's pppd, ipppd, syncPPP, asyncPPP ??
|
||||
Q02: error message "this system lacks PPP support"
|
||||
Q03: strange information using 'ifconfig'
|
||||
Q04: MPPP?? What's that and how can I use it ...
|
||||
Q05: I tried MPPP but it doesn't work
|
||||
Q06: can I use asynchronous PPP encapsulation with network devices
|
||||
Q07: A SunISDN machine can't connect to my i4l system
|
||||
Q08: I wanna talk to several machines, which need different configs
|
||||
Q09: Starting the ipppd, I get only error messages from i4l
|
||||
Q10: I wanna use dynamic IP address assignment
|
||||
Q11: I can't connect. How can I check where the problem is.
|
||||
Q12: How can I reduce login delay?
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
Q01: pppd, ipppd, syncPPP, asyncPPP .. what is that ?
|
||||
what should I use?
|
||||
A: The pppd is for asynchronous PPP .. asynchronous means
|
||||
here, the framing is character based. (e.g when
|
||||
using ttyI* or tty* devices)
|
||||
|
||||
The ipppd handles PPP packets coming in HDLC
|
||||
frames (bit based protocol) ... The PPP driver
|
||||
in isdn4linux pushes all IP packets direct
|
||||
to the network layer and all PPP protocol
|
||||
frames to the /dev/ippp* device.
|
||||
So, the ipppd is a simple external network
|
||||
protocol handler.
|
||||
|
||||
If you login into a remote machine using the
|
||||
/dev/ttyI* devices and then enable PPP on the
|
||||
remote terminal server -> use the 'old' pppd
|
||||
|
||||
If your remote side immediately starts to send
|
||||
frames ... you probably connect to a
|
||||
syncPPP machine .. use the network device part
|
||||
of isdn4linux with the 'syncppp' encapsulation
|
||||
and make sure, that the ipppd is running and
|
||||
connected to at least one /dev/ippp*. Check the
|
||||
isdn4linux manual on how to configure a network device.
|
||||
|
||||
--
|
||||
|
||||
Q02: when I start the ipppd .. I only get the
|
||||
error message "this system lacks PPP support"
|
||||
A: check that at least the device 'ippp0' exists.
|
||||
(you can check this e.g with the program 'ifconfig')
|
||||
The ipppd NEEDS this device under THIS name ..
|
||||
If this device doesn't exists, use:
|
||||
isdnctrl addif ippp0
|
||||
isdnctrl encap ippp0 syncppp
|
||||
... (see isdn4linux doc for more) ...
|
||||
A: Maybe you have compiled the ipppd with another
|
||||
kernel source tree than the kernel you currently
|
||||
run ...
|
||||
|
||||
--
|
||||
|
||||
Q03: when I list the netdevices with ifconfig I see, that
|
||||
my ISDN interface has a HWaddr and IRQ=0 and Base
|
||||
address = 0
|
||||
A: The device is a fake ethernet device .. ignore IRQ and baseaddr
|
||||
You need the HWaddr only for ethernet encapsulation.
|
||||
|
||||
--
|
||||
|
||||
Q04: MPPP?? What's that and how can I use it ...
|
||||
|
||||
A: MPPP or MP or MPP (Warning: MP is also an
|
||||
acronym for 'Multi Processor') stands for
|
||||
Multi Point to Point and means bundling
|
||||
of several channels to one logical stream.
|
||||
To enable MPPP negotiation you must call the
|
||||
ipppd with the '+mp' option.
|
||||
You must also configure a slave device for
|
||||
every additional channel. (see the i4l manual
|
||||
for more)
|
||||
To use channel bundling you must first activate
|
||||
the 'master' or initial call. Now you can add
|
||||
the slave channels with the command:
|
||||
isdnctrl addlink <device>
|
||||
e.g:
|
||||
isdnctrl addlink ippp0
|
||||
This is different from other encapsulations of
|
||||
isdn4linux! With syncPPP, there is no automatic
|
||||
activation of slave devices.
|
||||
|
||||
--
|
||||
|
||||
Q05: I tried MPPP but it doesn't work .. the ipppd
|
||||
writes in the debug log something like:
|
||||
.. rcvd [0][proto=0x3d] c0 00 00 00 80 fd 01 01 00 0a ...
|
||||
.. sent [0][LCP ProtRej id=0x2 00 3d c0 00 00 00 80 fd 01 ...
|
||||
|
||||
A: you forgot to compile MPPP/RFC1717 support into the
|
||||
ISDN Subsystem. Recompile with this option enabled.
|
||||
|
||||
--
|
||||
|
||||
Q06: can I use asynchronous PPP encapsulation
|
||||
over the network interface of isdn4linux ..
|
||||
|
||||
A: No .. that's not possible .. Use the standard
|
||||
PPP package over the /dev/ttyI* devices. You
|
||||
must not use the ipppd for this.
|
||||
|
||||
--
|
||||
|
||||
Q07: A SunISDN machine tries to connect my i4l system,
|
||||
which doesn't work.
|
||||
Checking the debug log I just saw garbage like:
|
||||
!![ ... fill in the line ... ]!!
|
||||
|
||||
A: The Sun tries to talk asynchronous PPP ... i4l
|
||||
can't understand this ... try to use the ttyI*
|
||||
devices with the standard PPP/pppd package
|
||||
|
||||
A: (from Alexanter Strauss: )
|
||||
!![ ... fill in mail ]!!
|
||||
|
||||
--
|
||||
|
||||
Q08: I wanna talk to remote machines, which need
|
||||
a different configuration. The only way
|
||||
I found to do this is to kill the ipppd and
|
||||
start a new one with another config to connect
|
||||
to the second machine.
|
||||
|
||||
A: you must bind a network interface explicitly to
|
||||
an ippp device, where you can connect a (for this
|
||||
interface) individually configured ipppd.
|
||||
|
||||
--
|
||||
|
||||
Q09: When I start the ipppd I only get error messages
|
||||
from the i4l driver ..
|
||||
|
||||
A: When starting, the ipppd calls functions which may
|
||||
trigger a network packet. (e.g gethostbyname()).
|
||||
Without the ipppd (at this moment, it is not
|
||||
fully started) we can't handle this network request.
|
||||
Try to configure hostnames necessary for the ipppd
|
||||
in your local /etc/hosts file or in a way, that
|
||||
your system can resolve it without using an
|
||||
isdn/ippp network-interface.
|
||||
|
||||
--
|
||||
|
||||
Q10: I wanna use dynamic IP address assignment ... How
|
||||
must I configure the network device.
|
||||
|
||||
A: At least you must have a route which forwards
|
||||
a packet to the ippp network-interface to trigger
|
||||
the dial-on-demand.
|
||||
A default route to the ippp-interface will work.
|
||||
Now you must choose a dummy IP address for your
|
||||
interface.
|
||||
If for some reason you can't set the default
|
||||
route to the ippp interface, you may take any
|
||||
address of the subnet from which you expect your
|
||||
dynamic IP number and set a 'network route' for
|
||||
this subnet to the ippp interface.
|
||||
To allow overriding of the dummy address you
|
||||
must call the ipppd with the 'ipcp-accept-local' option.
|
||||
|
||||
A: You must know, how the ipppd gets the addresses it wanna
|
||||
configure. If you don't give any option, the ipppd
|
||||
tries to negotiate the local host address!
|
||||
With the option 'noipdefault' it requests an address
|
||||
from the remote machine. With 'useifip' it gets the
|
||||
addresses from the net interface. Or you set the address
|
||||
on the option line with the <a.b.c.d:e.f.g.h> option.
|
||||
Note: the IP address of the remote machine must be configured
|
||||
locally or the remote machine must send it in an IPCP request.
|
||||
If your side doesn't know the IP address after negotiation, it
|
||||
closes the connection!
|
||||
You must allow overriding of address with the 'ipcp-accept-*'
|
||||
options, if you have set your own or the remote address
|
||||
explicitly.
|
||||
|
||||
A: Maybe you try these options .. e.g:
|
||||
|
||||
/sbin/ipppd :$REMOTE noipdefault /dev/ippp0
|
||||
|
||||
where REMOTE must be the address of the remote machine (the
|
||||
machine, which gives you your address)
|
||||
|
||||
--
|
||||
|
||||
Q11: I can't connect. How can I check where the problem is.
|
||||
|
||||
A: A good help log is the debug output from the ipppd...
|
||||
Check whether you can find there:
|
||||
- only a few LCP-conf-req SENT messages (less then 10)
|
||||
and then a Term-REQ:
|
||||
-> check whether your ISDN card is well configured
|
||||
it seems, that your machine doesn't dial
|
||||
(IRQ,IO,Proto, etc problems)
|
||||
Configure your ISDN card to print debug messages and
|
||||
check the /dev/isdnctrl output next time. There
|
||||
you can see, whether there is activity on the card/line.
|
||||
- there are at least a few RECV messages in the log:
|
||||
-> fine: your card is dialing and your remote machine
|
||||
tries to talk with you. Maybe only a missing
|
||||
authentication. Check your ipppd configuration again.
|
||||
- the ipppd exits for some reason:
|
||||
-> not good ... check /var/adm/syslog and /var/adm/daemon.
|
||||
Could be a bug in the ipppd.
|
||||
|
||||
--
|
||||
|
||||
Q12: How can I reduce login delay?
|
||||
|
||||
A: Log a login session ('debug' log) and check which options
|
||||
your remote side rejects. Next time configure your ipppd
|
||||
to not negotiate these options. Another 'side effect' is, that
|
||||
this increases redundancy. (e.g your remote side is buggy and
|
||||
rejects options in a wrong way).
|
||||
|
||||
|
||||
|
|
@ -23,8 +23,8 @@ running, the suggested command should tell you.
|
|||
|
||||
Again, keep in mind that this list assumes you are already functionally
|
||||
running a Linux kernel. Also, not all tools are necessary on all
|
||||
systems; obviously, if you don't have any ISDN hardware, for example,
|
||||
you probably needn't concern yourself with isdn4k-utils.
|
||||
systems; obviously, if you don't have any PC Card hardware, for example,
|
||||
you probably needn't concern yourself with pcmciautils.
|
||||
|
||||
====================== =============== ========================================
|
||||
Program Minimal version Command to check the version
|
||||
|
@ -45,7 +45,6 @@ btrfs-progs 0.18 btrfsck
|
|||
pcmciautils 004 pccardctl -V
|
||||
quota-tools 3.09 quota -V
|
||||
PPP 2.4.0 pppd --version
|
||||
isdn4k-utils 3.1pre1 isdnctrl 2>&1|grep version
|
||||
nfs-utils 1.0.5 showmount --version
|
||||
procps 3.2.0 ps --version
|
||||
oprofile 0.9 oprofiled --version
|
||||
|
@ -279,12 +278,6 @@ which can be made by::
|
|||
|
||||
as root.
|
||||
|
||||
Isdn4k-utils
|
||||
------------
|
||||
|
||||
Due to changes in the length of the phone number field, isdn4k-utils
|
||||
needs to be recompiled or (preferably) upgraded.
|
||||
|
||||
NFS-utils
|
||||
---------
|
||||
|
||||
|
@ -448,11 +441,6 @@ PPP
|
|||
|
||||
- <ftp://ftp.samba.org/pub/ppp/>
|
||||
|
||||
Isdn4k-utils
|
||||
------------
|
||||
|
||||
- <ftp://ftp.isdn4linux.de/pub/isdn4linux/utils/>
|
||||
|
||||
NFS-utils
|
||||
---------
|
||||
|
||||
|
|
22
MAINTAINERS
22
MAINTAINERS
|
@ -6679,9 +6679,7 @@ M: Paul Bolle <pebolle@tiscali.nl>
|
|||
L: gigaset307x-common@lists.sourceforge.net
|
||||
W: http://gigaset307x.sourceforge.net/
|
||||
S: Odd Fixes
|
||||
F: Documentation/isdn/README.gigaset
|
||||
F: drivers/isdn/gigaset/
|
||||
F: include/uapi/linux/gigaset_dev.h
|
||||
F: drivers/staging/isdn/gigaset/
|
||||
|
||||
GNSS SUBSYSTEM
|
||||
M: Johan Hovold <johan@kernel.org>
|
||||
|
@ -8362,18 +8360,26 @@ S: Supported
|
|||
W: http://www.linux-iscsi.org
|
||||
F: drivers/infiniband/ulp/isert
|
||||
|
||||
ISDN SUBSYSTEM
|
||||
ISDN/mISDN SUBSYSTEM
|
||||
M: Karsten Keil <isdn@linux-pingi.de>
|
||||
L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
|
||||
L: netdev@vger.kernel.org
|
||||
W: http://www.isdn4linux.de
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kkeil/isdn-2.6.git
|
||||
S: Maintained
|
||||
F: drivers/isdn/mISDN
|
||||
F: drivers/isdn/hardware
|
||||
|
||||
ISDN/CAPI SUBSYSTEM
|
||||
M: Karsten Keil <isdn@linux-pingi.de>
|
||||
L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
|
||||
L: netdev@vger.kernel.org
|
||||
W: http://www.isdn4linux.de
|
||||
S: Odd Fixes
|
||||
F: Documentation/isdn/
|
||||
F: drivers/isdn/
|
||||
F: include/linux/isdn.h
|
||||
F: drivers/isdn/capi/
|
||||
F: drivers/staging/isdn/
|
||||
F: net/bluetooth/cmtp/
|
||||
F: include/linux/isdn/
|
||||
F: include/uapi/linux/isdn.h
|
||||
F: include/uapi/linux/isdn/
|
||||
|
||||
IT87 HARDWARE MONITORING DRIVER
|
||||
|
|
|
@ -21,59 +21,8 @@ menuconfig ISDN
|
|||
|
||||
if ISDN
|
||||
|
||||
menuconfig ISDN_I4L
|
||||
tristate "Old ISDN4Linux (deprecated)"
|
||||
depends on TTY
|
||||
---help---
|
||||
This driver allows you to use an ISDN adapter for networking
|
||||
connections and as dialin/out device. The isdn-tty's have a built
|
||||
in AT-compatible modem emulator. Network devices support autodial,
|
||||
channel-bundling, callback and caller-authentication without having
|
||||
a daemon running. A reduced T.70 protocol is supported with tty's
|
||||
suitable for German BTX. On D-Channel, the protocols EDSS1
|
||||
(Euro-ISDN) and 1TR6 (German style) are supported. See
|
||||
<file:Documentation/isdn/README> for more information.
|
||||
|
||||
ISDN support in the linux kernel is moving towards a new API,
|
||||
called CAPI (Common ISDN Application Programming Interface).
|
||||
Therefore the old ISDN4Linux layer will eventually become obsolete.
|
||||
It is still available, though, for use with adapters that are not
|
||||
supported by the new CAPI subsystem yet.
|
||||
|
||||
source "drivers/isdn/i4l/Kconfig"
|
||||
|
||||
menuconfig ISDN_CAPI
|
||||
tristate "CAPI 2.0 subsystem"
|
||||
help
|
||||
This provides CAPI (the Common ISDN Application Programming
|
||||
Interface) Version 2.0, a standard making it easy for programs to
|
||||
access ISDN hardware in a device independent way. (For details see
|
||||
<http://www.capi.org/>.) CAPI supports making and accepting voice
|
||||
and data connections, controlling call options and protocols,
|
||||
as well as ISDN supplementary services like call forwarding or
|
||||
three-party conferences (if supported by the specific hardware
|
||||
driver).
|
||||
|
||||
Select this option and the appropriate hardware driver below if
|
||||
you have an ISDN adapter supported by the CAPI subsystem.
|
||||
|
||||
if ISDN_CAPI
|
||||
|
||||
source "drivers/isdn/capi/Kconfig"
|
||||
|
||||
source "drivers/isdn/hardware/Kconfig"
|
||||
|
||||
endif # ISDN_CAPI
|
||||
|
||||
source "drivers/isdn/gigaset/Kconfig"
|
||||
|
||||
source "drivers/isdn/hysdn/Kconfig"
|
||||
|
||||
source "drivers/isdn/mISDN/Kconfig"
|
||||
|
||||
config ISDN_HDLC
|
||||
tristate
|
||||
select CRC_CCITT
|
||||
select BITREVERSE
|
||||
|
||||
endif # ISDN
|
||||
|
|
|
@ -3,12 +3,6 @@
|
|||
|
||||
# Object files in subdirectories
|
||||
|
||||
obj-$(CONFIG_ISDN_I4L) += i4l/
|
||||
obj-$(CONFIG_ISDN_CAPI) += capi/
|
||||
obj-$(CONFIG_MISDN) += mISDN/
|
||||
obj-$(CONFIG_ISDN) += hardware/
|
||||
obj-$(CONFIG_ISDN_DIVERSION) += divert/
|
||||
obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/
|
||||
obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop/
|
||||
obj-$(CONFIG_HYSDN) += hysdn/
|
||||
obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset/
|
||||
|
|
|
@ -1,4 +1,22 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menuconfig ISDN_CAPI
|
||||
tristate "CAPI 2.0 subsystem"
|
||||
help
|
||||
This provides CAPI (the Common ISDN Application Programming
|
||||
Interface) Version 2.0, a standard making it easy for programs to
|
||||
access ISDN hardware in a device independent way. (For details see
|
||||
<http://www.capi.org/>.) CAPI supports making and accepting voice
|
||||
and data connections, controlling call options and protocols,
|
||||
as well as ISDN supplementary services like call forwarding or
|
||||
three-party conferences (if supported by the specific hardware
|
||||
driver).
|
||||
|
||||
This subsystem requires a hardware specific driver.
|
||||
See CONFIG_BT_CMTP for the last remaining regular driver
|
||||
in the kernel that uses the CAPI subsystem.
|
||||
|
||||
if ISDN_CAPI
|
||||
|
||||
config CAPI_TRACE
|
||||
bool "CAPI trace support"
|
||||
default y
|
||||
|
@ -27,15 +45,6 @@ config ISDN_CAPI_MIDDLEWARE
|
|||
device. If you want to use pppd with pppdcapiplugin to dial up to
|
||||
your ISP, say Y here.
|
||||
|
||||
config ISDN_CAPI_CAPIDRV
|
||||
tristate "CAPI2.0 capidrv interface support"
|
||||
depends on ISDN_I4L
|
||||
help
|
||||
This option provides the glue code to hook up CAPI driven cards to
|
||||
the legacy isdn4linux link layer. If you have a card which is
|
||||
supported by a CAPI driver, but still want to use old features like
|
||||
ippp interfaces or ttyI emulation, say Y/M here.
|
||||
|
||||
config ISDN_CAPI_CAPIDRV_VERBOSE
|
||||
bool "Verbose reason code reporting"
|
||||
depends on ISDN_CAPI_CAPIDRV
|
||||
|
@ -43,3 +52,5 @@ config ISDN_CAPI_CAPIDRV_VERBOSE
|
|||
If you say Y here, the capidrv interface will give verbose reasons
|
||||
for disconnecting. This will increase the size of the kernel by 7 KB.
|
||||
If unsure, say N.
|
||||
|
||||
endif
|
||||
|
|
|
@ -13,3 +13,5 @@ obj-$(CONFIG_ISDN_CAPI_CAPIDRV) += capidrv.o
|
|||
|
||||
kernelcapi-y := kcapi.o capiutil.o capilib.o
|
||||
kernelcapi-$(CONFIG_PROC_FS) += kcapi_proc.o
|
||||
|
||||
ccflags-y += -I$(srctree)/$(src)/../include -I$(srctree)/$(src)/../include/uapi
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,140 +0,0 @@
|
|||
/* $Id: capidrv.h,v 1.2.8.2 2001/09/23 22:24:33 kai Exp $
|
||||
*
|
||||
* ISDN4Linux Driver, using capi20 interface (kernelcapi)
|
||||
*
|
||||
* Copyright 1997 by Carsten Paeth <calle@calle.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __CAPIDRV_H__
|
||||
#define __CAPIDRV_H__
|
||||
|
||||
/*
|
||||
* LISTEN state machine
|
||||
*/
|
||||
#define ST_LISTEN_NONE 0 /* L-0 */
|
||||
#define ST_LISTEN_WAIT_CONF 1 /* L-0.1 */
|
||||
#define ST_LISTEN_ACTIVE 2 /* L-1 */
|
||||
#define ST_LISTEN_ACTIVE_WAIT_CONF 3 /* L-1.1 */
|
||||
|
||||
|
||||
#define EV_LISTEN_REQ 1 /* L-0 -> L-0.1
|
||||
L-1 -> L-1.1 */
|
||||
#define EV_LISTEN_CONF_ERROR 2 /* L-0.1 -> L-0
|
||||
L-1.1 -> L-1 */
|
||||
#define EV_LISTEN_CONF_EMPTY 3 /* L-0.1 -> L-0
|
||||
L-1.1 -> L-0 */
|
||||
#define EV_LISTEN_CONF_OK 4 /* L-0.1 -> L-1
|
||||
L-1.1 -> L.1 */
|
||||
|
||||
/*
|
||||
* per plci state machine
|
||||
*/
|
||||
#define ST_PLCI_NONE 0 /* P-0 */
|
||||
#define ST_PLCI_OUTGOING 1 /* P-0.1 */
|
||||
#define ST_PLCI_ALLOCATED 2 /* P-1 */
|
||||
#define ST_PLCI_ACTIVE 3 /* P-ACT */
|
||||
#define ST_PLCI_INCOMING 4 /* P-2 */
|
||||
#define ST_PLCI_FACILITY_IND 5 /* P-3 */
|
||||
#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_REJECT 7 /* P-2 -> 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-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-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
|
||||
*/
|
||||
|
||||
#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
|
||||
*/
|
||||
#define ST_NCCI_PREVIOUS -1
|
||||
#define ST_NCCI_NONE 0 /* N-0 */
|
||||
#define ST_NCCI_OUTGOING 1 /* N-0.1 */
|
||||
#define ST_NCCI_INCOMING 2 /* N-1 */
|
||||
#define ST_NCCI_ALLOCATED 3 /* N-2 */
|
||||
#define ST_NCCI_ACTIVE 4 /* N-ACT */
|
||||
#define ST_NCCI_RESETING 5 /* N-3 */
|
||||
#define ST_NCCI_DISCONNECTING 6 /* N-4 */
|
||||
#define ST_NCCI_DISCONNECTED 7 /* N-5 */
|
||||
|
||||
#define EV_NCCI_CONNECT_B3_REQ 1 /* N-0 -> N-0.1 */
|
||||
#define EV_NCCI_CONNECT_B3_IND 2 /* N-0 -> N.1 */
|
||||
#define EV_NCCI_CONNECT_B3_CONF_OK 3 /* N-0.1 -> N.2 */
|
||||
#define EV_NCCI_CONNECT_B3_CONF_ERROR 4 /* N-0.1 -> N.0 */
|
||||
#define EV_NCCI_CONNECT_B3_REJECT 5 /* N-1 -> N-4 */
|
||||
#define EV_NCCI_CONNECT_B3_RESP 6 /* N-1 -> N-2 */
|
||||
#define EV_NCCI_CONNECT_B3_ACTIVE_IND 7 /* N-2 -> N-ACT */
|
||||
#define EV_NCCI_RESET_B3_REQ 8 /* N-ACT -> N-3 */
|
||||
#define EV_NCCI_RESET_B3_IND 9 /* N-3 -> N-ACT */
|
||||
#define EV_NCCI_DISCONNECT_B3_IND 10 /* N-4 -> N.5 */
|
||||
#define EV_NCCI_DISCONNECT_B3_CONF_ERROR 11 /* N-4 -> previous */
|
||||
#define EV_NCCI_DISCONNECT_B3_REQ 12 /* N-1 -> N-4
|
||||
N-2 -> N-4
|
||||
N-3 -> N-4
|
||||
N-ACT -> N-4 */
|
||||
#define EV_NCCI_DISCONNECT_B3_RESP 13 /* N-5 -> N-0 */
|
||||
|
||||
#endif /* __CAPIDRV_H__ */
|
|
@ -1,10 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Makefile for the dss1_divert ISDN module
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_ISDN_DIVERSION) += dss1_divert.o
|
||||
|
||||
# Multipart objects.
|
||||
|
||||
dss1_divert-y := isdn_divert.o divert_procfs.o divert_init.o
|
|
@ -1,82 +0,0 @@
|
|||
/* $Id divert_init.c,v 1.5.6.2 2001/01/24 22:18:17 kai Exp $
|
||||
*
|
||||
* Module init for DSS1 diversion services for i4l.
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@isdn4linux.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "isdn_divert.h"
|
||||
|
||||
MODULE_DESCRIPTION("ISDN4Linux: Call diversion support");
|
||||
MODULE_AUTHOR("Werner Cornelius");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/****************************************/
|
||||
/* structure containing interface to hl */
|
||||
/****************************************/
|
||||
isdn_divert_if divert_if = {
|
||||
DIVERT_IF_MAGIC, /* magic value */
|
||||
DIVERT_CMD_REG, /* register cmd */
|
||||
ll_callback, /* callback routine from ll */
|
||||
NULL, /* command still not specified */
|
||||
NULL, /* drv_to_name */
|
||||
NULL, /* name_to_drv */
|
||||
};
|
||||
|
||||
/*************************/
|
||||
/* Module interface code */
|
||||
/* no cmd line parms */
|
||||
/*************************/
|
||||
static int __init divert_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (divert_dev_init()) {
|
||||
printk(KERN_WARNING "dss1_divert: cannot install device, not loaded\n");
|
||||
return (-EIO);
|
||||
}
|
||||
if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) {
|
||||
divert_dev_deinit();
|
||||
printk(KERN_WARNING "dss1_divert: error %d registering module, not loaded\n", i);
|
||||
return (-EIO);
|
||||
}
|
||||
printk(KERN_INFO "dss1_divert module successfully installed\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**********************/
|
||||
/* Module deinit code */
|
||||
/**********************/
|
||||
static void __exit divert_exit(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
divert_if.cmd = DIVERT_CMD_REL; /* release */
|
||||
if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR) {
|
||||
printk(KERN_WARNING "dss1_divert: error %d releasing module\n", i);
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
return;
|
||||
}
|
||||
if (divert_dev_deinit()) {
|
||||
printk(KERN_WARNING "dss1_divert: device busy, remove cancelled\n");
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
deleterule(-1); /* delete all rules and free mem */
|
||||
deleteprocs();
|
||||
printk(KERN_INFO "dss1_divert module successfully removed \n");
|
||||
}
|
||||
|
||||
module_init(divert_init);
|
||||
module_exit(divert_exit);
|
|
@ -1,336 +0,0 @@
|
|||
/* $Id: divert_procfs.c,v 1.11.6.2 2001/09/23 22:24:36 kai Exp $
|
||||
*
|
||||
* Filesystem handling for the diversion supplementary services.
|
||||
*
|
||||
* Copyright 1998 by Werner Cornelius (werner@isdn4linux.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#ifdef CONFIG_PROC_FS
|
||||
#include <linux/proc_fs.h>
|
||||
#else
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
#include <linux/sched.h>
|
||||
#include <linux/isdnif.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "isdn_divert.h"
|
||||
|
||||
|
||||
/*********************************/
|
||||
/* Variables for interface queue */
|
||||
/*********************************/
|
||||
ulong if_used = 0; /* number of interface users */
|
||||
static DEFINE_MUTEX(isdn_divert_mutex);
|
||||
static struct divert_info *divert_info_head = NULL; /* head of queue */
|
||||
static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */
|
||||
static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */
|
||||
static wait_queue_head_t rd_queue;
|
||||
|
||||
/*********************************/
|
||||
/* put an info buffer into queue */
|
||||
/*********************************/
|
||||
void
|
||||
put_info_buffer(char *cp)
|
||||
{
|
||||
struct divert_info *ib;
|
||||
unsigned long flags;
|
||||
|
||||
if (if_used <= 0)
|
||||
return;
|
||||
if (!cp)
|
||||
return;
|
||||
if (!*cp)
|
||||
return;
|
||||
if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
|
||||
return; /* no memory */
|
||||
strcpy(ib->info_start, cp); /* set output string */
|
||||
ib->next = NULL;
|
||||
spin_lock_irqsave(&divert_info_lock, flags);
|
||||
ib->usage_cnt = if_used;
|
||||
if (!divert_info_head)
|
||||
divert_info_head = ib; /* new head */
|
||||
else
|
||||
divert_info_tail->next = ib; /* follows existing messages */
|
||||
divert_info_tail = ib; /* new tail */
|
||||
|
||||
/* delete old entrys */
|
||||
while (divert_info_head->next) {
|
||||
if ((divert_info_head->usage_cnt <= 0) &&
|
||||
(divert_info_head->next->usage_cnt <= 0)) {
|
||||
ib = divert_info_head;
|
||||
divert_info_head = divert_info_head->next;
|
||||
kfree(ib);
|
||||
} else
|
||||
break;
|
||||
} /* divert_info_head->next */
|
||||
spin_unlock_irqrestore(&divert_info_lock, flags);
|
||||
wake_up_interruptible(&(rd_queue));
|
||||
} /* put_info_buffer */
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
/**********************************/
|
||||
/* deflection device read routine */
|
||||
/**********************************/
|
||||
static ssize_t
|
||||
isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t *off)
|
||||
{
|
||||
struct divert_info *inf;
|
||||
int len;
|
||||
|
||||
if (!(inf = *((struct divert_info **) file->private_data))) {
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
wait_event_interruptible(rd_queue, (inf =
|
||||
*((struct divert_info **) file->private_data)));
|
||||
}
|
||||
if (!inf)
|
||||
return (0);
|
||||
|
||||
inf->usage_cnt--; /* new usage count */
|
||||
file->private_data = &inf->next; /* next structure */
|
||||
if ((len = strlen(inf->info_start)) <= count) {
|
||||
if (copy_to_user(buf, inf->info_start, len))
|
||||
return -EFAULT;
|
||||
*off += len;
|
||||
return (len);
|
||||
}
|
||||
return (0);
|
||||
} /* isdn_divert_read */
|
||||
|
||||
/**********************************/
|
||||
/* deflection device write routine */
|
||||
/**********************************/
|
||||
static ssize_t
|
||||
isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
|
||||
{
|
||||
return (-ENODEV);
|
||||
} /* isdn_divert_write */
|
||||
|
||||
|
||||
/***************************************/
|
||||
/* select routines for various kernels */
|
||||
/***************************************/
|
||||
static __poll_t
|
||||
isdn_divert_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
__poll_t mask = 0;
|
||||
|
||||
poll_wait(file, &(rd_queue), wait);
|
||||
/* mask = EPOLLOUT | EPOLLWRNORM; */
|
||||
if (*((struct divert_info **) file->private_data)) {
|
||||
mask |= EPOLLIN | EPOLLRDNORM;
|
||||
}
|
||||
return mask;
|
||||
} /* isdn_divert_poll */
|
||||
|
||||
/****************/
|
||||
/* Open routine */
|
||||
/****************/
|
||||
static int
|
||||
isdn_divert_open(struct inode *ino, struct file *filep)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&divert_info_lock, flags);
|
||||
if_used++;
|
||||
if (divert_info_head)
|
||||
filep->private_data = &(divert_info_tail->next);
|
||||
else
|
||||
filep->private_data = &divert_info_head;
|
||||
spin_unlock_irqrestore(&divert_info_lock, flags);
|
||||
/* start_divert(); */
|
||||
return nonseekable_open(ino, filep);
|
||||
} /* isdn_divert_open */
|
||||
|
||||
/*******************/
|
||||
/* close routine */
|
||||
/*******************/
|
||||
static int
|
||||
isdn_divert_close(struct inode *ino, struct file *filep)
|
||||
{
|
||||
struct divert_info *inf;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&divert_info_lock, flags);
|
||||
if_used--;
|
||||
inf = *((struct divert_info **) filep->private_data);
|
||||
while (inf) {
|
||||
inf->usage_cnt--;
|
||||
inf = inf->next;
|
||||
}
|
||||
if (if_used <= 0)
|
||||
while (divert_info_head) {
|
||||
inf = divert_info_head;
|
||||
divert_info_head = divert_info_head->next;
|
||||
kfree(inf);
|
||||
}
|
||||
spin_unlock_irqrestore(&divert_info_lock, flags);
|
||||
return (0);
|
||||
} /* isdn_divert_close */
|
||||
|
||||
/*********/
|
||||
/* IOCTL */
|
||||
/*********/
|
||||
static int isdn_divert_ioctl_unlocked(struct file *file, uint cmd, ulong arg)
|
||||
{
|
||||
divert_ioctl dioctl;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
divert_rule *rulep;
|
||||
char *cp;
|
||||
|
||||
if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (cmd) {
|
||||
case IIOCGETVER:
|
||||
dioctl.drv_version = DIVERT_IIOC_VERSION; /* set version */
|
||||
break;
|
||||
|
||||
case IIOCGETDRV:
|
||||
if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)
|
||||
return (-EINVAL);
|
||||
break;
|
||||
|
||||
case IIOCGETNAM:
|
||||
cp = divert_if.drv_to_name(dioctl.getid.drvid);
|
||||
if (!cp)
|
||||
return (-EINVAL);
|
||||
if (!*cp)
|
||||
return (-EINVAL);
|
||||
strcpy(dioctl.getid.drvnam, cp);
|
||||
break;
|
||||
|
||||
case IIOCGETRULE:
|
||||
if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
|
||||
return (-EINVAL);
|
||||
dioctl.getsetrule.rule = *rulep; /* copy data */
|
||||
break;
|
||||
|
||||
case IIOCMODRULE:
|
||||
if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
|
||||
return (-EINVAL);
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
*rulep = dioctl.getsetrule.rule; /* copy data */
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
return (0); /* no copy required */
|
||||
break;
|
||||
|
||||
case IIOCINSRULE:
|
||||
return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule));
|
||||
break;
|
||||
|
||||
case IIOCDELRULE:
|
||||
return (deleterule(dioctl.getsetrule.ruleidx));
|
||||
break;
|
||||
|
||||
case IIOCDODFACT:
|
||||
return (deflect_extern_action(dioctl.fwd_ctrl.subcmd,
|
||||
dioctl.fwd_ctrl.callid,
|
||||
dioctl.fwd_ctrl.to_nr));
|
||||
|
||||
case IIOCDOCFACT:
|
||||
case IIOCDOCFDIS:
|
||||
case IIOCDOCFINT:
|
||||
if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid))
|
||||
return (-EINVAL); /* invalid driver */
|
||||
if (strnlen(dioctl.cf_ctrl.msn, sizeof(dioctl.cf_ctrl.msn)) ==
|
||||
sizeof(dioctl.cf_ctrl.msn))
|
||||
return -EINVAL;
|
||||
if (strnlen(dioctl.cf_ctrl.fwd_nr, sizeof(dioctl.cf_ctrl.fwd_nr)) ==
|
||||
sizeof(dioctl.cf_ctrl.fwd_nr))
|
||||
return -EINVAL;
|
||||
if ((i = cf_command(dioctl.cf_ctrl.drvid,
|
||||
(cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2,
|
||||
dioctl.cf_ctrl.cfproc,
|
||||
dioctl.cf_ctrl.msn,
|
||||
dioctl.cf_ctrl.service,
|
||||
dioctl.cf_ctrl.fwd_nr,
|
||||
&dioctl.cf_ctrl.procid)))
|
||||
return (i);
|
||||
break;
|
||||
|
||||
default:
|
||||
return (-EINVAL);
|
||||
} /* switch cmd */
|
||||
return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0;
|
||||
} /* isdn_divert_ioctl */
|
||||
|
||||
static long isdn_divert_ioctl(struct file *file, uint cmd, ulong arg)
|
||||
{
|
||||
long ret;
|
||||
|
||||
mutex_lock(&isdn_divert_mutex);
|
||||
ret = isdn_divert_ioctl_unlocked(file, cmd, arg);
|
||||
mutex_unlock(&isdn_divert_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations isdn_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = isdn_divert_read,
|
||||
.write = isdn_divert_write,
|
||||
.poll = isdn_divert_poll,
|
||||
.unlocked_ioctl = isdn_divert_ioctl,
|
||||
.open = isdn_divert_open,
|
||||
.release = isdn_divert_close,
|
||||
};
|
||||
|
||||
/****************************/
|
||||
/* isdn subdir in /proc/net */
|
||||
/****************************/
|
||||
static struct proc_dir_entry *isdn_proc_entry = NULL;
|
||||
static struct proc_dir_entry *isdn_divert_entry = NULL;
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
/***************************************************************************/
|
||||
/* divert_dev_init must be called before the proc filesystem may be used */
|
||||
/***************************************************************************/
|
||||
int
|
||||
divert_dev_init(void)
|
||||
{
|
||||
|
||||
init_waitqueue_head(&rd_queue);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
isdn_proc_entry = proc_mkdir("isdn", init_net.proc_net);
|
||||
if (!isdn_proc_entry)
|
||||
return (-1);
|
||||
isdn_divert_entry = proc_create("divert", S_IFREG | S_IRUGO,
|
||||
isdn_proc_entry, &isdn_fops);
|
||||
if (!isdn_divert_entry) {
|
||||
remove_proc_entry("isdn", init_net.proc_net);
|
||||
return (-1);
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
return (0);
|
||||
} /* divert_dev_init */
|
||||
|
||||
/***************************************************************************/
|
||||
/* divert_dev_deinit must be called before leaving isdn when included as */
|
||||
/* a module. */
|
||||
/***************************************************************************/
|
||||
int
|
||||
divert_dev_deinit(void)
|
||||
{
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
remove_proc_entry("divert", isdn_proc_entry);
|
||||
remove_proc_entry("isdn", init_net.proc_net);
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
return (0);
|
||||
} /* divert_dev_deinit */
|
|
@ -1,846 +0,0 @@
|
|||
/* $Id: isdn_divert.c,v 1.6.6.3 2001/09/23 22:24:36 kai Exp $
|
||||
*
|
||||
* DSS1 main diversion supplementary handling for i4l.
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@isdn4linux.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
#include "isdn_divert.h"
|
||||
|
||||
/**********************************/
|
||||
/* structure keeping calling info */
|
||||
/**********************************/
|
||||
struct call_struc {
|
||||
isdn_ctrl ics; /* delivered setup + driver parameters */
|
||||
ulong divert_id; /* Id delivered to user */
|
||||
unsigned char akt_state; /* actual state */
|
||||
char deflect_dest[35]; /* deflection destination */
|
||||
struct timer_list timer; /* timer control structure */
|
||||
char info[90]; /* device info output */
|
||||
struct call_struc *next; /* pointer to next entry */
|
||||
struct call_struc *prev;
|
||||
};
|
||||
|
||||
|
||||
/********************************************/
|
||||
/* structure keeping deflection table entry */
|
||||
/********************************************/
|
||||
struct deflect_struc {
|
||||
struct deflect_struc *next, *prev;
|
||||
divert_rule rule; /* used rule */
|
||||
};
|
||||
|
||||
|
||||
/*****************************************/
|
||||
/* variables for main diversion services */
|
||||
/*****************************************/
|
||||
/* diversion/deflection processes */
|
||||
static struct call_struc *divert_head = NULL; /* head of remembered entrys */
|
||||
static ulong next_id = 1; /* next info id */
|
||||
static struct deflect_struc *table_head = NULL;
|
||||
static struct deflect_struc *table_tail = NULL;
|
||||
static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */
|
||||
|
||||
DEFINE_SPINLOCK(divert_lock);
|
||||
|
||||
/***************************/
|
||||
/* timer callback function */
|
||||
/***************************/
|
||||
static void deflect_timer_expire(struct timer_list *t)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct call_struc *cs = from_timer(cs, t, timer);
|
||||
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
del_timer(&cs->timer); /* delete active timer */
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
|
||||
switch (cs->akt_state) {
|
||||
case DEFLECT_PROCEED:
|
||||
cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */
|
||||
divert_if.ll_cmd(&cs->ics);
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
|
||||
cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
|
||||
add_timer(&cs->timer);
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
break;
|
||||
|
||||
case DEFLECT_ALERT:
|
||||
cs->ics.command = ISDN_CMD_REDIR; /* protocol */
|
||||
strlcpy(cs->ics.parm.setup.phone, cs->deflect_dest, sizeof(cs->ics.parm.setup.phone));
|
||||
strcpy(cs->ics.parm.setup.eazmsn, "Testtext delayed");
|
||||
divert_if.ll_cmd(&cs->ics);
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
|
||||
cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
|
||||
add_timer(&cs->timer);
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
break;
|
||||
|
||||
case DEFLECT_AUTODEL:
|
||||
default:
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
if (cs->prev)
|
||||
cs->prev->next = cs->next; /* forward link */
|
||||
else
|
||||
divert_head = cs->next;
|
||||
if (cs->next)
|
||||
cs->next->prev = cs->prev; /* back link */
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
kfree(cs);
|
||||
return;
|
||||
|
||||
} /* switch */
|
||||
} /* deflect_timer_func */
|
||||
|
||||
|
||||
/*****************************************/
|
||||
/* handle call forwarding de/activations */
|
||||
/* 0 = deact, 1 = act, 2 = interrogate */
|
||||
/*****************************************/
|
||||
int cf_command(int drvid, int mode,
|
||||
u_char proc, char *msn,
|
||||
u_char service, char *fwd_nr, ulong *procid)
|
||||
{
|
||||
unsigned long flags;
|
||||
int retval, msnlen;
|
||||
int fwd_len;
|
||||
char *p, *ielenp, tmp[60];
|
||||
struct call_struc *cs;
|
||||
|
||||
if (strchr(msn, '.')) return (-EINVAL); /* subaddress not allowed in msn */
|
||||
if ((proc & 0x7F) > 2) return (-EINVAL);
|
||||
proc &= 3;
|
||||
p = tmp;
|
||||
*p++ = 0x30; /* enumeration */
|
||||
ielenp = p++; /* remember total length position */
|
||||
*p++ = 0xa; /* proc tag */
|
||||
*p++ = 1; /* length */
|
||||
*p++ = proc & 0x7F; /* procedure to de/activate/interrogate */
|
||||
*p++ = 0xa; /* service tag */
|
||||
*p++ = 1; /* length */
|
||||
*p++ = service; /* service to handle */
|
||||
|
||||
if (mode == 1) {
|
||||
if (!*fwd_nr) return (-EINVAL); /* destination missing */
|
||||
if (strchr(fwd_nr, '.')) return (-EINVAL); /* subaddress not allowed */
|
||||
fwd_len = strlen(fwd_nr);
|
||||
*p++ = 0x30; /* number enumeration */
|
||||
*p++ = fwd_len + 2; /* complete forward to len */
|
||||
*p++ = 0x80; /* fwd to nr */
|
||||
*p++ = fwd_len; /* length of number */
|
||||
strcpy(p, fwd_nr); /* copy number */
|
||||
p += fwd_len; /* pointer beyond fwd */
|
||||
} /* activate */
|
||||
|
||||
msnlen = strlen(msn);
|
||||
*p++ = 0x80; /* msn number */
|
||||
if (msnlen > 1) {
|
||||
*p++ = msnlen; /* length */
|
||||
strcpy(p, msn);
|
||||
p += msnlen;
|
||||
} else
|
||||
*p++ = 0;
|
||||
|
||||
*ielenp = p - ielenp - 1; /* set total IE length */
|
||||
|
||||
/* allocate mem for information struct */
|
||||
if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
|
||||
return (-ENOMEM); /* no memory */
|
||||
timer_setup(&cs->timer, deflect_timer_expire, 0);
|
||||
cs->info[0] = '\0';
|
||||
cs->ics.driver = drvid;
|
||||
cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */
|
||||
cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */
|
||||
cs->ics.parm.dss1_io.proc = (mode == 1) ? 7 : (mode == 2) ? 11 : 8; /* operation */
|
||||
cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */
|
||||
cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */
|
||||
cs->ics.parm.dss1_io.data = tmp; /* start of buffer */
|
||||
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
*procid = cs->ics.parm.dss1_io.ll_id;
|
||||
|
||||
sprintf(cs->info, "%d 0x%lx %s%s 0 %s %02x %d%s%s\n",
|
||||
(!mode) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT,
|
||||
cs->ics.parm.dss1_io.ll_id,
|
||||
(mode != 2) ? "" : "0 ",
|
||||
divert_if.drv_to_name(cs->ics.driver),
|
||||
msn,
|
||||
service & 0xFF,
|
||||
proc,
|
||||
(mode != 1) ? "" : " 0 ",
|
||||
(mode != 1) ? "" : fwd_nr);
|
||||
|
||||
retval = divert_if.ll_cmd(&cs->ics); /* execute command */
|
||||
|
||||
if (!retval) {
|
||||
cs->prev = NULL;
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->next = divert_head;
|
||||
divert_head = cs;
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
} else
|
||||
kfree(cs);
|
||||
return (retval);
|
||||
} /* cf_command */
|
||||
|
||||
|
||||
/****************************************/
|
||||
/* handle a external deflection command */
|
||||
/****************************************/
|
||||
int deflect_extern_action(u_char cmd, ulong callid, char *to_nr)
|
||||
{
|
||||
struct call_struc *cs;
|
||||
isdn_ctrl ic;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if ((cmd & 0x7F) > 2) return (-EINVAL); /* invalid command */
|
||||
cs = divert_head; /* start of parameter list */
|
||||
while (cs) {
|
||||
if (cs->divert_id == callid) break; /* found */
|
||||
cs = cs->next;
|
||||
} /* search entry */
|
||||
if (!cs) return (-EINVAL); /* invalid callid */
|
||||
|
||||
ic.driver = cs->ics.driver;
|
||||
ic.arg = cs->ics.arg;
|
||||
i = -EINVAL;
|
||||
if (cs->akt_state == DEFLECT_AUTODEL) return (i); /* no valid call */
|
||||
switch (cmd & 0x7F) {
|
||||
case 0: /* hangup */
|
||||
del_timer(&cs->timer);
|
||||
ic.command = ISDN_CMD_HANGUP;
|
||||
i = divert_if.ll_cmd(&ic);
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
|
||||
cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
|
||||
add_timer(&cs->timer);
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
break;
|
||||
|
||||
case 1: /* alert */
|
||||
if (cs->akt_state == DEFLECT_ALERT) return (0);
|
||||
cmd &= 0x7F; /* never wait */
|
||||
del_timer(&cs->timer);
|
||||
ic.command = ISDN_CMD_ALERT;
|
||||
if ((i = divert_if.ll_cmd(&ic))) {
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
|
||||
cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
|
||||
add_timer(&cs->timer);
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
} else
|
||||
cs->akt_state = DEFLECT_ALERT;
|
||||
break;
|
||||
|
||||
case 2: /* redir */
|
||||
del_timer(&cs->timer);
|
||||
strlcpy(cs->ics.parm.setup.phone, to_nr, sizeof(cs->ics.parm.setup.phone));
|
||||
strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual");
|
||||
ic.command = ISDN_CMD_REDIR;
|
||||
if ((i = divert_if.ll_cmd(&ic))) {
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
|
||||
cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
|
||||
add_timer(&cs->timer);
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
} else
|
||||
cs->akt_state = DEFLECT_ALERT;
|
||||
break;
|
||||
|
||||
} /* switch */
|
||||
return (i);
|
||||
} /* deflect_extern_action */
|
||||
|
||||
/********************************/
|
||||
/* insert a new rule before idx */
|
||||
/********************************/
|
||||
int insertrule(int idx, divert_rule *newrule)
|
||||
{
|
||||
struct deflect_struc *ds, *ds1 = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
if (!(ds = kmalloc(sizeof(struct deflect_struc), GFP_KERNEL)))
|
||||
return (-ENOMEM); /* no memory */
|
||||
|
||||
ds->rule = *newrule; /* set rule */
|
||||
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
|
||||
if (idx >= 0) {
|
||||
ds1 = table_head;
|
||||
while ((ds1) && (idx > 0))
|
||||
{ idx--;
|
||||
ds1 = ds1->next;
|
||||
}
|
||||
if (!ds1) idx = -1;
|
||||
}
|
||||
|
||||
if (idx < 0) {
|
||||
ds->prev = table_tail; /* previous entry */
|
||||
ds->next = NULL; /* end of chain */
|
||||
if (ds->prev)
|
||||
ds->prev->next = ds; /* last forward */
|
||||
else
|
||||
table_head = ds; /* is first entry */
|
||||
table_tail = ds; /* end of queue */
|
||||
} else {
|
||||
ds->next = ds1; /* next entry */
|
||||
ds->prev = ds1->prev; /* prev entry */
|
||||
ds1->prev = ds; /* backward chain old element */
|
||||
if (!ds->prev)
|
||||
table_head = ds; /* first element */
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
return (0);
|
||||
} /* insertrule */
|
||||
|
||||
/***********************************/
|
||||
/* delete the rule at position idx */
|
||||
/***********************************/
|
||||
int deleterule(int idx)
|
||||
{
|
||||
struct deflect_struc *ds, *ds1;
|
||||
unsigned long flags;
|
||||
|
||||
if (idx < 0) {
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
ds = table_head;
|
||||
table_head = NULL;
|
||||
table_tail = NULL;
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
while (ds) {
|
||||
ds1 = ds;
|
||||
ds = ds->next;
|
||||
kfree(ds1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
ds = table_head;
|
||||
|
||||
while ((ds) && (idx > 0)) {
|
||||
idx--;
|
||||
ds = ds->next;
|
||||
}
|
||||
|
||||
if (!ds) {
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
return (-EINVAL);
|
||||
}
|
||||
|
||||
if (ds->next)
|
||||
ds->next->prev = ds->prev; /* backward chain */
|
||||
else
|
||||
table_tail = ds->prev; /* end of chain */
|
||||
|
||||
if (ds->prev)
|
||||
ds->prev->next = ds->next; /* forward chain */
|
||||
else
|
||||
table_head = ds->next; /* start of chain */
|
||||
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
kfree(ds);
|
||||
return (0);
|
||||
} /* deleterule */
|
||||
|
||||
/*******************************************/
|
||||
/* get a pointer to a specific rule number */
|
||||
/*******************************************/
|
||||
divert_rule *getruleptr(int idx)
|
||||
{
|
||||
struct deflect_struc *ds = table_head;
|
||||
|
||||
if (idx < 0) return (NULL);
|
||||
while ((ds) && (idx >= 0)) {
|
||||
if (!(idx--)) {
|
||||
return (&ds->rule);
|
||||
break;
|
||||
}
|
||||
ds = ds->next;
|
||||
}
|
||||
return (NULL);
|
||||
} /* getruleptr */
|
||||
|
||||
/*************************************************/
|
||||
/* called from common module on an incoming call */
|
||||
/*************************************************/
|
||||
static int isdn_divert_icall(isdn_ctrl *ic)
|
||||
{
|
||||
int retval = 0;
|
||||
unsigned long flags;
|
||||
struct call_struc *cs = NULL;
|
||||
struct deflect_struc *dv;
|
||||
char *p, *p1;
|
||||
u_char accept;
|
||||
|
||||
/* first check the internal deflection table */
|
||||
for (dv = table_head; dv; dv = dv->next) {
|
||||
/* scan table */
|
||||
if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) ||
|
||||
((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL)))
|
||||
continue; /* call option check */
|
||||
if (!(dv->rule.drvid & (1L << ic->driver)))
|
||||
continue; /* driver not matching */
|
||||
if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1))
|
||||
continue; /* si1 not matching */
|
||||
if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2))
|
||||
continue; /* si2 not matching */
|
||||
|
||||
p = dv->rule.my_msn;
|
||||
p1 = ic->parm.setup.eazmsn;
|
||||
accept = 0;
|
||||
while (*p) {
|
||||
/* complete compare */
|
||||
if (*p == '-') {
|
||||
accept = 1; /* call accepted */
|
||||
break;
|
||||
}
|
||||
if (*p++ != *p1++)
|
||||
break; /* not accepted */
|
||||
if ((!*p) && (!*p1))
|
||||
accept = 1;
|
||||
} /* complete compare */
|
||||
if (!accept) continue; /* not accepted */
|
||||
|
||||
if ((strcmp(dv->rule.caller, "0")) ||
|
||||
(ic->parm.setup.phone[0])) {
|
||||
p = dv->rule.caller;
|
||||
p1 = ic->parm.setup.phone;
|
||||
accept = 0;
|
||||
while (*p) {
|
||||
/* complete compare */
|
||||
if (*p == '-') {
|
||||
accept = 1; /* call accepted */
|
||||
break;
|
||||
}
|
||||
if (*p++ != *p1++)
|
||||
break; /* not accepted */
|
||||
if ((!*p) && (!*p1))
|
||||
accept = 1;
|
||||
} /* complete compare */
|
||||
if (!accept) continue; /* not accepted */
|
||||
}
|
||||
|
||||
switch (dv->rule.action) {
|
||||
case DEFLECT_IGNORE:
|
||||
return 0;
|
||||
|
||||
case DEFLECT_ALERT:
|
||||
case DEFLECT_PROCEED:
|
||||
case DEFLECT_REPORT:
|
||||
case DEFLECT_REJECT:
|
||||
if (dv->rule.action == DEFLECT_PROCEED)
|
||||
if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime)))
|
||||
return (0); /* no external deflection needed */
|
||||
if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
|
||||
return (0); /* no memory */
|
||||
timer_setup(&cs->timer, deflect_timer_expire, 0);
|
||||
cs->info[0] = '\0';
|
||||
|
||||
cs->ics = *ic; /* copy incoming data */
|
||||
if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone, "0");
|
||||
if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn, "0");
|
||||
cs->ics.parm.setup.screen = dv->rule.screen;
|
||||
if (dv->rule.waittime)
|
||||
cs->timer.expires = jiffies + (HZ * dv->rule.waittime);
|
||||
else if (dv->rule.action == DEFLECT_PROCEED)
|
||||
cs->timer.expires = jiffies + (HZ * extern_wait_max);
|
||||
else
|
||||
cs->timer.expires = 0;
|
||||
cs->akt_state = dv->rule.action;
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->divert_id = next_id++; /* new sequence number */
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
cs->prev = NULL;
|
||||
if (cs->akt_state == DEFLECT_ALERT) {
|
||||
strcpy(cs->deflect_dest, dv->rule.to_nr);
|
||||
if (!cs->timer.expires) {
|
||||
strcpy(ic->parm.setup.eazmsn,
|
||||
"Testtext direct");
|
||||
ic->parm.setup.screen = dv->rule.screen;
|
||||
strlcpy(ic->parm.setup.phone, dv->rule.to_nr, sizeof(ic->parm.setup.phone));
|
||||
cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
|
||||
cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
|
||||
retval = 5;
|
||||
} else
|
||||
retval = 1; /* alerting */
|
||||
} else {
|
||||
cs->deflect_dest[0] = '\0';
|
||||
retval = 4; /* only proceed */
|
||||
}
|
||||
snprintf(cs->info, sizeof(cs->info),
|
||||
"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",
|
||||
cs->akt_state,
|
||||
cs->divert_id,
|
||||
divert_if.drv_to_name(cs->ics.driver),
|
||||
(ic->command == ISDN_STAT_ICALLW) ? "1" : "0",
|
||||
cs->ics.parm.setup.phone,
|
||||
cs->ics.parm.setup.eazmsn,
|
||||
cs->ics.parm.setup.si1,
|
||||
cs->ics.parm.setup.si2,
|
||||
cs->ics.parm.setup.screen,
|
||||
dv->rule.waittime,
|
||||
cs->deflect_dest);
|
||||
if ((dv->rule.action == DEFLECT_REPORT) ||
|
||||
(dv->rule.action == DEFLECT_REJECT)) {
|
||||
put_info_buffer(cs->info);
|
||||
kfree(cs); /* remove */
|
||||
return ((dv->rule.action == DEFLECT_REPORT) ? 0 : 2); /* nothing to do */
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0; /* ignore call */
|
||||
} /* switch action */
|
||||
break; /* will break the 'for' looping */
|
||||
} /* scan_table */
|
||||
|
||||
if (cs) {
|
||||
cs->prev = NULL;
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs->next = divert_head;
|
||||
divert_head = cs;
|
||||
if (cs->timer.expires) add_timer(&cs->timer);
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
|
||||
put_info_buffer(cs->info);
|
||||
return (retval);
|
||||
} else
|
||||
return (0);
|
||||
} /* isdn_divert_icall */
|
||||
|
||||
|
||||
void deleteprocs(void)
|
||||
{
|
||||
struct call_struc *cs, *cs1;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
cs = divert_head;
|
||||
divert_head = NULL;
|
||||
while (cs) {
|
||||
del_timer(&cs->timer);
|
||||
cs1 = cs;
|
||||
cs = cs->next;
|
||||
kfree(cs1);
|
||||
}
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
} /* deleteprocs */
|
||||
|
||||
/****************************************************/
|
||||
/* put a address including address type into buffer */
|
||||
/****************************************************/
|
||||
static int put_address(char *st, u_char *p, int len)
|
||||
{
|
||||
u_char retval = 0;
|
||||
u_char adr_typ = 0; /* network standard */
|
||||
|
||||
if (len < 2) return (retval);
|
||||
if (*p == 0xA1) {
|
||||
retval = *(++p) + 2; /* total length */
|
||||
if (retval > len) return (0); /* too short */
|
||||
len = retval - 2; /* remaining length */
|
||||
if (len < 3) return (0);
|
||||
if ((*(++p) != 0x0A) || (*(++p) != 1)) return (0);
|
||||
adr_typ = *(++p);
|
||||
len -= 3;
|
||||
p++;
|
||||
if (len < 2) return (0);
|
||||
if (*p++ != 0x12) return (0);
|
||||
if (*p > len) return (0); /* check number length */
|
||||
len = *p++;
|
||||
} else if (*p == 0x80) {
|
||||
retval = *(++p) + 2; /* total length */
|
||||
if (retval > len) return (0);
|
||||
len = retval - 2;
|
||||
p++;
|
||||
} else
|
||||
return (0); /* invalid address information */
|
||||
|
||||
sprintf(st, "%d ", adr_typ);
|
||||
st += strlen(st);
|
||||
if (!len)
|
||||
*st++ = '-';
|
||||
else
|
||||
while (len--)
|
||||
*st++ = *p++;
|
||||
*st = '\0';
|
||||
return (retval);
|
||||
} /* put_address */
|
||||
|
||||
/*************************************/
|
||||
/* report a successful interrogation */
|
||||
/*************************************/
|
||||
static int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)
|
||||
{
|
||||
char *src = ic->parm.dss1_io.data;
|
||||
int restlen = ic->parm.dss1_io.datalen;
|
||||
int cnt = 1;
|
||||
u_char n, n1;
|
||||
char st[90], *p, *stp;
|
||||
|
||||
if (restlen < 2) return (-100); /* frame too short */
|
||||
if (*src++ != 0x30) return (-101);
|
||||
if ((n = *src++) > 0x81) return (-102); /* invalid length field */
|
||||
restlen -= 2; /* remaining bytes */
|
||||
if (n == 0x80) {
|
||||
if (restlen < 2) return (-103);
|
||||
if ((*(src + restlen - 1)) || (*(src + restlen - 2))) return (-104);
|
||||
restlen -= 2;
|
||||
} else if (n == 0x81) {
|
||||
n = *src++;
|
||||
restlen--;
|
||||
if (n > restlen) return (-105);
|
||||
restlen = n;
|
||||
} else if (n > restlen)
|
||||
return (-106);
|
||||
else
|
||||
restlen = n; /* standard format */
|
||||
if (restlen < 3) return (-107); /* no procedure */
|
||||
if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return (-108);
|
||||
restlen -= 3;
|
||||
if (restlen < 2) return (-109); /* list missing */
|
||||
if (*src == 0x31) {
|
||||
src++;
|
||||
if ((n = *src++) > 0x81) return (-110); /* invalid length field */
|
||||
restlen -= 2; /* remaining bytes */
|
||||
if (n == 0x80) {
|
||||
if (restlen < 2) return (-111);
|
||||
if ((*(src + restlen - 1)) || (*(src + restlen - 2))) return (-112);
|
||||
restlen -= 2;
|
||||
} else if (n == 0x81) {
|
||||
n = *src++;
|
||||
restlen--;
|
||||
if (n > restlen) return (-113);
|
||||
restlen = n;
|
||||
} else if (n > restlen)
|
||||
return (-114);
|
||||
else
|
||||
restlen = n; /* standard format */
|
||||
} /* result list header */
|
||||
|
||||
while (restlen >= 2) {
|
||||
stp = st;
|
||||
sprintf(stp, "%d 0x%lx %d %s ", DIVERT_REPORT, ic->parm.dss1_io.ll_id,
|
||||
cnt++, divert_if.drv_to_name(ic->driver));
|
||||
stp += strlen(stp);
|
||||
if (*src++ != 0x30) return (-115); /* invalid enum */
|
||||
n = *src++;
|
||||
restlen -= 2;
|
||||
if (n > restlen) return (-116); /* enum length wrong */
|
||||
restlen -= n;
|
||||
p = src; /* one entry */
|
||||
src += n;
|
||||
if (!(n1 = put_address(stp, p, n & 0xFF))) continue;
|
||||
stp += strlen(stp);
|
||||
p += n1;
|
||||
n -= n1;
|
||||
if (n < 6) continue; /* no service and proc */
|
||||
if ((*p++ != 0x0A) || (*p++ != 1)) continue;
|
||||
sprintf(stp, " 0x%02x ", (*p++) & 0xFF);
|
||||
stp += strlen(stp);
|
||||
if ((*p++ != 0x0A) || (*p++ != 1)) continue;
|
||||
sprintf(stp, "%d ", (*p++) & 0xFF);
|
||||
stp += strlen(stp);
|
||||
n -= 6;
|
||||
if (n > 2) {
|
||||
if (*p++ != 0x30) continue;
|
||||
if (*p > (n - 2)) continue;
|
||||
n = *p++;
|
||||
if (!(n1 = put_address(stp, p, n & 0xFF))) continue;
|
||||
stp += strlen(stp);
|
||||
}
|
||||
sprintf(stp, "\n");
|
||||
put_info_buffer(st);
|
||||
} /* while restlen */
|
||||
if (restlen) return (-117);
|
||||
return (0);
|
||||
} /* interrogate_success */
|
||||
|
||||
/*********************************************/
|
||||
/* callback for protocol specific extensions */
|
||||
/*********************************************/
|
||||
static int prot_stat_callback(isdn_ctrl *ic)
|
||||
{
|
||||
struct call_struc *cs, *cs1;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
cs = divert_head; /* start of list */
|
||||
cs1 = NULL;
|
||||
while (cs) {
|
||||
if (ic->driver == cs->ics.driver) {
|
||||
switch (cs->ics.arg) {
|
||||
case DSS1_CMD_INVOKE:
|
||||
if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) &&
|
||||
(cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id)) {
|
||||
switch (ic->arg) {
|
||||
case DSS1_STAT_INVOKE_ERR:
|
||||
sprintf(cs->info, "128 0x%lx 0x%x\n",
|
||||
ic->parm.dss1_io.ll_id,
|
||||
ic->parm.dss1_io.timeout);
|
||||
put_info_buffer(cs->info);
|
||||
break;
|
||||
|
||||
case DSS1_STAT_INVOKE_RES:
|
||||
switch (cs->ics.parm.dss1_io.proc) {
|
||||
case 7:
|
||||
case 8:
|
||||
put_info_buffer(cs->info);
|
||||
break;
|
||||
|
||||
case 11:
|
||||
i = interrogate_success(ic, cs);
|
||||
if (i)
|
||||
sprintf(cs->info, "%d 0x%lx %d\n", DIVERT_REPORT,
|
||||
ic->parm.dss1_io.ll_id, i);
|
||||
put_info_buffer(cs->info);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "dss1_divert: unknown proc %d\n", cs->ics.parm.dss1_io.proc);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n", ic->arg);
|
||||
break;
|
||||
}
|
||||
cs1 = cs; /* remember structure */
|
||||
cs = NULL;
|
||||
continue; /* abort search */
|
||||
} /* id found */
|
||||
break;
|
||||
|
||||
case DSS1_CMD_INVOKE_ABORT:
|
||||
printk(KERN_WARNING "dss1_divert unhandled invoke abort\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n", cs->ics.arg);
|
||||
break;
|
||||
} /* switch ics.arg */
|
||||
cs = cs->next;
|
||||
} /* driver ok */
|
||||
}
|
||||
|
||||
if (!cs1) {
|
||||
printk(KERN_WARNING "dss1_divert unhandled process\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (cs1->ics.driver == -1) {
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
del_timer(&cs1->timer);
|
||||
if (cs1->prev)
|
||||
cs1->prev->next = cs1->next; /* forward link */
|
||||
else
|
||||
divert_head = cs1->next;
|
||||
if (cs1->next)
|
||||
cs1->next->prev = cs1->prev; /* back link */
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
kfree(cs1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
} /* prot_stat_callback */
|
||||
|
||||
|
||||
/***************************/
|
||||
/* status callback from HL */
|
||||
/***************************/
|
||||
static int isdn_divert_stat_callback(isdn_ctrl *ic)
|
||||
{
|
||||
struct call_struc *cs, *cs1;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
retval = -1;
|
||||
cs = divert_head; /* start of list */
|
||||
while (cs) {
|
||||
if ((ic->driver == cs->ics.driver) &&
|
||||
(ic->arg == cs->ics.arg)) {
|
||||
switch (ic->command) {
|
||||
case ISDN_STAT_DHUP:
|
||||
sprintf(cs->info, "129 0x%lx\n", cs->divert_id);
|
||||
del_timer(&cs->timer);
|
||||
cs->ics.driver = -1;
|
||||
break;
|
||||
|
||||
case ISDN_STAT_CAUSE:
|
||||
sprintf(cs->info, "130 0x%lx %s\n", cs->divert_id, ic->parm.num);
|
||||
break;
|
||||
|
||||
case ISDN_STAT_REDIR:
|
||||
sprintf(cs->info, "131 0x%lx\n", cs->divert_id);
|
||||
del_timer(&cs->timer);
|
||||
cs->ics.driver = -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
sprintf(cs->info, "999 0x%lx 0x%x\n", cs->divert_id, (int)(ic->command));
|
||||
break;
|
||||
}
|
||||
put_info_buffer(cs->info);
|
||||
retval = 0;
|
||||
}
|
||||
cs1 = cs;
|
||||
cs = cs->next;
|
||||
if (cs1->ics.driver == -1) {
|
||||
spin_lock_irqsave(&divert_lock, flags);
|
||||
if (cs1->prev)
|
||||
cs1->prev->next = cs1->next; /* forward link */
|
||||
else
|
||||
divert_head = cs1->next;
|
||||
if (cs1->next)
|
||||
cs1->next->prev = cs1->prev; /* back link */
|
||||
spin_unlock_irqrestore(&divert_lock, flags);
|
||||
kfree(cs1);
|
||||
}
|
||||
}
|
||||
return (retval); /* not found */
|
||||
} /* isdn_divert_stat_callback */
|
||||
|
||||
|
||||
/********************/
|
||||
/* callback from ll */
|
||||
/********************/
|
||||
int ll_callback(isdn_ctrl *ic)
|
||||
{
|
||||
switch (ic->command) {
|
||||
case ISDN_STAT_ICALL:
|
||||
case ISDN_STAT_ICALLW:
|
||||
return (isdn_divert_icall(ic));
|
||||
break;
|
||||
|
||||
case ISDN_STAT_PROT:
|
||||
if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO) {
|
||||
if (ic->arg != DSS1_STAT_INVOKE_BRD)
|
||||
return (prot_stat_callback(ic));
|
||||
else
|
||||
return (0); /* DSS1 invoke broadcast */
|
||||
} else
|
||||
return (-1); /* protocol not euro */
|
||||
|
||||
default:
|
||||
return (isdn_divert_stat_callback(ic));
|
||||
}
|
||||
} /* ll_callback */
|
|
@ -1,132 +0,0 @@
|
|||
/* $Id: isdn_divert.h,v 1.5.6.1 2001/09/23 22:24:36 kai Exp $
|
||||
*
|
||||
* Header for the diversion supplementary ioctl interface.
|
||||
*
|
||||
* Copyright 1998 by Werner Cornelius (werner@ikt.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/******************************************/
|
||||
/* IOCTL codes for interface to user prog */
|
||||
/******************************************/
|
||||
#define DIVERT_IIOC_VERSION 0x01 /* actual version */
|
||||
#define IIOCGETVER _IO('I', 1) /* get version of interface */
|
||||
#define IIOCGETDRV _IO('I', 2) /* get driver number */
|
||||
#define IIOCGETNAM _IO('I', 3) /* get driver name */
|
||||
#define IIOCGETRULE _IO('I', 4) /* read one rule */
|
||||
#define IIOCMODRULE _IO('I', 5) /* modify/replace a rule */
|
||||
#define IIOCINSRULE _IO('I', 6) /* insert/append one rule */
|
||||
#define IIOCDELRULE _IO('I', 7) /* delete a rule */
|
||||
#define IIOCDODFACT _IO('I', 8) /* hangup/reject/alert/immediately deflect a call */
|
||||
#define IIOCDOCFACT _IO('I', 9) /* activate control forwarding in PBX */
|
||||
#define IIOCDOCFDIS _IO('I', 10) /* deactivate control forwarding in PBX */
|
||||
#define IIOCDOCFINT _IO('I', 11) /* interrogate control forwarding in PBX */
|
||||
|
||||
/*************************************/
|
||||
/* states reported through interface */
|
||||
/*************************************/
|
||||
#define DEFLECT_IGNORE 0 /* ignore incoming call */
|
||||
#define DEFLECT_REPORT 1 /* only report */
|
||||
#define DEFLECT_PROCEED 2 /* deflect when externally triggered */
|
||||
#define DEFLECT_ALERT 3 /* alert and deflect after delay */
|
||||
#define DEFLECT_REJECT 4 /* reject immediately */
|
||||
#define DIVERT_ACTIVATE 5 /* diversion activate */
|
||||
#define DIVERT_DEACTIVATE 6 /* diversion deactivate */
|
||||
#define DIVERT_REPORT 7 /* interrogation result */
|
||||
#define DEFLECT_AUTODEL 255 /* only for internal use */
|
||||
|
||||
#define DEFLECT_ALL_IDS 0xFFFFFFFF /* all drivers selected */
|
||||
|
||||
typedef struct {
|
||||
ulong drvid; /* driver ids, bit mapped */
|
||||
char my_msn[35]; /* desired msn, subaddr allowed */
|
||||
char caller[35]; /* caller id, partial string with * + subaddr allowed */
|
||||
char to_nr[35]; /* deflected to number incl. subaddress */
|
||||
u_char si1, si2; /* service indicators, si1=bitmask, si1+2 0 = all */
|
||||
u_char screen; /* screening: 0 = no info, 1 = info, 2 = nfo with nr */
|
||||
u_char callopt; /* option for call handling:
|
||||
0 = all calls
|
||||
1 = only non waiting calls
|
||||
2 = only waiting calls */
|
||||
u_char action; /* desired action:
|
||||
0 = don't report call -> ignore
|
||||
1 = report call, do not allow/proceed for deflection
|
||||
2 = report call, send proceed, wait max waittime secs
|
||||
3 = report call, alert and deflect after waittime
|
||||
4 = report call, reject immediately
|
||||
actions 1-2 only take place if interface is opened
|
||||
*/
|
||||
u_char waittime; /* maximum wait time for proceeding */
|
||||
} divert_rule;
|
||||
|
||||
typedef union {
|
||||
int drv_version; /* return of driver version */
|
||||
struct {
|
||||
int drvid; /* id of driver */
|
||||
char drvnam[30]; /* name of driver */
|
||||
} getid;
|
||||
struct {
|
||||
int ruleidx; /* index of rule */
|
||||
divert_rule rule; /* rule parms */
|
||||
} getsetrule;
|
||||
struct {
|
||||
u_char subcmd; /* 0 = hangup/reject,
|
||||
1 = alert,
|
||||
2 = deflect */
|
||||
ulong callid; /* id of call delivered by ascii output */
|
||||
char to_nr[35]; /* destination when deflect,
|
||||
else uus1 string (maxlen 31),
|
||||
data from rule used if empty */
|
||||
} fwd_ctrl;
|
||||
struct {
|
||||
int drvid; /* id of driver */
|
||||
u_char cfproc; /* cfu = 0, cfb = 1, cfnr = 2 */
|
||||
ulong procid; /* process id returned when no error */
|
||||
u_char service; /* basically coded service, 0 = all */
|
||||
char msn[25]; /* desired msn, empty = all */
|
||||
char fwd_nr[35];/* forwarded to number + subaddress */
|
||||
} cf_ctrl;
|
||||
} divert_ioctl;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/isdnif.h>
|
||||
#include <linux/isdn_divertif.h>
|
||||
|
||||
#define AUTODEL_TIME 30 /* timeout in s to delete internal entries */
|
||||
|
||||
/**************************************************/
|
||||
/* structure keeping ascii info for device output */
|
||||
/**************************************************/
|
||||
struct divert_info {
|
||||
struct divert_info *next;
|
||||
ulong usage_cnt; /* number of files still to work */
|
||||
char info_start[2]; /* info string start */
|
||||
};
|
||||
|
||||
|
||||
/**************/
|
||||
/* Prototypes */
|
||||
/**************/
|
||||
extern spinlock_t divert_lock;
|
||||
|
||||
extern ulong if_used; /* number of interface users */
|
||||
extern int divert_dev_deinit(void);
|
||||
extern int divert_dev_init(void);
|
||||
extern void put_info_buffer(char *);
|
||||
extern int ll_callback(isdn_ctrl *);
|
||||
extern isdn_divert_if divert_if;
|
||||
extern divert_rule *getruleptr(int);
|
||||
extern int insertrule(int, divert_rule *);
|
||||
extern int deleterule(int);
|
||||
extern void deleteprocs(void);
|
||||
extern int deflect_extern_action(u_char, ulong, char *);
|
||||
extern int cf_command(int, int, u_char, char *, u_char, char *, ulong *);
|
||||
|
||||
#endif /* __KERNEL__ */
|
|
@ -1,695 +0,0 @@
|
|||
/*
|
||||
* Stuff used by all variants of the driver
|
||||
*
|
||||
* Copyright (c) 2001 by Stefan Eilers,
|
||||
* Hansjoerg Lipp <hjlipp@web.de>,
|
||||
* Tilman Schmidt <tilman@imap.cc>.
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/isdnif.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#define SBUFSIZE 4096 /* sk_buff payload size */
|
||||
#define TRANSBUFSIZE 768 /* bytes per skb for transparent receive */
|
||||
#define HW_HDR_LEN 2 /* Header size used to store ack info */
|
||||
#define MAX_BUF_SIZE (SBUFSIZE - HW_HDR_LEN) /* max data packet from LL */
|
||||
|
||||
/* == Handling of I4L IO =====================================================*/
|
||||
|
||||
/* writebuf_from_LL
|
||||
* called by LL to transmit data on an open channel
|
||||
* inserts the buffer data into the send queue and starts the transmission
|
||||
* Note that this operation must not sleep!
|
||||
* When the buffer is processed completely, gigaset_skb_sent() should be called.
|
||||
* parameters:
|
||||
* driverID driver ID as assigned by LL
|
||||
* channel channel number
|
||||
* ack if != 0 LL wants to be notified on completion via
|
||||
* statcallb(ISDN_STAT_BSENT)
|
||||
* skb skb containing data to send
|
||||
* return value:
|
||||
* number of accepted bytes
|
||||
* 0 if temporarily unable to accept data (out of buffer space)
|
||||
* <0 on error (eg. -EINVAL)
|
||||
*/
|
||||
static int writebuf_from_LL(int driverID, int channel, int ack,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct cardstate *cs = gigaset_get_cs_by_id(driverID);
|
||||
struct bc_state *bcs;
|
||||
unsigned char *ack_header;
|
||||
unsigned len;
|
||||
|
||||
if (!cs) {
|
||||
pr_err("%s: invalid driver ID (%d)\n", __func__, driverID);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (channel < 0 || channel >= cs->channels) {
|
||||
dev_err(cs->dev, "%s: invalid channel ID (%d)\n",
|
||||
__func__, channel);
|
||||
return -ENODEV;
|
||||
}
|
||||
bcs = &cs->bcs[channel];
|
||||
|
||||
/* can only handle linear sk_buffs */
|
||||
if (skb_linearize(skb) < 0) {
|
||||
dev_err(cs->dev, "%s: skb_linearize failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
len = skb->len;
|
||||
|
||||
gig_dbg(DEBUG_LLDATA,
|
||||
"Receiving data from LL (id: %d, ch: %d, ack: %d, sz: %d)",
|
||||
driverID, channel, ack, len);
|
||||
|
||||
if (!len) {
|
||||
if (ack)
|
||||
dev_notice(cs->dev, "%s: not ACKing empty packet\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
if (len > MAX_BUF_SIZE) {
|
||||
dev_err(cs->dev, "%s: packet too large (%d bytes)\n",
|
||||
__func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set up acknowledgement header */
|
||||
if (skb_headroom(skb) < HW_HDR_LEN) {
|
||||
/* should never happen */
|
||||
dev_err(cs->dev, "%s: insufficient skb headroom\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skb_set_mac_header(skb, -HW_HDR_LEN);
|
||||
skb->mac_len = HW_HDR_LEN;
|
||||
ack_header = skb_mac_header(skb);
|
||||
if (ack) {
|
||||
ack_header[0] = len & 0xff;
|
||||
ack_header[1] = len >> 8;
|
||||
} else {
|
||||
ack_header[0] = ack_header[1] = 0;
|
||||
}
|
||||
gig_dbg(DEBUG_MCMD, "skb: len=%u, ack=%d: %02x %02x",
|
||||
len, ack, ack_header[0], ack_header[1]);
|
||||
|
||||
/* pass to device-specific module */
|
||||
return cs->ops->send_skb(bcs, skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_skb_sent() - acknowledge sending an skb
|
||||
* @bcs: B channel descriptor structure.
|
||||
* @skb: sent data.
|
||||
*
|
||||
* Called by hardware module {bas,ser,usb}_gigaset when the data in a
|
||||
* skb has been successfully sent, for signalling completion to the LL.
|
||||
*/
|
||||
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
unsigned char *ack_header = skb_mac_header(skb);
|
||||
unsigned len;
|
||||
isdn_ctrl response;
|
||||
|
||||
++bcs->trans_up;
|
||||
|
||||
if (skb->len)
|
||||
dev_warn(bcs->cs->dev, "%s: skb->len==%d\n",
|
||||
__func__, skb->len);
|
||||
|
||||
len = ack_header[0] + ((unsigned) ack_header[1] << 8);
|
||||
if (len) {
|
||||
gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)",
|
||||
bcs->cs->myid, bcs->channel, len);
|
||||
|
||||
response.driver = bcs->cs->myid;
|
||||
response.command = ISDN_STAT_BSENT;
|
||||
response.arg = bcs->channel;
|
||||
response.parm.length = len;
|
||||
iif->statcallb(&response);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_sent);
|
||||
|
||||
/**
|
||||
* gigaset_skb_rcvd() - pass received skb to LL
|
||||
* @bcs: B channel descriptor structure.
|
||||
* @skb: received data.
|
||||
*
|
||||
* Called by hardware module {bas,ser,usb}_gigaset when user data has
|
||||
* been successfully received, for passing to the LL.
|
||||
* Warning: skb must not be accessed anymore!
|
||||
*/
|
||||
void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
|
||||
iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb);
|
||||
bcs->trans_down++;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
|
||||
|
||||
/**
|
||||
* gigaset_isdn_rcv_err() - signal receive error
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by hardware module {bas,ser,usb}_gigaset when a receive error
|
||||
* has occurred, for signalling to the LL.
|
||||
*/
|
||||
void gigaset_isdn_rcv_err(struct bc_state *bcs)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
isdn_ctrl response;
|
||||
|
||||
/* if currently ignoring packets, just count down */
|
||||
if (bcs->ignore) {
|
||||
bcs->ignore--;
|
||||
return;
|
||||
}
|
||||
|
||||
/* update statistics */
|
||||
bcs->corrupted++;
|
||||
|
||||
/* error -> LL */
|
||||
gig_dbg(DEBUG_CMD, "sending L1ERR");
|
||||
response.driver = bcs->cs->myid;
|
||||
response.command = ISDN_STAT_L1ERR;
|
||||
response.arg = bcs->channel;
|
||||
response.parm.errcode = ISDN_STAT_L1ERR_RECV;
|
||||
iif->statcallb(&response);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
|
||||
|
||||
/* This function will be called by LL to send commands
|
||||
* NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL,
|
||||
* so don't put too much effort into it.
|
||||
*/
|
||||
static int command_from_LL(isdn_ctrl *cntrl)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
struct bc_state *bcs;
|
||||
int retval = 0;
|
||||
char **commands;
|
||||
int ch;
|
||||
int i;
|
||||
size_t l;
|
||||
|
||||
gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx",
|
||||
cntrl->driver, cntrl->command, cntrl->arg);
|
||||
|
||||
cs = gigaset_get_cs_by_id(cntrl->driver);
|
||||
if (cs == NULL) {
|
||||
pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
|
||||
return -ENODEV;
|
||||
}
|
||||
ch = cntrl->arg & 0xff;
|
||||
|
||||
switch (cntrl->command) {
|
||||
case ISDN_CMD_IOCTL:
|
||||
dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
|
||||
return -EINVAL;
|
||||
|
||||
case ISDN_CMD_DIAL:
|
||||
gig_dbg(DEBUG_CMD,
|
||||
"ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
|
||||
cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
|
||||
cntrl->parm.setup.si1, cntrl->parm.setup.si2);
|
||||
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_DIAL: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
if (gigaset_get_channel(bcs) < 0) {
|
||||
dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
switch (bcs->proto2) {
|
||||
case L2_HDLC:
|
||||
bcs->rx_bufsize = SBUFSIZE;
|
||||
break;
|
||||
default: /* assume transparent */
|
||||
bcs->rx_bufsize = TRANSBUFSIZE;
|
||||
}
|
||||
dev_kfree_skb(bcs->rx_skb);
|
||||
gigaset_new_rx_skb(bcs);
|
||||
|
||||
commands = kcalloc(AT_NUM, sizeof(*commands), GFP_ATOMIC);
|
||||
if (!commands) {
|
||||
gigaset_free_channel(bcs);
|
||||
dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l = 3 + strlen(cntrl->parm.setup.phone);
|
||||
commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC);
|
||||
if (!commands[AT_DIAL])
|
||||
goto oom;
|
||||
if (cntrl->parm.setup.phone[0] == '*' &&
|
||||
cntrl->parm.setup.phone[1] == '*') {
|
||||
/* internal call: translate ** prefix to CTP value */
|
||||
commands[AT_TYPE] = kstrdup("^SCTP=0\r", GFP_ATOMIC);
|
||||
if (!commands[AT_TYPE])
|
||||
goto oom;
|
||||
snprintf(commands[AT_DIAL], l,
|
||||
"D%s\r", cntrl->parm.setup.phone + 2);
|
||||
} else {
|
||||
commands[AT_TYPE] = kstrdup("^SCTP=1\r", GFP_ATOMIC);
|
||||
if (!commands[AT_TYPE])
|
||||
goto oom;
|
||||
snprintf(commands[AT_DIAL], l,
|
||||
"D%s\r", cntrl->parm.setup.phone);
|
||||
}
|
||||
|
||||
l = strlen(cntrl->parm.setup.eazmsn);
|
||||
if (l) {
|
||||
l += 8;
|
||||
commands[AT_MSN] = kmalloc(l, GFP_ATOMIC);
|
||||
if (!commands[AT_MSN])
|
||||
goto oom;
|
||||
snprintf(commands[AT_MSN], l, "^SMSN=%s\r",
|
||||
cntrl->parm.setup.eazmsn);
|
||||
}
|
||||
|
||||
switch (cntrl->parm.setup.si1) {
|
||||
case 1: /* audio */
|
||||
/* BC = 9090A3: 3.1 kHz audio, A-law */
|
||||
commands[AT_BC] = kstrdup("^SBC=9090A3\r", GFP_ATOMIC);
|
||||
if (!commands[AT_BC])
|
||||
goto oom;
|
||||
break;
|
||||
case 7: /* data */
|
||||
default: /* hope the app knows what it is doing */
|
||||
/* BC = 8890: unrestricted digital information */
|
||||
commands[AT_BC] = kstrdup("^SBC=8890\r", GFP_ATOMIC);
|
||||
if (!commands[AT_BC])
|
||||
goto oom;
|
||||
}
|
||||
/* ToDo: other si1 values, inspect si2, set HLC/LLC */
|
||||
|
||||
commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
|
||||
if (!commands[AT_PROTO])
|
||||
goto oom;
|
||||
snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
|
||||
|
||||
commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
|
||||
if (!commands[AT_ISO])
|
||||
goto oom;
|
||||
snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
|
||||
(unsigned) bcs->channel + 1);
|
||||
|
||||
if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
|
||||
bcs->at_state.seq_index, NULL)) {
|
||||
for (i = 0; i < AT_NUM; ++i)
|
||||
kfree(commands[i]);
|
||||
kfree(commands);
|
||||
gigaset_free_channel(bcs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
break;
|
||||
case ISDN_CMD_ACCEPTD:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_ACCEPTD");
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
switch (bcs->proto2) {
|
||||
case L2_HDLC:
|
||||
bcs->rx_bufsize = SBUFSIZE;
|
||||
break;
|
||||
default: /* assume transparent */
|
||||
bcs->rx_bufsize = TRANSBUFSIZE;
|
||||
}
|
||||
dev_kfree_skb(bcs->rx_skb);
|
||||
gigaset_new_rx_skb(bcs);
|
||||
if (!gigaset_add_event(cs, &bcs->at_state,
|
||||
EV_ACCEPT, NULL, 0, NULL))
|
||||
return -ENOMEM;
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
break;
|
||||
case ISDN_CMD_HANGUP:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_HANGUP");
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
if (!gigaset_add_event(cs, &bcs->at_state,
|
||||
EV_HUP, NULL, 0, NULL))
|
||||
return -ENOMEM;
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
break;
|
||||
case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
|
||||
dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
|
||||
break;
|
||||
case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
|
||||
dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
|
||||
cntrl->parm.num);
|
||||
break;
|
||||
case ISDN_CMD_SETL2: /* Set L2 to given protocol */
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
if (bcs->chstate & CHS_D_UP) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL2: channel active (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (cntrl->arg >> 8) {
|
||||
case ISDN_PROTO_L2_HDLC:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC");
|
||||
bcs->proto2 = L2_HDLC;
|
||||
break;
|
||||
case ISDN_PROTO_L2_TRANS:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE");
|
||||
bcs->proto2 = L2_VOICE;
|
||||
break;
|
||||
default:
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL2: unsupported protocol (%lu)\n",
|
||||
cntrl->arg >> 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case ISDN_CMD_SETL3: /* Set L3 to given protocol */
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL3");
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
|
||||
cntrl->arg >> 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
gig_dbg(DEBUG_CMD, "unknown command %d from LL",
|
||||
cntrl->command);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
oom:
|
||||
dev_err(bcs->cs->dev, "out of memory\n");
|
||||
for (i = 0; i < AT_NUM; ++i)
|
||||
kfree(commands[i]);
|
||||
kfree(commands);
|
||||
gigaset_free_channel(bcs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
|
||||
{
|
||||
isdn_if *iif = cs->iif;
|
||||
isdn_ctrl command;
|
||||
|
||||
command.driver = cs->myid;
|
||||
command.command = cmd;
|
||||
command.arg = 0;
|
||||
iif->statcallb(&command);
|
||||
}
|
||||
|
||||
static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
isdn_ctrl command;
|
||||
|
||||
command.driver = bcs->cs->myid;
|
||||
command.command = cmd;
|
||||
command.arg = bcs->channel;
|
||||
iif->statcallb(&command);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_icall() - signal incoming call
|
||||
* @at_state: connection state structure.
|
||||
*
|
||||
* Called by main module to notify the LL that an incoming call has been
|
||||
* received. @at_state contains the parameters of the call.
|
||||
*
|
||||
* Return value: call disposition (ICALL_*)
|
||||
*/
|
||||
int gigaset_isdn_icall(struct at_state_t *at_state)
|
||||
{
|
||||
struct cardstate *cs = at_state->cs;
|
||||
struct bc_state *bcs = at_state->bcs;
|
||||
isdn_if *iif = cs->iif;
|
||||
isdn_ctrl response;
|
||||
int retval;
|
||||
|
||||
/* fill ICALL structure */
|
||||
response.parm.setup.si1 = 0; /* default: unknown */
|
||||
response.parm.setup.si2 = 0;
|
||||
response.parm.setup.screen = 0;
|
||||
response.parm.setup.plan = 0;
|
||||
if (!at_state->str_var[STR_ZBC]) {
|
||||
/* no BC (internal call): assume speech, A-law */
|
||||
response.parm.setup.si1 = 1;
|
||||
} else if (!strcmp(at_state->str_var[STR_ZBC], "8890")) {
|
||||
/* unrestricted digital information */
|
||||
response.parm.setup.si1 = 7;
|
||||
} else if (!strcmp(at_state->str_var[STR_ZBC], "8090A3")) {
|
||||
/* speech, A-law */
|
||||
response.parm.setup.si1 = 1;
|
||||
} else if (!strcmp(at_state->str_var[STR_ZBC], "9090A3")) {
|
||||
/* 3,1 kHz audio, A-law */
|
||||
response.parm.setup.si1 = 1;
|
||||
response.parm.setup.si2 = 2;
|
||||
} else {
|
||||
dev_warn(cs->dev, "RING ignored - unsupported BC %s\n",
|
||||
at_state->str_var[STR_ZBC]);
|
||||
return ICALL_IGNORE;
|
||||
}
|
||||
if (at_state->str_var[STR_NMBR]) {
|
||||
strlcpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
|
||||
sizeof response.parm.setup.phone);
|
||||
} else
|
||||
response.parm.setup.phone[0] = 0;
|
||||
if (at_state->str_var[STR_ZCPN]) {
|
||||
strlcpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
|
||||
sizeof response.parm.setup.eazmsn);
|
||||
} else
|
||||
response.parm.setup.eazmsn[0] = 0;
|
||||
|
||||
if (!bcs) {
|
||||
dev_notice(cs->dev, "no channel for incoming call\n");
|
||||
response.command = ISDN_STAT_ICALLW;
|
||||
response.arg = 0;
|
||||
} else {
|
||||
gig_dbg(DEBUG_CMD, "Sending ICALL");
|
||||
response.command = ISDN_STAT_ICALL;
|
||||
response.arg = bcs->channel;
|
||||
}
|
||||
response.driver = cs->myid;
|
||||
retval = iif->statcallb(&response);
|
||||
gig_dbg(DEBUG_CMD, "Response: %d", retval);
|
||||
switch (retval) {
|
||||
case 0: /* no takers */
|
||||
return ICALL_IGNORE;
|
||||
case 1: /* alerting */
|
||||
bcs->chstate |= CHS_NOTIFY_LL;
|
||||
return ICALL_ACCEPT;
|
||||
case 2: /* reject */
|
||||
return ICALL_REJECT;
|
||||
case 3: /* incomplete */
|
||||
dev_warn(cs->dev,
|
||||
"LL requested unsupported feature: Incomplete Number\n");
|
||||
return ICALL_IGNORE;
|
||||
case 4: /* proceeding */
|
||||
/* Gigaset will send ALERTING anyway.
|
||||
* There doesn't seem to be a way to avoid this.
|
||||
*/
|
||||
return ICALL_ACCEPT;
|
||||
case 5: /* deflect */
|
||||
dev_warn(cs->dev,
|
||||
"LL requested unsupported feature: Call Deflection\n");
|
||||
return ICALL_IGNORE;
|
||||
default:
|
||||
dev_err(cs->dev, "LL error %d on ICALL\n", retval);
|
||||
return ICALL_IGNORE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_connD() - signal D channel connect
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the D channel connection has
|
||||
* been established.
|
||||
*/
|
||||
void gigaset_isdn_connD(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending DCONN");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_hupD() - signal D channel hangup
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the D channel connection has
|
||||
* been shut down.
|
||||
*/
|
||||
void gigaset_isdn_hupD(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending DHUP");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_connB() - signal B channel connect
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the B channel connection has
|
||||
* been established.
|
||||
*/
|
||||
void gigaset_isdn_connB(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending BCONN");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_hupB() - signal B channel hangup
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the B channel connection has
|
||||
* been shut down.
|
||||
*/
|
||||
void gigaset_isdn_hupB(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending BHUP");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_start() - signal device availability
|
||||
* @cs: device descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the device is available for
|
||||
* use.
|
||||
*/
|
||||
void gigaset_isdn_start(struct cardstate *cs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending RUN");
|
||||
gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_stop() - signal device unavailability
|
||||
* @cs: device descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the device is no longer
|
||||
* available for use.
|
||||
*/
|
||||
void gigaset_isdn_stop(struct cardstate *cs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending STOP");
|
||||
gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_regdev() - register to LL
|
||||
* @cs: device descriptor structure.
|
||||
* @isdnid: device name.
|
||||
*
|
||||
* Return value: 0 on success, error code < 0 on failure
|
||||
*/
|
||||
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
|
||||
{
|
||||
isdn_if *iif;
|
||||
|
||||
iif = kmalloc(sizeof *iif, GFP_KERNEL);
|
||||
if (!iif) {
|
||||
pr_err("out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
|
||||
>= sizeof iif->id) {
|
||||
pr_err("ID too long: %s\n", isdnid);
|
||||
kfree(iif);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iif->owner = THIS_MODULE;
|
||||
iif->channels = cs->channels;
|
||||
iif->maxbufsize = MAX_BUF_SIZE;
|
||||
iif->features = ISDN_FEATURE_L2_TRANS |
|
||||
ISDN_FEATURE_L2_HDLC |
|
||||
ISDN_FEATURE_L2_X75I |
|
||||
ISDN_FEATURE_L3_TRANS |
|
||||
ISDN_FEATURE_P_EURO;
|
||||
iif->hl_hdrlen = HW_HDR_LEN; /* Area for storing ack */
|
||||
iif->command = command_from_LL;
|
||||
iif->writebuf_skb = writebuf_from_LL;
|
||||
iif->writecmd = NULL; /* Don't support isdnctrl */
|
||||
iif->readstat = NULL; /* Don't support isdnctrl */
|
||||
iif->rcvcallb_skb = NULL; /* Will be set by LL */
|
||||
iif->statcallb = NULL; /* Will be set by LL */
|
||||
|
||||
if (!register_isdn(iif)) {
|
||||
pr_err("register_isdn failed\n");
|
||||
kfree(iif);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs->iif = iif;
|
||||
cs->myid = iif->channels; /* Set my device id */
|
||||
cs->hw_hdr_len = HW_HDR_LEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_unregdev() - unregister device from LL
|
||||
* @cs: device descriptor structure.
|
||||
*/
|
||||
void gigaset_isdn_unregdev(struct cardstate *cs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending UNLOAD");
|
||||
gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
|
||||
kfree(cs->iif);
|
||||
cs->iif = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_regdrv() - register driver to LL
|
||||
*/
|
||||
void gigaset_isdn_regdrv(void)
|
||||
{
|
||||
pr_info("ISDN4Linux interface\n");
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_unregdrv() - unregister driver from LL
|
||||
*/
|
||||
void gigaset_isdn_unregdrv(void)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# ISDN hardware drivers
|
||||
#
|
||||
comment "CAPI hardware drivers"
|
||||
|
||||
source "drivers/isdn/hardware/avm/Kconfig"
|
||||
|
|
@ -3,5 +3,4 @@
|
|||
|
||||
# Object files in subdirectories
|
||||
|
||||
obj-$(CONFIG_CAPI_AVM) += avm/
|
||||
obj-$(CONFIG_MISDN) += mISDN/
|
||||
|
|
|
@ -79,11 +79,14 @@ config MISDN_NETJET
|
|||
depends on PCI
|
||||
depends on TTY
|
||||
select MISDN_IPAC
|
||||
select ISDN_HDLC
|
||||
select ISDN_I4L
|
||||
select MISDN_HDLC
|
||||
help
|
||||
Enable support for Traverse Technologies NETJet PCI cards.
|
||||
|
||||
config MISDN_HDLC
|
||||
tristate
|
||||
select CRC_CCITT
|
||||
select BITREVERSE
|
||||
|
||||
config MISDN_IPAC
|
||||
tristate
|
||||
|
|
|
@ -15,3 +15,5 @@ obj-$(CONFIG_MISDN_NETJET) += netjet.o
|
|||
# chip modules
|
||||
obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
|
||||
obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o
|
||||
|
||||
obj-$(CONFIG_MISDN_HDLC) += isdnhdlc.o
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/crc-ccitt.h>
|
||||
#include <linux/isdn/hdlc.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include "isdnhdlc.h"
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
#include "ipac.h"
|
||||
#include "iohelper.h"
|
||||
#include "netjet.h"
|
||||
#include <linux/isdn/hdlc.h>
|
||||
#include "isdnhdlc.h"
|
||||
|
||||
#define NETJET_REV "2.0"
|
||||
|
||||
|
|
|
@ -1,423 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
menu "Passive cards"
|
||||
|
||||
config ISDN_DRV_HISAX
|
||||
tristate "HiSax SiemensChipSet driver support"
|
||||
select CRC_CCITT
|
||||
---help---
|
||||
This is a driver supporting the Siemens chipset on various
|
||||
ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles
|
||||
S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many
|
||||
compatibles).
|
||||
|
||||
HiSax is just the name of this driver, not the name of any hardware.
|
||||
|
||||
If you have a card with such a chipset, you should say Y here and
|
||||
also to the configuration option of the driver for your particular
|
||||
card, below.
|
||||
|
||||
if ISDN_DRV_HISAX
|
||||
|
||||
comment "D-channel protocol features"
|
||||
|
||||
config HISAX_EURO
|
||||
bool "HiSax Support for EURO/DSS1"
|
||||
help
|
||||
Say Y or N according to the D-channel protocol which your local
|
||||
telephone service company provides.
|
||||
|
||||
The call control protocol E-DSS1 is used in most European countries.
|
||||
If unsure, say Y.
|
||||
|
||||
config DE_AOC
|
||||
bool "Support for german chargeinfo"
|
||||
depends on HISAX_EURO
|
||||
help
|
||||
If you want that the HiSax hardware driver sends messages to the
|
||||
upper level of the isdn code on each AOCD (Advice Of Charge, During
|
||||
the call -- transmission of the fee information during a call) and
|
||||
on each AOCE (Advice Of Charge, at the End of the call --
|
||||
transmission of fee information at the end of the call), say Y here.
|
||||
This works only in Germany.
|
||||
|
||||
config HISAX_NO_SENDCOMPLETE
|
||||
bool "Disable sending complete"
|
||||
depends on HISAX_EURO
|
||||
help
|
||||
If you have trouble with some ugly exchanges or you live in
|
||||
Australia select this option.
|
||||
|
||||
config HISAX_NO_LLC
|
||||
bool "Disable sending low layer compatibility"
|
||||
depends on HISAX_EURO
|
||||
help
|
||||
If you have trouble with some ugly exchanges try to select this
|
||||
option.
|
||||
|
||||
config HISAX_NO_KEYPAD
|
||||
bool "Disable keypad protocol option"
|
||||
depends on HISAX_EURO
|
||||
help
|
||||
If you like to send special dial strings including * or # without
|
||||
using the keypad protocol, select this option.
|
||||
|
||||
config HISAX_1TR6
|
||||
bool "HiSax Support for german 1TR6"
|
||||
help
|
||||
Say Y or N according to the D-channel protocol which your local
|
||||
telephone service company provides.
|
||||
|
||||
1TR6 is an old call control protocol which was used in Germany
|
||||
before E-DSS1 was established. Nowadays, all new lines in Germany
|
||||
use E-DSS1.
|
||||
|
||||
config HISAX_NI1
|
||||
bool "HiSax Support for US NI1"
|
||||
help
|
||||
Enable this if you like to use ISDN in US on a NI1 basic rate
|
||||
interface.
|
||||
|
||||
config HISAX_MAX_CARDS
|
||||
int "Maximum number of cards supported by HiSax"
|
||||
default "8"
|
||||
help
|
||||
This option allows you to specify the maximum number of cards which
|
||||
the HiSax driver will be able to handle.
|
||||
|
||||
comment "HiSax supported cards"
|
||||
|
||||
config HISAX_16_0
|
||||
bool "Teles 16.0/8.0"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
|
||||
and many compatibles.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port/shmem settings.
|
||||
|
||||
config HISAX_16_3
|
||||
bool "Teles 16.3 or PNP or PCMCIA"
|
||||
help
|
||||
This enables HiSax support for the Teles ISDN-cards S0-16.3 the
|
||||
Teles/Creatix PnP and the Teles PCMCIA.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_TELESPCI
|
||||
bool "Teles PCI"
|
||||
depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN)))
|
||||
help
|
||||
This enables HiSax support for the Teles PCI.
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it.
|
||||
|
||||
config HISAX_S0BOX
|
||||
bool "Teles S0Box"
|
||||
help
|
||||
This enables HiSax support for the Teles/Creatix parallel port
|
||||
S0BOX. See <file:Documentation/isdn/README.HiSax> on how to
|
||||
configure it.
|
||||
|
||||
config HISAX_AVM_A1
|
||||
bool "AVM A1 (Fritz)"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the AVM A1 (aka "Fritz").
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_FRITZPCI
|
||||
bool "AVM PnP/PCI (Fritz!PnP/PCI)"
|
||||
depends on BROKEN || !PPC64
|
||||
help
|
||||
This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it.
|
||||
|
||||
config HISAX_AVM_A1_PCMCIA
|
||||
bool "AVM A1 PCMCIA (Fritz)"
|
||||
help
|
||||
This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it.
|
||||
|
||||
config HISAX_ELSA
|
||||
bool "Elsa cards"
|
||||
help
|
||||
This enables HiSax support for the Elsa Mircolink ISA cards, for the
|
||||
Elsa Quickstep series cards and Elsa PCMCIA.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_IX1MICROR2
|
||||
bool "ITK ix1-micro Revision 2"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the ITK ix1-micro Revision 2 card.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_DIEHLDIVA
|
||||
bool "Eicon.Diehl Diva cards"
|
||||
help
|
||||
This enables HiSax support for the Eicon.Diehl Diva none PRO
|
||||
versions passive ISDN cards.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_ASUSCOM
|
||||
bool "ASUSCOM ISA cards"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the AsusCom and their OEM versions
|
||||
passive ISDN ISA cards.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_TELEINT
|
||||
bool "TELEINT cards"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the TELEINT SA1 semiactiv ISDN card.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_HFCS
|
||||
bool "HFC-S based cards"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the HFC-S 2BDS0 based cards, like
|
||||
teles 16.3c.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_SEDLBAUER
|
||||
bool "Sedlbauer cards"
|
||||
help
|
||||
This enables HiSax support for the Sedlbauer passive ISDN cards.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using the different cards, a different D-channel protocol, or
|
||||
non-standard IRQ/port settings.
|
||||
|
||||
config HISAX_SPORTSTER
|
||||
bool "USR Sportster internal TA"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the USR Sportster internal TA card.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_MIC
|
||||
bool "MIC card"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the ITH MIC card.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_NETJET
|
||||
bool "NETjet card"
|
||||
depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN) || MICROBLAZE))
|
||||
depends on VIRT_TO_BUS
|
||||
help
|
||||
This enables HiSax support for the NetJet from Traverse
|
||||
Technologies.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_NETJET_U
|
||||
bool "NETspider U card"
|
||||
depends on PCI && (BROKEN || !(PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN) || MICROBLAZE))
|
||||
depends on VIRT_TO_BUS
|
||||
help
|
||||
This enables HiSax support for the Netspider U interface ISDN card
|
||||
from Traverse Technologies.
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_NICCY
|
||||
bool "Niccy PnP/PCI card"
|
||||
help
|
||||
This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_ISURF
|
||||
bool "Siemens I-Surf card"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the Siemens I-Talk/I-Surf card with
|
||||
ISAR chip.
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_HSTSAPHIR
|
||||
bool "HST Saphir card"
|
||||
depends on ISA
|
||||
help
|
||||
This enables HiSax support for the HST Saphir card.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_BKM_A4T
|
||||
bool "Telekom A4T card"
|
||||
depends on PCI
|
||||
help
|
||||
This enables HiSax support for the Telekom A4T card.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_SCT_QUADRO
|
||||
bool "Scitel Quadro card"
|
||||
depends on PCI
|
||||
help
|
||||
This enables HiSax support for the Scitel Quadro card.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_GAZEL
|
||||
bool "Gazel cards"
|
||||
help
|
||||
This enables HiSax support for the Gazel cards.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_HFC_PCI
|
||||
bool "HFC PCI-Bus cards"
|
||||
depends on PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN)))
|
||||
help
|
||||
This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
|
||||
|
||||
For more information see under
|
||||
<file:Documentation/isdn/README.hfc-pci>.
|
||||
|
||||
config HISAX_W6692
|
||||
bool "Winbond W6692 based cards"
|
||||
depends on PCI
|
||||
help
|
||||
This enables HiSax support for Winbond W6692 based PCI ISDN cards.
|
||||
|
||||
See <file:Documentation/isdn/README.HiSax> on how to configure it
|
||||
using a different D-channel protocol, or non-standard IRQ/port
|
||||
settings.
|
||||
|
||||
config HISAX_HFC_SX
|
||||
bool "HFC-S+, HFC-SP, HFC-PCMCIA cards"
|
||||
help
|
||||
This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA
|
||||
cards. This code is not finished yet.
|
||||
|
||||
config HISAX_ENTERNOW_PCI
|
||||
bool "Formula-n enter:now PCI card"
|
||||
depends on HISAX_NETJET && PCI && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || (XTENSA && !CPU_LITTLE_ENDIAN)))
|
||||
help
|
||||
This enables HiSax support for the Formula-n enter:now PCI
|
||||
ISDN card.
|
||||
|
||||
config HISAX_DEBUG
|
||||
bool "HiSax debugging"
|
||||
help
|
||||
This enables debugging code in the new-style HiSax drivers, i.e.
|
||||
the ST5481 USB driver currently.
|
||||
If in doubt, say yes.
|
||||
|
||||
comment "HiSax PCMCIA card service modules"
|
||||
|
||||
config HISAX_SEDLBAUER_CS
|
||||
tristate "Sedlbauer PCMCIA cards"
|
||||
depends on PCMCIA && HISAX_SEDLBAUER
|
||||
help
|
||||
This enables the PCMCIA client driver for the Sedlbauer Speed Star
|
||||
and Speed Star II cards.
|
||||
|
||||
config HISAX_ELSA_CS
|
||||
tristate "ELSA PCMCIA MicroLink cards"
|
||||
depends on PCMCIA && HISAX_ELSA
|
||||
help
|
||||
This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink
|
||||
card.
|
||||
|
||||
config HISAX_AVM_A1_CS
|
||||
tristate "AVM A1 PCMCIA cards"
|
||||
depends on PCMCIA && ISDN_DRV_HISAX
|
||||
help
|
||||
This enables the PCMCIA client driver for the AVM A1 / Fritz!Card
|
||||
PCMCIA cards.
|
||||
|
||||
config HISAX_TELES_CS
|
||||
tristate "TELES PCMCIA cards"
|
||||
depends on PCMCIA && HISAX_16_3
|
||||
help
|
||||
This enables the PCMCIA client driver for the Teles PCMCIA cards.
|
||||
|
||||
comment "HiSax sub driver modules"
|
||||
|
||||
config HISAX_ST5481
|
||||
tristate "ST5481 USB ISDN modem"
|
||||
depends on USB
|
||||
select ISDN_HDLC
|
||||
select CRC_CCITT
|
||||
select BITREVERSE
|
||||
help
|
||||
This enables the driver for ST5481 based USB ISDN adapters,
|
||||
e.g. the BeWan Gazel 128 USB
|
||||
|
||||
config HISAX_HFCUSB
|
||||
tristate "HFC USB based ISDN modems"
|
||||
depends on USB
|
||||
help
|
||||
This enables the driver for HFC USB based ISDN modems.
|
||||
|
||||
config HISAX_HFC4S8S
|
||||
tristate "HFC-4S/8S based ISDN cards"
|
||||
help
|
||||
This enables the driver for HFC-4S/8S based ISDN cards.
|
||||
|
||||
config HISAX_FRITZ_PCIPNP
|
||||
tristate "AVM Fritz!Card PCI/PCIv2/PnP support"
|
||||
depends on PCI
|
||||
help
|
||||
This enables the driver for the AVM Fritz!Card PCI,
|
||||
Fritz!Card PCI v2 and Fritz!Card PnP.
|
||||
(the latter also needs you to select "ISA Plug and Play support"
|
||||
from the menu "Plug and Play configuration")
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for the hisax ISDN device driver
|
||||
|
||||
# The target object and module list name.
|
||||
|
||||
# Define maximum number of cards
|
||||
|
||||
ccflags-y := -DHISAX_MAX_CARDS=$(CONFIG_HISAX_MAX_CARDS)
|
||||
|
||||
obj-$(CONFIG_ISDN_DRV_HISAX) += hisax.o
|
||||
obj-$(CONFIG_HISAX_SEDLBAUER_CS) += sedlbauer_cs.o
|
||||
obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o
|
||||
obj-$(CONFIG_HISAX_AVM_A1_CS) += avma1_cs.o
|
||||
obj-$(CONFIG_HISAX_TELES_CS) += teles_cs.o
|
||||
obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o
|
||||
obj-$(CONFIG_HISAX_HFCUSB) += hfc_usb.o
|
||||
obj-$(CONFIG_HISAX_HFC4S8S) += hfc4s8s_l1.o
|
||||
obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_isac.o hisax_fcpcipnp.o
|
||||
|
||||
# Multipart objects.
|
||||
|
||||
hisax_st5481-y := st5481_init.o st5481_usb.o st5481_d.o \
|
||||
st5481_b.o
|
||||
|
||||
hisax-y := config.o isdnl1.o tei.o isdnl2.o isdnl3.o \
|
||||
lmgr.o q931.o callc.o fsm.o
|
||||
hisax-$(CONFIG_HISAX_EURO) += l3dss1.o
|
||||
hisax-$(CONFIG_HISAX_NI1) += l3ni1.o
|
||||
hisax-$(CONFIG_HISAX_1TR6) += l3_1tr6.o
|
||||
|
||||
hisax-$(CONFIG_HISAX_16_0) += teles0.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_16_3) += teles3.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_TELESPCI) += telespci.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_S0BOX) += s0box.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_AVM_A1) += avm_a1.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_AVM_A1_PCMCIA) += avm_a1p.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_FRITZPCI) += avm_pci.o isac.o arcofi.o
|
||||
hisax-$(CONFIG_HISAX_ELSA) += elsa.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_IX1MICROR2) += ix1_micro.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o ipacx.o
|
||||
hisax-$(CONFIG_HISAX_ASUSCOM) += asuscom.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_TELEINT) += teleint.o isac.o arcofi.o hfc_2bs0.o
|
||||
hisax-$(CONFIG_HISAX_SEDLBAUER) += sedlbauer.o isac.o arcofi.o hscx.o \
|
||||
isar.o
|
||||
hisax-$(CONFIG_HISAX_SPORTSTER) += sportster.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_MIC) += mic.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_NETJET) += nj_s.o netjet.o isac.o arcofi.o
|
||||
hisax-$(CONFIG_HISAX_NETJET_U) += nj_u.o netjet.o icc.o
|
||||
hisax-$(CONFIG_HISAX_HFCS) += hfcscard.o hfc_2bds0.o
|
||||
hisax-$(CONFIG_HISAX_HFC_PCI) += hfc_pci.o
|
||||
hisax-$(CONFIG_HISAX_HFC_SX) += hfc_sx.o
|
||||
hisax-$(CONFIG_HISAX_NICCY) += niccy.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_ISURF) += isurf.o isac.o arcofi.o isar.o
|
||||
hisax-$(CONFIG_HISAX_HSTSAPHIR) += saphir.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_BKM_A4T) += bkm_a4t.o isac.o arcofi.o jade.o
|
||||
hisax-$(CONFIG_HISAX_SCT_QUADRO) += bkm_a8.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_GAZEL) += gazel.o isac.o arcofi.o hscx.o
|
||||
hisax-$(CONFIG_HISAX_W6692) += w6692.o
|
||||
hisax-$(CONFIG_HISAX_ENTERNOW_PCI) += enternow_pci.o amd7930_fn.o
|
||||
|
|
@ -1,794 +0,0 @@
|
|||
/* gerdes_amd7930.c,v 0.99 2001/10/02
|
||||
*
|
||||
* gerdes_amd7930.c Amd 79C30A and 79C32A specific routines
|
||||
* (based on HiSax driver by Karsten Keil)
|
||||
*
|
||||
* Author Christoph Ersfeld <info@formula-n.de>
|
||||
* Formula-n Europe AG (www.formula-n.com)
|
||||
* previously Gerdes AG
|
||||
*
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
* Version 0.99 is the first release of this driver and there are
|
||||
* certainly a few bugs.
|
||||
*
|
||||
* Please don't report any malfunction to me without sending
|
||||
* (compressed) debug-logs.
|
||||
* It would be nearly impossible to retrace it.
|
||||
*
|
||||
* Log D-channel-processing as follows:
|
||||
*
|
||||
* 1. Load hisax with card-specific parameters, this example ist for
|
||||
* Formula-n enter:now ISDN PCI and compatible
|
||||
* (f.e. Gerdes Power ISDN PCI)
|
||||
*
|
||||
* modprobe hisax type=41 protocol=2 id=gerdes
|
||||
*
|
||||
* if you chose an other value for id, you need to modify the
|
||||
* code below, too.
|
||||
*
|
||||
* 2. set debug-level
|
||||
*
|
||||
* hisaxctrl gerdes 1 0x3ff
|
||||
* hisaxctrl gerdes 11 0x4f
|
||||
* cat /dev/isdnctrl >> ~/log &
|
||||
*
|
||||
* Please take also a look into /var/log/messages if there is
|
||||
* anything importand concerning HISAX.
|
||||
*
|
||||
*
|
||||
* Credits:
|
||||
* Programming the driver for Formula-n enter:now ISDN PCI and
|
||||
* necessary this driver for the used Amd 7930 D-channel-controller
|
||||
* was spnsored by Formula-n Europe AG.
|
||||
* Thanks to Karsten Keil and Petr Novak, who gave me support in
|
||||
* Hisax-specific questions.
|
||||
* I want so say special thanks to Carl-Friedrich Braun, who had to
|
||||
* answer a lot of questions about generally ISDN and about handling
|
||||
* of the Amd-Chip.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "hisax.h"
|
||||
#include "isdnl1.h"
|
||||
#include "isac.h"
|
||||
#include "amd7930_fn.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gfp.h>
|
||||
|
||||
static void Amd7930_new_ph(struct IsdnCardState *cs);
|
||||
|
||||
static WORD initAMD[] = {
|
||||
0x0100,
|
||||
|
||||
0x00A5, 3, 0x01, 0x40, 0x58, // LPR, LMR1, LMR2
|
||||
0x0086, 1, 0x0B, // DMR1 (D-Buffer TH-Interrupts on)
|
||||
0x0087, 1, 0xFF, // DMR2
|
||||
0x0092, 1, 0x03, // EFCR (extended mode d-channel-fifo on)
|
||||
0x0090, 4, 0xFE, 0xFF, 0x02, 0x0F, // FRAR4, SRAR4, DMR3, DMR4 (address recognition )
|
||||
0x0084, 2, 0x80, 0x00, // DRLR
|
||||
0x00C0, 1, 0x47, // PPCR1
|
||||
0x00C8, 1, 0x01, // PPCR2
|
||||
|
||||
0x0102,
|
||||
0x0107,
|
||||
0x01A1, 1,
|
||||
0x0121, 1,
|
||||
0x0189, 2,
|
||||
|
||||
0x0045, 4, 0x61, 0x72, 0x00, 0x00, // MCR1, MCR2, MCR3, MCR4
|
||||
0x0063, 2, 0x08, 0x08, // GX
|
||||
0x0064, 2, 0x08, 0x08, // GR
|
||||
0x0065, 2, 0x99, 0x00, // GER
|
||||
0x0066, 2, 0x7C, 0x8B, // STG
|
||||
0x0067, 2, 0x00, 0x00, // FTGR1, FTGR2
|
||||
0x0068, 2, 0x20, 0x20, // ATGR1, ATGR2
|
||||
0x0069, 1, 0x4F, // MMR1
|
||||
0x006A, 1, 0x00, // MMR2
|
||||
0x006C, 1, 0x40, // MMR3
|
||||
0x0021, 1, 0x02, // INIT
|
||||
0x00A3, 1, 0x40, // LMR1
|
||||
|
||||
0xFFFF
|
||||
};
|
||||
|
||||
|
||||
static void /* macro wWordAMD */
|
||||
WriteWordAmd7930(struct IsdnCardState *cs, BYTE reg, WORD val)
|
||||
{
|
||||
wByteAMD(cs, 0x00, reg);
|
||||
wByteAMD(cs, 0x01, LOBYTE(val));
|
||||
wByteAMD(cs, 0x01, HIBYTE(val));
|
||||
}
|
||||
|
||||
static WORD /* macro rWordAMD */
|
||||
ReadWordAmd7930(struct IsdnCardState *cs, BYTE reg)
|
||||
{
|
||||
WORD res;
|
||||
/* direct access register */
|
||||
if (reg < 8) {
|
||||
res = rByteAMD(cs, reg);
|
||||
res += 256 * rByteAMD(cs, reg);
|
||||
}
|
||||
/* indirect access register */
|
||||
else {
|
||||
wByteAMD(cs, 0x00, reg);
|
||||
res = rByteAMD(cs, 0x01);
|
||||
res += 256 * rByteAMD(cs, 0x01);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
Amd7930_ph_command(struct IsdnCardState *cs, u_char command, char *s)
|
||||
{
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "AMD7930: %s: ph_command 0x%02X", s, command);
|
||||
|
||||
cs->dc.amd7930.lmr1 = command;
|
||||
wByteAMD(cs, 0xA3, command);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static BYTE i430States[] = {
|
||||
// to reset F3 F4 F5 F6 F7 F8 AR from
|
||||
0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // init
|
||||
0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // reset
|
||||
0x01, 0x02, 0x00, 0x00, 0x00, 0x09, 0x05, 0x04, // F3
|
||||
0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F4
|
||||
0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F5
|
||||
0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00, // F6
|
||||
0x11, 0x13, 0x00, 0x00, 0x1B, 0x00, 0x15, 0x00, // F7
|
||||
0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // F8
|
||||
0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0A}; // AR
|
||||
|
||||
|
||||
/* Row init - reset F3 F4 F5 F6 F7 F8 AR */
|
||||
static BYTE stateHelper[] = { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
|
||||
|
||||
|
||||
|
||||
|
||||
static void
|
||||
Amd7930_get_state(struct IsdnCardState *cs) {
|
||||
BYTE lsr = rByteAMD(cs, 0xA1);
|
||||
cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
|
||||
Amd7930_new_ph(cs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
Amd7930_new_ph(struct IsdnCardState *cs)
|
||||
{
|
||||
u_char index = stateHelper[cs->dc.amd7930.old_state] * 8 + stateHelper[cs->dc.amd7930.ph_state] - 1;
|
||||
u_char message = i430States[index];
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "AMD7930: new_ph %d, old_ph %d, message %d, index %d",
|
||||
cs->dc.amd7930.ph_state, cs->dc.amd7930.old_state, message & 0x0f, index);
|
||||
|
||||
cs->dc.amd7930.old_state = cs->dc.amd7930.ph_state;
|
||||
|
||||
/* abort transmit if nessesary */
|
||||
if ((message & 0xf0) && (cs->tx_skb)) {
|
||||
wByteAMD(cs, 0x21, 0xC2);
|
||||
wByteAMD(cs, 0x21, 0x02);
|
||||
}
|
||||
|
||||
switch (message & 0x0f) {
|
||||
|
||||
case (1):
|
||||
l1_msg(cs, HW_RESET | INDICATION, NULL);
|
||||
Amd7930_get_state(cs);
|
||||
break;
|
||||
case (2): /* init, Card starts in F3 */
|
||||
l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
|
||||
break;
|
||||
case (3):
|
||||
l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
|
||||
break;
|
||||
case (4):
|
||||
l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
|
||||
Amd7930_ph_command(cs, 0x50, "HW_ENABLE REQUEST");
|
||||
break;
|
||||
case (5):
|
||||
l1_msg(cs, HW_RSYNC | INDICATION, NULL);
|
||||
break;
|
||||
case (6):
|
||||
l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
|
||||
break;
|
||||
case (7): /* init, Card starts in F7 */
|
||||
l1_msg(cs, HW_RSYNC | INDICATION, NULL);
|
||||
l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
|
||||
break;
|
||||
case (8):
|
||||
l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
|
||||
/* fall through */
|
||||
case (9):
|
||||
Amd7930_ph_command(cs, 0x40, "HW_ENABLE REQ cleared if set");
|
||||
l1_msg(cs, HW_RSYNC | INDICATION, NULL);
|
||||
l1_msg(cs, HW_INFO2 | INDICATION, NULL);
|
||||
l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
|
||||
break;
|
||||
case (10):
|
||||
Amd7930_ph_command(cs, 0x40, "T3 expired, HW_ENABLE REQ cleared");
|
||||
cs->dc.amd7930.old_state = 3;
|
||||
break;
|
||||
case (11):
|
||||
l1_msg(cs, HW_INFO2 | INDICATION, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
Amd7930_bh(struct work_struct *work)
|
||||
{
|
||||
struct IsdnCardState *cs =
|
||||
container_of(work, struct IsdnCardState, tqueue);
|
||||
struct PStack *stptr;
|
||||
|
||||
if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
|
||||
if (cs->debug)
|
||||
debugl1(cs, "Amd7930: bh, D-Channel Busy cleared");
|
||||
stptr = cs->stlist;
|
||||
while (stptr != NULL) {
|
||||
stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
}
|
||||
if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "AMD7930: bh, D_L1STATECHANGE");
|
||||
Amd7930_new_ph(cs);
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "AMD7930: bh, D_RCVBUFREADY");
|
||||
DChannel_proc_rcv(cs);
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "AMD7930: bh, D_XMTBUFREADY");
|
||||
DChannel_proc_xmt(cs);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Amd7930_empty_Dfifo(struct IsdnCardState *cs, int flag)
|
||||
{
|
||||
|
||||
BYTE stat, der;
|
||||
BYTE *ptr;
|
||||
struct sk_buff *skb;
|
||||
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "Amd7930: empty_Dfifo");
|
||||
|
||||
|
||||
ptr = cs->rcvbuf + cs->rcvidx;
|
||||
|
||||
/* AMD interrupts off */
|
||||
AmdIrqOff(cs);
|
||||
|
||||
/* read D-Channel-Fifo*/
|
||||
stat = rByteAMD(cs, 0x07); // DSR2
|
||||
|
||||
/* while Data in Fifo ... */
|
||||
while ((stat & 2) && ((ptr-cs->rcvbuf) < MAX_DFRAME_LEN_L1)) {
|
||||
*ptr = rByteAMD(cs, 0x04); // DCRB
|
||||
ptr++;
|
||||
stat = rByteAMD(cs, 0x07); // DSR2
|
||||
cs->rcvidx = ptr - cs->rcvbuf;
|
||||
|
||||
/* Paket ready? */
|
||||
if (stat & 1) {
|
||||
|
||||
der = rWordAMD(cs, 0x03);
|
||||
|
||||
/* no errors, packet ok */
|
||||
if (!der && !flag) {
|
||||
rWordAMD(cs, 0x89); // clear DRCR
|
||||
|
||||
if ((cs->rcvidx) > 0) {
|
||||
if (!(skb = alloc_skb(cs->rcvidx, GFP_ATOMIC)))
|
||||
printk(KERN_WARNING "HiSax: Amd7930: empty_Dfifo, D receive out of memory!\n");
|
||||
else {
|
||||
/* Debugging */
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "Amd7930: empty_Dfifo cnt: %d |", cs->rcvidx);
|
||||
QuickHex(t, cs->rcvbuf, cs->rcvidx);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
/* moves received data in sk-buffer */
|
||||
skb_put_data(skb, cs->rcvbuf,
|
||||
cs->rcvidx);
|
||||
skb_queue_tail(&cs->rq, skb);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/* throw damaged packets away, reset receive-buffer, indicate RX */
|
||||
ptr = cs->rcvbuf;
|
||||
cs->rcvidx = 0;
|
||||
schedule_event(cs, D_RCVBUFREADY);
|
||||
}
|
||||
}
|
||||
/* Packet to long, overflow */
|
||||
if (cs->rcvidx >= MAX_DFRAME_LEN_L1) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "AMD7930: empty_Dfifo L2-Framelength overrun");
|
||||
cs->rcvidx = 0;
|
||||
return;
|
||||
}
|
||||
/* AMD interrupts on */
|
||||
AmdIrqOn(cs);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
Amd7930_fill_Dfifo(struct IsdnCardState *cs)
|
||||
{
|
||||
|
||||
WORD dtcrr, dtcrw, len, count;
|
||||
BYTE txstat, dmr3;
|
||||
BYTE *ptr, *deb_ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "Amd7930: fill_Dfifo");
|
||||
|
||||
if ((!cs->tx_skb) || (cs->tx_skb->len <= 0))
|
||||
return;
|
||||
|
||||
dtcrw = 0;
|
||||
if (!cs->dc.amd7930.tx_xmtlen)
|
||||
/* new Frame */
|
||||
len = dtcrw = cs->tx_skb->len;
|
||||
/* continue frame */
|
||||
else len = cs->dc.amd7930.tx_xmtlen;
|
||||
|
||||
|
||||
/* AMD interrupts off */
|
||||
AmdIrqOff(cs);
|
||||
|
||||
deb_ptr = ptr = cs->tx_skb->data;
|
||||
|
||||
/* while free place in tx-fifo available and data in sk-buffer */
|
||||
txstat = 0x10;
|
||||
while ((txstat & 0x10) && (cs->tx_cnt < len)) {
|
||||
wByteAMD(cs, 0x04, *ptr);
|
||||
ptr++;
|
||||
cs->tx_cnt++;
|
||||
txstat = rByteAMD(cs, 0x07);
|
||||
}
|
||||
count = ptr - cs->tx_skb->data;
|
||||
skb_pull(cs->tx_skb, count);
|
||||
|
||||
|
||||
dtcrr = rWordAMD(cs, 0x85); // DTCR
|
||||
dmr3 = rByteAMD(cs, 0x8E);
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC) {
|
||||
debugl1(cs, "Amd7930: fill_Dfifo, DMR3: 0x%02X, DTCR read: 0x%04X write: 0x%02X 0x%02X", dmr3, dtcrr, LOBYTE(dtcrw), HIBYTE(dtcrw));
|
||||
}
|
||||
|
||||
/* writeing of dtcrw starts transmit */
|
||||
if (!cs->dc.amd7930.tx_xmtlen) {
|
||||
wWordAMD(cs, 0x85, dtcrw);
|
||||
cs->dc.amd7930.tx_xmtlen = dtcrw;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
debugl1(cs, "Amd7930: fill_Dfifo dbusytimer running");
|
||||
del_timer(&cs->dbusytimer);
|
||||
}
|
||||
cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
|
||||
add_timer(&cs->dbusytimer);
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "Amd7930: fill_Dfifo cnt: %d |", count);
|
||||
QuickHex(t, deb_ptr, count);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
/* AMD interrupts on */
|
||||
AmdIrqOn(cs);
|
||||
}
|
||||
|
||||
|
||||
void Amd7930_interrupt(struct IsdnCardState *cs, BYTE irflags)
|
||||
{
|
||||
BYTE dsr1, dsr2, lsr;
|
||||
WORD der;
|
||||
|
||||
while (irflags)
|
||||
{
|
||||
|
||||
dsr1 = rByteAMD(cs, 0x02);
|
||||
der = rWordAMD(cs, 0x03);
|
||||
dsr2 = rByteAMD(cs, 0x07);
|
||||
lsr = rByteAMD(cs, 0xA1);
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: interrupt: flags: 0x%02X, DSR1: 0x%02X, DSR2: 0x%02X, LSR: 0x%02X, DER=0x%04X", irflags, dsr1, dsr2, lsr, der);
|
||||
|
||||
/* D error -> read DER and DSR2 bit 2 */
|
||||
if (der || (dsr2 & 4)) {
|
||||
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "Amd7930: interrupt: D error DER=0x%04X", der);
|
||||
|
||||
/* RX, TX abort if collision detected */
|
||||
if (der & 2) {
|
||||
wByteAMD(cs, 0x21, 0xC2);
|
||||
wByteAMD(cs, 0x21, 0x02);
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
/* restart frame */
|
||||
if (cs->tx_skb) {
|
||||
skb_push(cs->tx_skb, cs->tx_cnt);
|
||||
cs->tx_cnt = 0;
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
Amd7930_fill_Dfifo(cs);
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: Amd7930 D-Collision, no skb\n");
|
||||
debugl1(cs, "Amd7930: interrupt: D-Collision, no skb");
|
||||
}
|
||||
}
|
||||
/* remove damaged data from fifo */
|
||||
Amd7930_empty_Dfifo(cs, 1);
|
||||
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
/* restart TX-Frame */
|
||||
if (cs->tx_skb) {
|
||||
skb_push(cs->tx_skb, cs->tx_cnt);
|
||||
cs->tx_cnt = 0;
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
Amd7930_fill_Dfifo(cs);
|
||||
}
|
||||
}
|
||||
|
||||
/* D TX FIFO empty -> fill */
|
||||
if (irflags & 1) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: interrupt: clear Timer and fill D-TX-FIFO if data");
|
||||
|
||||
/* AMD interrupts off */
|
||||
AmdIrqOff(cs);
|
||||
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
if (cs->tx_skb) {
|
||||
if (cs->tx_skb->len)
|
||||
Amd7930_fill_Dfifo(cs);
|
||||
}
|
||||
/* AMD interrupts on */
|
||||
AmdIrqOn(cs);
|
||||
}
|
||||
|
||||
|
||||
/* D RX FIFO full or tiny packet in Fifo -> empty */
|
||||
if ((irflags & 2) || (dsr1 & 2)) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: interrupt: empty D-FIFO");
|
||||
Amd7930_empty_Dfifo(cs, 0);
|
||||
}
|
||||
|
||||
|
||||
/* D-Frame transmit complete */
|
||||
if (dsr1 & 64) {
|
||||
if (cs->debug & L1_DEB_ISAC) {
|
||||
debugl1(cs, "Amd7930: interrupt: transmit packet ready");
|
||||
}
|
||||
/* AMD interrupts off */
|
||||
AmdIrqOff(cs);
|
||||
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
|
||||
if (cs->tx_skb) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: interrupt: TX-Packet ready, freeing skb");
|
||||
dev_kfree_skb_irq(cs->tx_skb);
|
||||
cs->tx_cnt = 0;
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
cs->tx_skb = NULL;
|
||||
}
|
||||
if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: interrupt: TX-Packet ready, next packet dequeued");
|
||||
cs->tx_cnt = 0;
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
Amd7930_fill_Dfifo(cs);
|
||||
}
|
||||
else
|
||||
schedule_event(cs, D_XMTBUFREADY);
|
||||
/* AMD interrupts on */
|
||||
AmdIrqOn(cs);
|
||||
}
|
||||
|
||||
/* LIU status interrupt -> read LSR, check statechanges */
|
||||
if (lsr & 0x38) {
|
||||
/* AMD interrupts off */
|
||||
AmdIrqOff(cs);
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd: interrupt: LSR=0x%02X, LIU is in state %d", lsr, ((lsr & 0x7) + 2));
|
||||
|
||||
cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
|
||||
|
||||
schedule_event(cs, D_L1STATECHANGE);
|
||||
/* AMD interrupts on */
|
||||
AmdIrqOn(cs);
|
||||
}
|
||||
|
||||
/* reads Interrupt-Register again. If there is a new interrupt-flag: restart handler */
|
||||
irflags = rByteAMD(cs, 0x00);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
Amd7930_l1hw(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: l1hw called, pr: 0x%04X", pr);
|
||||
|
||||
switch (pr) {
|
||||
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);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->tx_skb) {
|
||||
skb_queue_tail(&cs->sq, skb);
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA Queued", 0);
|
||||
#endif
|
||||
} else {
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA", 0);
|
||||
#endif
|
||||
Amd7930_fill_Dfifo(cs);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | INDICATION):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->tx_skb) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "Amd7930: l1hw: l2l1 tx_skb exist this shouldn't happen");
|
||||
skb_queue_tail(&cs->sq, skb);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
}
|
||||
if (cs->debug & DEB_DLOG_HEX)
|
||||
LogFrame(cs, skb->data, skb->len);
|
||||
if (cs->debug & DEB_DLOG_VERBOSE)
|
||||
dlogframe(cs, skb, 0);
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA_PULLED", 0);
|
||||
#endif
|
||||
Amd7930_fill_Dfifo(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | REQUEST):
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
debugl1(cs, "Amd7930: l1hw: -> PH_REQUEST_PULL, skb: %s", (cs->tx_skb) ? "yes" : "no");
|
||||
#endif
|
||||
if (!cs->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 (HW_RESET | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->dc.amd7930.ph_state == 8) {
|
||||
/* b-channels off, PH-AR cleared
|
||||
* change to F3 */
|
||||
Amd7930_ph_command(cs, 0x20, "HW_RESET REQUEST"); //LMR1 bit 5
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
} else {
|
||||
Amd7930_ph_command(cs, 0x40, "HW_RESET REQUEST");
|
||||
cs->dc.amd7930.ph_state = 2;
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
Amd7930_new_ph(cs);
|
||||
}
|
||||
break;
|
||||
case (HW_ENABLE | REQUEST):
|
||||
cs->dc.amd7930.ph_state = 9;
|
||||
Amd7930_new_ph(cs);
|
||||
break;
|
||||
case (HW_INFO3 | REQUEST):
|
||||
// automatic
|
||||
break;
|
||||
case (HW_TESTLOOP | REQUEST):
|
||||
/* not implemented yet */
|
||||
break;
|
||||
case (HW_DEACTIVATE | RESPONSE):
|
||||
skb_queue_purge(&cs->rq);
|
||||
skb_queue_purge(&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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
break;
|
||||
default:
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "Amd7930: l1hw: unknown %04x", pr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setstack_Amd7930(struct PStack *st, struct IsdnCardState *cs)
|
||||
{
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: setstack called");
|
||||
|
||||
st->l1.l1hw = Amd7930_l1hw;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
DC_Close_Amd7930(struct IsdnCardState *cs) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: DC_Close called");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dbusy_timer_handler(struct timer_list *t)
|
||||
{
|
||||
struct IsdnCardState *cs = from_timer(cs, t, dbusytimer);
|
||||
u_long flags;
|
||||
struct PStack *stptr;
|
||||
WORD dtcr, der;
|
||||
BYTE dsr1, dsr2;
|
||||
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: dbusy_timer expired!");
|
||||
|
||||
if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
/* D Transmit Byte Count Register:
|
||||
* Counts down packet's number of Bytes, 0 if packet ready */
|
||||
dtcr = rWordAMD(cs, 0x85);
|
||||
dsr1 = rByteAMD(cs, 0x02);
|
||||
dsr2 = rByteAMD(cs, 0x07);
|
||||
der = rWordAMD(cs, 0x03);
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: dbusy_timer_handler: DSR1=0x%02X, DSR2=0x%02X, DER=0x%04X, cs->tx_skb->len=%u, tx_stat=%u, dtcr=%u, cs->tx_cnt=%u", dsr1, dsr2, der, cs->tx_skb->len, cs->dc.amd7930.tx_xmtlen, dtcr, cs->tx_cnt);
|
||||
|
||||
if ((cs->dc.amd7930.tx_xmtlen - dtcr) < cs->tx_cnt) { /* D-Channel Busy */
|
||||
test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
|
||||
stptr = cs->stlist;
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
while (stptr != NULL) {
|
||||
stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* discard frame; reset transceiver */
|
||||
test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
|
||||
if (cs->tx_skb) {
|
||||
dev_kfree_skb_any(cs->tx_skb);
|
||||
cs->tx_cnt = 0;
|
||||
cs->tx_skb = NULL;
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: Amd7930: D-Channel Busy no skb\n");
|
||||
debugl1(cs, "Amd7930: D-Channel Busy no skb");
|
||||
|
||||
}
|
||||
/* Transmitter reset, abort transmit */
|
||||
wByteAMD(cs, 0x21, 0x82);
|
||||
wByteAMD(cs, 0x21, 0x02);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
cs->irq_func(cs->irq, cs);
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: dbusy_timer_handler: Transmitter reset");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Amd7930_init(struct IsdnCardState *cs)
|
||||
{
|
||||
WORD *ptr;
|
||||
BYTE cmd, cnt;
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "Amd7930: initamd called");
|
||||
|
||||
cs->dc.amd7930.tx_xmtlen = 0;
|
||||
cs->dc.amd7930.old_state = 0;
|
||||
cs->dc.amd7930.lmr1 = 0x40;
|
||||
cs->dc.amd7930.ph_command = Amd7930_ph_command;
|
||||
cs->setstack_d = setstack_Amd7930;
|
||||
cs->DC_Close = DC_Close_Amd7930;
|
||||
|
||||
/* AMD Initialisation */
|
||||
for (ptr = initAMD; *ptr != 0xFFFF; ) {
|
||||
cmd = LOBYTE(*ptr);
|
||||
|
||||
/* read */
|
||||
if (*ptr++ >= 0x100) {
|
||||
if (cmd < 8)
|
||||
/* reset register */
|
||||
rByteAMD(cs, cmd);
|
||||
else {
|
||||
wByteAMD(cs, 0x00, cmd);
|
||||
for (cnt = *ptr++; cnt > 0; cnt--)
|
||||
rByteAMD(cs, 0x01);
|
||||
}
|
||||
}
|
||||
/* write */
|
||||
else if (cmd < 8)
|
||||
wByteAMD(cs, cmd, LOBYTE(*ptr++));
|
||||
|
||||
else {
|
||||
wByteAMD(cs, 0x00, cmd);
|
||||
for (cnt = *ptr++; cnt > 0; cnt--)
|
||||
wByteAMD(cs, 0x01, LOBYTE(*ptr++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup_Amd7930(struct IsdnCardState *cs)
|
||||
{
|
||||
INIT_WORK(&cs->tqueue, Amd7930_bh);
|
||||
timer_setup(&cs->dbusytimer, dbusy_timer_handler, 0);
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/* drivers/isdn/hisax/amd7930_fn.h
|
||||
*
|
||||
* gerdes_amd7930.h Header-file included by
|
||||
* gerdes_amd7930.c
|
||||
*
|
||||
* Author Christoph Ersfeld <info@formula-n.de>
|
||||
* Formula-n Europe AG (www.formula-n.com)
|
||||
* previously Gerdes AG
|
||||
*
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#define BYTE unsigned char
|
||||
#define WORD unsigned int
|
||||
#define rByteAMD(cs, reg) cs->readisac(cs, reg)
|
||||
#define wByteAMD(cs, reg, val) cs->writeisac(cs, reg, val)
|
||||
#define rWordAMD(cs, reg) ReadWordAmd7930(cs, reg)
|
||||
#define wWordAMD(cs, reg, val) WriteWordAmd7930(cs, reg, val)
|
||||
#define HIBYTE(w) ((unsigned char)((w & 0xff00) / 256))
|
||||
#define LOBYTE(w) ((unsigned char)(w & 0x00ff))
|
||||
|
||||
#define AmdIrqOff(cs) cs->dc.amd7930.setIrqMask(cs, 0)
|
||||
#define AmdIrqOn(cs) cs->dc.amd7930.setIrqMask(cs, 1)
|
||||
|
||||
#define AMD_CR 0x00
|
||||
#define AMD_DR 0x01
|
||||
|
||||
|
||||
#define DBUSY_TIMER_VALUE 80
|
||||
|
||||
extern void Amd7930_interrupt(struct IsdnCardState *, unsigned char);
|
||||
extern void Amd7930_init(struct IsdnCardState *);
|
||||
extern void setup_Amd7930(struct IsdnCardState *);
|
|
@ -1,131 +0,0 @@
|
|||
/* $Id: arcofi.c,v 1.14.2.3 2004/01/13 14:31:24 keil Exp $
|
||||
*
|
||||
* Ansteuerung ARCOFI 2165
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include "hisax.h"
|
||||
#include "isdnl1.h"
|
||||
#include "isac.h"
|
||||
#include "arcofi.h"
|
||||
|
||||
#define ARCOFI_TIMER_VALUE 20
|
||||
|
||||
static void
|
||||
add_arcofi_timer(struct IsdnCardState *cs) {
|
||||
if (test_and_set_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
|
||||
del_timer(&cs->dc.isac.arcofitimer);
|
||||
}
|
||||
cs->dc.isac.arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ) / 1000);
|
||||
add_timer(&cs->dc.isac.arcofitimer);
|
||||
}
|
||||
|
||||
static void
|
||||
send_arcofi(struct IsdnCardState *cs) {
|
||||
add_arcofi_timer(cs);
|
||||
cs->dc.isac.mon_txp = 0;
|
||||
cs->dc.isac.mon_txc = cs->dc.isac.arcofi_list->len;
|
||||
memcpy(cs->dc.isac.mon_tx, cs->dc.isac.arcofi_list->msg, cs->dc.isac.mon_txc);
|
||||
switch (cs->dc.isac.arcofi_bc) {
|
||||
case 0: break;
|
||||
case 1: cs->dc.isac.mon_tx[1] |= 0x40;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
cs->dc.isac.mocr &= 0x0f;
|
||||
cs->dc.isac.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
(void) cs->readisac(cs, ISAC_MOSR);
|
||||
cs->writeisac(cs, ISAC_MOX1, cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
|
||||
cs->dc.isac.mocr |= 0x10;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
}
|
||||
|
||||
int
|
||||
arcofi_fsm(struct IsdnCardState *cs, int event, void *data) {
|
||||
if (cs->debug & L1_DEB_MONITOR) {
|
||||
debugl1(cs, "arcofi state %d event %d", cs->dc.isac.arcofi_state, event);
|
||||
}
|
||||
if (event == ARCOFI_TIMEOUT) {
|
||||
cs->dc.isac.arcofi_state = ARCOFI_NOP;
|
||||
test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags);
|
||||
wake_up(&cs->dc.isac.arcofi_wait);
|
||||
return (1);
|
||||
}
|
||||
switch (cs->dc.isac.arcofi_state) {
|
||||
case ARCOFI_NOP:
|
||||
if (event == ARCOFI_START) {
|
||||
cs->dc.isac.arcofi_list = data;
|
||||
cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
|
||||
send_arcofi(cs);
|
||||
}
|
||||
break;
|
||||
case ARCOFI_TRANSMIT:
|
||||
if (event == ARCOFI_TX_END) {
|
||||
if (cs->dc.isac.arcofi_list->receive) {
|
||||
add_arcofi_timer(cs);
|
||||
cs->dc.isac.arcofi_state = ARCOFI_RECEIVE;
|
||||
} else {
|
||||
if (cs->dc.isac.arcofi_list->next) {
|
||||
cs->dc.isac.arcofi_list =
|
||||
cs->dc.isac.arcofi_list->next;
|
||||
send_arcofi(cs);
|
||||
} else {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
|
||||
del_timer(&cs->dc.isac.arcofitimer);
|
||||
}
|
||||
cs->dc.isac.arcofi_state = ARCOFI_NOP;
|
||||
wake_up(&cs->dc.isac.arcofi_wait);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ARCOFI_RECEIVE:
|
||||
if (event == ARCOFI_RX_END) {
|
||||
if (cs->dc.isac.arcofi_list->next) {
|
||||
cs->dc.isac.arcofi_list =
|
||||
cs->dc.isac.arcofi_list->next;
|
||||
cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
|
||||
send_arcofi(cs);
|
||||
} else {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
|
||||
del_timer(&cs->dc.isac.arcofitimer);
|
||||
}
|
||||
cs->dc.isac.arcofi_state = ARCOFI_NOP;
|
||||
wake_up(&cs->dc.isac.arcofi_wait);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debugl1(cs, "Arcofi unknown state %x", cs->dc.isac.arcofi_state);
|
||||
return (2);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
arcofi_timer(struct timer_list *t) {
|
||||
struct IsdnCardState *cs = from_timer(cs, t, dc.isac.arcofitimer);
|
||||
arcofi_fsm(cs, ARCOFI_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
clear_arcofi(struct IsdnCardState *cs) {
|
||||
if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
|
||||
del_timer(&cs->dc.isac.arcofitimer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
init_arcofi(struct IsdnCardState *cs) {
|
||||
timer_setup(&cs->dc.isac.arcofitimer, arcofi_timer, 0);
|
||||
init_waitqueue_head(&cs->dc.isac.arcofi_wait);
|
||||
test_and_set_bit(HW_ARCOFI, &cs->HW_Flags);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/* $Id: arcofi.h,v 1.6.6.2 2001/09/23 22:24:46 kai Exp $
|
||||
*
|
||||
* Ansteuerung ARCOFI 2165
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#define ARCOFI_USE 1
|
||||
|
||||
/* states */
|
||||
#define ARCOFI_NOP 0
|
||||
#define ARCOFI_TRANSMIT 1
|
||||
#define ARCOFI_RECEIVE 2
|
||||
/* events */
|
||||
#define ARCOFI_START 1
|
||||
#define ARCOFI_TX_END 2
|
||||
#define ARCOFI_RX_END 3
|
||||
#define ARCOFI_TIMEOUT 4
|
||||
|
||||
extern int arcofi_fsm(struct IsdnCardState *cs, int event, void *data);
|
||||
extern void init_arcofi(struct IsdnCardState *cs);
|
||||
extern void clear_arcofi(struct IsdnCardState *cs);
|
|
@ -1,423 +0,0 @@
|
|||
/* $Id: asuscom.c,v 1.14.2.4 2004/01/13 23:48:39 keil Exp $
|
||||
*
|
||||
* low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to ASUSCOM NETWORK INC. Taiwan and Dynalink NL for information
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/isapnp.h>
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "ipac.h"
|
||||
#include "hscx.h"
|
||||
#include "isdnl1.h"
|
||||
|
||||
static const char *Asuscom_revision = "$Revision: 1.14.2.4 $";
|
||||
|
||||
#define byteout(addr, val) outb(val, addr)
|
||||
#define bytein(addr) inb(addr)
|
||||
|
||||
#define ASUS_ISAC 0
|
||||
#define ASUS_HSCX 1
|
||||
#define ASUS_ADR 2
|
||||
#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 */
|
||||
|
||||
static inline u_char
|
||||
readreg(unsigned int ale, unsigned int adr, u_char off)
|
||||
{
|
||||
register u_char ret;
|
||||
|
||||
byteout(ale, off);
|
||||
ret = bytein(adr);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static inline void
|
||||
readfifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
|
||||
{
|
||||
byteout(ale, off);
|
||||
insb(adr, data, size);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
|
||||
{
|
||||
byteout(ale, off);
|
||||
byteout(adr, data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
|
||||
{
|
||||
byteout(ale, off);
|
||||
outsb(adr, data, size);
|
||||
}
|
||||
|
||||
/* Interface functions */
|
||||
|
||||
static u_char
|
||||
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
{
|
||||
return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value);
|
||||
}
|
||||
|
||||
static void
|
||||
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return (readreg(cs->hw.asus.adr,
|
||||
cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0)));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.asus.adr,
|
||||
cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value);
|
||||
}
|
||||
|
||||
/*
|
||||
* fast interrupt HSCX stuff goes here
|
||||
*/
|
||||
|
||||
#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \
|
||||
cs->hw.asus.hscx, reg + (nr ? 0x40 : 0))
|
||||
#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \
|
||||
cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data)
|
||||
|
||||
#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \
|
||||
cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
|
||||
|
||||
#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \
|
||||
cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
|
||||
|
||||
#include "hscx_irq.c"
|
||||
|
||||
static irqreturn_t
|
||||
asuscom_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char val;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
|
||||
Start_HSCX:
|
||||
if (val)
|
||||
hscx_int_main(cs, val);
|
||||
val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
|
||||
Start_ISAC:
|
||||
if (val)
|
||||
isac_interrupt(cs, val);
|
||||
val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
|
||||
if (val) {
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "HSCX IntStat after IntRoutine");
|
||||
goto Start_HSCX;
|
||||
}
|
||||
val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
|
||||
if (val) {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ISAC IntStat after IntRoutine");
|
||||
goto Start_ISAC;
|
||||
}
|
||||
writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF);
|
||||
writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF);
|
||||
writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF);
|
||||
writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0);
|
||||
writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0);
|
||||
writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
asuscom_interrupt_ipac(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char ista, val, icnt = 5;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
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);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
release_io_asuscom(struct IsdnCardState *cs)
|
||||
{
|
||||
int bytecnt = 8;
|
||||
|
||||
if (cs->hw.asus.cfg_reg)
|
||||
release_region(cs->hw.asus.cfg_reg, bytecnt);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_asuscom(struct IsdnCardState *cs)
|
||||
{
|
||||
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 */
|
||||
mdelay(10);
|
||||
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 */
|
||||
mdelay(10);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_asuscom(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_RELEASE:
|
||||
release_io_asuscom(cs);
|
||||
return (0);
|
||||
case CARD_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
cs->debug |= L1_DEB_IPAC;
|
||||
inithscxisac(cs, 3);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
static struct isapnp_device_id asus_ids[] = {
|
||||
{ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688),
|
||||
ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688),
|
||||
(unsigned long) "Asus1688 PnP" },
|
||||
{ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690),
|
||||
ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690),
|
||||
(unsigned long) "Asus1690 PnP" },
|
||||
{ ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020),
|
||||
ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020),
|
||||
(unsigned long) "Isurf2 PnP" },
|
||||
{ ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000),
|
||||
ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000),
|
||||
(unsigned long) "Iscas TE320" },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct isapnp_device_id *ipid = &asus_ids[0];
|
||||
static struct pnp_card *pnp_c = NULL;
|
||||
#endif
|
||||
|
||||
int setup_asuscom(struct IsdnCard *card)
|
||||
{
|
||||
int bytecnt;
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
u_char val;
|
||||
char tmp[64];
|
||||
|
||||
strcpy(tmp, Asuscom_revision);
|
||||
printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp));
|
||||
if (cs->typ != ISDN_CTYPE_ASUSCOM)
|
||||
return (0);
|
||||
#ifdef __ISAPNP__
|
||||
if (!card->para[1] && isapnp_present()) {
|
||||
struct pnp_dev *pnp_d;
|
||||
while (ipid->card_vendor) {
|
||||
if ((pnp_c = pnp_find_card(ipid->card_vendor,
|
||||
ipid->card_device, pnp_c))) {
|
||||
pnp_d = NULL;
|
||||
if ((pnp_d = pnp_find_dev(pnp_c,
|
||||
ipid->vendor, ipid->function, pnp_d))) {
|
||||
int err;
|
||||
|
||||
printk(KERN_INFO "HiSax: %s detected\n",
|
||||
(char *)ipid->driver_data);
|
||||
pnp_disable_dev(pnp_d);
|
||||
err = pnp_activate_dev(pnp_d);
|
||||
if (err < 0) {
|
||||
printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
|
||||
__func__, err);
|
||||
return (0);
|
||||
}
|
||||
card->para[1] = pnp_port_start(pnp_d, 0);
|
||||
card->para[0] = pnp_irq(pnp_d, 0);
|
||||
if (card->para[0] == -1 || !card->para[1]) {
|
||||
printk(KERN_ERR "AsusPnP:some resources are missing %ld/%lx\n",
|
||||
card->para[0], card->para[1]);
|
||||
pnp_disable_dev(pnp_d);
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
printk(KERN_ERR "AsusPnP: PnP error card found, no device\n");
|
||||
}
|
||||
}
|
||||
ipid++;
|
||||
pnp_c = NULL;
|
||||
}
|
||||
if (!ipid->card_vendor) {
|
||||
printk(KERN_INFO "AsusPnP: no ISAPnP card found\n");
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bytecnt = 8;
|
||||
cs->hw.asus.cfg_reg = card->para[1];
|
||||
cs->irq = card->para[0];
|
||||
if (!request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: ISDNLink config port %x-%x already in use\n",
|
||||
cs->hw.asus.cfg_reg,
|
||||
cs->hw.asus.cfg_reg + bytecnt);
|
||||
return (0);
|
||||
}
|
||||
printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
|
||||
cs->hw.asus.cfg_reg, cs->irq);
|
||||
setup_isac(cs);
|
||||
cs->BC_Read_Reg = &ReadHSCX;
|
||||
cs->BC_Write_Reg = &WriteHSCX;
|
||||
cs->BC_Send_Data = &hscx_fill_fifo;
|
||||
cs->cardmsg = &Asus_card_msg;
|
||||
val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE,
|
||||
cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
|
||||
if ((val == 1) || (val == 2)) {
|
||||
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;
|
||||
cs->irq_func = &asuscom_interrupt_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;
|
||||
cs->irq_func = &asuscom_interrupt;
|
||||
ISACVersion(cs, "ISDNLink:");
|
||||
if (HscxVersion(cs, "ISDNLink:")) {
|
||||
printk(KERN_WARNING
|
||||
"ISDNLink: wrong HSCX versions check IO address\n");
|
||||
release_io_asuscom(cs);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
return (1);
|
||||
}
|
|
@ -1,307 +0,0 @@
|
|||
/* $Id: avm_a1.c,v 2.15.2.4 2004/01/13 21:46:03 keil Exp $
|
||||
*
|
||||
* low level stuff for AVM A1 (Fritz) isdn cards
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "hscx.h"
|
||||
#include "isdnl1.h"
|
||||
|
||||
static const char *avm_revision = "$Revision: 2.15.2.4 $";
|
||||
|
||||
#define AVM_A1_STAT_ISAC 0x01
|
||||
#define AVM_A1_STAT_HSCX 0x02
|
||||
#define AVM_A1_STAT_TIMER 0x04
|
||||
|
||||
#define byteout(addr, val) outb(val, addr)
|
||||
#define bytein(addr) inb(addr)
|
||||
|
||||
static inline u_char
|
||||
readreg(unsigned int adr, u_char off)
|
||||
{
|
||||
return (bytein(adr + off));
|
||||
}
|
||||
|
||||
static inline void
|
||||
writereg(unsigned int adr, u_char off, u_char data)
|
||||
{
|
||||
byteout(adr + off, data);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
read_fifo(unsigned int adr, u_char *data, int size)
|
||||
{
|
||||
insb(adr, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
write_fifo(unsigned int adr, u_char *data, int size)
|
||||
{
|
||||
outsb(adr, data, size);
|
||||
}
|
||||
|
||||
/* Interface functions */
|
||||
|
||||
static u_char
|
||||
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
{
|
||||
return (readreg(cs->hw.avm.isac, offset));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.avm.isac, offset, value);
|
||||
}
|
||||
|
||||
static void
|
||||
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
read_fifo(cs->hw.avm.isacfifo, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
write_fifo(cs->hw.avm.isacfifo, data, size);
|
||||
}
|
||||
|
||||
static u_char
|
||||
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
|
||||
{
|
||||
return (readreg(cs->hw.avm.hscx[hscx], offset));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.avm.hscx[hscx], offset, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* fast interrupt HSCX stuff goes here
|
||||
*/
|
||||
|
||||
#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg)
|
||||
#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data)
|
||||
#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
|
||||
#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
|
||||
|
||||
#include "hscx_irq.c"
|
||||
|
||||
static irqreturn_t
|
||||
avm_a1_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char val, sval;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) {
|
||||
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)
|
||||
debugl1(cs, "avm IntStatus %x", sval);
|
||||
if (!(sval & AVM_A1_STAT_HSCX)) {
|
||||
val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
|
||||
if (val)
|
||||
hscx_int_main(cs, val);
|
||||
}
|
||||
if (!(sval & AVM_A1_STAT_ISAC)) {
|
||||
val = readreg(cs->hw.avm.isac, ISAC_ISTA);
|
||||
if (val)
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
}
|
||||
writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF);
|
||||
writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF);
|
||||
writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF);
|
||||
writereg(cs->hw.avm.isac, ISAC_MASK, 0x0);
|
||||
writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0);
|
||||
writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static inline void
|
||||
release_ioregs(struct IsdnCardState *cs, int mask)
|
||||
{
|
||||
release_region(cs->hw.avm.cfg_reg, 8);
|
||||
if (mask & 1)
|
||||
release_region(cs->hw.avm.isac + 32, 32);
|
||||
if (mask & 2)
|
||||
release_region(cs->hw.avm.isacfifo, 1);
|
||||
if (mask & 4)
|
||||
release_region(cs->hw.avm.hscx[0] + 32, 32);
|
||||
if (mask & 8)
|
||||
release_region(cs->hw.avm.hscxfifo[0], 1);
|
||||
if (mask & 0x10)
|
||||
release_region(cs->hw.avm.hscx[1] + 32, 32);
|
||||
if (mask & 0x20)
|
||||
release_region(cs->hw.avm.hscxfifo[1], 1);
|
||||
}
|
||||
|
||||
static int
|
||||
AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
return (0);
|
||||
case CARD_RELEASE:
|
||||
release_ioregs(cs, 0x3f);
|
||||
return (0);
|
||||
case CARD_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
inithscxisac(cs, 1);
|
||||
byteout(cs->hw.avm.cfg_reg, 0x16);
|
||||
byteout(cs->hw.avm.cfg_reg, 0x1E);
|
||||
inithscxisac(cs, 2);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int setup_avm_a1(struct IsdnCard *card)
|
||||
{
|
||||
u_char val;
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
|
||||
strcpy(tmp, avm_revision);
|
||||
printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
|
||||
if (cs->typ != ISDN_CTYPE_A1)
|
||||
return (0);
|
||||
|
||||
cs->hw.avm.cfg_reg = card->para[1] + 0x1800;
|
||||
cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20;
|
||||
cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20;
|
||||
cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20;
|
||||
cs->hw.avm.isacfifo = card->para[1] + 0x1000;
|
||||
cs->hw.avm.hscxfifo[0] = card->para[1];
|
||||
cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800;
|
||||
cs->irq = card->para[0];
|
||||
if (!request_region(cs->hw.avm.cfg_reg, 8, "avm cfg")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: AVM A1 config port %x-%x already in use\n",
|
||||
cs->hw.avm.cfg_reg,
|
||||
cs->hw.avm.cfg_reg + 8);
|
||||
return (0);
|
||||
}
|
||||
if (!request_region(cs->hw.avm.isac + 32, 32, "HiSax isac")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: AVM A1 isac ports %x-%x already in use\n",
|
||||
cs->hw.avm.isac + 32,
|
||||
cs->hw.avm.isac + 64);
|
||||
release_ioregs(cs, 0);
|
||||
return (0);
|
||||
}
|
||||
if (!request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: AVM A1 isac fifo port %x already in use\n",
|
||||
cs->hw.avm.isacfifo);
|
||||
release_ioregs(cs, 1);
|
||||
return (0);
|
||||
}
|
||||
if (!request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: AVM A1 hscx A ports %x-%x already in use\n",
|
||||
cs->hw.avm.hscx[0] + 32,
|
||||
cs->hw.avm.hscx[0] + 64);
|
||||
release_ioregs(cs, 3);
|
||||
return (0);
|
||||
}
|
||||
if (!request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: AVM A1 hscx A fifo port %x already in use\n",
|
||||
cs->hw.avm.hscxfifo[0]);
|
||||
release_ioregs(cs, 7);
|
||||
return (0);
|
||||
}
|
||||
if (!request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: AVM A1 hscx B ports %x-%x already in use\n",
|
||||
cs->hw.avm.hscx[1] + 32,
|
||||
cs->hw.avm.hscx[1] + 64);
|
||||
release_ioregs(cs, 0xf);
|
||||
return (0);
|
||||
}
|
||||
if (!request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: AVM A1 hscx B fifo port %x already in use\n",
|
||||
cs->hw.avm.hscxfifo[1]);
|
||||
release_ioregs(cs, 0x1f);
|
||||
return (0);
|
||||
}
|
||||
byteout(cs->hw.avm.cfg_reg, 0x0);
|
||||
HZDELAY(HZ / 5 + 1);
|
||||
byteout(cs->hw.avm.cfg_reg, 0x1);
|
||||
HZDELAY(HZ / 5 + 1);
|
||||
byteout(cs->hw.avm.cfg_reg, 0x0);
|
||||
HZDELAY(HZ / 5 + 1);
|
||||
val = cs->irq;
|
||||
if (val == 9)
|
||||
val = 2;
|
||||
byteout(cs->hw.avm.cfg_reg + 1, val);
|
||||
HZDELAY(HZ / 5 + 1);
|
||||
byteout(cs->hw.avm.cfg_reg, 0x0);
|
||||
HZDELAY(HZ / 5 + 1);
|
||||
|
||||
val = bytein(cs->hw.avm.cfg_reg);
|
||||
printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
|
||||
cs->hw.avm.cfg_reg, val);
|
||||
val = bytein(cs->hw.avm.cfg_reg + 3);
|
||||
printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
|
||||
cs->hw.avm.cfg_reg + 3, val);
|
||||
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);
|
||||
val = bytein(cs->hw.avm.cfg_reg);
|
||||
printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
|
||||
cs->hw.avm.cfg_reg, val);
|
||||
|
||||
printk(KERN_INFO "HiSax: AVM A1 config irq:%d cfg:0x%X\n",
|
||||
cs->irq,
|
||||
cs->hw.avm.cfg_reg);
|
||||
printk(KERN_INFO
|
||||
"HiSax: isac:0x%X/0x%X\n",
|
||||
cs->hw.avm.isac + 32, cs->hw.avm.isacfifo);
|
||||
printk(KERN_INFO
|
||||
"HiSax: hscx A:0x%X/0x%X hscx B:0x%X/0x%X\n",
|
||||
cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0],
|
||||
cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[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;
|
||||
setup_isac(cs);
|
||||
cs->cardmsg = &AVM_card_msg;
|
||||
cs->irq_func = &avm_a1_interrupt;
|
||||
ISACVersion(cs, "AVM A1:");
|
||||
if (HscxVersion(cs, "AVM A1:")) {
|
||||
printk(KERN_WARNING
|
||||
"AVM A1: wrong HSCX versions check IO address\n");
|
||||
release_ioregs(cs, 0x3f);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
|
@ -1,267 +0,0 @@
|
|||
/* $Id: avm_a1p.c,v 2.9.2.5 2004/01/24 20:47:19 keil Exp $
|
||||
*
|
||||
* low level stuff for the following AVM cards:
|
||||
* A1 PCMCIA
|
||||
* FRITZ!Card PCMCIA
|
||||
* FRITZ!Card PCMCIA 2.0
|
||||
*
|
||||
* Author Carsten Paeth
|
||||
* Copyright by Carsten Paeth <calle@calle.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#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.9.2.5 $";
|
||||
|
||||
static inline u_char
|
||||
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
{
|
||||
u_char ret;
|
||||
|
||||
offset -= 0x20;
|
||||
byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_REG_OFFSET + offset);
|
||||
ret = bytein(cs->hw.avm.cfg_reg + DATAREG_OFFSET);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
{
|
||||
offset -= 0x20;
|
||||
byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_REG_OFFSET + offset);
|
||||
byteout(cs->hw.avm.cfg_reg + DATAREG_OFFSET, value);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_FIFO_OFFSET);
|
||||
insb(cs->hw.avm.cfg_reg + DATAREG_OFFSET, data, size);
|
||||
}
|
||||
|
||||
static inline void
|
||||
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
byteout(cs->hw.avm.cfg_reg + ADDRREG_OFFSET, ISAC_FIFO_OFFSET);
|
||||
outsb(cs->hw.avm.cfg_reg + DATAREG_OFFSET, data, size);
|
||||
}
|
||||
|
||||
static inline u_char
|
||||
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
|
||||
{
|
||||
u_char ret;
|
||||
|
||||
offset -= 0x20;
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
|
||||
{
|
||||
offset -= 0x20;
|
||||
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);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char *data, int size)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static inline void
|
||||
WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char *data, int size)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 irqreturn_t
|
||||
avm_a1p_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char val, sval;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
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);
|
||||
}
|
||||
if (sval & ASL0_R_ISAC) {
|
||||
val = ReadISAC(cs, ISAC_ISTA);
|
||||
if (val)
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
}
|
||||
WriteHSCX(cs, 0, HSCX_MASK, 0xff);
|
||||
WriteHSCX(cs, 1, HSCX_MASK, 0xff);
|
||||
WriteISAC(cs, ISAC_MASK, 0xff);
|
||||
WriteISAC(cs, ISAC_MASK, 0x00);
|
||||
WriteHSCX(cs, 0, HSCX_MASK, 0x00);
|
||||
WriteHSCX(cs, 1, HSCX_MASK, 0x00);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int
|
||||
AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
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);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return 0;
|
||||
|
||||
case CARD_RELEASE:
|
||||
/* free_irq is done in HiSax_closecard(). */
|
||||
/* free_irq(cs->irq, cs); */
|
||||
return 0;
|
||||
|
||||
case CARD_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
byteout(cs->hw.avm.cfg_reg + ASL0_OFFSET, ASL0_W_TDISABLE | ASL0_W_TRESET | ASL0_W_IRQENABLE);
|
||||
clear_pending_isac_ints(cs);
|
||||
clear_pending_hscx_ints(cs);
|
||||
inithscxisac(cs, 1);
|
||||
inithscxisac(cs, 2);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
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;
|
||||
}
|
||||
|
||||
int setup_avm_a1_pcmcia(struct IsdnCard *card)
|
||||
{
|
||||
u_char model, vers;
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
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];
|
||||
|
||||
|
||||
byteout(cs->hw.avm.cfg_reg + ASL1_OFFSET, ASL1_W_ENABLE_S0);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
setup_isac(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 = &AVM_card_msg;
|
||||
cs->irq_flags = IRQF_SHARED;
|
||||
cs->irq_func = &avm_a1p_interrupt;
|
||||
|
||||
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);
|
||||
}
|
|
@ -1,904 +0,0 @@
|
|||
/* $Id: avm_pci.c,v 1.29.2.4 2004/02/11 13:21:32 keil Exp $
|
||||
*
|
||||
* low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to AVM, Berlin for information
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/isapnp.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
static const char *avm_pci_rev = "$Revision: 1.29.2.4 $";
|
||||
|
||||
#define AVM_FRITZ_PCI 1
|
||||
#define AVM_FRITZ_PNP 2
|
||||
|
||||
#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;
|
||||
|
||||
outb(idx, cs->hw.avm.cfg_reg + 4);
|
||||
val = inb(cs->hw.avm.isac + (offset & 0xf));
|
||||
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;
|
||||
|
||||
outb(idx, cs->hw.avm.cfg_reg + 4);
|
||||
outb(value, cs->hw.avm.isac + (offset & 0xf));
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
outl(idx, cs->hw.avm.cfg_reg + 4);
|
||||
val = inl(cs->hw.avm.isac + offset);
|
||||
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;
|
||||
|
||||
outl(idx, cs->hw.avm.cfg_reg + 4);
|
||||
outl(value, cs->hw.avm.isac + offset);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
outb(idx, cs->hw.avm.cfg_reg + 4);
|
||||
val = inb(cs->hw.avm.isac + offset);
|
||||
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;
|
||||
|
||||
outb(idx, cs->hw.avm.cfg_reg + 4);
|
||||
outb(value, cs->hw.avm.isac + offset);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
}
|
||||
|
||||
static 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 --> %d ichan %d --> %d",
|
||||
'A' + hdlc, bcs->mode, mode, hdlc, bc);
|
||||
bcs->hw.hdlc.ctrl.ctrl = 0;
|
||||
switch (mode) {
|
||||
case (-1): /* used for init */
|
||||
bcs->mode = 1;
|
||||
bcs->channel = bc;
|
||||
bc = 0;
|
||||
/* fall through */
|
||||
case (L1_MODE_NULL):
|
||||
if (bcs->mode == L1_MODE_NULL)
|
||||
return;
|
||||
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->mode = L1_MODE_NULL;
|
||||
bcs->channel = bc;
|
||||
break;
|
||||
case (L1_MODE_TRANS):
|
||||
bcs->mode = mode;
|
||||
bcs->channel = bc;
|
||||
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;
|
||||
schedule_event(bcs, B_XMTBUFREADY);
|
||||
break;
|
||||
case (L1_MODE_HDLC):
|
||||
bcs->mode = mode;
|
||||
bcs->channel = bc;
|
||||
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;
|
||||
schedule_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;
|
||||
}
|
||||
p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
|
||||
ptr = (u_int *)p;
|
||||
bcs->hw.hdlc.rcvidx += count;
|
||||
if (cs->subtyp == AVM_FRITZ_PCI) {
|
||||
outl(idx, cs->hw.avm.cfg_reg + 4);
|
||||
while (cnt < count) {
|
||||
#ifdef __powerpc__
|
||||
*ptr++ = in_be32((unsigned *)(cs->hw.avm.isac + _IO_BASE));
|
||||
#else
|
||||
*ptr++ = inl(cs->hw.avm.isac);
|
||||
#endif /* __powerpc__ */
|
||||
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, "%s", 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/%u", count, bcs->tx_skb->len);
|
||||
p = bcs->tx_skb->data;
|
||||
ptr = (u_int *)p;
|
||||
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) {
|
||||
#ifdef __powerpc__
|
||||
out_be32((unsigned *)(cs->hw.avm.isac + _IO_BASE), *ptr++);
|
||||
#else
|
||||
outl(*ptr++, cs->hw.avm.isac);
|
||||
#endif /* __powerpc__ */
|
||||
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, "%s", bcs->blog);
|
||||
}
|
||||
}
|
||||
|
||||
static 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 {
|
||||
skb_put_data(skb,
|
||||
bcs->hw.hdlc.rcvbuf,
|
||||
bcs->hw.hdlc.rcvidx);
|
||||
skb_queue_tail(&bcs->rqueue, skb);
|
||||
}
|
||||
bcs->hw.hdlc.rcvidx = 0;
|
||||
schedule_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;
|
||||
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 (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) &&
|
||||
(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
|
||||
u_long flags;
|
||||
spin_lock_irqsave(&bcs->aclock, flags);
|
||||
bcs->ackcnt += bcs->hw.hdlc.count;
|
||||
spin_unlock_irqrestore(&bcs->aclock, flags);
|
||||
schedule_event(bcs, B_ACKPENDING);
|
||||
}
|
||||
dev_kfree_skb_irq(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);
|
||||
schedule_event(bcs, B_XMTBUFREADY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
HDLC_irq_main(struct IsdnCardState *cs)
|
||||
{
|
||||
u_int stat;
|
||||
struct BCState *bcs;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hdlc_l2l1(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct BCState *bcs = st->l1.bcs;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
|
||||
switch (pr) {
|
||||
case (PH_DATA | REQUEST):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
skb_queue_tail(&bcs->squeue, skb);
|
||||
} else {
|
||||
bcs->tx_skb = skb;
|
||||
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->hw.hdlc.count = 0;
|
||||
bcs->cs->BC_Send_Data(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | INDICATION):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n");
|
||||
} else {
|
||||
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->tx_skb = skb;
|
||||
bcs->hw.hdlc.count = 0;
|
||||
bcs->cs->BC_Send_Data(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | REQUEST):
|
||||
if (!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):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
modehdlc(bcs, st->l1.mode, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | REQUEST):
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | CONFIRM):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
modehdlc(bcs, 0, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
close_hdlcstate(struct BCState *bcs)
|
||||
{
|
||||
modehdlc(bcs, 0, 0);
|
||||
if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
|
||||
kfree(bcs->hw.hdlc.rcvbuf);
|
||||
bcs->hw.hdlc.rcvbuf = NULL;
|
||||
kfree(bcs->blog);
|
||||
bcs->blog = NULL;
|
||||
skb_queue_purge(&bcs->rqueue);
|
||||
skb_queue_purge(&bcs->squeue);
|
||||
if (bcs->tx_skb) {
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void __init
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
static 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, -1, 0);
|
||||
modehdlc(cs->bcs + 1, -1, 1);
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
avm_pcipnp_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_long flags;
|
||||
u_char val;
|
||||
u_char sval;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
sval = inb(cs->hw.avm.cfg_reg + 2);
|
||||
if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
|
||||
/* possible a shared IRQ reqest */
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
|
||||
val = ReadISAC(cs, ISAC_ISTA);
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
|
||||
HDLC_irq_main(cs);
|
||||
}
|
||||
WriteISAC(cs, ISAC_MASK, 0xFF);
|
||||
WriteISAC(cs, ISAC_MASK, 0x0);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
reset_avmpcipnp(struct IsdnCardState *cs)
|
||||
{
|
||||
printk(KERN_INFO "AVM PCI/PnP: reset\n");
|
||||
outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
|
||||
mdelay(10);
|
||||
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);
|
||||
mdelay(10);
|
||||
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_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_avmpcipnp(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
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_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_avmpcipnp(cs);
|
||||
clear_pending_isac_ints(cs);
|
||||
initisac(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);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int avm_setup_rest(struct IsdnCardState *cs)
|
||||
{
|
||||
u_int val, ver;
|
||||
|
||||
cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
|
||||
if (!request_region(cs->hw.avm.cfg_reg, 32,
|
||||
(cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: Fritz!PCI/PNP config port %x-%x already in use\n",
|
||||
cs->hw.avm.cfg_reg,
|
||||
cs->hw.avm.cfg_reg + 31);
|
||||
return (0);
|
||||
}
|
||||
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);
|
||||
cs->BC_Read_Reg = &ReadHDLCPnP;
|
||||
cs->BC_Write_Reg = &WriteHDLCPnP;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
|
||||
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);
|
||||
|
||||
setup_isac(cs);
|
||||
cs->readisac = &ReadISAC;
|
||||
cs->writeisac = &WriteISAC;
|
||||
cs->readisacfifo = &ReadISACfifo;
|
||||
cs->writeisacfifo = &WriteISACfifo;
|
||||
cs->BC_Send_Data = &hdlc_fill_fifo;
|
||||
cs->cardmsg = &AVM_card_msg;
|
||||
cs->irq_func = &avm_pcipnp_interrupt;
|
||||
cs->writeisac(cs, ISAC_MASK, 0xFF);
|
||||
ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
|
||||
return (1);
|
||||
}
|
||||
|
||||
#ifndef __ISAPNP__
|
||||
|
||||
static int avm_pnp_setup(struct IsdnCardState *cs)
|
||||
{
|
||||
return (1); /* no-op: success */
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static struct pnp_card *pnp_avm_c = NULL;
|
||||
|
||||
static int avm_pnp_setup(struct IsdnCardState *cs)
|
||||
{
|
||||
struct pnp_dev *pnp_avm_d = NULL;
|
||||
|
||||
if (!isapnp_present())
|
||||
return (1); /* no-op: success */
|
||||
|
||||
if ((pnp_avm_c = pnp_find_card(
|
||||
ISAPNP_VENDOR('A', 'V', 'M'),
|
||||
ISAPNP_FUNCTION(0x0900), pnp_avm_c))) {
|
||||
if ((pnp_avm_d = pnp_find_dev(pnp_avm_c,
|
||||
ISAPNP_VENDOR('A', 'V', 'M'),
|
||||
ISAPNP_FUNCTION(0x0900), pnp_avm_d))) {
|
||||
int err;
|
||||
|
||||
pnp_disable_dev(pnp_avm_d);
|
||||
err = pnp_activate_dev(pnp_avm_d);
|
||||
if (err < 0) {
|
||||
printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
|
||||
__func__, err);
|
||||
return (0);
|
||||
}
|
||||
cs->hw.avm.cfg_reg =
|
||||
pnp_port_start(pnp_avm_d, 0);
|
||||
cs->irq = pnp_irq(pnp_avm_d, 0);
|
||||
if (cs->irq == -1) {
|
||||
printk(KERN_ERR "FritzPnP:No IRQ\n");
|
||||
return (0);
|
||||
}
|
||||
if (!cs->hw.avm.cfg_reg) {
|
||||
printk(KERN_ERR "FritzPnP:No IO address\n");
|
||||
return (0);
|
||||
}
|
||||
cs->subtyp = AVM_FRITZ_PNP;
|
||||
|
||||
return (2); /* goto 'ready' label */
|
||||
}
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
#endif /* __ISAPNP__ */
|
||||
|
||||
#ifndef CONFIG_PCI
|
||||
|
||||
static int avm_pci_setup(struct IsdnCardState *cs)
|
||||
{
|
||||
return (1); /* no-op: success */
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static struct pci_dev *dev_avm = NULL;
|
||||
|
||||
static int avm_pci_setup(struct IsdnCardState *cs)
|
||||
{
|
||||
if ((dev_avm = hisax_find_pci_device(PCI_VENDOR_ID_AVM,
|
||||
PCI_DEVICE_ID_AVM_A1, dev_avm))) {
|
||||
|
||||
if (pci_enable_device(dev_avm))
|
||||
return (0);
|
||||
|
||||
cs->irq = dev_avm->irq;
|
||||
if (!cs->irq) {
|
||||
printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1);
|
||||
if (!cs->hw.avm.cfg_reg) {
|
||||
printk(KERN_ERR "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);
|
||||
}
|
||||
|
||||
cs->irq_flags |= IRQF_SHARED;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
int setup_avm_pcipnp(struct IsdnCard *card)
|
||||
{
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
int rc;
|
||||
|
||||
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]) {
|
||||
/* old manual method */
|
||||
cs->hw.avm.cfg_reg = card->para[1];
|
||||
cs->irq = card->para[0];
|
||||
cs->subtyp = AVM_FRITZ_PNP;
|
||||
goto ready;
|
||||
}
|
||||
|
||||
rc = avm_pnp_setup(cs);
|
||||
if (rc < 1)
|
||||
return (0);
|
||||
if (rc == 2)
|
||||
goto ready;
|
||||
|
||||
rc = avm_pci_setup(cs);
|
||||
if (rc < 1)
|
||||
return (0);
|
||||
|
||||
ready:
|
||||
return avm_setup_rest(cs);
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
* PCMCIA client driver for AVM A1 / Fritz!PCMCIA
|
||||
*
|
||||
* Author Carsten Paeth
|
||||
* Copyright 1998-2001 by Carsten Paeth <calle@calle.in-berlin.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include "hisax_cfg.h"
|
||||
|
||||
MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/* Parameters that can be set with 'insmod' */
|
||||
|
||||
static int isdnprot = 2;
|
||||
|
||||
module_param(isdnprot, int, 0);
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
static int avma1cs_config(struct pcmcia_device *link);
|
||||
static void avma1cs_release(struct pcmcia_device *link);
|
||||
static void avma1cs_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
static int avma1cs_probe(struct pcmcia_device *p_dev)
|
||||
{
|
||||
dev_dbg(&p_dev->dev, "avma1cs_attach()\n");
|
||||
|
||||
/* General socket configuration */
|
||||
p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
|
||||
p_dev->config_index = 1;
|
||||
p_dev->config_regs = PRESENT_OPTION;
|
||||
|
||||
return avma1cs_config(p_dev);
|
||||
} /* avma1cs_attach */
|
||||
|
||||
static void avma1cs_detach(struct pcmcia_device *link)
|
||||
{
|
||||
dev_dbg(&link->dev, "avma1cs_detach(0x%p)\n", link);
|
||||
avma1cs_release(link);
|
||||
kfree(link->priv);
|
||||
} /* avma1cs_detach */
|
||||
|
||||
static int avma1cs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
p_dev->resource[0]->end = 16;
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
|
||||
p_dev->io_lines = 5;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
}
|
||||
|
||||
|
||||
static int avma1cs_config(struct pcmcia_device *link)
|
||||
{
|
||||
int i = -1;
|
||||
char devname[128];
|
||||
IsdnCard_t icard;
|
||||
int busy = 0;
|
||||
|
||||
dev_dbg(&link->dev, "avma1cs_config(0x%p)\n", link);
|
||||
|
||||
devname[0] = 0;
|
||||
if (link->prod_id[1])
|
||||
strlcpy(devname, link->prod_id[1], sizeof(devname));
|
||||
|
||||
if (pcmcia_loop_config(link, avma1cs_configcheck, NULL))
|
||||
return -ENODEV;
|
||||
|
||||
do {
|
||||
/*
|
||||
* allocate an interrupt line
|
||||
*/
|
||||
if (!link->irq) {
|
||||
/* undo */
|
||||
pcmcia_disable_device(link);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* configure the PCMCIA socket
|
||||
*/
|
||||
i = pcmcia_enable_device(link);
|
||||
if (i != 0) {
|
||||
pcmcia_disable_device(link);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
/* If any step failed, release any partially configured state */
|
||||
if (i != 0) {
|
||||
avma1cs_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
icard.para[0] = link->irq;
|
||||
icard.para[1] = link->resource[0]->start;
|
||||
icard.protocol = isdnprot;
|
||||
icard.typ = ISDN_CTYPE_A1_PCMCIA;
|
||||
|
||||
i = hisax_init_pcmcia(link, &busy, &icard);
|
||||
if (i < 0) {
|
||||
printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 "
|
||||
"PCMCIA %d at i/o %#x\n", i,
|
||||
(unsigned int) link->resource[0]->start);
|
||||
avma1cs_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
link->priv = (void *) (unsigned long) i;
|
||||
|
||||
return 0;
|
||||
} /* avma1cs_config */
|
||||
|
||||
static void avma1cs_release(struct pcmcia_device *link)
|
||||
{
|
||||
unsigned long minor = (unsigned long) link->priv;
|
||||
|
||||
dev_dbg(&link->dev, "avma1cs_release(0x%p)\n", link);
|
||||
|
||||
/* now unregister function with hisax */
|
||||
HiSax_closecard(minor);
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
} /* avma1cs_release */
|
||||
|
||||
static const struct pcmcia_device_id avma1cs_ids[] = {
|
||||
PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb),
|
||||
PCMCIA_DEVICE_PROD_ID12("ISDN", "CARD", 0x8d9761c8, 0x01c5aa7b),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, avma1cs_ids);
|
||||
|
||||
static struct pcmcia_driver avma1cs_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "avma1_cs",
|
||||
.probe = avma1cs_probe,
|
||||
.remove = avma1cs_detach,
|
||||
.id_table = avma1cs_ids,
|
||||
};
|
||||
module_pcmcia_driver(avma1cs_driver);
|
|
@ -1,358 +0,0 @@
|
|||
/* $Id: bkm_a4t.c,v 1.22.2.4 2004/01/14 16:04:48 keil Exp $
|
||||
*
|
||||
* low level stuff for T-Berkom A4T
|
||||
*
|
||||
* Author Roland Klabunde
|
||||
* Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "hscx.h"
|
||||
#include "jade.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/pci.h>
|
||||
#include "bkm_ax.h"
|
||||
|
||||
static const char *bkm_a4t_revision = "$Revision: 1.22.2.4 $";
|
||||
|
||||
|
||||
static inline u_char
|
||||
readreg(unsigned int ale, unsigned long adr, u_char off)
|
||||
{
|
||||
register u_int ret;
|
||||
unsigned int *po = (unsigned int *) adr; /* Postoffice */
|
||||
|
||||
*po = (GCS_2 | PO_WRITE | off);
|
||||
__WAITI20__(po);
|
||||
*po = (ale | PO_READ);
|
||||
__WAITI20__(po);
|
||||
ret = *po;
|
||||
return ((unsigned char) ret);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
readfifo(unsigned int ale, unsigned long adr, u_char off, u_char *data, int size)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
*data++ = readreg(ale, adr, off);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
writereg(unsigned int ale, unsigned long adr, u_char off, u_char data)
|
||||
{
|
||||
unsigned int *po = (unsigned int *) adr; /* Postoffice */
|
||||
*po = (GCS_2 | PO_WRITE | off);
|
||||
__WAITI20__(po);
|
||||
*po = (ale | PO_WRITE | data);
|
||||
__WAITI20__(po);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
writefifo(unsigned int ale, unsigned long adr, u_char off, u_char *data, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
writereg(ale, adr, off, *data++);
|
||||
}
|
||||
|
||||
|
||||
/* Interface functions */
|
||||
|
||||
static u_char
|
||||
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
{
|
||||
return (readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset, value);
|
||||
}
|
||||
|
||||
static void
|
||||
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
readfifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
writefifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
|
||||
}
|
||||
|
||||
static u_char
|
||||
ReadJADE(struct IsdnCardState *cs, int jade, u_char offset)
|
||||
{
|
||||
return (readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80))));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteJADE(struct IsdnCardState *cs, int jade, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)), value);
|
||||
}
|
||||
|
||||
/*
|
||||
* fast interrupt JADE stuff goes here
|
||||
*/
|
||||
|
||||
#define READJADE(cs, nr, reg) readreg(cs->hw.ax.jade_ale, \
|
||||
cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)))
|
||||
#define WRITEJADE(cs, nr, reg, data) writereg(cs->hw.ax.jade_ale, \
|
||||
cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), data)
|
||||
|
||||
#define READJADEFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.jade_ale, \
|
||||
cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
|
||||
#define WRITEJADEFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.jade_ale, \
|
||||
cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
|
||||
|
||||
#include "jade_irq.c"
|
||||
|
||||
static irqreturn_t
|
||||
bkm_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char val = 0;
|
||||
u_long flags;
|
||||
I20_REGISTER_FILE *pI20_Regs;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
|
||||
|
||||
/* ISDN interrupt pending? */
|
||||
if (pI20_Regs->i20IntStatus & intISDN) {
|
||||
/* Reset the ISDN interrupt */
|
||||
pI20_Regs->i20IntStatus = intISDN;
|
||||
/* Disable ISDN interrupt */
|
||||
pI20_Regs->i20IntCtrl &= ~intISDN;
|
||||
/* Channel A first */
|
||||
val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0x80);
|
||||
if (val) {
|
||||
jade_int_main(cs, val, 0);
|
||||
}
|
||||
/* Channel B */
|
||||
val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0xC0);
|
||||
if (val) {
|
||||
jade_int_main(cs, val, 1);
|
||||
}
|
||||
/* D-Channel */
|
||||
val = readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, ISAC_ISTA);
|
||||
if (val) {
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
/* Reenable ISDN interrupt */
|
||||
pI20_Regs->i20IntCtrl |= intISDN;
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
release_io_bkm(struct IsdnCardState *cs)
|
||||
{
|
||||
if (cs->hw.ax.base) {
|
||||
iounmap((void *) cs->hw.ax.base);
|
||||
cs->hw.ax.base = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
|
||||
{
|
||||
if (cs->typ == ISDN_CTYPE_BKM_A4T) {
|
||||
I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
|
||||
if (bEnable)
|
||||
pI20_Regs->i20IntCtrl |= (intISDN | intPCI);
|
||||
else
|
||||
/* CAUTION: This disables the video capture driver too */
|
||||
pI20_Regs->i20IntCtrl &= ~(intISDN | intPCI);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reset_bkm(struct IsdnCardState *cs)
|
||||
{
|
||||
if (cs->typ == ISDN_CTYPE_BKM_A4T) {
|
||||
I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
|
||||
/* Issue the I20 soft reset */
|
||||
pI20_Regs->i20SysControl = 0xFF; /* all in */
|
||||
mdelay(10);
|
||||
/* Remove the soft reset */
|
||||
pI20_Regs->i20SysControl = sysRESET | 0xFF;
|
||||
mdelay(10);
|
||||
/* Set our configuration */
|
||||
pI20_Regs->i20SysControl = sysRESET | sysCFG;
|
||||
/* Issue ISDN reset */
|
||||
pI20_Regs->i20GuestControl = guestWAIT_CFG |
|
||||
g_A4T_JADE_RES |
|
||||
g_A4T_ISAR_RES |
|
||||
g_A4T_ISAC_RES |
|
||||
g_A4T_JADE_BOOTR |
|
||||
g_A4T_ISAR_BOOTR;
|
||||
mdelay(10);
|
||||
|
||||
/* Remove RESET state from ISDN */
|
||||
pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES |
|
||||
g_A4T_JADE_RES |
|
||||
g_A4T_ISAR_RES);
|
||||
mdelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
/* Disable ints */
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
enable_bkm_int(cs, 0);
|
||||
reset_bkm(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_RELEASE:
|
||||
/* Sanity */
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
enable_bkm_int(cs, 0);
|
||||
reset_bkm(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
release_io_bkm(cs);
|
||||
return (0);
|
||||
case CARD_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
clear_pending_isac_ints(cs);
|
||||
clear_pending_jade_ints(cs);
|
||||
initisac(cs);
|
||||
initjade(cs);
|
||||
/* Enable ints */
|
||||
enable_bkm_int(cs, 1);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int a4t_pci_probe(struct pci_dev *dev_a4t, struct IsdnCardState *cs,
|
||||
u_int *found, u_int *pci_memaddr)
|
||||
{
|
||||
u16 sub_sys;
|
||||
u16 sub_vendor;
|
||||
|
||||
sub_vendor = dev_a4t->subsystem_vendor;
|
||||
sub_sys = dev_a4t->subsystem_device;
|
||||
if ((sub_sys == PCI_DEVICE_ID_BERKOM_A4T) && (sub_vendor == PCI_VENDOR_ID_BERKOM)) {
|
||||
if (pci_enable_device(dev_a4t))
|
||||
return (0); /* end loop & function */
|
||||
*found = 1;
|
||||
*pci_memaddr = pci_resource_start(dev_a4t, 0);
|
||||
cs->irq = dev_a4t->irq;
|
||||
return (1); /* end loop */
|
||||
}
|
||||
|
||||
return (-1); /* continue looping */
|
||||
}
|
||||
|
||||
static int a4t_cs_init(struct IsdnCard *card, struct IsdnCardState *cs,
|
||||
u_int pci_memaddr)
|
||||
{
|
||||
I20_REGISTER_FILE *pI20_Regs;
|
||||
|
||||
if (!cs->irq) { /* IRQ range check ?? */
|
||||
printk(KERN_WARNING "HiSax: Telekom A4T: No IRQ\n");
|
||||
return (0);
|
||||
}
|
||||
cs->hw.ax.base = (long) ioremap(pci_memaddr, 4096);
|
||||
/* Check suspecious address */
|
||||
pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
|
||||
if ((pI20_Regs->i20IntStatus & 0x8EFFFFFF) != 0) {
|
||||
printk(KERN_WARNING "HiSax: Telekom A4T address "
|
||||
"%lx-%lx suspicious\n",
|
||||
cs->hw.ax.base, cs->hw.ax.base + 4096);
|
||||
iounmap((void *) cs->hw.ax.base);
|
||||
cs->hw.ax.base = 0;
|
||||
return (0);
|
||||
}
|
||||
cs->hw.ax.isac_adr = cs->hw.ax.base + PO_OFFSET;
|
||||
cs->hw.ax.jade_adr = cs->hw.ax.base + PO_OFFSET;
|
||||
cs->hw.ax.isac_ale = GCS_1;
|
||||
cs->hw.ax.jade_ale = GCS_3;
|
||||
|
||||
printk(KERN_INFO "HiSax: Telekom A4T: Card configured at "
|
||||
"0x%lX IRQ %d\n",
|
||||
cs->hw.ax.base, cs->irq);
|
||||
|
||||
setup_isac(cs);
|
||||
cs->readisac = &ReadISAC;
|
||||
cs->writeisac = &WriteISAC;
|
||||
cs->readisacfifo = &ReadISACfifo;
|
||||
cs->writeisacfifo = &WriteISACfifo;
|
||||
cs->BC_Read_Reg = &ReadJADE;
|
||||
cs->BC_Write_Reg = &WriteJADE;
|
||||
cs->BC_Send_Data = &jade_fill_fifo;
|
||||
cs->cardmsg = &BKM_card_msg;
|
||||
cs->irq_func = &bkm_interrupt;
|
||||
cs->irq_flags |= IRQF_SHARED;
|
||||
ISACVersion(cs, "Telekom A4T:");
|
||||
/* Jade version */
|
||||
JadeVersion(cs, "Telekom A4T:");
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static struct pci_dev *dev_a4t = NULL;
|
||||
|
||||
int setup_bkm_a4t(struct IsdnCard *card)
|
||||
{
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
u_int pci_memaddr = 0, found = 0;
|
||||
int ret;
|
||||
|
||||
strcpy(tmp, bkm_a4t_revision);
|
||||
printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
|
||||
if (cs->typ == ISDN_CTYPE_BKM_A4T) {
|
||||
cs->subtyp = BKM_A4T;
|
||||
} else
|
||||
return (0);
|
||||
|
||||
while ((dev_a4t = hisax_find_pci_device(PCI_VENDOR_ID_ZORAN,
|
||||
PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) {
|
||||
ret = a4t_pci_probe(dev_a4t, cs, &found, &pci_memaddr);
|
||||
if (!ret)
|
||||
return (0);
|
||||
if (ret > 0)
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
printk(KERN_WARNING "HiSax: Telekom A4T: Card not found\n");
|
||||
return (0);
|
||||
}
|
||||
if (!pci_memaddr) {
|
||||
printk(KERN_WARNING "HiSax: Telekom A4T: "
|
||||
"No Memory base address\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
return a4t_cs_init(card, cs, pci_memaddr);
|
||||
}
|
|
@ -1,433 +0,0 @@
|
|||
/* $Id: bkm_a8.c,v 1.22.2.4 2004/01/15 14:02:34 keil Exp $
|
||||
*
|
||||
* low level stuff for Scitel Quadro (4*S0, passive)
|
||||
*
|
||||
* Author Roland Klabunde
|
||||
* Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "ipac.h"
|
||||
#include "hscx.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/pci.h>
|
||||
#include "bkm_ax.h"
|
||||
|
||||
#define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */
|
||||
|
||||
static const char sct_quadro_revision[] = "$Revision: 1.22.2.4 $";
|
||||
|
||||
static const char *sct_quadro_subtypes[] =
|
||||
{
|
||||
"",
|
||||
"#1",
|
||||
"#2",
|
||||
"#3",
|
||||
"#4"
|
||||
};
|
||||
|
||||
|
||||
#define wordout(addr, val) outw(val, addr)
|
||||
#define wordin(addr) inw(addr)
|
||||
|
||||
static inline u_char
|
||||
readreg(unsigned int ale, unsigned int adr, u_char off)
|
||||
{
|
||||
register u_char ret;
|
||||
wordout(ale, off);
|
||||
ret = wordin(adr) & 0xFF;
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static inline void
|
||||
readfifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
|
||||
{
|
||||
int i;
|
||||
wordout(ale, off);
|
||||
for (i = 0; i < size; i++)
|
||||
data[i] = wordin(adr) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
|
||||
{
|
||||
wordout(ale, off);
|
||||
wordout(adr, data);
|
||||
}
|
||||
|
||||
static inline void
|
||||
writefifo(unsigned int ale, unsigned int adr, u_char off, u_char *data, int size)
|
||||
{
|
||||
int i;
|
||||
wordout(ale, off);
|
||||
for (i = 0; i < size; i++)
|
||||
wordout(adr, data[i]);
|
||||
}
|
||||
|
||||
/* Interface functions */
|
||||
|
||||
static u_char
|
||||
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
{
|
||||
return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value);
|
||||
}
|
||||
|
||||
static void
|
||||
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
|
||||
}
|
||||
|
||||
|
||||
static u_char
|
||||
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
|
||||
{
|
||||
return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0)));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
|
||||
{
|
||||
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value);
|
||||
}
|
||||
|
||||
/* Set the specific ipac to active */
|
||||
static void
|
||||
set_ipac_active(struct IsdnCardState *cs, u_int active)
|
||||
{
|
||||
/* set irq mask */
|
||||
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK,
|
||||
active ? 0xc0 : 0xff);
|
||||
}
|
||||
|
||||
/*
|
||||
* fast interrupt HSCX stuff goes here
|
||||
*/
|
||||
|
||||
#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \
|
||||
cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0))
|
||||
#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \
|
||||
cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data)
|
||||
#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \
|
||||
cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
|
||||
#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \
|
||||
cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
|
||||
|
||||
#include "hscx_irq.c"
|
||||
|
||||
static irqreturn_t
|
||||
bkm_interrupt_ipac(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char ista, val, icnt = 5;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
|
||||
if (!(ista & 0x3f)) { /* not this IPAC */
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
Start_IPAC:
|
||||
if (cs->debug & L1_DEB_IPAC)
|
||||
debugl1(cs, "IPAC ISTA %02X", ista);
|
||||
if (ista & 0x0f) {
|
||||
val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, 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.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80);
|
||||
if (val) {
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
}
|
||||
if (ista & 0x10) {
|
||||
val = 0x01;
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
|
||||
if ((ista & 0x3f) && icnt) {
|
||||
icnt--;
|
||||
goto Start_IPAC;
|
||||
}
|
||||
if (!icnt)
|
||||
printk(KERN_WARNING "HiSax: Scitel Quadro (%s) IRQ LOOP\n",
|
||||
sct_quadro_subtypes[cs->subtyp]);
|
||||
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF);
|
||||
writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
release_io_sct_quadro(struct IsdnCardState *cs)
|
||||
{
|
||||
release_region(cs->hw.ax.base & 0xffffffc0, 128);
|
||||
if (cs->subtyp == SCT_1)
|
||||
release_region(cs->hw.ax.plx_adr, 64);
|
||||
}
|
||||
|
||||
static void
|
||||
enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
|
||||
{
|
||||
if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
|
||||
if (bEnable)
|
||||
wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41));
|
||||
else
|
||||
wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
reset_bkm(struct IsdnCardState *cs)
|
||||
{
|
||||
if (cs->subtyp == SCT_1) {
|
||||
wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4));
|
||||
mdelay(10);
|
||||
/* Remove the soft reset */
|
||||
wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
|
||||
mdelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
/* Disable ints */
|
||||
set_ipac_active(cs, 0);
|
||||
enable_bkm_int(cs, 0);
|
||||
reset_bkm(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_RELEASE:
|
||||
/* Sanity */
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
set_ipac_active(cs, 0);
|
||||
enable_bkm_int(cs, 0);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
release_io_sct_quadro(cs);
|
||||
return (0);
|
||||
case CARD_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
cs->debug |= L1_DEB_IPAC;
|
||||
set_ipac_active(cs, 1);
|
||||
inithscxisac(cs, 3);
|
||||
/* Enable ints */
|
||||
enable_bkm_int(cs, 1);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int sct_alloc_io(u_int adr, u_int len)
|
||||
{
|
||||
if (!request_region(adr, len, "scitel")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: Scitel port %#x-%#x already in use\n",
|
||||
adr, adr + len);
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct pci_dev *dev_a8 = NULL;
|
||||
static u16 sub_vendor_id = 0;
|
||||
static u16 sub_sys_id = 0;
|
||||
static u_char pci_bus = 0;
|
||||
static u_char pci_device_fn = 0;
|
||||
static u_char pci_irq = 0;
|
||||
|
||||
int setup_sct_quadro(struct IsdnCard *card)
|
||||
{
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
u_int found = 0;
|
||||
u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5;
|
||||
|
||||
strcpy(tmp, sct_quadro_revision);
|
||||
printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
|
||||
if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
|
||||
cs->subtyp = SCT_1; /* Preset */
|
||||
} else
|
||||
return (0);
|
||||
|
||||
/* Identify subtype by para[0] */
|
||||
if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4)
|
||||
cs->subtyp = card->para[0];
|
||||
else {
|
||||
printk(KERN_WARNING "HiSax: Scitel Quadro: Invalid "
|
||||
"subcontroller in configuration, default to 1\n");
|
||||
return (0);
|
||||
}
|
||||
if ((cs->subtyp != SCT_1) && ((sub_sys_id != PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) ||
|
||||
(sub_vendor_id != PCI_VENDOR_ID_BERKOM)))
|
||||
return (0);
|
||||
if (cs->subtyp == SCT_1) {
|
||||
while ((dev_a8 = hisax_find_pci_device(PCI_VENDOR_ID_PLX,
|
||||
PCI_DEVICE_ID_PLX_9050, dev_a8))) {
|
||||
|
||||
sub_vendor_id = dev_a8->subsystem_vendor;
|
||||
sub_sys_id = dev_a8->subsystem_device;
|
||||
if ((sub_sys_id == PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) &&
|
||||
(sub_vendor_id == PCI_VENDOR_ID_BERKOM)) {
|
||||
if (pci_enable_device(dev_a8))
|
||||
return (0);
|
||||
pci_ioaddr1 = pci_resource_start(dev_a8, 1);
|
||||
pci_irq = dev_a8->irq;
|
||||
pci_bus = dev_a8->bus->number;
|
||||
pci_device_fn = dev_a8->devfn;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
printk(KERN_WARNING "HiSax: Scitel Quadro (%s): "
|
||||
"Card not found\n",
|
||||
sct_quadro_subtypes[cs->subtyp]);
|
||||
return (0);
|
||||
}
|
||||
#ifdef ATTEMPT_PCI_REMAPPING
|
||||
/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */
|
||||
if ((pci_ioaddr1 & 0x80) && (dev_a8->revision == 1)) {
|
||||
printk(KERN_WARNING "HiSax: Scitel Quadro (%s): "
|
||||
"PLX rev 1, remapping required!\n",
|
||||
sct_quadro_subtypes[cs->subtyp]);
|
||||
/* Restart PCI negotiation */
|
||||
pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, (u_int)-1);
|
||||
/* Move up by 0x80 byte */
|
||||
pci_ioaddr1 += 0x80;
|
||||
pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, pci_ioaddr1);
|
||||
dev_a8->resource[1].start = pci_ioaddr1;
|
||||
}
|
||||
#endif /* End HACK */
|
||||
}
|
||||
if (!pci_irq) { /* IRQ range check ?? */
|
||||
printk(KERN_WARNING "HiSax: Scitel Quadro (%s): No IRQ\n",
|
||||
sct_quadro_subtypes[cs->subtyp]);
|
||||
return (0);
|
||||
}
|
||||
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_1, &pci_ioaddr1);
|
||||
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_2, &pci_ioaddr2);
|
||||
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_3, &pci_ioaddr3);
|
||||
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_4, &pci_ioaddr4);
|
||||
pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_5, &pci_ioaddr5);
|
||||
if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) {
|
||||
printk(KERN_WARNING "HiSax: Scitel Quadro (%s): "
|
||||
"No IO base address(es)\n",
|
||||
sct_quadro_subtypes[cs->subtyp]);
|
||||
return (0);
|
||||
}
|
||||
pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK;
|
||||
/* Take over */
|
||||
cs->irq = pci_irq;
|
||||
cs->irq_flags |= IRQF_SHARED;
|
||||
/* pci_ioaddr1 is unique to all subdevices */
|
||||
/* pci_ioaddr2 is for the fourth subdevice only */
|
||||
/* pci_ioaddr3 is for the third subdevice only */
|
||||
/* pci_ioaddr4 is for the second subdevice only */
|
||||
/* pci_ioaddr5 is for the first subdevice only */
|
||||
cs->hw.ax.plx_adr = pci_ioaddr1;
|
||||
/* Enter all ipac_base addresses */
|
||||
switch (cs->subtyp) {
|
||||
case 1:
|
||||
cs->hw.ax.base = pci_ioaddr5 + 0x00;
|
||||
if (sct_alloc_io(pci_ioaddr1, 128))
|
||||
return (0);
|
||||
if (sct_alloc_io(pci_ioaddr5, 64))
|
||||
return (0);
|
||||
/* disable all IPAC */
|
||||
writereg(pci_ioaddr5, pci_ioaddr5 + 4,
|
||||
IPAC_MASK, 0xFF);
|
||||
writereg(pci_ioaddr4 + 0x08, pci_ioaddr4 + 0x0c,
|
||||
IPAC_MASK, 0xFF);
|
||||
writereg(pci_ioaddr3 + 0x10, pci_ioaddr3 + 0x14,
|
||||
IPAC_MASK, 0xFF);
|
||||
writereg(pci_ioaddr2 + 0x20, pci_ioaddr2 + 0x24,
|
||||
IPAC_MASK, 0xFF);
|
||||
break;
|
||||
case 2:
|
||||
cs->hw.ax.base = pci_ioaddr4 + 0x08;
|
||||
if (sct_alloc_io(pci_ioaddr4, 64))
|
||||
return (0);
|
||||
break;
|
||||
case 3:
|
||||
cs->hw.ax.base = pci_ioaddr3 + 0x10;
|
||||
if (sct_alloc_io(pci_ioaddr3, 64))
|
||||
return (0);
|
||||
break;
|
||||
case 4:
|
||||
cs->hw.ax.base = pci_ioaddr2 + 0x20;
|
||||
if (sct_alloc_io(pci_ioaddr2, 64))
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
/* For isac and hscx data path */
|
||||
cs->hw.ax.data_adr = cs->hw.ax.base + 4;
|
||||
|
||||
printk(KERN_INFO "HiSax: Scitel Quadro (%s) configured at "
|
||||
"0x%.4lX, 0x%.4lX, 0x%.4lX and IRQ %d\n",
|
||||
sct_quadro_subtypes[cs->subtyp],
|
||||
cs->hw.ax.plx_adr,
|
||||
cs->hw.ax.base,
|
||||
cs->hw.ax.data_adr,
|
||||
cs->irq);
|
||||
|
||||
test_and_set_bit(HW_IPAC, &cs->HW_Flags);
|
||||
|
||||
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 = &BKM_card_msg;
|
||||
cs->irq_func = &bkm_interrupt_ipac;
|
||||
|
||||
printk(KERN_INFO "HiSax: Scitel Quadro (%s): IPAC Version %d\n",
|
||||
sct_quadro_subtypes[cs->subtyp],
|
||||
readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID));
|
||||
return (1);
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
/* $Id: bkm_ax.h,v 1.5.6.3 2001/09/23 22:24:46 kai Exp $
|
||||
*
|
||||
* low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive)
|
||||
*
|
||||
* Author Roland Klabunde
|
||||
* Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BKM_AX_H__
|
||||
#define __BKM_AX_H__
|
||||
|
||||
/* Supported boards (subtypes) */
|
||||
#define SCT_1 1
|
||||
#define SCT_2 2
|
||||
#define SCT_3 3
|
||||
#define SCT_4 4
|
||||
#define BKM_A4T 5
|
||||
|
||||
#define PLX_ADDR_PLX 0x14 /* Addr PLX configuration */
|
||||
#define PLX_ADDR_ISAC 0x18 /* Addr ISAC */
|
||||
#define PLX_ADDR_HSCX 0x1C /* Addr HSCX */
|
||||
#define PLX_ADDR_ALE 0x20 /* Addr ALE */
|
||||
#define PLX_ADDR_ALEPLUS 0x24 /* Next Addr behind ALE */
|
||||
|
||||
#define PLX_SUBVEN 0x2C /* Offset SubVendor */
|
||||
#define PLX_SUBSYS 0x2E /* Offset SubSystem */
|
||||
|
||||
|
||||
/* Application specific registers I20 (Siemens SZB6120H) */
|
||||
typedef struct {
|
||||
/* Video front end horizontal configuration register */
|
||||
volatile u_int i20VFEHorzCfg; /* Offset 00 */
|
||||
/* Video front end vertical configuration register */
|
||||
volatile u_int i20VFEVertCfg; /* Offset 04 */
|
||||
/* Video front end scaler and pixel format register */
|
||||
volatile u_int i20VFEScaler; /* Offset 08 */
|
||||
/* Video display top register */
|
||||
volatile u_int i20VDispTop; /* Offset 0C */
|
||||
/* Video display bottom register */
|
||||
volatile u_int i20VDispBottom; /* Offset 10 */
|
||||
/* Video stride, status and frame grab register */
|
||||
volatile u_int i20VidFrameGrab;/* Offset 14 */
|
||||
/* Video display configuration register */
|
||||
volatile u_int i20VDispCfg; /* Offset 18 */
|
||||
/* Video masking map top */
|
||||
volatile u_int i20VMaskTop; /* Offset 1C */
|
||||
/* Video masking map bottom */
|
||||
volatile u_int i20VMaskBottom; /* Offset 20 */
|
||||
/* Overlay control register */
|
||||
volatile u_int i20OvlyControl; /* Offset 24 */
|
||||
/* System, PCI and general purpose pins control register */
|
||||
volatile u_int i20SysControl; /* Offset 28 */
|
||||
#define sysRESET 0x01000000 /* bit 24:Softreset (Low) */
|
||||
/* GPIO 4...0: Output fixed for our cfg! */
|
||||
#define sysCFG 0x000000E0 /* GPIO 7,6,5: Input */
|
||||
/* General purpose pins and guest bus control register */
|
||||
volatile u_int i20GuestControl;/* Offset 2C */
|
||||
#define guestWAIT_CFG 0x00005555 /* 4 PCI waits for all */
|
||||
#define guestISDN_INT_E 0x01000000 /* ISDN Int en (low) */
|
||||
#define guestVID_INT_E 0x02000000 /* Video interrupt en (low) */
|
||||
#define guestADI1_INT_R 0x04000000 /* ADI #1 int req (low) */
|
||||
#define guestADI2_INT_R 0x08000000 /* ADI #2 int req (low) */
|
||||
#define guestISDN_RES 0x10000000 /* ISDN reset bit (high) */
|
||||
#define guestADI1_INT_S 0x20000000 /* ADI #1 int pending (low) */
|
||||
#define guestADI2_INT_S 0x40000000 /* ADI #2 int pending (low) */
|
||||
#define guestISDN_INT_S 0x80000000 /* ISAC int pending (low) */
|
||||
|
||||
#define g_A4T_JADE_RES 0x01000000 /* JADE Reset (High) */
|
||||
#define g_A4T_ISAR_RES 0x02000000 /* ISAR Reset (High) */
|
||||
#define g_A4T_ISAC_RES 0x04000000 /* ISAC Reset (High) */
|
||||
#define g_A4T_JADE_BOOTR 0x08000000 /* JADE enable boot SRAM (Low) NOT USED */
|
||||
#define g_A4T_ISAR_BOOTR 0x10000000 /* ISAR enable boot SRAM (Low) NOT USED */
|
||||
#define g_A4T_JADE_INT_S 0x20000000 /* JADE interrupt pnd (Low) */
|
||||
#define g_A4T_ISAR_INT_S 0x40000000 /* ISAR interrupt pnd (Low) */
|
||||
#define g_A4T_ISAC_INT_S 0x80000000 /* ISAC interrupt pnd (Low) */
|
||||
|
||||
volatile u_int i20CodeSource; /* Offset 30 */
|
||||
volatile u_int i20CodeXferCtrl;/* Offset 34 */
|
||||
volatile u_int i20CodeMemPtr; /* Offset 38 */
|
||||
|
||||
volatile u_int i20IntStatus; /* Offset 3C */
|
||||
volatile u_int i20IntCtrl; /* Offset 40 */
|
||||
#define intISDN 0x40000000 /* GIRQ1En (ISAC/ADI) (High) */
|
||||
#define intVID 0x20000000 /* GIRQ0En (VSYNC) (High) */
|
||||
#define intCOD 0x10000000 /* CodRepIrqEn (High) */
|
||||
#define intPCI 0x01000000 /* PCI IntA enable (High) */
|
||||
|
||||
volatile u_int i20I2CCtrl; /* Offset 44 */
|
||||
} I20_REGISTER_FILE, *PI20_REGISTER_FILE;
|
||||
|
||||
/*
|
||||
* Postoffice structure for A4T
|
||||
*
|
||||
*/
|
||||
#define PO_OFFSET 0x00000200 /* Postoffice offset from base */
|
||||
|
||||
#define GCS_0 0x00000000 /* Guest bus chip selects */
|
||||
#define GCS_1 0x00100000
|
||||
#define GCS_2 0x00200000
|
||||
#define GCS_3 0x00300000
|
||||
|
||||
#define PO_READ 0x00000000 /* R/W from/to guest bus */
|
||||
#define PO_WRITE 0x00800000
|
||||
|
||||
#define PO_PEND 0x02000000
|
||||
|
||||
#define POSTOFFICE(postoffice) *(volatile unsigned int *)(postoffice)
|
||||
|
||||
/* Wait unlimited (don't worry) */
|
||||
#define __WAITI20__(postoffice) \
|
||||
do { \
|
||||
while ((POSTOFFICE(postoffice) & PO_PEND)) ; \
|
||||
} while (0)
|
||||
|
||||
#endif /* __BKM_AX_H__ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,218 +0,0 @@
|
|||
/*======================================================================
|
||||
|
||||
An elsa_cs PCMCIA client driver
|
||||
|
||||
This driver is for the Elsa PCM ISDN Cards, i.e. the MicroLink
|
||||
|
||||
|
||||
The contents of this file are subject to the Mozilla Public
|
||||
License Version 1.1 (the "License"); you may not use this file
|
||||
except in compliance with the License. You may obtain a copy of
|
||||
the License at http://www.mozilla.org/MPL/
|
||||
|
||||
Software distributed under the License is distributed on an "AS
|
||||
IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
rights and limitations under the License.
|
||||
|
||||
The initial developer of the original code is David A. Hinds
|
||||
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds
|
||||
are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
|
||||
|
||||
Modifications from dummy_cs.c are Copyright (C) 1999-2001 Klaus
|
||||
Lichtenwalder <Lichtenwalder@ACM.org>. All Rights Reserved.
|
||||
|
||||
Alternatively, the contents of this file may be used under the
|
||||
terms of the GNU General Public License version 2 (the "GPL"), in
|
||||
which case the provisions of the GPL are applicable instead of the
|
||||
above. If you wish to allow the use of your version of this file
|
||||
only under the terms of the GPL and not to allow others to use
|
||||
your version of this file under the MPL, indicate your decision
|
||||
by deleting the provisions above and replace them with the notice
|
||||
and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this
|
||||
file under either the MPL or the GPL.
|
||||
|
||||
======================================================================*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include "hisax_cfg.h"
|
||||
|
||||
MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Elsa PCM cards");
|
||||
MODULE_AUTHOR("Klaus Lichtenwalder");
|
||||
MODULE_LICENSE("Dual MPL/GPL");
|
||||
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
/* Parameters that can be set with 'insmod' */
|
||||
|
||||
static int protocol = 2; /* EURO-ISDN Default */
|
||||
module_param(protocol, int, 0);
|
||||
|
||||
static int elsa_cs_config(struct pcmcia_device *link);
|
||||
static void elsa_cs_release(struct pcmcia_device *link);
|
||||
static void elsa_cs_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
typedef struct local_info_t {
|
||||
struct pcmcia_device *p_dev;
|
||||
int busy;
|
||||
int cardnr;
|
||||
} local_info_t;
|
||||
|
||||
static int elsa_cs_probe(struct pcmcia_device *link)
|
||||
{
|
||||
local_info_t *local;
|
||||
|
||||
dev_dbg(&link->dev, "elsa_cs_attach()\n");
|
||||
|
||||
/* Allocate space for private device-specific data */
|
||||
local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
|
||||
if (!local) return -ENOMEM;
|
||||
|
||||
local->p_dev = link;
|
||||
link->priv = local;
|
||||
|
||||
local->cardnr = -1;
|
||||
|
||||
return elsa_cs_config(link);
|
||||
} /* elsa_cs_attach */
|
||||
|
||||
static void elsa_cs_detach(struct pcmcia_device *link)
|
||||
{
|
||||
local_info_t *info = link->priv;
|
||||
|
||||
dev_dbg(&link->dev, "elsa_cs_detach(0x%p)\n", link);
|
||||
|
||||
info->busy = 1;
|
||||
elsa_cs_release(link);
|
||||
|
||||
kfree(info);
|
||||
} /* elsa_cs_detach */
|
||||
|
||||
static int elsa_cs_configcheck(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
int j;
|
||||
|
||||
p_dev->io_lines = 3;
|
||||
p_dev->resource[0]->end = 8;
|
||||
p_dev->resource[0]->flags &= IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
|
||||
|
||||
if ((p_dev->resource[0]->end) && p_dev->resource[0]->start) {
|
||||
printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
|
||||
if (!pcmcia_request_io(p_dev))
|
||||
return 0;
|
||||
} else {
|
||||
printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
|
||||
for (j = 0x2f0; j > 0x100; j -= 0x10) {
|
||||
p_dev->resource[0]->start = j;
|
||||
if (!pcmcia_request_io(p_dev))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int elsa_cs_config(struct pcmcia_device *link)
|
||||
{
|
||||
int i;
|
||||
IsdnCard_t icard;
|
||||
|
||||
dev_dbg(&link->dev, "elsa_config(0x%p)\n", link);
|
||||
|
||||
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
|
||||
|
||||
i = pcmcia_loop_config(link, elsa_cs_configcheck, NULL);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
if (!link->irq)
|
||||
goto failed;
|
||||
|
||||
i = pcmcia_enable_device(link);
|
||||
if (i != 0)
|
||||
goto failed;
|
||||
|
||||
icard.para[0] = link->irq;
|
||||
icard.para[1] = link->resource[0]->start;
|
||||
icard.protocol = protocol;
|
||||
icard.typ = ISDN_CTYPE_ELSA_PCMCIA;
|
||||
|
||||
i = hisax_init_pcmcia(link, &(((local_info_t *)link->priv)->busy), &icard);
|
||||
if (i < 0) {
|
||||
printk(KERN_ERR "elsa_cs: failed to initialize Elsa "
|
||||
"PCMCIA %d with %pR\n", i, link->resource[0]);
|
||||
elsa_cs_release(link);
|
||||
} else
|
||||
((local_info_t *)link->priv)->cardnr = i;
|
||||
|
||||
return 0;
|
||||
failed:
|
||||
elsa_cs_release(link);
|
||||
return -ENODEV;
|
||||
} /* elsa_cs_config */
|
||||
|
||||
static void elsa_cs_release(struct pcmcia_device *link)
|
||||
{
|
||||
local_info_t *local = link->priv;
|
||||
|
||||
dev_dbg(&link->dev, "elsa_cs_release(0x%p)\n", link);
|
||||
|
||||
if (local) {
|
||||
if (local->cardnr >= 0) {
|
||||
/* no unregister function with hisax */
|
||||
HiSax_closecard(local->cardnr);
|
||||
}
|
||||
}
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
} /* elsa_cs_release */
|
||||
|
||||
static int elsa_suspend(struct pcmcia_device *link)
|
||||
{
|
||||
local_info_t *dev = link->priv;
|
||||
|
||||
dev->busy = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elsa_resume(struct pcmcia_device *link)
|
||||
{
|
||||
local_info_t *dev = link->priv;
|
||||
|
||||
dev->busy = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pcmcia_device_id elsa_ids[] = {
|
||||
PCMCIA_DEVICE_PROD_ID12("ELSA AG (Aachen, Germany)", "MicroLink ISDN/MC ", 0x983de2c4, 0x333ba257),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELSA GmbH, Aachen", "MicroLink ISDN/MC ", 0x639e5718, 0x333ba257),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, elsa_ids);
|
||||
|
||||
static struct pcmcia_driver elsa_cs_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "elsa_cs",
|
||||
.probe = elsa_cs_probe,
|
||||
.remove = elsa_cs_detach,
|
||||
.id_table = elsa_ids,
|
||||
.suspend = elsa_suspend,
|
||||
.resume = elsa_resume,
|
||||
};
|
||||
module_pcmcia_driver(elsa_cs_driver);
|
|
@ -1,659 +0,0 @@
|
|||
/* $Id: elsa_ser.c,v 2.14.2.3 2004/02/11 13:21:33 keil Exp $
|
||||
*
|
||||
* stuff for the serial modem on ELSA cards
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/serial.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define MAX_MODEM_BUF 256
|
||||
#define WAKEUP_CHARS (MAX_MODEM_BUF / 2)
|
||||
#define RS_ISR_PASS_LIMIT 256
|
||||
#define BASE_BAUD (1843200 / 16)
|
||||
|
||||
//#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 ELSA_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 ELSA_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 ELSA_SERIAL_NOPAUSE_IO
|
||||
debugl1(cs, "outp %s %02x", ModemOut[offset], value);
|
||||
#else
|
||||
debugl1(cs, "outP %s %02x", ModemOut[offset], value);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ELSA_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;
|
||||
|
||||
|
||||
/* byte size and parity */
|
||||
cval = 0x03;
|
||||
/* 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);
|
||||
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);
|
||||
}
|
||||
|
||||
static int mstartup(struct IsdnCardState *cs)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
/*
|
||||
* 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:
|
||||
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)
|
||||
{
|
||||
|
||||
#ifdef SERIAL_DEBUG_OPEN
|
||||
printk(KERN_DEBUG"Shutting down serial ....");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
#ifdef SERIAL_DEBUG_OPEN
|
||||
printk(" done\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int
|
||||
write_modem(struct BCState *bcs) {
|
||||
int ret = 0;
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int count, len, fp;
|
||||
|
||||
if (!bcs->tx_skb)
|
||||
return 0;
|
||||
if (bcs->tx_skb->len <= 0)
|
||||
return 0;
|
||||
len = bcs->tx_skb->len;
|
||||
if (len > MAX_MODEM_BUF - cs->hw.elsa.transcnt)
|
||||
len = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
|
||||
fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
|
||||
fp &= (MAX_MODEM_BUF - 1);
|
||||
count = len;
|
||||
if (count > MAX_MODEM_BUF - fp) {
|
||||
count = MAX_MODEM_BUF - fp;
|
||||
skb_copy_from_linear_data(bcs->tx_skb,
|
||||
cs->hw.elsa.transbuf + fp, count);
|
||||
skb_pull(bcs->tx_skb, count);
|
||||
cs->hw.elsa.transcnt += count;
|
||||
ret = count;
|
||||
count = len - count;
|
||||
fp = 0;
|
||||
}
|
||||
skb_copy_from_linear_data(bcs->tx_skb,
|
||||
cs->hw.elsa.transbuf + fp, 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);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static inline void
|
||||
modem_fill(struct BCState *bcs) {
|
||||
|
||||
if (bcs->tx_skb) {
|
||||
if (bcs->tx_skb->len) {
|
||||
write_modem(bcs);
|
||||
return;
|
||||
} else {
|
||||
if (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) &&
|
||||
(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
|
||||
u_long flags;
|
||||
spin_lock_irqsave(&bcs->aclock, flags);
|
||||
bcs->ackcnt += bcs->hw.hscx.count;
|
||||
spin_unlock_irqrestore(&bcs->aclock, flags);
|
||||
schedule_event(bcs, B_ACKPENDING);
|
||||
}
|
||||
dev_kfree_skb_any(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);
|
||||
schedule_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 {
|
||||
skb_put_data(skb, cs->hw.elsa.rcvbuf,
|
||||
cs->hw.elsa.rcvcnt);
|
||||
skb_queue_tail(&cs->hw.elsa.bcs->rqueue, skb);
|
||||
}
|
||||
schedule_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, "%s", 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void rs_interrupt_elsa(struct IsdnCardState *cs)
|
||||
{
|
||||
int status, iir, msr;
|
||||
int pass_counter = 0;
|
||||
|
||||
#ifdef SERIAL_DEBUG_INTR
|
||||
printk(KERN_DEBUG "rs_interrupt_single(%d)...", cs->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, NULL);
|
||||
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);
|
||||
|
||||
static void
|
||||
close_elsastate(struct BCState *bcs)
|
||||
{
|
||||
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;
|
||||
}
|
||||
skb_queue_purge(&bcs->rqueue);
|
||||
skb_queue_purge(&bcs->squeue);
|
||||
if (bcs->tx_skb) {
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
|
||||
int count, fp;
|
||||
u_char *msg = buf;
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
|
||||
return;
|
||||
}
|
||||
fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
|
||||
fp &= (MAX_MODEM_BUF - 1);
|
||||
count = len;
|
||||
if (count > MAX_MODEM_BUF - fp) {
|
||||
count = MAX_MODEM_BUF - fp;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
modem_set_init(struct IsdnCardState *cs) {
|
||||
int timeout;
|
||||
|
||||
#define RCV_DELAY 20
|
||||
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);
|
||||
mdelay(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);
|
||||
mdelay(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);
|
||||
mdelay(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);
|
||||
mdelay(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);
|
||||
mdelay(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);
|
||||
mdelay(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);
|
||||
mdelay(RCV_DELAY);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_set_dial(struct IsdnCardState *cs, int outgoing) {
|
||||
int timeout;
|
||||
#define RCV_DELAY 20
|
||||
|
||||
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);
|
||||
mdelay(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);
|
||||
mdelay(RCV_DELAY);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_l2l1(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct BCState *bcs = st->l1.bcs;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
|
||||
if (pr == (PH_DATA | REQUEST)) {
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
skb_queue_tail(&bcs->squeue, skb);
|
||||
} else {
|
||||
bcs->tx_skb = skb;
|
||||
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->hw.hscx.count = 0;
|
||||
write_modem(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
} else if (pr == (PH_ACTIVATE | REQUEST)) {
|
||||
test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
|
||||
set_arcofi(bcs->cs, st->l1.bc);
|
||||
mstartup(bcs->cs);
|
||||
modem_set_dial(bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
|
||||
bcs->cs->hw.elsa.MFlag = 2;
|
||||
} else if (pr == (PH_DEACTIVATE | REQUEST)) {
|
||||
test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
bcs->cs->dc.isac.arcofi_bc = st->l1.bc;
|
||||
arcofi_fsm(bcs->cs, ARCOFI_START, &ARCOFI_XOP_0);
|
||||
wait_event_interruptible(bcs->cs->dc.isac.arcofi_wait,
|
||||
bcs->cs->dc.isac.arcofi_state == ARCOFI_NOP);
|
||||
bcs->cs->hw.elsa.MFlag = 1;
|
||||
} else {
|
||||
printk(KERN_WARNING "ElsaSer: unknown pr %x\n", pr);
|
||||
}
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
}
|
|
@ -1,420 +0,0 @@
|
|||
/* enternow_pci.c,v 0.99 2001/10/02
|
||||
*
|
||||
* enternow_pci.c Card-specific routines for
|
||||
* Formula-n enter:now ISDN PCI ab
|
||||
* Gerdes AG Power ISDN PCI
|
||||
* Woerltronic SA 16 PCI
|
||||
* (based on HiSax driver by Karsten Keil)
|
||||
*
|
||||
* Author Christoph Ersfeld <info@formula-n.de>
|
||||
* Formula-n Europe AG (www.formula-n.com)
|
||||
* previously Gerdes AG
|
||||
*
|
||||
*
|
||||
* This file is (c) under GNU PUBLIC LICENSE
|
||||
*
|
||||
* Notes:
|
||||
* This driver interfaces to netjet.c which performs B-channel
|
||||
* processing.
|
||||
*
|
||||
* Version 0.99 is the first release of this driver and there are
|
||||
* certainly a few bugs.
|
||||
* It isn't testet on linux 2.4 yet, so consider this code to be
|
||||
* beta.
|
||||
*
|
||||
* Please don't report me any malfunction without sending
|
||||
* (compressed) debug-logs.
|
||||
* It would be nearly impossible to retrace it.
|
||||
*
|
||||
* Log D-channel-processing as follows:
|
||||
*
|
||||
* 1. Load hisax with card-specific parameters, this example ist for
|
||||
* Formula-n enter:now ISDN PCI and compatible
|
||||
* (f.e. Gerdes Power ISDN PCI)
|
||||
*
|
||||
* modprobe hisax type=41 protocol=2 id=gerdes
|
||||
*
|
||||
* if you chose an other value for id, you need to modify the
|
||||
* code below, too.
|
||||
*
|
||||
* 2. set debug-level
|
||||
*
|
||||
* hisaxctrl gerdes 1 0x3ff
|
||||
* hisaxctrl gerdes 11 0x4f
|
||||
* cat /dev/isdnctrl >> ~/log &
|
||||
*
|
||||
* Please take also a look into /var/log/messages if there is
|
||||
* anything importand concerning HISAX.
|
||||
*
|
||||
*
|
||||
* Credits:
|
||||
* Programming the driver for Formula-n enter:now ISDN PCI and
|
||||
* necessary the driver for the used Amd 7930 D-channel-controller
|
||||
* was spnsored by Formula-n Europe AG.
|
||||
* Thanks to Karsten Keil and Petr Novak, who gave me support in
|
||||
* Hisax-specific questions.
|
||||
* I want so say special thanks to Carl-Friedrich Braun, who had to
|
||||
* answer a lot of questions about generally ISDN and about handling
|
||||
* of the Amd-Chip.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "isdnl1.h"
|
||||
#include "amd7930_fn.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include "netjet.h"
|
||||
|
||||
|
||||
|
||||
static const char *enternow_pci_rev = "$Revision: 1.1.4.5 $";
|
||||
|
||||
|
||||
/* for PowerISDN PCI */
|
||||
#define TJ_AMD_IRQ 0x20
|
||||
#define TJ_LED1 0x40
|
||||
#define TJ_LED2 0x80
|
||||
|
||||
|
||||
/* The window to [the] AMD [chip]...
|
||||
* From address hw.njet.base + TJ_AMD_PORT onwards, the AMD
|
||||
* maps [consecutive/multiple] 8 bits into the TigerJet I/O space
|
||||
* -> 0x01 of the AMD at hw.njet.base + 0C4 */
|
||||
#define TJ_AMD_PORT 0xC0
|
||||
|
||||
|
||||
|
||||
/* *************************** I/O-Interface functions ************************************* */
|
||||
|
||||
|
||||
/* cs->readisac, macro rByteAMD */
|
||||
static unsigned char
|
||||
ReadByteAmd7930(struct IsdnCardState *cs, unsigned char offset)
|
||||
{
|
||||
/* direct register */
|
||||
if (offset < 8)
|
||||
return (inb(cs->hw.njet.isac + 4 * offset));
|
||||
|
||||
/* indirect register */
|
||||
else {
|
||||
outb(offset, cs->hw.njet.isac + 4 * AMD_CR);
|
||||
return (inb(cs->hw.njet.isac + 4 * AMD_DR));
|
||||
}
|
||||
}
|
||||
|
||||
/* cs->writeisac, macro wByteAMD */
|
||||
static void
|
||||
WriteByteAmd7930(struct IsdnCardState *cs, unsigned char offset, unsigned char value)
|
||||
{
|
||||
/* direct register */
|
||||
if (offset < 8)
|
||||
outb(value, cs->hw.njet.isac + 4 * offset);
|
||||
|
||||
/* indirect register */
|
||||
else {
|
||||
outb(offset, cs->hw.njet.isac + 4 * AMD_CR);
|
||||
outb(value, cs->hw.njet.isac + 4 * AMD_DR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
enpci_setIrqMask(struct IsdnCardState *cs, unsigned char val) {
|
||||
if (!val)
|
||||
outb(0x00, cs->hw.njet.base + NETJET_IRQMASK1);
|
||||
else
|
||||
outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
|
||||
}
|
||||
|
||||
|
||||
static unsigned char dummyrr(struct IsdnCardState *cs, int chan, unsigned char off)
|
||||
{
|
||||
return (5);
|
||||
}
|
||||
|
||||
static void dummywr(struct IsdnCardState *cs, int chan, unsigned char off, unsigned char value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************************************************** */
|
||||
|
||||
|
||||
static void
|
||||
reset_enpci(struct IsdnCardState *cs)
|
||||
{
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "enter:now PCI: reset");
|
||||
|
||||
/* Reset on, (also for AMD) */
|
||||
cs->hw.njet.ctrl_reg = 0x07;
|
||||
outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
|
||||
mdelay(20);
|
||||
/* Reset off */
|
||||
cs->hw.njet.ctrl_reg = 0x30;
|
||||
outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
|
||||
/* 20ms delay */
|
||||
mdelay(20);
|
||||
cs->hw.njet.auxd = 0; // LED-status
|
||||
cs->hw.njet.dmactrl = 0;
|
||||
outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL);
|
||||
outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
|
||||
outb(cs->hw.njet.auxd, cs->hw.njet.auxa); // LED off
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
enpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
unsigned char *chan;
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "enter:now PCI: card_msg: 0x%04X", mt);
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_enpci(cs);
|
||||
Amd7930_init(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case CARD_RELEASE:
|
||||
release_io_netjet(cs);
|
||||
break;
|
||||
case CARD_INIT:
|
||||
reset_enpci(cs);
|
||||
inittiger(cs);
|
||||
/* irq must be on here */
|
||||
Amd7930_init(cs);
|
||||
break;
|
||||
case CARD_TEST:
|
||||
break;
|
||||
case MDL_ASSIGN:
|
||||
/* TEI assigned, LED1 on */
|
||||
cs->hw.njet.auxd = TJ_AMD_IRQ << 1;
|
||||
outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
|
||||
break;
|
||||
case MDL_REMOVE:
|
||||
/* TEI removed, LEDs off */
|
||||
cs->hw.njet.auxd = 0;
|
||||
outb(0x00, cs->hw.njet.base + NETJET_AUXDATA);
|
||||
break;
|
||||
case MDL_BC_ASSIGN:
|
||||
/* activate B-channel */
|
||||
chan = (unsigned char *)arg;
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "enter:now PCI: assign phys. BC %d in AMD LMR1", *chan);
|
||||
|
||||
cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 | (*chan + 1)), "MDL_BC_ASSIGN");
|
||||
/* at least one b-channel in use, LED 2 on */
|
||||
cs->hw.njet.auxd |= TJ_AMD_IRQ << 2;
|
||||
outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
|
||||
break;
|
||||
case MDL_BC_RELEASE:
|
||||
/* deactivate B-channel */
|
||||
chan = (unsigned char *)arg;
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "enter:now PCI: release phys. BC %d in Amd LMR1", *chan);
|
||||
|
||||
cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 & ~(*chan + 1)), "MDL_BC_RELEASE");
|
||||
/* no b-channel active -> LED2 off */
|
||||
if (!(cs->dc.amd7930.lmr1 & 3)) {
|
||||
cs->hw.njet.auxd &= ~(TJ_AMD_IRQ << 2);
|
||||
outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
enpci_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
unsigned char s0val, s1val, ir;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
s1val = inb(cs->hw.njet.base + NETJET_IRQSTAT1);
|
||||
|
||||
/* AMD threw an interrupt */
|
||||
if (!(s1val & TJ_AMD_IRQ)) {
|
||||
/* read and clear interrupt-register */
|
||||
ir = ReadByteAmd7930(cs, 0x00);
|
||||
Amd7930_interrupt(cs, ir);
|
||||
s1val = 1;
|
||||
} else
|
||||
s1val = 0;
|
||||
s0val = inb(cs->hw.njet.base + NETJET_IRQSTAT0);
|
||||
if ((s0val | s1val) == 0) { // shared IRQ
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
if (s0val)
|
||||
outb(s0val, cs->hw.njet.base + NETJET_IRQSTAT0);
|
||||
|
||||
/* DMA-Interrupt: B-channel-stuff */
|
||||
/* set bits in sval to indicate which page is free */
|
||||
if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
|
||||
inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
|
||||
/* the 2nd write page is free */
|
||||
s0val = 0x08;
|
||||
else /* the 1st write page is free */
|
||||
s0val = 0x04;
|
||||
if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
|
||||
inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
|
||||
/* the 2nd read page is free */
|
||||
s0val = s0val | 0x02;
|
||||
else /* the 1st read page is free */
|
||||
s0val = s0val | 0x01;
|
||||
if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
|
||||
{
|
||||
if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
cs->hw.njet.irqstat0 = s0val;
|
||||
if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) !=
|
||||
(cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
|
||||
/* we have a read dma int */
|
||||
read_tiger(cs);
|
||||
if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
|
||||
(cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
|
||||
/* we have a write dma int */
|
||||
write_tiger(cs);
|
||||
test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int en_pci_probe(struct pci_dev *dev_netjet, struct IsdnCardState *cs)
|
||||
{
|
||||
if (pci_enable_device(dev_netjet))
|
||||
return (0);
|
||||
cs->irq = dev_netjet->irq;
|
||||
if (!cs->irq) {
|
||||
printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n");
|
||||
return (0);
|
||||
}
|
||||
cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
|
||||
if (!cs->hw.njet.base) {
|
||||
printk(KERN_WARNING "enter:now PCI: No IO-Adr for PCI card found\n");
|
||||
return (0);
|
||||
}
|
||||
/* checks Sub-Vendor ID because system crashes with Traverse-Card */
|
||||
if ((dev_netjet->subsystem_vendor != 0x55) ||
|
||||
(dev_netjet->subsystem_device != 0x02)) {
|
||||
printk(KERN_WARNING "enter:now: You tried to load this driver with an incompatible TigerJet-card\n");
|
||||
printk(KERN_WARNING "Use type=20 for Traverse NetJet PCI Card.\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void en_cs_init(struct IsdnCard *card, struct IsdnCardState *cs)
|
||||
{
|
||||
cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
|
||||
cs->hw.njet.isac = cs->hw.njet.base + 0xC0; // Fenster zum AMD
|
||||
|
||||
/* Reset an */
|
||||
cs->hw.njet.ctrl_reg = 0x07; // geändert von 0xff
|
||||
outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
|
||||
/* 20 ms Pause */
|
||||
mdelay(20);
|
||||
|
||||
cs->hw.njet.ctrl_reg = 0x30; /* Reset Off and status read clear */
|
||||
outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
|
||||
mdelay(10);
|
||||
|
||||
cs->hw.njet.auxd = 0x00; // war 0xc0
|
||||
cs->hw.njet.dmactrl = 0;
|
||||
|
||||
outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL);
|
||||
outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
|
||||
outb(cs->hw.njet.auxd, cs->hw.njet.auxa);
|
||||
}
|
||||
|
||||
static int en_cs_init_rest(struct IsdnCard *card, struct IsdnCardState *cs)
|
||||
{
|
||||
const int bytecnt = 256;
|
||||
|
||||
printk(KERN_INFO
|
||||
"enter:now PCI: PCI card configured at 0x%lx IRQ %d\n",
|
||||
cs->hw.njet.base, cs->irq);
|
||||
if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: enter:now config port %lx-%lx already in use\n",
|
||||
cs->hw.njet.base,
|
||||
cs->hw.njet.base + bytecnt);
|
||||
return (0);
|
||||
}
|
||||
|
||||
setup_Amd7930(cs);
|
||||
cs->hw.njet.last_is0 = 0;
|
||||
/* macro rByteAMD */
|
||||
cs->readisac = &ReadByteAmd7930;
|
||||
/* macro wByteAMD */
|
||||
cs->writeisac = &WriteByteAmd7930;
|
||||
cs->dc.amd7930.setIrqMask = &enpci_setIrqMask;
|
||||
|
||||
cs->BC_Read_Reg = &dummyrr;
|
||||
cs->BC_Write_Reg = &dummywr;
|
||||
cs->BC_Send_Data = &netjet_fill_dma;
|
||||
cs->cardmsg = &enpci_card_msg;
|
||||
cs->irq_func = &enpci_interrupt;
|
||||
cs->irq_flags |= IRQF_SHARED;
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
static struct pci_dev *dev_netjet = NULL;
|
||||
|
||||
/* called by config.c */
|
||||
int setup_enternow_pci(struct IsdnCard *card)
|
||||
{
|
||||
int ret;
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
#error "not running on big endian machines now"
|
||||
#endif
|
||||
|
||||
strcpy(tmp, enternow_pci_rev);
|
||||
printk(KERN_INFO "HiSax: Formula-n Europe AG enter:now ISDN PCI driver Rev. %s\n", HiSax_getrev(tmp));
|
||||
if (cs->typ != ISDN_CTYPE_ENTERNOW)
|
||||
return (0);
|
||||
test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if ((dev_netjet = hisax_find_pci_device(PCI_VENDOR_ID_TIGERJET,
|
||||
PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) {
|
||||
ret = en_pci_probe(dev_netjet, cs);
|
||||
if (!ret)
|
||||
return (0);
|
||||
} else {
|
||||
printk(KERN_WARNING "enter:now PCI: No PCI card found\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
en_cs_init(card, cs);
|
||||
break;
|
||||
}
|
||||
|
||||
return en_cs_init_rest(card, cs);
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
/* $Id: fsm.c,v 1.14.6.4 2001/09/23 22:24:47 kai Exp $
|
||||
*
|
||||
* Finite state machine
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
* by Kai Germaschewski <kai.germaschewski@gmx.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to Jan den Ouden
|
||||
* Fritz Elfert
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
|
||||
#define FSM_TIMER_DEBUG 0
|
||||
|
||||
int
|
||||
FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount)
|
||||
{
|
||||
int i;
|
||||
|
||||
fsm->jumpmatrix =
|
||||
kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count,
|
||||
fsm->event_count),
|
||||
GFP_KERNEL);
|
||||
if (!fsm->jumpmatrix)
|
||||
return -ENOMEM;
|
||||
|
||||
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(%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] = (FSMFNPTR)fnlist[i].routine;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
FsmFree(struct Fsm *fsm)
|
||||
{
|
||||
kfree((void *) fsm->jumpmatrix);
|
||||
}
|
||||
|
||||
int
|
||||
FsmEvent(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FSMFNPTR r;
|
||||
|
||||
if ((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 = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
|
||||
if (r) {
|
||||
if (fi->debug)
|
||||
fi->printdebug(fi, "State %s Event %s",
|
||||
fi->fsm->strState[fi->state],
|
||||
fi->fsm->strEvent[event]);
|
||||
r(fi, event, arg);
|
||||
return (0);
|
||||
} else {
|
||||
if (fi->debug)
|
||||
fi->printdebug(fi, "State %s Event %s no routine",
|
||||
fi->fsm->strState[fi->state],
|
||||
fi->fsm->strEvent[event]);
|
||||
return (!0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FsmChangeState(struct FsmInst *fi, int newstate)
|
||||
{
|
||||
fi->state = newstate;
|
||||
if (fi->debug)
|
||||
fi->printdebug(fi, "ChangeState %s",
|
||||
fi->fsm->strState[newstate]);
|
||||
}
|
||||
|
||||
static void
|
||||
FsmExpireTimer(struct timer_list *t)
|
||||
{
|
||||
struct FsmTimer *ft = from_timer(ft, t, tl);
|
||||
#if FSM_TIMER_DEBUG
|
||||
if (ft->fi->debug)
|
||||
ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
|
||||
#endif
|
||||
FsmEvent(ft->fi, ft->event, ft->arg);
|
||||
}
|
||||
|
||||
void
|
||||
FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
|
||||
{
|
||||
ft->fi = fi;
|
||||
#if FSM_TIMER_DEBUG
|
||||
if (ft->fi->debug)
|
||||
ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
|
||||
#endif
|
||||
timer_setup(&ft->tl, FsmExpireTimer, 0);
|
||||
}
|
||||
|
||||
void
|
||||
FsmDelTimer(struct FsmTimer *ft, int where)
|
||||
{
|
||||
#if FSM_TIMER_DEBUG
|
||||
if (ft->fi->debug)
|
||||
ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
|
||||
#endif
|
||||
del_timer(&ft->tl);
|
||||
}
|
||||
|
||||
int
|
||||
FsmAddTimer(struct FsmTimer *ft,
|
||||
int millisec, int event, void *arg, int where)
|
||||
{
|
||||
|
||||
#if FSM_TIMER_DEBUG
|
||||
if (ft->fi->debug)
|
||||
ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
|
||||
(long) ft, millisec, where);
|
||||
#endif
|
||||
|
||||
if (timer_pending(&ft->tl)) {
|
||||
printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
|
||||
ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
|
||||
return -1;
|
||||
}
|
||||
ft->event = event;
|
||||
ft->arg = arg;
|
||||
ft->tl.expires = jiffies + (millisec * HZ) / 1000;
|
||||
add_timer(&ft->tl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
FsmRestartTimer(struct FsmTimer *ft,
|
||||
int millisec, int event, void *arg, int where)
|
||||
{
|
||||
|
||||
#if FSM_TIMER_DEBUG
|
||||
if (ft->fi->debug)
|
||||
ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
|
||||
(long) ft, millisec, where);
|
||||
#endif
|
||||
|
||||
if (timer_pending(&ft->tl))
|
||||
del_timer(&ft->tl);
|
||||
ft->event = event;
|
||||
ft->arg = arg;
|
||||
ft->tl.expires = jiffies + (millisec * HZ) / 1000;
|
||||
add_timer(&ft->tl);
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/* $Id: fsm.h,v 1.3.2.2 2001/09/23 22:24:47 kai Exp $
|
||||
*
|
||||
* Finite state machine
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
* by Kai Germaschewski <kai.germaschewski@gmx.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __FSM_H__
|
||||
#define __FSM_H__
|
||||
|
||||
#include <linux/timer.h>
|
||||
|
||||
struct FsmInst;
|
||||
|
||||
typedef void (*FSMFNPTR)(struct FsmInst *, int, void *);
|
||||
|
||||
struct Fsm {
|
||||
FSMFNPTR *jumpmatrix;
|
||||
int state_count, event_count;
|
||||
char **strEvent, **strState;
|
||||
};
|
||||
|
||||
struct FsmInst {
|
||||
struct Fsm *fsm;
|
||||
int state;
|
||||
int debug;
|
||||
void *userdata;
|
||||
int userint;
|
||||
void (*printdebug) (struct FsmInst *, char *, ...);
|
||||
};
|
||||
|
||||
struct FsmNode {
|
||||
int state, event;
|
||||
void (*routine) (struct FsmInst *, int, void *);
|
||||
};
|
||||
|
||||
struct FsmTimer {
|
||||
struct FsmInst *fi;
|
||||
struct timer_list tl;
|
||||
int event;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
|
||||
void FsmFree(struct Fsm *fsm);
|
||||
int FsmEvent(struct FsmInst *fi, int event, void *arg);
|
||||
void FsmChangeState(struct FsmInst *fi, int newstate);
|
||||
void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
|
||||
int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
|
||||
void *arg, int where);
|
||||
void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
|
||||
void *arg, int where);
|
||||
void FsmDelTimer(struct FsmTimer *ft, int where);
|
||||
|
||||
#endif
|
|
@ -1,691 +0,0 @@
|
|||
/* $Id: gazel.c,v 2.19.2.4 2004/01/14 16:04:48 keil Exp $
|
||||
*
|
||||
* low level stuff for Gazel isdn cards
|
||||
*
|
||||
* Author BeWan Systems
|
||||
* based on source code from Karsten Keil
|
||||
* Copyright by BeWan Systems
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "hscx.h"
|
||||
#include "isdnl1.h"
|
||||
#include "ipac.h"
|
||||
#include <linux/pci.h>
|
||||
|
||||
static const char *gazel_revision = "$Revision: 2.19.2.4 $";
|
||||
|
||||
#define R647 1
|
||||
#define R685 2
|
||||
#define R753 3
|
||||
#define R742 4
|
||||
|
||||
#define PLX_CNTRL 0x50 /* registre de controle PLX */
|
||||
#define RESET_GAZEL 0x4
|
||||
#define RESET_9050 0x40000000
|
||||
#define PLX_INCSR 0x4C /* registre d'IT du 9050 */
|
||||
#define INT_ISAC_EN 0x8 /* 1 = enable IT isac */
|
||||
#define INT_ISAC 0x20 /* 1 = IT isac en cours */
|
||||
#define INT_HSCX_EN 0x1 /* 1 = enable IT hscx */
|
||||
#define INT_HSCX 0x4 /* 1 = IT hscx en cours */
|
||||
#define INT_PCI_EN 0x40 /* 1 = enable IT PCI */
|
||||
#define INT_IPAC_EN 0x3 /* enable IT ipac */
|
||||
|
||||
|
||||
#define byteout(addr, val) outb(val, addr)
|
||||
#define bytein(addr) inb(addr)
|
||||
|
||||
static inline u_char
|
||||
readreg(unsigned int adr, u_short off)
|
||||
{
|
||||
return bytein(adr + off);
|
||||
}
|
||||
|
||||
static inline void
|
||||
writereg(unsigned int adr, u_short off, u_char data)
|
||||
{
|
||||
byteout(adr + off, data);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
read_fifo(unsigned int adr, u_char *data, int size)
|
||||
{
|
||||
insb(adr, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
write_fifo(unsigned int adr, u_char *data, int size)
|
||||
{
|
||||
outsb(adr, data, size);
|
||||
}
|
||||
|
||||
static inline u_char
|
||||
readreg_ipac(unsigned int adr, u_short off)
|
||||
{
|
||||
register u_char ret;
|
||||
|
||||
byteout(adr, off);
|
||||
ret = bytein(adr + 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
writereg_ipac(unsigned int adr, u_short off, u_char data)
|
||||
{
|
||||
byteout(adr, off);
|
||||
byteout(adr + 4, data);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
read_fifo_ipac(unsigned int adr, u_short off, u_char *data, int size)
|
||||
{
|
||||
byteout(adr, off);
|
||||
insb(adr + 4, data, size);
|
||||
}
|
||||
|
||||
static void
|
||||
write_fifo_ipac(unsigned int adr, u_short off, u_char *data, int size)
|
||||
{
|
||||
byteout(adr, off);
|
||||
outsb(adr + 4, data, size);
|
||||
}
|
||||
|
||||
/* Interface functions */
|
||||
|
||||
static u_char
|
||||
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
{
|
||||
u_short off2 = offset;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
return (readreg(cs->hw.gazel.isac, off2));
|
||||
case R753:
|
||||
case R742:
|
||||
return (readreg_ipac(cs->hw.gazel.ipac, 0x80 + off2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
{
|
||||
u_short off2 = offset;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
writereg(cs->hw.gazel.isac, off2, value);
|
||||
break;
|
||||
case R753:
|
||||
case R742:
|
||||
writereg_ipac(cs->hw.gazel.ipac, 0x80 + off2, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
case R685:
|
||||
read_fifo(cs->hw.gazel.isacfifo, data, size);
|
||||
break;
|
||||
case R753:
|
||||
case R742:
|
||||
read_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
case R685:
|
||||
write_fifo(cs->hw.gazel.isacfifo, data, size);
|
||||
break;
|
||||
case R753:
|
||||
case R742:
|
||||
write_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char *data, int size)
|
||||
{
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
case R685:
|
||||
read_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
|
||||
break;
|
||||
case R753:
|
||||
case R742:
|
||||
read_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char *data, int size)
|
||||
{
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
case R685:
|
||||
write_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
|
||||
break;
|
||||
case R753:
|
||||
case R742:
|
||||
write_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static u_char
|
||||
ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
|
||||
{
|
||||
u_short off2 = offset;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
return (readreg(cs->hw.gazel.hscx[hscx], off2));
|
||||
case R753:
|
||||
case R742:
|
||||
return (readreg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
|
||||
{
|
||||
u_short off2 = offset;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
|
||||
/* fall through */
|
||||
case R685:
|
||||
writereg(cs->hw.gazel.hscx[hscx], off2, value);
|
||||
break;
|
||||
case R753:
|
||||
case R742:
|
||||
writereg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 irqreturn_t
|
||||
gazel_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
#define MAXCOUNT 5
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char valisac, valhscx;
|
||||
int count = 0;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
do {
|
||||
valhscx = ReadHSCX(cs, 1, HSCX_ISTA);
|
||||
if (valhscx)
|
||||
hscx_int_main(cs, valhscx);
|
||||
valisac = ReadISAC(cs, ISAC_ISTA);
|
||||
if (valisac)
|
||||
isac_interrupt(cs, valisac);
|
||||
count++;
|
||||
} while ((valhscx || valisac) && (count < MAXCOUNT));
|
||||
|
||||
WriteHSCX(cs, 0, HSCX_MASK, 0xFF);
|
||||
WriteHSCX(cs, 1, HSCX_MASK, 0xFF);
|
||||
WriteISAC(cs, ISAC_MASK, 0xFF);
|
||||
WriteISAC(cs, ISAC_MASK, 0x0);
|
||||
WriteHSCX(cs, 0, HSCX_MASK, 0x0);
|
||||
WriteHSCX(cs, 1, HSCX_MASK, 0x0);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static irqreturn_t
|
||||
gazel_interrupt_ipac(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char ista, val;
|
||||
int count = 0;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
ista = ReadISAC(cs, IPAC_ISTA - 0x80);
|
||||
do {
|
||||
if (ista & 0x0f) {
|
||||
val = ReadHSCX(cs, 1, HSCX_ISTA);
|
||||
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 & ReadISAC(cs, ISAC_ISTA);
|
||||
if (val) {
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
}
|
||||
if (ista & 0x10) {
|
||||
val = 0x01;
|
||||
isac_interrupt(cs, val);
|
||||
}
|
||||
ista = ReadISAC(cs, IPAC_ISTA - 0x80);
|
||||
count++;
|
||||
}
|
||||
while ((ista & 0x3f) && (count < MAXCOUNT));
|
||||
|
||||
WriteISAC(cs, IPAC_MASK - 0x80, 0xFF);
|
||||
WriteISAC(cs, IPAC_MASK - 0x80, 0xC0);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
release_io_gazel(struct IsdnCardState *cs)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
for (i = 0x0000; i < 0xC000; i += 0x1000)
|
||||
release_region(i + cs->hw.gazel.hscx[0], 16);
|
||||
release_region(0xC000 + cs->hw.gazel.hscx[0], 1);
|
||||
break;
|
||||
|
||||
case R685:
|
||||
release_region(cs->hw.gazel.hscx[0], 0x100);
|
||||
release_region(cs->hw.gazel.cfg_reg, 0x80);
|
||||
break;
|
||||
|
||||
case R753:
|
||||
release_region(cs->hw.gazel.ipac, 0x8);
|
||||
release_region(cs->hw.gazel.cfg_reg, 0x80);
|
||||
break;
|
||||
|
||||
case R742:
|
||||
release_region(cs->hw.gazel.ipac, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
reset_gazel(struct IsdnCardState *cs)
|
||||
{
|
||||
unsigned long plxcntrl, addr = cs->hw.gazel.cfg_reg;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
writereg(addr, 0, 0);
|
||||
HZDELAY(10);
|
||||
writereg(addr, 0, 1);
|
||||
HZDELAY(2);
|
||||
break;
|
||||
case R685:
|
||||
plxcntrl = inl(addr + PLX_CNTRL);
|
||||
plxcntrl |= (RESET_9050 + RESET_GAZEL);
|
||||
outl(plxcntrl, addr + PLX_CNTRL);
|
||||
plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
|
||||
HZDELAY(4);
|
||||
outl(plxcntrl, addr + PLX_CNTRL);
|
||||
HZDELAY(10);
|
||||
outb(INT_ISAC_EN + INT_HSCX_EN + INT_PCI_EN, addr + PLX_INCSR);
|
||||
break;
|
||||
case R753:
|
||||
plxcntrl = inl(addr + PLX_CNTRL);
|
||||
plxcntrl |= (RESET_9050 + RESET_GAZEL);
|
||||
outl(plxcntrl, addr + PLX_CNTRL);
|
||||
plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
|
||||
WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
|
||||
HZDELAY(4);
|
||||
outl(plxcntrl, addr + PLX_CNTRL);
|
||||
HZDELAY(10);
|
||||
WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
|
||||
WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
|
||||
WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
|
||||
WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
|
||||
WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
|
||||
outb(INT_IPAC_EN + INT_PCI_EN, addr + PLX_INCSR);
|
||||
WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
|
||||
break;
|
||||
case R742:
|
||||
WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
|
||||
HZDELAY(4);
|
||||
WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
|
||||
WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
|
||||
WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
|
||||
WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
|
||||
WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
|
||||
WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
Gazel_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_gazel(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_RELEASE:
|
||||
release_io_gazel(cs);
|
||||
return (0);
|
||||
case CARD_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
inithscxisac(cs, 1);
|
||||
if ((cs->subtyp == R647) || (cs->subtyp == R685)) {
|
||||
int i;
|
||||
for (i = 0; i < (2 + MAX_WAITING_CALLS); i++) {
|
||||
cs->bcs[i].hw.hscx.tsaxr0 = 0x1f;
|
||||
cs->bcs[i].hw.hscx.tsaxr1 = 0x23;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs)
|
||||
{
|
||||
unsigned int i, j, base = 0, adr = 0, len = 0;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
base = cs->hw.gazel.hscx[0];
|
||||
if (!request_region(adr = (0xC000 + base), len = 1, "gazel"))
|
||||
goto error;
|
||||
for (i = 0x0000; i < 0xC000; i += 0x1000) {
|
||||
if (!request_region(adr = (i + base), len = 16, "gazel"))
|
||||
goto error;
|
||||
}
|
||||
if (i != 0xC000) {
|
||||
for (j = 0; j < i; j += 0x1000)
|
||||
release_region(j + base, 16);
|
||||
release_region(0xC000 + base, 1);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case R685:
|
||||
if (!request_region(adr = cs->hw.gazel.hscx[0], len = 0x100, "gazel"))
|
||||
goto error;
|
||||
if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
|
||||
release_region(cs->hw.gazel.hscx[0], 0x100);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case R753:
|
||||
if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
|
||||
goto error;
|
||||
if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
|
||||
release_region(cs->hw.gazel.ipac, 8);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case R742:
|
||||
if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
printk(KERN_WARNING "Gazel: io ports 0x%x-0x%x already in use\n",
|
||||
adr, adr + len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs)
|
||||
{
|
||||
printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n");
|
||||
// we got an irq parameter, assume it is an ISA card
|
||||
// R742 decodes address even in not started...
|
||||
// R647 returns FF if not present or not started
|
||||
// eventually needs improvment
|
||||
if (readreg_ipac(card->para[1], IPAC_ID) == 1)
|
||||
cs->subtyp = R742;
|
||||
else
|
||||
cs->subtyp = R647;
|
||||
|
||||
setup_isac(cs);
|
||||
cs->hw.gazel.cfg_reg = card->para[1] + 0xC000;
|
||||
cs->hw.gazel.ipac = card->para[1];
|
||||
cs->hw.gazel.isac = card->para[1] + 0x8000;
|
||||
cs->hw.gazel.hscx[0] = card->para[1];
|
||||
cs->hw.gazel.hscx[1] = card->para[1] + 0x4000;
|
||||
cs->irq = card->para[0];
|
||||
cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
|
||||
cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
|
||||
cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
printk(KERN_INFO "Gazel: Card ISA R647/R648 found\n");
|
||||
cs->dc.isac.adf2 = 0x87;
|
||||
printk(KERN_INFO
|
||||
"Gazel: config irq:%d isac:0x%X cfg:0x%X\n",
|
||||
cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
|
||||
printk(KERN_INFO
|
||||
"Gazel: hscx A:0x%X hscx B:0x%X\n",
|
||||
cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
|
||||
|
||||
break;
|
||||
case R742:
|
||||
printk(KERN_INFO "Gazel: Card ISA R742 found\n");
|
||||
test_and_set_bit(HW_IPAC, &cs->HW_Flags);
|
||||
printk(KERN_INFO
|
||||
"Gazel: config irq:%d ipac:0x%X\n",
|
||||
cs->irq, cs->hw.gazel.ipac);
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static struct pci_dev *dev_tel = NULL;
|
||||
|
||||
static int setup_gazelpci(struct IsdnCardState *cs)
|
||||
{
|
||||
u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0;
|
||||
u_char pci_irq = 0, found;
|
||||
u_int nbseek, seekcard;
|
||||
|
||||
printk(KERN_WARNING "Gazel: PCI card automatic recognition\n");
|
||||
|
||||
found = 0;
|
||||
seekcard = PCI_DEVICE_ID_PLX_R685;
|
||||
for (nbseek = 0; nbseek < 4; nbseek++) {
|
||||
if ((dev_tel = hisax_find_pci_device(PCI_VENDOR_ID_PLX,
|
||||
seekcard, dev_tel))) {
|
||||
if (pci_enable_device(dev_tel))
|
||||
return 1;
|
||||
pci_irq = dev_tel->irq;
|
||||
pci_ioaddr0 = pci_resource_start(dev_tel, 1);
|
||||
pci_ioaddr1 = pci_resource_start(dev_tel, 2);
|
||||
found = 1;
|
||||
}
|
||||
if (found)
|
||||
break;
|
||||
else {
|
||||
switch (seekcard) {
|
||||
case PCI_DEVICE_ID_PLX_R685:
|
||||
seekcard = PCI_DEVICE_ID_PLX_R753;
|
||||
break;
|
||||
case PCI_DEVICE_ID_PLX_R753:
|
||||
seekcard = PCI_DEVICE_ID_PLX_DJINN_ITOO;
|
||||
break;
|
||||
case PCI_DEVICE_ID_PLX_DJINN_ITOO:
|
||||
seekcard = PCI_DEVICE_ID_PLX_OLITEC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
printk(KERN_WARNING "Gazel: No PCI card found\n");
|
||||
return (1);
|
||||
}
|
||||
if (!pci_irq) {
|
||||
printk(KERN_WARNING "Gazel: No IRQ for PCI card found\n");
|
||||
return 1;
|
||||
}
|
||||
cs->hw.gazel.pciaddr[0] = pci_ioaddr0;
|
||||
cs->hw.gazel.pciaddr[1] = pci_ioaddr1;
|
||||
setup_isac(cs);
|
||||
pci_ioaddr1 &= 0xfffe;
|
||||
cs->hw.gazel.cfg_reg = pci_ioaddr0 & 0xfffe;
|
||||
cs->hw.gazel.ipac = pci_ioaddr1;
|
||||
cs->hw.gazel.isac = pci_ioaddr1 + 0x80;
|
||||
cs->hw.gazel.hscx[0] = pci_ioaddr1;
|
||||
cs->hw.gazel.hscx[1] = pci_ioaddr1 + 0x40;
|
||||
cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
|
||||
cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
|
||||
cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
|
||||
cs->irq = pci_irq;
|
||||
cs->irq_flags |= IRQF_SHARED;
|
||||
|
||||
switch (seekcard) {
|
||||
case PCI_DEVICE_ID_PLX_R685:
|
||||
printk(KERN_INFO "Gazel: Card PCI R685 found\n");
|
||||
cs->subtyp = R685;
|
||||
cs->dc.isac.adf2 = 0x87;
|
||||
printk(KERN_INFO
|
||||
"Gazel: config irq:%d isac:0x%X cfg:0x%X\n",
|
||||
cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
|
||||
printk(KERN_INFO
|
||||
"Gazel: hscx A:0x%X hscx B:0x%X\n",
|
||||
cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
|
||||
break;
|
||||
case PCI_DEVICE_ID_PLX_R753:
|
||||
case PCI_DEVICE_ID_PLX_DJINN_ITOO:
|
||||
case PCI_DEVICE_ID_PLX_OLITEC:
|
||||
printk(KERN_INFO "Gazel: Card PCI R753 found\n");
|
||||
cs->subtyp = R753;
|
||||
test_and_set_bit(HW_IPAC, &cs->HW_Flags);
|
||||
printk(KERN_INFO
|
||||
"Gazel: config irq:%d ipac:0x%X cfg:0x%X\n",
|
||||
cs->irq, cs->hw.gazel.ipac, cs->hw.gazel.cfg_reg);
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif /* CONFIG_PCI */
|
||||
|
||||
int setup_gazel(struct IsdnCard *card)
|
||||
{
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
u_char val;
|
||||
|
||||
strcpy(tmp, gazel_revision);
|
||||
printk(KERN_INFO "Gazel: Driver Revision %s\n", HiSax_getrev(tmp));
|
||||
|
||||
if (cs->typ != ISDN_CTYPE_GAZEL)
|
||||
return (0);
|
||||
|
||||
if (card->para[0]) {
|
||||
if (setup_gazelisa(card, cs))
|
||||
return (0);
|
||||
} else {
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
if (setup_gazelpci(cs))
|
||||
return (0);
|
||||
#else
|
||||
printk(KERN_WARNING "Gazel: Card PCI requested and NO_PCI_BIOS, unable to config\n");
|
||||
return (0);
|
||||
#endif /* CONFIG_PCI */
|
||||
}
|
||||
|
||||
if (reserve_regions(card, cs)) {
|
||||
return (0);
|
||||
}
|
||||
if (reset_gazel(cs)) {
|
||||
printk(KERN_WARNING "Gazel: wrong IRQ\n");
|
||||
release_io_gazel(cs);
|
||||
return (0);
|
||||
}
|
||||
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 = &Gazel_card_msg;
|
||||
|
||||
switch (cs->subtyp) {
|
||||
case R647:
|
||||
case R685:
|
||||
cs->irq_func = &gazel_interrupt;
|
||||
ISACVersion(cs, "Gazel:");
|
||||
if (HscxVersion(cs, "Gazel:")) {
|
||||
printk(KERN_WARNING
|
||||
"Gazel: wrong HSCX versions check IO address\n");
|
||||
release_io_gazel(cs);
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
case R742:
|
||||
case R753:
|
||||
cs->irq_func = &gazel_interrupt_ipac;
|
||||
val = ReadISAC(cs, IPAC_ID - 0x80);
|
||||
printk(KERN_INFO "Gazel: IPAC version %x\n", val);
|
||||
break;
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,89 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/***************************************************************/
|
||||
/* $Id: hfc4s8s_l1.h,v 1.1 2005/02/02 17:28:55 martinb1 Exp $ */
|
||||
/* */
|
||||
/* This file is a minimal required extraction of hfc48scu.h */
|
||||
/* (Genero 3.2, HFC XML 1.7a for HFC-E1, HFC-4S and HFC-8S) */
|
||||
/* */
|
||||
/* To get this complete register description contact */
|
||||
/* Cologne Chip AG : */
|
||||
/* Internet: http://www.colognechip.com/ */
|
||||
/* E-Mail: info@colognechip.com */
|
||||
/***************************************************************/
|
||||
|
||||
#ifndef _HFC4S8S_L1_H_
|
||||
#define _HFC4S8S_L1_H_
|
||||
|
||||
|
||||
/*
|
||||
* include Genero generated HFC-4S/8S header file hfc48scu.h
|
||||
* for complete register description. This will define _HFC48SCU_H_
|
||||
* to prevent redefinitions
|
||||
*/
|
||||
|
||||
// #include "hfc48scu.h"
|
||||
|
||||
#ifndef _HFC48SCU_H_
|
||||
#define _HFC48SCU_H_
|
||||
|
||||
#ifndef PCI_VENDOR_ID_CCD
|
||||
#define PCI_VENDOR_ID_CCD 0x1397
|
||||
#endif
|
||||
|
||||
#define CHIP_ID_4S 0x0C
|
||||
#define CHIP_ID_8S 0x08
|
||||
#define PCI_DEVICE_ID_4S 0x08B4
|
||||
#define PCI_DEVICE_ID_8S 0x16B8
|
||||
|
||||
#define R_IRQ_MISC 0x11
|
||||
#define M_TI_IRQ 0x02
|
||||
#define A_ST_RD_STA 0x30
|
||||
#define A_ST_WR_STA 0x30
|
||||
#define M_SET_G2_G3 0x80
|
||||
#define A_ST_CTRL0 0x31
|
||||
#define A_ST_CTRL2 0x33
|
||||
#define A_ST_CLK_DLY 0x37
|
||||
#define A_Z1 0x04
|
||||
#define A_Z2 0x06
|
||||
#define R_CIRM 0x00
|
||||
#define M_SRES 0x08
|
||||
#define R_CTRL 0x01
|
||||
#define R_BRG_PCM_CFG 0x02
|
||||
#define M_PCM_CLK 0x20
|
||||
#define R_RAM_MISC 0x0C
|
||||
#define M_FZ_MD 0x80
|
||||
#define R_FIFO_MD 0x0D
|
||||
#define A_INC_RES_FIFO 0x0E
|
||||
#define R_FIFO 0x0F
|
||||
#define A_F1 0x0C
|
||||
#define A_F2 0x0D
|
||||
#define R_IRQ_OVIEW 0x10
|
||||
#define R_CHIP_ID 0x16
|
||||
#define R_STATUS 0x1C
|
||||
#define M_BUSY 0x01
|
||||
#define M_MISC_IRQSTA 0x40
|
||||
#define M_FR_IRQSTA 0x80
|
||||
#define R_CHIP_RV 0x1F
|
||||
#define R_IRQ_CTRL 0x13
|
||||
#define M_FIFO_IRQ 0x01
|
||||
#define M_GLOB_IRQ_EN 0x08
|
||||
#define R_PCM_MD0 0x14
|
||||
#define M_PCM_MD 0x01
|
||||
#define A_FIFO_DATA0 0x80
|
||||
#define R_TI_WD 0x1A
|
||||
#define R_PWM1 0x39
|
||||
#define R_PWM_MD 0x46
|
||||
#define R_IRQ_FIFO_BL0 0xC8
|
||||
#define A_CON_HDLC 0xFA
|
||||
#define A_SUBCH_CFG 0xFB
|
||||
#define A_IRQ_MSK 0xFF
|
||||
#define R_SCI_MSK 0x12
|
||||
#define R_ST_SEL 0x16
|
||||
#define R_ST_SYNC 0x17
|
||||
#define M_AUTO_SYNC 0x08
|
||||
#define R_SCI 0x12
|
||||
#define R_IRQMSK_MISC 0x11
|
||||
#define M_TI_IRQMSK 0x02
|
||||
|
||||
#endif /* _HFC4S8S_L1_H_ */
|
||||
#endif /* _HFC48SCU_H_ */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,128 +0,0 @@
|
|||
/* $Id: hfc_2bds0.h,v 1.6.2.2 2004/01/12 22:52:26 keil Exp $
|
||||
*
|
||||
* specific defines for CCD's HFC 2BDS0
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#define HFCD_CIRM 0x18
|
||||
#define HFCD_CTMT 0x19
|
||||
#define HFCD_INT_M1 0x1A
|
||||
#define HFCD_INT_M2 0x1B
|
||||
#define HFCD_INT_S1 0x1E
|
||||
#define HFCD_STAT 0x1C
|
||||
#define HFCD_STAT_DISB 0x1D
|
||||
#define HFCD_STATES 0x30
|
||||
#define HFCD_SCTRL 0x31
|
||||
#define HFCD_TEST 0x32
|
||||
#define HFCD_SQ 0x34
|
||||
#define HFCD_CLKDEL 0x37
|
||||
#define HFCD_MST_MODE 0x2E
|
||||
#define HFCD_CONN 0x2F
|
||||
|
||||
#define HFCD_FIFO 0x80
|
||||
#define HFCD_Z1 0x10
|
||||
#define HFCD_Z2 0x18
|
||||
#define HFCD_Z_LOW 0x00
|
||||
#define HFCD_Z_HIGH 0x04
|
||||
#define HFCD_F1_INC 0x12
|
||||
#define HFCD_FIFO_IN 0x16
|
||||
#define HFCD_F1 0x1a
|
||||
#define HFCD_F2 0x1e
|
||||
#define HFCD_F2_INC 0x22
|
||||
#define HFCD_FIFO_OUT 0x26
|
||||
#define HFCD_REC 0x01
|
||||
#define HFCD_SEND 0x00
|
||||
|
||||
#define HFCB_FIFO 0x80
|
||||
#define HFCB_Z1 0x00
|
||||
#define HFCB_Z2 0x08
|
||||
#define HFCB_Z_LOW 0x00
|
||||
#define HFCB_Z_HIGH 0x04
|
||||
#define HFCB_F1_INC 0x28
|
||||
#define HFCB_FIFO_IN 0x2c
|
||||
#define HFCB_F1 0x30
|
||||
#define HFCB_F2 0x34
|
||||
#define HFCB_F2_INC 0x38
|
||||
#define HFCB_FIFO_OUT 0x3c
|
||||
#define HFCB_REC 0x01
|
||||
#define HFCB_SEND 0x00
|
||||
#define HFCB_B1 0x00
|
||||
#define HFCB_B2 0x02
|
||||
#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1)
|
||||
|
||||
#define HFCD_STATUS 0
|
||||
#define HFCD_DATA 1
|
||||
#define HFCD_DATA_NODEB 2
|
||||
|
||||
/* Status (READ) */
|
||||
#define HFCD_BUSY 0x01
|
||||
#define HFCD_BUSY_NBUSY 0x04
|
||||
#define HFCD_TIMER_ELAP 0x10
|
||||
#define HFCD_STATINT 0x20
|
||||
#define HFCD_FRAMEINT 0x40
|
||||
#define HFCD_ANYINT 0x80
|
||||
|
||||
/* CTMT (Write) */
|
||||
#define HFCD_CLTIMER 0x80
|
||||
#define HFCD_TIM25 0x00
|
||||
#define HFCD_TIM50 0x08
|
||||
#define HFCD_TIM400 0x10
|
||||
#define HFCD_TIM800 0x18
|
||||
#define HFCD_AUTO_TIMER 0x20
|
||||
#define HFCD_TRANSB2 0x02
|
||||
#define HFCD_TRANSB1 0x01
|
||||
|
||||
/* CIRM (Write) */
|
||||
#define HFCD_RESET 0x08
|
||||
#define HFCD_MEM8K 0x10
|
||||
#define HFCD_INTA 0x01
|
||||
#define HFCD_INTB 0x02
|
||||
#define HFCD_INTC 0x03
|
||||
#define HFCD_INTD 0x04
|
||||
#define HFCD_INTE 0x05
|
||||
#define HFCD_INTF 0x06
|
||||
|
||||
/* INT_M1;INT_S1 */
|
||||
#define HFCD_INTS_B1TRANS 0x01
|
||||
#define HFCD_INTS_B2TRANS 0x02
|
||||
#define HFCD_INTS_DTRANS 0x04
|
||||
#define HFCD_INTS_B1REC 0x08
|
||||
#define HFCD_INTS_B2REC 0x10
|
||||
#define HFCD_INTS_DREC 0x20
|
||||
#define HFCD_INTS_L1STATE 0x40
|
||||
#define HFCD_INTS_TIMER 0x80
|
||||
|
||||
/* INT_M2 */
|
||||
#define HFCD_IRQ_ENABLE 0x08
|
||||
|
||||
/* STATES */
|
||||
#define HFCD_LOAD_STATE 0x10
|
||||
#define HFCD_ACTIVATE 0x20
|
||||
#define HFCD_DO_ACTION 0x40
|
||||
|
||||
/* HFCD_MST_MODE */
|
||||
#define HFCD_MASTER 0x01
|
||||
|
||||
/* HFCD_SCTRL */
|
||||
#define SCTRL_B1_ENA 0x01
|
||||
#define SCTRL_B2_ENA 0x02
|
||||
#define SCTRL_LOW_PRIO 0x08
|
||||
#define SCTRL_SQ_ENA 0x10
|
||||
#define SCTRL_TEST 0x20
|
||||
#define SCTRL_NONE_CAP 0x40
|
||||
#define SCTRL_PWR_DOWN 0x80
|
||||
|
||||
/* HFCD_TEST */
|
||||
#define HFCD_AUTO_AWAKE 0x01
|
||||
|
||||
extern void main_irq_2bds0(struct BCState *bcs);
|
||||
extern void init2bds0(struct IsdnCardState *cs);
|
||||
extern void release2bds0(struct IsdnCardState *cs);
|
||||
extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val);
|
||||
extern void set_cs_func(struct IsdnCardState *cs);
|
|
@ -1,591 +0,0 @@
|
|||
/* $Id: hfc_2bs0.c,v 1.20.2.6 2004/02/11 13:21:33 keil Exp $
|
||||
*
|
||||
* specific routines for CCD's HFC 2BS0
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "hfc_2bs0.h"
|
||||
#include "isac.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static inline int
|
||||
WaitForBusy(struct IsdnCardState *cs)
|
||||
{
|
||||
int to = 130;
|
||||
u_char val;
|
||||
|
||||
while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
|
||||
val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 |
|
||||
(cs->hw.hfc.cip & 3));
|
||||
udelay(1);
|
||||
to--;
|
||||
}
|
||||
if (!to) {
|
||||
printk(KERN_WARNING "HiSax: %s timeout\n", __func__);
|
||||
return (0);
|
||||
} else
|
||||
return (to);
|
||||
}
|
||||
|
||||
static inline int
|
||||
WaitNoBusy(struct IsdnCardState *cs)
|
||||
{
|
||||
int to = 125;
|
||||
|
||||
while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
|
||||
udelay(1);
|
||||
to--;
|
||||
}
|
||||
if (!to) {
|
||||
printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
|
||||
return (0);
|
||||
} else
|
||||
return (to);
|
||||
}
|
||||
|
||||
static int
|
||||
GetFreeFifoBytes(struct BCState *bcs)
|
||||
{
|
||||
int s;
|
||||
|
||||
if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
|
||||
return (bcs->cs->hw.hfc.fifosize);
|
||||
s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
|
||||
if (s <= 0)
|
||||
s += bcs->cs->hw.hfc.fifosize;
|
||||
s = bcs->cs->hw.hfc.fifosize - s;
|
||||
return (s);
|
||||
}
|
||||
|
||||
static int
|
||||
ReadZReg(struct BCState *bcs, u_char reg)
|
||||
{
|
||||
int val;
|
||||
|
||||
WaitNoBusy(bcs->cs);
|
||||
val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH);
|
||||
WaitNoBusy(bcs->cs);
|
||||
val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW);
|
||||
return (val);
|
||||
}
|
||||
|
||||
static void
|
||||
hfc_clear_fifo(struct BCState *bcs)
|
||||
{
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int idx, cnt;
|
||||
int rcnt, z1, z2;
|
||||
u_char cip, f1, f2;
|
||||
|
||||
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
|
||||
debugl1(cs, "hfc_clear_fifo");
|
||||
cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
|
||||
cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
|
||||
WaitForBusy(cs);
|
||||
}
|
||||
WaitNoBusy(cs);
|
||||
f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
|
||||
cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
WaitNoBusy(cs);
|
||||
f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
|
||||
z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
|
||||
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)
|
||||
debugl1(cs, "hfc clear %d f1(%d) f2(%d)",
|
||||
bcs->channel, f1, f2);
|
||||
rcnt = z1 - z2;
|
||||
if (rcnt < 0)
|
||||
rcnt += cs->hw.hfc.fifosize;
|
||||
if (rcnt)
|
||||
rcnt++;
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
|
||||
bcs->channel, z1, z2, rcnt);
|
||||
cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
idx = 0;
|
||||
while ((idx < rcnt) && WaitNoBusy(cs)) {
|
||||
cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
|
||||
idx++;
|
||||
}
|
||||
if (f1 != f2) {
|
||||
WaitNoBusy(cs);
|
||||
cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
|
||||
HFC_CHANNEL(bcs->channel));
|
||||
WaitForBusy(cs);
|
||||
}
|
||||
cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
WaitNoBusy(cs);
|
||||
f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
|
||||
cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
WaitNoBusy(cs);
|
||||
f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
|
||||
z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
|
||||
z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static struct sk_buff
|
||||
*
|
||||
hfc_empty_fifo(struct BCState *bcs, int count)
|
||||
{
|
||||
u_char *ptr;
|
||||
struct sk_buff *skb;
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int idx;
|
||||
int chksum;
|
||||
u_char stat, cip;
|
||||
|
||||
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
|
||||
debugl1(cs, "hfc_empty_fifo");
|
||||
idx = 0;
|
||||
if (count > HSCX_BUFMAX + 3) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "hfc_empty_fifo: incoming packet too large");
|
||||
cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
while ((idx++ < count) && WaitNoBusy(cs))
|
||||
cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
|
||||
WaitNoBusy(cs);
|
||||
stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
|
||||
HFC_CHANNEL(bcs->channel));
|
||||
WaitForBusy(cs);
|
||||
return (NULL);
|
||||
}
|
||||
if ((count < 4) && (bcs->mode != L1_MODE_TRANS)) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "hfc_empty_fifo: incoming packet too small");
|
||||
cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
while ((idx++ < count) && WaitNoBusy(cs))
|
||||
cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
|
||||
WaitNoBusy(cs);
|
||||
stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
|
||||
HFC_CHANNEL(bcs->channel));
|
||||
WaitForBusy(cs);
|
||||
#ifdef ERROR_STATISTIC
|
||||
bcs->err_inv++;
|
||||
#endif
|
||||
return (NULL);
|
||||
}
|
||||
if (bcs->mode == L1_MODE_TRANS)
|
||||
count -= 1;
|
||||
else
|
||||
count -= 3;
|
||||
if (!(skb = dev_alloc_skb(count)))
|
||||
printk(KERN_WARNING "HFC: receive out of memory\n");
|
||||
else {
|
||||
ptr = skb_put(skb, count);
|
||||
idx = 0;
|
||||
cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
while ((idx < count) && WaitNoBusy(cs)) {
|
||||
*ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
|
||||
idx++;
|
||||
}
|
||||
if (idx != count) {
|
||||
debugl1(cs, "RFIFO BUSY error");
|
||||
printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
|
||||
dev_kfree_skb_any(skb);
|
||||
if (bcs->mode != L1_MODE_TRANS) {
|
||||
WaitNoBusy(cs);
|
||||
stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
|
||||
HFC_CHANNEL(bcs->channel));
|
||||
WaitForBusy(cs);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
if (bcs->mode != L1_MODE_TRANS) {
|
||||
WaitNoBusy(cs);
|
||||
chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
|
||||
WaitNoBusy(cs);
|
||||
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)
|
||||
debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
|
||||
bcs->channel, chksum, stat);
|
||||
if (stat) {
|
||||
debugl1(cs, "FIFO CRC error");
|
||||
dev_kfree_skb_any(skb);
|
||||
skb = NULL;
|
||||
#ifdef ERROR_STATISTIC
|
||||
bcs->err_crc++;
|
||||
#endif
|
||||
}
|
||||
WaitNoBusy(cs);
|
||||
stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
|
||||
HFC_CHANNEL(bcs->channel));
|
||||
WaitForBusy(cs);
|
||||
}
|
||||
}
|
||||
return (skb);
|
||||
}
|
||||
|
||||
static void
|
||||
hfc_fill_fifo(struct BCState *bcs)
|
||||
{
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int idx, fcnt;
|
||||
int count;
|
||||
int z1, z2;
|
||||
u_char cip;
|
||||
|
||||
if (!bcs->tx_skb)
|
||||
return;
|
||||
if (bcs->tx_skb->len <= 0)
|
||||
return;
|
||||
|
||||
cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel);
|
||||
if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
|
||||
cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
|
||||
WaitForBusy(cs);
|
||||
}
|
||||
WaitNoBusy(cs);
|
||||
if (bcs->mode != L1_MODE_TRANS) {
|
||||
bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
|
||||
cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
|
||||
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)
|
||||
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]);
|
||||
fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
|
||||
if (fcnt < 0)
|
||||
fcnt += 32;
|
||||
if (fcnt > 30) {
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "hfc_fill_fifo more as 30 frames");
|
||||
return;
|
||||
}
|
||||
count = GetFreeFifoBytes(bcs);
|
||||
}
|
||||
else {
|
||||
WaitForBusy(cs);
|
||||
z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
|
||||
z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
|
||||
count = z1 - z2;
|
||||
if (count < 0)
|
||||
count += cs->hw.hfc.fifosize;
|
||||
} /* L1_MODE_TRANS */
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "hfc_fill_fifo %d count(%u/%d)",
|
||||
bcs->channel, bcs->tx_skb->len,
|
||||
count);
|
||||
if (count < bcs->tx_skb->len) {
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "hfc_fill_fifo no fifo mem");
|
||||
return;
|
||||
}
|
||||
cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
|
||||
idx = 0;
|
||||
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->tx_skb->len;
|
||||
bcs->tx_cnt -= count;
|
||||
if (PACKET_NOACK == bcs->tx_skb->pkt_type)
|
||||
count = -1;
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
if (bcs->mode != L1_MODE_TRANS) {
|
||||
WaitForBusy(cs);
|
||||
WaitNoBusy(cs);
|
||||
cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
|
||||
}
|
||||
if (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) &&
|
||||
(count >= 0)) {
|
||||
u_long flags;
|
||||
spin_lock_irqsave(&bcs->aclock, flags);
|
||||
bcs->ackcnt += count;
|
||||
spin_unlock_irqrestore(&bcs->aclock, flags);
|
||||
schedule_event(bcs, B_ACKPENDING);
|
||||
}
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
main_irq_hfc(struct BCState *bcs)
|
||||
{
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int z1, z2, rcnt;
|
||||
u_char f1, f2, cip;
|
||||
int receive, transmit, count = 5;
|
||||
struct sk_buff *skb;
|
||||
|
||||
Begin:
|
||||
count--;
|
||||
cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
|
||||
cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
|
||||
WaitForBusy(cs);
|
||||
}
|
||||
WaitNoBusy(cs);
|
||||
receive = 0;
|
||||
if (bcs->mode == L1_MODE_HDLC) {
|
||||
f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
|
||||
cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
|
||||
WaitNoBusy(cs);
|
||||
f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
|
||||
if (f1 != f2) {
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
|
||||
bcs->channel, f1, f2);
|
||||
receive = 1;
|
||||
}
|
||||
}
|
||||
if (receive || (bcs->mode == L1_MODE_TRANS)) {
|
||||
WaitForBusy(cs);
|
||||
z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
|
||||
z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
|
||||
rcnt = z1 - z2;
|
||||
if (rcnt < 0)
|
||||
rcnt += cs->hw.hfc.fifosize;
|
||||
if ((bcs->mode == L1_MODE_HDLC) || (rcnt)) {
|
||||
rcnt++;
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
|
||||
bcs->channel, z1, z2, rcnt);
|
||||
/* sti(); */
|
||||
if ((skb = hfc_empty_fifo(bcs, rcnt))) {
|
||||
skb_queue_tail(&bcs->rqueue, skb);
|
||||
schedule_event(bcs, B_RCVBUFREADY);
|
||||
}
|
||||
}
|
||||
receive = 1;
|
||||
}
|
||||
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->tx_skb = skb_dequeue(&bcs->squeue))) {
|
||||
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 {
|
||||
transmit = 0;
|
||||
schedule_event(bcs, B_XMTBUFREADY);
|
||||
}
|
||||
}
|
||||
if ((receive || transmit) && count)
|
||||
goto Begin;
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
mode_hfc(struct BCState *bcs, int mode, int bc)
|
||||
{
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d",
|
||||
mode, bc, bcs->channel);
|
||||
bcs->mode = mode;
|
||||
bcs->channel = bc;
|
||||
|
||||
switch (mode) {
|
||||
case (L1_MODE_NULL):
|
||||
if (bc) {
|
||||
cs->hw.hfc.ctmt &= ~1;
|
||||
cs->hw.hfc.isac_spcr &= ~0x03;
|
||||
}
|
||||
else {
|
||||
cs->hw.hfc.ctmt &= ~2;
|
||||
cs->hw.hfc.isac_spcr &= ~0x0c;
|
||||
}
|
||||
break;
|
||||
case (L1_MODE_TRANS):
|
||||
cs->hw.hfc.ctmt &= ~(1 << bc); /* set HDLC mode */
|
||||
cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
|
||||
hfc_clear_fifo(bcs); /* complete fifo clear */
|
||||
if (bc) {
|
||||
cs->hw.hfc.ctmt |= 1;
|
||||
cs->hw.hfc.isac_spcr &= ~0x03;
|
||||
cs->hw.hfc.isac_spcr |= 0x02;
|
||||
} else {
|
||||
cs->hw.hfc.ctmt |= 2;
|
||||
cs->hw.hfc.isac_spcr &= ~0x0c;
|
||||
cs->hw.hfc.isac_spcr |= 0x08;
|
||||
}
|
||||
break;
|
||||
case (L1_MODE_HDLC):
|
||||
if (bc) {
|
||||
cs->hw.hfc.ctmt &= ~1;
|
||||
cs->hw.hfc.isac_spcr &= ~0x03;
|
||||
cs->hw.hfc.isac_spcr |= 0x02;
|
||||
} else {
|
||||
cs->hw.hfc.ctmt &= ~2;
|
||||
cs->hw.hfc.isac_spcr &= ~0x0c;
|
||||
cs->hw.hfc.isac_spcr |= 0x08;
|
||||
}
|
||||
break;
|
||||
}
|
||||
cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
|
||||
cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr);
|
||||
if (mode == L1_MODE_HDLC)
|
||||
hfc_clear_fifo(bcs);
|
||||
}
|
||||
|
||||
static void
|
||||
hfc_l2l1(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct BCState *bcs = st->l1.bcs;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
|
||||
switch (pr) {
|
||||
case (PH_DATA | REQUEST):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
skb_queue_tail(&bcs->squeue, skb);
|
||||
} else {
|
||||
bcs->tx_skb = skb;
|
||||
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->cs->BC_Send_Data(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | INDICATION):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
|
||||
} else {
|
||||
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->tx_skb = skb;
|
||||
bcs->cs->BC_Send_Data(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | REQUEST):
|
||||
if (!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):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
mode_hfc(bcs, st->l1.mode, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | REQUEST):
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | CONFIRM):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
mode_hfc(bcs, 0, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
close_hfcstate(struct BCState *bcs)
|
||||
{
|
||||
mode_hfc(bcs, 0, bcs->channel);
|
||||
if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
|
||||
skb_queue_purge(&bcs->rqueue);
|
||||
skb_queue_purge(&bcs->squeue);
|
||||
if (bcs->tx_skb) {
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
}
|
||||
}
|
||||
test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
|
||||
}
|
||||
|
||||
static int
|
||||
open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
|
||||
{
|
||||
if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
|
||||
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->tx_cnt = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
setstack_hfc(struct PStack *st, struct BCState *bcs)
|
||||
{
|
||||
bcs->channel = st->l1.bc;
|
||||
if (open_hfcstate(st->l1.hardware, bcs))
|
||||
return (-1);
|
||||
st->l1.bcs = bcs;
|
||||
st->l2.l2l1 = hfc_l2l1;
|
||||
setstack_manager(st);
|
||||
bcs->st = st;
|
||||
setstack_l1_B(st);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
init_send(struct BCState *bcs)
|
||||
{
|
||||
int i;
|
||||
|
||||
bcs->hw.hfc.send = kmalloc_array(32, sizeof(unsigned int), GFP_ATOMIC);
|
||||
if (!bcs->hw.hfc.send) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: No memory for hfc.send\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < 32; i++)
|
||||
bcs->hw.hfc.send[i] = 0x1fff;
|
||||
}
|
||||
|
||||
void
|
||||
inithfc(struct IsdnCardState *cs)
|
||||
{
|
||||
init_send(&cs->bcs[0]);
|
||||
init_send(&cs->bcs[1]);
|
||||
cs->BC_Send_Data = &hfc_fill_fifo;
|
||||
cs->bcs[0].BC_SetStack = setstack_hfc;
|
||||
cs->bcs[1].BC_SetStack = setstack_hfc;
|
||||
cs->bcs[0].BC_Close = close_hfcstate;
|
||||
cs->bcs[1].BC_Close = close_hfcstate;
|
||||
mode_hfc(cs->bcs, 0, 0);
|
||||
mode_hfc(cs->bcs + 1, 0, 0);
|
||||
}
|
||||
|
||||
void
|
||||
releasehfc(struct IsdnCardState *cs)
|
||||
{
|
||||
kfree(cs->bcs[0].hw.hfc.send);
|
||||
cs->bcs[0].hw.hfc.send = NULL;
|
||||
kfree(cs->bcs[1].hw.hfc.send);
|
||||
cs->bcs[1].hw.hfc.send = NULL;
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/* $Id: hfc_2bs0.h,v 1.5.2.2 2004/01/12 22:52:26 keil Exp $
|
||||
*
|
||||
* specific defines for CCD's HFC 2BS0
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#define HFC_CTMT 0xe0
|
||||
#define HFC_CIRM 0xc0
|
||||
#define HFC_CIP 0x80
|
||||
#define HFC_Z1 0x00
|
||||
#define HFC_Z2 0x08
|
||||
#define HFC_Z_LOW 0x00
|
||||
#define HFC_Z_HIGH 0x04
|
||||
#define HFC_F1_INC 0x28
|
||||
#define HFC_FIFO_IN 0x2c
|
||||
#define HFC_F1 0x30
|
||||
#define HFC_F2 0x34
|
||||
#define HFC_F2_INC 0x38
|
||||
#define HFC_FIFO_OUT 0x3c
|
||||
#define HFC_B1 0x00
|
||||
#define HFC_B2 0x02
|
||||
#define HFC_REC 0x01
|
||||
#define HFC_SEND 0x00
|
||||
#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1)
|
||||
|
||||
#define HFC_STATUS 0
|
||||
#define HFC_DATA 1
|
||||
#define HFC_DATA_NODEB 2
|
||||
|
||||
/* Status (READ) */
|
||||
#define HFC_BUSY 0x01
|
||||
#define HFC_TIMINT 0x02
|
||||
#define HFC_EXTINT 0x04
|
||||
|
||||
/* CTMT (Write) */
|
||||
#define HFC_CLTIMER 0x10
|
||||
#define HFC_TIM50MS 0x08
|
||||
#define HFC_TIMIRQE 0x04
|
||||
#define HFC_TRANSB2 0x02
|
||||
#define HFC_TRANSB1 0x01
|
||||
|
||||
/* CIRM (Write) */
|
||||
#define HFC_RESET 0x08
|
||||
#define HFC_MEM8K 0x10
|
||||
#define HFC_INTA 0x01
|
||||
#define HFC_INTB 0x02
|
||||
#define HFC_INTC 0x03
|
||||
#define HFC_INTD 0x04
|
||||
#define HFC_INTE 0x05
|
||||
#define HFC_INTF 0x06
|
||||
|
||||
extern void main_irq_hfc(struct BCState *bcs);
|
||||
extern void inithfc(struct IsdnCardState *cs);
|
||||
extern void releasehfc(struct IsdnCardState *cs);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,235 +0,0 @@
|
|||
/* $Id: hfc_pci.h,v 1.10.2.2 2004/01/12 22:52:26 keil Exp $
|
||||
*
|
||||
* specific defines for CCD's HFC 2BDS0 PCI chips
|
||||
*
|
||||
* Author Werner Cornelius
|
||||
* Copyright by Werner Cornelius <werner@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************************************/
|
||||
/* thresholds for transparent B-channel mode */
|
||||
/* change mask and threshold simultaneously */
|
||||
/*********************************************/
|
||||
#define HFCPCI_BTRANS_THRESHOLD 128
|
||||
#define HFCPCI_BTRANS_THRESMASK 0x00
|
||||
|
||||
|
||||
|
||||
/* defines for PCI config */
|
||||
|
||||
#define PCI_ENA_MEMIO 0x02
|
||||
#define PCI_ENA_MASTER 0x04
|
||||
|
||||
|
||||
/* GCI/IOM bus monitor registers */
|
||||
|
||||
#define HCFPCI_C_I 0x08
|
||||
#define HFCPCI_TRxR 0x0C
|
||||
#define HFCPCI_MON1_D 0x28
|
||||
#define HFCPCI_MON2_D 0x2C
|
||||
|
||||
|
||||
/* GCI/IOM bus timeslot registers */
|
||||
|
||||
#define HFCPCI_B1_SSL 0x80
|
||||
#define HFCPCI_B2_SSL 0x84
|
||||
#define HFCPCI_AUX1_SSL 0x88
|
||||
#define HFCPCI_AUX2_SSL 0x8C
|
||||
#define HFCPCI_B1_RSL 0x90
|
||||
#define HFCPCI_B2_RSL 0x94
|
||||
#define HFCPCI_AUX1_RSL 0x98
|
||||
#define HFCPCI_AUX2_RSL 0x9C
|
||||
|
||||
/* GCI/IOM bus data registers */
|
||||
|
||||
#define HFCPCI_B1_D 0xA0
|
||||
#define HFCPCI_B2_D 0xA4
|
||||
#define HFCPCI_AUX1_D 0xA8
|
||||
#define HFCPCI_AUX2_D 0xAC
|
||||
|
||||
/* GCI/IOM bus configuration registers */
|
||||
|
||||
#define HFCPCI_MST_EMOD 0xB4
|
||||
#define HFCPCI_MST_MODE 0xB8
|
||||
#define HFCPCI_CONNECT 0xBC
|
||||
|
||||
|
||||
/* Interrupt and status registers */
|
||||
|
||||
#define HFCPCI_FIFO_EN 0x44
|
||||
#define HFCPCI_TRM 0x48
|
||||
#define HFCPCI_B_MODE 0x4C
|
||||
#define HFCPCI_CHIP_ID 0x58
|
||||
#define HFCPCI_CIRM 0x60
|
||||
#define HFCPCI_CTMT 0x64
|
||||
#define HFCPCI_INT_M1 0x68
|
||||
#define HFCPCI_INT_M2 0x6C
|
||||
#define HFCPCI_INT_S1 0x78
|
||||
#define HFCPCI_INT_S2 0x7C
|
||||
#define HFCPCI_STATUS 0x70
|
||||
|
||||
/* S/T section registers */
|
||||
|
||||
#define HFCPCI_STATES 0xC0
|
||||
#define HFCPCI_SCTRL 0xC4
|
||||
#define HFCPCI_SCTRL_E 0xC8
|
||||
#define HFCPCI_SCTRL_R 0xCC
|
||||
#define HFCPCI_SQ 0xD0
|
||||
#define HFCPCI_CLKDEL 0xDC
|
||||
#define HFCPCI_B1_REC 0xF0
|
||||
#define HFCPCI_B1_SEND 0xF0
|
||||
#define HFCPCI_B2_REC 0xF4
|
||||
#define HFCPCI_B2_SEND 0xF4
|
||||
#define HFCPCI_D_REC 0xF8
|
||||
#define HFCPCI_D_SEND 0xF8
|
||||
#define HFCPCI_E_REC 0xFC
|
||||
|
||||
|
||||
/* bits in status register (READ) */
|
||||
#define HFCPCI_PCI_PROC 0x02
|
||||
#define HFCPCI_NBUSY 0x04
|
||||
#define HFCPCI_TIMER_ELAP 0x10
|
||||
#define HFCPCI_STATINT 0x20
|
||||
#define HFCPCI_FRAMEINT 0x40
|
||||
#define HFCPCI_ANYINT 0x80
|
||||
|
||||
/* bits in CTMT (Write) */
|
||||
#define HFCPCI_CLTIMER 0x80
|
||||
#define HFCPCI_TIM3_125 0x04
|
||||
#define HFCPCI_TIM25 0x10
|
||||
#define HFCPCI_TIM50 0x14
|
||||
#define HFCPCI_TIM400 0x18
|
||||
#define HFCPCI_TIM800 0x1C
|
||||
#define HFCPCI_AUTO_TIMER 0x20
|
||||
#define HFCPCI_TRANSB2 0x02
|
||||
#define HFCPCI_TRANSB1 0x01
|
||||
|
||||
/* bits in CIRM (Write) */
|
||||
#define HFCPCI_AUX_MSK 0x07
|
||||
#define HFCPCI_RESET 0x08
|
||||
#define HFCPCI_B1_REV 0x40
|
||||
#define HFCPCI_B2_REV 0x80
|
||||
|
||||
/* bits in INT_M1 and INT_S1 */
|
||||
#define HFCPCI_INTS_B1TRANS 0x01
|
||||
#define HFCPCI_INTS_B2TRANS 0x02
|
||||
#define HFCPCI_INTS_DTRANS 0x04
|
||||
#define HFCPCI_INTS_B1REC 0x08
|
||||
#define HFCPCI_INTS_B2REC 0x10
|
||||
#define HFCPCI_INTS_DREC 0x20
|
||||
#define HFCPCI_INTS_L1STATE 0x40
|
||||
#define HFCPCI_INTS_TIMER 0x80
|
||||
|
||||
/* bits in INT_M2 */
|
||||
#define HFCPCI_PROC_TRANS 0x01
|
||||
#define HFCPCI_GCI_I_CHG 0x02
|
||||
#define HFCPCI_GCI_MON_REC 0x04
|
||||
#define HFCPCI_IRQ_ENABLE 0x08
|
||||
#define HFCPCI_PMESEL 0x80
|
||||
|
||||
/* bits in STATES */
|
||||
#define HFCPCI_STATE_MSK 0x0F
|
||||
#define HFCPCI_LOAD_STATE 0x10
|
||||
#define HFCPCI_ACTIVATE 0x20
|
||||
#define HFCPCI_DO_ACTION 0x40
|
||||
#define HFCPCI_NT_G2_G3 0x80
|
||||
|
||||
/* bits in HFCD_MST_MODE */
|
||||
#define HFCPCI_MASTER 0x01
|
||||
#define HFCPCI_SLAVE 0x00
|
||||
/* remaining bits are for codecs control */
|
||||
|
||||
/* bits in HFCD_SCTRL */
|
||||
#define SCTRL_B1_ENA 0x01
|
||||
#define SCTRL_B2_ENA 0x02
|
||||
#define SCTRL_MODE_TE 0x00
|
||||
#define SCTRL_MODE_NT 0x04
|
||||
#define SCTRL_LOW_PRIO 0x08
|
||||
#define SCTRL_SQ_ENA 0x10
|
||||
#define SCTRL_TEST 0x20
|
||||
#define SCTRL_NONE_CAP 0x40
|
||||
#define SCTRL_PWR_DOWN 0x80
|
||||
|
||||
/* bits in SCTRL_E */
|
||||
#define HFCPCI_AUTO_AWAKE 0x01
|
||||
#define HFCPCI_DBIT_1 0x04
|
||||
#define HFCPCI_IGNORE_COL 0x08
|
||||
#define HFCPCI_CHG_B1_B2 0x80
|
||||
|
||||
/****************************/
|
||||
/* bits in FIFO_EN register */
|
||||
/****************************/
|
||||
#define HFCPCI_FIFOEN_B1 0x03
|
||||
#define HFCPCI_FIFOEN_B2 0x0C
|
||||
#define HFCPCI_FIFOEN_DTX 0x10
|
||||
#define HFCPCI_FIFOEN_B1TX 0x01
|
||||
#define HFCPCI_FIFOEN_B1RX 0x02
|
||||
#define HFCPCI_FIFOEN_B2TX 0x04
|
||||
#define HFCPCI_FIFOEN_B2RX 0x08
|
||||
|
||||
|
||||
/***********************************/
|
||||
/* definitions of fifo memory area */
|
||||
/***********************************/
|
||||
#define MAX_D_FRAMES 15
|
||||
#define MAX_B_FRAMES 31
|
||||
#define B_SUB_VAL 0x200
|
||||
#define B_FIFO_SIZE (0x2000 - B_SUB_VAL)
|
||||
#define D_FIFO_SIZE 512
|
||||
#define D_FREG_MASK 0xF
|
||||
|
||||
typedef struct {
|
||||
unsigned short z1; /* Z1 pointer 16 Bit */
|
||||
unsigned short z2; /* Z2 pointer 16 Bit */
|
||||
} z_type;
|
||||
|
||||
typedef struct {
|
||||
u_char data[D_FIFO_SIZE]; /* FIFO data space */
|
||||
u_char fill1[0x20A0 - D_FIFO_SIZE]; /* reserved, do not use */
|
||||
u_char f1, f2; /* f pointers */
|
||||
u_char fill2[0x20C0 - 0x20A2]; /* reserved, do not use */
|
||||
z_type za[MAX_D_FRAMES + 1]; /* mask index with D_FREG_MASK for access */
|
||||
u_char fill3[0x4000 - 0x2100]; /* align 16K */
|
||||
} dfifo_type;
|
||||
|
||||
typedef struct {
|
||||
z_type za[MAX_B_FRAMES + 1]; /* only range 0x0..0x1F allowed */
|
||||
u_char f1, f2; /* f pointers */
|
||||
u_char fill[0x2100 - 0x2082]; /* alignment */
|
||||
} bzfifo_type;
|
||||
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
dfifo_type d_tx; /* D-send channel */
|
||||
dfifo_type d_rx; /* D-receive channel */
|
||||
} d_chan;
|
||||
struct {
|
||||
u_char fill1[0x200];
|
||||
u_char txdat_b1[B_FIFO_SIZE];
|
||||
bzfifo_type txbz_b1;
|
||||
|
||||
bzfifo_type txbz_b2;
|
||||
u_char txdat_b2[B_FIFO_SIZE];
|
||||
|
||||
u_char fill2[D_FIFO_SIZE];
|
||||
|
||||
u_char rxdat_b1[B_FIFO_SIZE];
|
||||
bzfifo_type rxbz_b1;
|
||||
|
||||
bzfifo_type rxbz_b2;
|
||||
u_char rxdat_b2[B_FIFO_SIZE];
|
||||
} b_chans;
|
||||
u_char fill[32768];
|
||||
} fifo_area;
|
||||
|
||||
|
||||
#define Write_hfc(a, b, c) (writeb(c, (a->hw.hfcpci.pci_io) + b))
|
||||
#define Read_hfc(a, b) (readb((a->hw.hfcpci.pci_io) + b))
|
||||
|
||||
extern void main_irq_hcpci(struct BCState *bcs);
|
||||
extern void releasehfcpci(struct IsdnCardState *cs);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,196 +0,0 @@
|
|||
/* $Id: hfc_sx.h,v 1.2.6.1 2001/09/23 22:24:48 kai Exp $
|
||||
*
|
||||
* specific defines for CCD's HFC 2BDS0 S+,SP chips
|
||||
*
|
||||
* Author Werner Cornelius
|
||||
* based on existing driver for CCD HFC PCI cards
|
||||
* Copyright by Werner Cornelius <werner@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************************************/
|
||||
/* thresholds for transparent B-channel mode */
|
||||
/* change mask and threshold simultaneously */
|
||||
/*********************************************/
|
||||
#define HFCSX_BTRANS_THRESHOLD 128
|
||||
#define HFCSX_BTRANS_THRESMASK 0x00
|
||||
|
||||
/* GCI/IOM bus monitor registers */
|
||||
|
||||
#define HFCSX_C_I 0x02
|
||||
#define HFCSX_TRxR 0x03
|
||||
#define HFCSX_MON1_D 0x0A
|
||||
#define HFCSX_MON2_D 0x0B
|
||||
|
||||
|
||||
/* GCI/IOM bus timeslot registers */
|
||||
|
||||
#define HFCSX_B1_SSL 0x20
|
||||
#define HFCSX_B2_SSL 0x21
|
||||
#define HFCSX_AUX1_SSL 0x22
|
||||
#define HFCSX_AUX2_SSL 0x23
|
||||
#define HFCSX_B1_RSL 0x24
|
||||
#define HFCSX_B2_RSL 0x25
|
||||
#define HFCSX_AUX1_RSL 0x26
|
||||
#define HFCSX_AUX2_RSL 0x27
|
||||
|
||||
/* GCI/IOM bus data registers */
|
||||
|
||||
#define HFCSX_B1_D 0x28
|
||||
#define HFCSX_B2_D 0x29
|
||||
#define HFCSX_AUX1_D 0x2A
|
||||
#define HFCSX_AUX2_D 0x2B
|
||||
|
||||
/* GCI/IOM bus configuration registers */
|
||||
|
||||
#define HFCSX_MST_EMOD 0x2D
|
||||
#define HFCSX_MST_MODE 0x2E
|
||||
#define HFCSX_CONNECT 0x2F
|
||||
|
||||
|
||||
/* Interrupt and status registers */
|
||||
|
||||
#define HFCSX_TRM 0x12
|
||||
#define HFCSX_B_MODE 0x13
|
||||
#define HFCSX_CHIP_ID 0x16
|
||||
#define HFCSX_CIRM 0x18
|
||||
#define HFCSX_CTMT 0x19
|
||||
#define HFCSX_INT_M1 0x1A
|
||||
#define HFCSX_INT_M2 0x1B
|
||||
#define HFCSX_INT_S1 0x1E
|
||||
#define HFCSX_INT_S2 0x1F
|
||||
#define HFCSX_STATUS 0x1C
|
||||
|
||||
/* S/T section registers */
|
||||
|
||||
#define HFCSX_STATES 0x30
|
||||
#define HFCSX_SCTRL 0x31
|
||||
#define HFCSX_SCTRL_E 0x32
|
||||
#define HFCSX_SCTRL_R 0x33
|
||||
#define HFCSX_SQ 0x34
|
||||
#define HFCSX_CLKDEL 0x37
|
||||
#define HFCSX_B1_REC 0x3C
|
||||
#define HFCSX_B1_SEND 0x3C
|
||||
#define HFCSX_B2_REC 0x3D
|
||||
#define HFCSX_B2_SEND 0x3D
|
||||
#define HFCSX_D_REC 0x3E
|
||||
#define HFCSX_D_SEND 0x3E
|
||||
#define HFCSX_E_REC 0x3F
|
||||
|
||||
/****************/
|
||||
/* FIFO section */
|
||||
/****************/
|
||||
#define HFCSX_FIF_SEL 0x10
|
||||
#define HFCSX_FIF_Z1L 0x80
|
||||
#define HFCSX_FIF_Z1H 0x84
|
||||
#define HFCSX_FIF_Z2L 0x88
|
||||
#define HFCSX_FIF_Z2H 0x8C
|
||||
#define HFCSX_FIF_INCF1 0xA8
|
||||
#define HFCSX_FIF_DWR 0xAC
|
||||
#define HFCSX_FIF_F1 0xB0
|
||||
#define HFCSX_FIF_F2 0xB4
|
||||
#define HFCSX_FIF_INCF2 0xB8
|
||||
#define HFCSX_FIF_DRD 0xBC
|
||||
|
||||
/* bits in status register (READ) */
|
||||
#define HFCSX_SX_PROC 0x02
|
||||
#define HFCSX_NBUSY 0x04
|
||||
#define HFCSX_TIMER_ELAP 0x10
|
||||
#define HFCSX_STATINT 0x20
|
||||
#define HFCSX_FRAMEINT 0x40
|
||||
#define HFCSX_ANYINT 0x80
|
||||
|
||||
/* bits in CTMT (Write) */
|
||||
#define HFCSX_CLTIMER 0x80
|
||||
#define HFCSX_TIM3_125 0x04
|
||||
#define HFCSX_TIM25 0x10
|
||||
#define HFCSX_TIM50 0x14
|
||||
#define HFCSX_TIM400 0x18
|
||||
#define HFCSX_TIM800 0x1C
|
||||
#define HFCSX_AUTO_TIMER 0x20
|
||||
#define HFCSX_TRANSB2 0x02
|
||||
#define HFCSX_TRANSB1 0x01
|
||||
|
||||
/* bits in CIRM (Write) */
|
||||
#define HFCSX_IRQ_SELMSK 0x07
|
||||
#define HFCSX_IRQ_SELDIS 0x00
|
||||
#define HFCSX_RESET 0x08
|
||||
#define HFCSX_FIFO_RESET 0x80
|
||||
|
||||
|
||||
/* bits in INT_M1 and INT_S1 */
|
||||
#define HFCSX_INTS_B1TRANS 0x01
|
||||
#define HFCSX_INTS_B2TRANS 0x02
|
||||
#define HFCSX_INTS_DTRANS 0x04
|
||||
#define HFCSX_INTS_B1REC 0x08
|
||||
#define HFCSX_INTS_B2REC 0x10
|
||||
#define HFCSX_INTS_DREC 0x20
|
||||
#define HFCSX_INTS_L1STATE 0x40
|
||||
#define HFCSX_INTS_TIMER 0x80
|
||||
|
||||
/* bits in INT_M2 */
|
||||
#define HFCSX_PROC_TRANS 0x01
|
||||
#define HFCSX_GCI_I_CHG 0x02
|
||||
#define HFCSX_GCI_MON_REC 0x04
|
||||
#define HFCSX_IRQ_ENABLE 0x08
|
||||
|
||||
/* bits in STATES */
|
||||
#define HFCSX_STATE_MSK 0x0F
|
||||
#define HFCSX_LOAD_STATE 0x10
|
||||
#define HFCSX_ACTIVATE 0x20
|
||||
#define HFCSX_DO_ACTION 0x40
|
||||
#define HFCSX_NT_G2_G3 0x80
|
||||
|
||||
/* bits in HFCD_MST_MODE */
|
||||
#define HFCSX_MASTER 0x01
|
||||
#define HFCSX_SLAVE 0x00
|
||||
/* remaining bits are for codecs control */
|
||||
|
||||
/* bits in HFCD_SCTRL */
|
||||
#define SCTRL_B1_ENA 0x01
|
||||
#define SCTRL_B2_ENA 0x02
|
||||
#define SCTRL_MODE_TE 0x00
|
||||
#define SCTRL_MODE_NT 0x04
|
||||
#define SCTRL_LOW_PRIO 0x08
|
||||
#define SCTRL_SQ_ENA 0x10
|
||||
#define SCTRL_TEST 0x20
|
||||
#define SCTRL_NONE_CAP 0x40
|
||||
#define SCTRL_PWR_DOWN 0x80
|
||||
|
||||
/* bits in SCTRL_E */
|
||||
#define HFCSX_AUTO_AWAKE 0x01
|
||||
#define HFCSX_DBIT_1 0x04
|
||||
#define HFCSX_IGNORE_COL 0x08
|
||||
#define HFCSX_CHG_B1_B2 0x80
|
||||
|
||||
/**********************************/
|
||||
/* definitions for FIFO selection */
|
||||
/**********************************/
|
||||
#define HFCSX_SEL_D_RX 5
|
||||
#define HFCSX_SEL_D_TX 4
|
||||
#define HFCSX_SEL_B1_RX 1
|
||||
#define HFCSX_SEL_B1_TX 0
|
||||
#define HFCSX_SEL_B2_RX 3
|
||||
#define HFCSX_SEL_B2_TX 2
|
||||
|
||||
#define MAX_D_FRAMES 15
|
||||
#define MAX_B_FRAMES 31
|
||||
#define B_SUB_VAL_32K 0x0200
|
||||
#define B_FIFO_SIZE_32K (0x2000 - B_SUB_VAL_32K)
|
||||
#define B_SUB_VAL_8K 0x1A00
|
||||
#define B_FIFO_SIZE_8K (0x2000 - B_SUB_VAL_8K)
|
||||
#define D_FIFO_SIZE 512
|
||||
#define D_FREG_MASK 0xF
|
||||
|
||||
/************************************************************/
|
||||
/* structure holding additional dynamic data -> send marker */
|
||||
/************************************************************/
|
||||
struct hfcsx_extra {
|
||||
unsigned short marker[2 * (MAX_B_FRAMES + 1) + (MAX_D_FRAMES + 1)];
|
||||
};
|
||||
|
||||
extern void main_irq_hfcsx(struct BCState *bcs);
|
||||
extern void releasehfcsx(struct IsdnCardState *cs);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,208 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* hfc_usb.h
|
||||
*
|
||||
* $Id: hfc_usb.h,v 1.1.2.5 2007/08/20 14:36:03 mbachem Exp $
|
||||
*/
|
||||
|
||||
#ifndef __HFC_USB_H__
|
||||
#define __HFC_USB_H__
|
||||
|
||||
#define DRIVER_AUTHOR "Peter Sprenger (sprenger@moving-byters.de)"
|
||||
#define DRIVER_DESC "HFC-S USB based HiSAX ISDN driver"
|
||||
|
||||
|
||||
#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */
|
||||
#define HFC_TIMER_T3 8000 /* timeout for l1 activation timer */
|
||||
#define HFC_TIMER_T4 500 /* time for state change interval */
|
||||
|
||||
#define HFCUSB_L1_STATECHANGE 0 /* L1 state changed */
|
||||
#define HFCUSB_L1_DRX 1 /* D-frame received */
|
||||
#define HFCUSB_L1_ERX 2 /* E-frame received */
|
||||
#define HFCUSB_L1_DTX 4 /* D-frames completed */
|
||||
|
||||
#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */
|
||||
|
||||
#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */
|
||||
#define HFCUSB_TX_THRESHOLD 64 /* threshold for fifo report bit tx */
|
||||
|
||||
#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */
|
||||
#define HFCUSB_CIRM 0x00 /* cirm register index */
|
||||
#define HFCUSB_USB_SIZE 0x07 /* int length register */
|
||||
#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */
|
||||
#define HFCUSB_F_CROSS 0x0b /* bit order register */
|
||||
#define HFCUSB_CLKDEL 0x37 /* bit delay register */
|
||||
#define HFCUSB_CON_HDLC 0xfa /* channel connect register */
|
||||
#define HFCUSB_HDLC_PAR 0xfb
|
||||
#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */
|
||||
#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */
|
||||
#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */
|
||||
#define HFCUSB_F_THRES 0x0c /* threshold register */
|
||||
#define HFCUSB_FIFO 0x0f /* fifo select register */
|
||||
#define HFCUSB_F_USAGE 0x1a /* fifo usage register */
|
||||
#define HFCUSB_MST_MODE0 0x14
|
||||
#define HFCUSB_MST_MODE1 0x15
|
||||
#define HFCUSB_P_DATA 0x1f
|
||||
#define HFCUSB_INC_RES_F 0x0e
|
||||
#define HFCUSB_STATES 0x30
|
||||
|
||||
#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */
|
||||
|
||||
|
||||
/* fifo registers */
|
||||
#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */
|
||||
#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */
|
||||
#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */
|
||||
#define HFCUSB_B2_TX 2
|
||||
#define HFCUSB_B2_RX 3
|
||||
#define HFCUSB_D_TX 4
|
||||
#define HFCUSB_D_RX 5
|
||||
#define HFCUSB_PCM_TX 6
|
||||
#define HFCUSB_PCM_RX 7
|
||||
|
||||
/*
|
||||
* used to switch snd_transfer_mode for different TA modes e.g. the Billion USB TA just
|
||||
* supports ISO out, while the Cologne Chip EVAL TA just supports BULK out
|
||||
*/
|
||||
#define USB_INT 0
|
||||
#define USB_BULK 1
|
||||
#define USB_ISOC 2
|
||||
|
||||
#define ISOC_PACKETS_D 8
|
||||
#define ISOC_PACKETS_B 8
|
||||
#define ISO_BUFFER_SIZE 128
|
||||
|
||||
/* Fifo flow Control for TX ISO */
|
||||
#define SINK_MAX 68
|
||||
#define SINK_MIN 48
|
||||
#define SINK_DMIN 12
|
||||
#define SINK_DMAX 18
|
||||
#define BITLINE_INF (-64 * 8)
|
||||
|
||||
/* HFC-S USB register access by Control-URSs */
|
||||
#define write_usb(a, b, c) usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), NULL, 0, HFC_CTRL_TIMEOUT)
|
||||
#define read_usb(a, b, c) usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), 1, HFC_CTRL_TIMEOUT)
|
||||
#define HFC_CTRL_BUFSIZE 32
|
||||
|
||||
/* entry and size of output/input control buffer */
|
||||
typedef struct {
|
||||
__u8 hfc_reg; /* register number */
|
||||
__u8 reg_val; /* value to be written (or read) */
|
||||
int action; /* data for action handler */
|
||||
} ctrl_buft;
|
||||
|
||||
/* Debugging Flags */
|
||||
#define HFCUSB_DBG_INIT 0x0001
|
||||
#define HFCUSB_DBG_STATES 0x0002
|
||||
#define HFCUSB_DBG_DCHANNEL 0x0080
|
||||
#define HFCUSB_DBG_FIFO_ERR 0x4000
|
||||
#define HFCUSB_DBG_VERBOSE_USB 0x8000
|
||||
|
||||
/*
|
||||
* URB error codes:
|
||||
* Used to represent a list of values and their respective symbolic names
|
||||
*/
|
||||
struct hfcusb_symbolic_list {
|
||||
const int num;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct hfcusb_symbolic_list urb_errlist[] = {
|
||||
{-ENOMEM, "No memory for allocation of internal structures"},
|
||||
{-ENOSPC, "The host controller's bandwidth is already consumed"},
|
||||
{-ENOENT, "URB was canceled by unlink_urb"},
|
||||
{-EXDEV, "ISO transfer only partially completed"},
|
||||
{-EAGAIN, "Too match scheduled for the future"},
|
||||
{-ENXIO, "URB already queued"},
|
||||
{-EFBIG, "Too much ISO frames requested"},
|
||||
{-ENOSR, "Buffer error (overrun)"},
|
||||
{-EPIPE, "Specified endpoint is stalled (device not responding)"},
|
||||
{-EOVERFLOW, "Babble (bad cable?)"},
|
||||
{-EPROTO, "Bit-stuff error (bad cable?)"},
|
||||
{-EILSEQ, "CRC/Timeout"},
|
||||
{-ETIMEDOUT, "NAK (device does not respond)"},
|
||||
{-ESHUTDOWN, "Device unplugged"},
|
||||
{-1, NULL}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* device dependent information to support different
|
||||
* ISDN Ta's using the HFC-S USB chip
|
||||
*/
|
||||
|
||||
/* USB descriptor need to contain one of the following EndPoint combination: */
|
||||
#define CNF_4INT3ISO 1 // 4 INT IN, 3 ISO OUT
|
||||
#define CNF_3INT3ISO 2 // 3 INT IN, 3 ISO OUT
|
||||
#define CNF_4ISO3ISO 3 // 4 ISO IN, 3 ISO OUT
|
||||
#define CNF_3ISO3ISO 4 // 3 ISO IN, 3 ISO OUT
|
||||
|
||||
#define EP_NUL 1 // Endpoint at this position not allowed
|
||||
#define EP_NOP 2 // all type of endpoints allowed at this position
|
||||
#define EP_ISO 3 // Isochron endpoint mandatory at this position
|
||||
#define EP_BLK 4 // Bulk endpoint mandatory at this position
|
||||
#define EP_INT 5 // Interrupt endpoint mandatory at this position
|
||||
|
||||
/*
|
||||
* List of all supported endpoint configuration sets, used to find the
|
||||
* best matching endpoint configuration within a devices' USB descriptor.
|
||||
* We need at least 3 RX endpoints, and 3 TX endpoints, either
|
||||
* INT-in and ISO-out, or ISO-in and ISO-out)
|
||||
* with 4 RX endpoints even E-Channel logging is possible
|
||||
*/
|
||||
static int validconf[][19] = {
|
||||
// INT in, ISO out config
|
||||
{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT,
|
||||
EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
|
||||
CNF_4INT3ISO, 2, 1},
|
||||
{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL,
|
||||
EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
|
||||
CNF_3INT3ISO, 2, 0},
|
||||
// ISO in, ISO out config
|
||||
{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
|
||||
EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO,
|
||||
CNF_4ISO3ISO, 2, 1},
|
||||
{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
|
||||
EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL,
|
||||
CNF_3ISO3ISO, 2, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // EOL element
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HISAX_DEBUG
|
||||
// string description of chosen config
|
||||
static char *conf_str[] = {
|
||||
"4 Interrupt IN + 3 Isochron OUT",
|
||||
"3 Interrupt IN + 3 Isochron OUT",
|
||||
"4 Isochron IN + 3 Isochron OUT",
|
||||
"3 Isochron IN + 3 Isochron OUT"
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int vendor; // vendor id
|
||||
int prod_id; // product id
|
||||
char *vend_name; // vendor string
|
||||
__u8 led_scheme; // led display scheme
|
||||
signed short led_bits[8]; // array of 8 possible LED bitmask settings
|
||||
} vendor_data;
|
||||
|
||||
#define LED_OFF 0 // no LED support
|
||||
#define LED_SCHEME1 1 // LED standard scheme
|
||||
#define LED_SCHEME2 2 // not used yet...
|
||||
|
||||
#define LED_POWER_ON 1
|
||||
#define LED_POWER_OFF 2
|
||||
#define LED_S0_ON 3
|
||||
#define LED_S0_OFF 4
|
||||
#define LED_B1_ON 5
|
||||
#define LED_B1_OFF 6
|
||||
#define LED_B1_DATA 7
|
||||
#define LED_B2_ON 8
|
||||
#define LED_B2_OFF 9
|
||||
#define LED_B2_DATA 10
|
||||
|
||||
#define LED_NORMAL 0 // LEDs are normal
|
||||
#define LED_INVERTED 1 // LEDs are inverted
|
||||
|
||||
|
||||
#endif // __HFC_USB_H__
|
|
@ -1,261 +0,0 @@
|
|||
/* $Id: hfcscard.c,v 1.10.2.4 2004/01/14 16:04:48 keil Exp $
|
||||
*
|
||||
* low level stuff for hfcs based cards (Teles3c, ACER P10)
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/isapnp.h>
|
||||
#include "hisax.h"
|
||||
#include "hfc_2bds0.h"
|
||||
#include "isdnl1.h"
|
||||
|
||||
static const char *hfcs_revision = "$Revision: 1.10.2.4 $";
|
||||
|
||||
static irqreturn_t
|
||||
hfcs_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char val, stat;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
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)
|
||||
debugl1(cs, "HFCS: stat(%02x) s1(%02x)", stat, val);
|
||||
hfc2bds0_interrupt(cs, val);
|
||||
} else {
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "HFCS: irq_no_irq stat(%02x)", stat);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
hfcs_Timer(struct timer_list *t)
|
||||
{
|
||||
struct IsdnCardState *cs = from_timer(cs, t, hw.hfcD.timer);
|
||||
cs->hw.hfcD.timer.expires = jiffies + 75;
|
||||
/* WD RESET */
|
||||
/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80);
|
||||
add_timer(&cs->hw.hfcD.timer);
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
release_io_hfcs(struct IsdnCardState *cs)
|
||||
{
|
||||
release2bds0(cs);
|
||||
del_timer(&cs->hw.hfcD.timer);
|
||||
if (cs->hw.hfcD.addr)
|
||||
release_region(cs->hw.hfcD.addr, 2);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_hfcs(struct IsdnCardState *cs)
|
||||
{
|
||||
printk(KERN_INFO "HFCS: resetting card\n");
|
||||
cs->hw.hfcD.cirm = HFCD_RESET;
|
||||
if (cs->typ == ISDN_CTYPE_TELES3C)
|
||||
cs->hw.hfcD.cirm |= HFCD_MEM8K;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */
|
||||
mdelay(10);
|
||||
cs->hw.hfcD.cirm = 0;
|
||||
if (cs->typ == ISDN_CTYPE_TELES3C)
|
||||
cs->hw.hfcD.cirm |= HFCD_MEM8K;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset Off */
|
||||
mdelay(10);
|
||||
if (cs->typ == ISDN_CTYPE_TELES3C)
|
||||
cs->hw.hfcD.cirm |= HFCD_INTB;
|
||||
else if (cs->typ == ISDN_CTYPE_ACERP10)
|
||||
cs->hw.hfcD.cirm |= HFCD_INTA;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e);
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */
|
||||
cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
|
||||
cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE;
|
||||
cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS |
|
||||
HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC |
|
||||
HFCD_INTS_DREC | HFCD_INTS_L1STATE;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1);
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2);
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */
|
||||
udelay(10);
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */
|
||||
cs->hw.hfcD.mst_m = HFCD_MASTER;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); /* HFC Master */
|
||||
cs->hw.hfcD.sctrl = 0;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
|
||||
}
|
||||
|
||||
static int
|
||||
hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
int delay;
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "HFCS: card_msg %x", mt);
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_hfcs(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_RELEASE:
|
||||
release_io_hfcs(cs);
|
||||
return (0);
|
||||
case CARD_INIT:
|
||||
delay = (75 * HZ) / 100 + 1;
|
||||
mod_timer(&cs->hw.hfcD.timer, jiffies + delay);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_hfcs(cs);
|
||||
init2bds0(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
delay = (80 * HZ) / 1000 + 1;
|
||||
msleep(80);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
cs->hw.hfcD.ctmt |= HFCD_TIM800;
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
|
||||
cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
static struct isapnp_device_id hfc_ids[] = {
|
||||
{ ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114),
|
||||
ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114),
|
||||
(unsigned long) "Acer P10" },
|
||||
{ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002),
|
||||
ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002),
|
||||
(unsigned long) "Billion 2" },
|
||||
{ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001),
|
||||
ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001),
|
||||
(unsigned long) "Billion 1" },
|
||||
{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410),
|
||||
ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410),
|
||||
(unsigned long) "IStar PnP" },
|
||||
{ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610),
|
||||
ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610),
|
||||
(unsigned long) "Teles 16.3c" },
|
||||
{ ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001),
|
||||
ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001),
|
||||
(unsigned long) "Tornado Tipa C" },
|
||||
{ ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001),
|
||||
ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001),
|
||||
(unsigned long) "Genius Speed Surfer" },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static struct isapnp_device_id *ipid = &hfc_ids[0];
|
||||
static struct pnp_card *pnp_c = NULL;
|
||||
#endif
|
||||
|
||||
int setup_hfcs(struct IsdnCard *card)
|
||||
{
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
|
||||
strcpy(tmp, hfcs_revision);
|
||||
printk(KERN_INFO "HiSax: HFC-S driver Rev. %s\n", HiSax_getrev(tmp));
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
if (!card->para[1] && isapnp_present()) {
|
||||
struct pnp_dev *pnp_d;
|
||||
while (ipid->card_vendor) {
|
||||
if ((pnp_c = pnp_find_card(ipid->card_vendor,
|
||||
ipid->card_device, pnp_c))) {
|
||||
pnp_d = NULL;
|
||||
if ((pnp_d = pnp_find_dev(pnp_c,
|
||||
ipid->vendor, ipid->function, pnp_d))) {
|
||||
int err;
|
||||
|
||||
printk(KERN_INFO "HiSax: %s detected\n",
|
||||
(char *)ipid->driver_data);
|
||||
pnp_disable_dev(pnp_d);
|
||||
err = pnp_activate_dev(pnp_d);
|
||||
if (err < 0) {
|
||||
printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
|
||||
__func__, err);
|
||||
return (0);
|
||||
}
|
||||
card->para[1] = pnp_port_start(pnp_d, 0);
|
||||
card->para[0] = pnp_irq(pnp_d, 0);
|
||||
if (card->para[0] == -1 || !card->para[1]) {
|
||||
printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n",
|
||||
card->para[0], card->para[1]);
|
||||
pnp_disable_dev(pnp_d);
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
printk(KERN_ERR "HFC PnP: PnP error card found, no device\n");
|
||||
}
|
||||
}
|
||||
ipid++;
|
||||
pnp_c = NULL;
|
||||
}
|
||||
if (!ipid->card_vendor) {
|
||||
printk(KERN_INFO "HFC PnP: no ISAPnP card found\n");
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
cs->hw.hfcD.addr = card->para[1] & 0xfffe;
|
||||
cs->irq = card->para[0];
|
||||
cs->hw.hfcD.cip = 0;
|
||||
cs->hw.hfcD.int_s1 = 0;
|
||||
cs->hw.hfcD.send = NULL;
|
||||
cs->bcs[0].hw.hfc.send = NULL;
|
||||
cs->bcs[1].hw.hfc.send = NULL;
|
||||
cs->hw.hfcD.dfifosize = 512;
|
||||
cs->dc.hfcd.ph_state = 0;
|
||||
cs->hw.hfcD.fifo = 255;
|
||||
if (cs->typ == ISDN_CTYPE_TELES3C) {
|
||||
cs->hw.hfcD.bfifosize = 1024 + 512;
|
||||
} else if (cs->typ == ISDN_CTYPE_ACERP10) {
|
||||
cs->hw.hfcD.bfifosize = 7 * 1024 + 512;
|
||||
} else
|
||||
return (0);
|
||||
if (!request_region(cs->hw.hfcD.addr, 2, "HFCS isdn")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: %s config port %x-%x already in use\n",
|
||||
CardType[card->typ],
|
||||
cs->hw.hfcD.addr,
|
||||
cs->hw.hfcD.addr + 2);
|
||||
return (0);
|
||||
}
|
||||
printk(KERN_INFO
|
||||
"HFCS: defined at 0x%x IRQ %d HZ %d\n",
|
||||
cs->hw.hfcD.addr,
|
||||
cs->irq, HZ);
|
||||
if (cs->typ == ISDN_CTYPE_TELES3C) {
|
||||
/* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */
|
||||
outb(0x00, cs->hw.hfcD.addr);
|
||||
outb(0x56, cs->hw.hfcD.addr | 1);
|
||||
} else if (cs->typ == ISDN_CTYPE_ACERP10) {
|
||||
/* Acer P10 IO ADR is 0x300 */
|
||||
outb(0x00, cs->hw.hfcD.addr);
|
||||
outb(0x57, cs->hw.hfcD.addr | 1);
|
||||
}
|
||||
set_cs_func(cs);
|
||||
timer_setup(&cs->hw.hfcD.timer, hfcs_Timer, 0);
|
||||
cs->cardmsg = &hfcs_card_msg;
|
||||
cs->irq_func = &hfcs_interrupt;
|
||||
return (1);
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,66 +0,0 @@
|
|||
/* $Id: hisax_cfg.h,v 1.1.2.1 2004/01/24 20:47:23 keil Exp $
|
||||
* define of the basic HiSax configuration structures
|
||||
* and pcmcia interface
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#define ISDN_CTYPE_16_0 1
|
||||
#define ISDN_CTYPE_8_0 2
|
||||
#define ISDN_CTYPE_16_3 3
|
||||
#define ISDN_CTYPE_PNP 4
|
||||
#define ISDN_CTYPE_A1 5
|
||||
#define ISDN_CTYPE_ELSA 6
|
||||
#define ISDN_CTYPE_ELSA_PNP 7
|
||||
#define ISDN_CTYPE_TELESPCMCIA 8
|
||||
#define ISDN_CTYPE_IX1MICROR2 9
|
||||
#define ISDN_CTYPE_ELSA_PCMCIA 10
|
||||
#define ISDN_CTYPE_DIEHLDIVA 11
|
||||
#define ISDN_CTYPE_ASUSCOM 12
|
||||
#define ISDN_CTYPE_TELEINT 13
|
||||
#define ISDN_CTYPE_TELES3C 14
|
||||
#define ISDN_CTYPE_SEDLBAUER 15
|
||||
#define ISDN_CTYPE_SPORTSTER 16
|
||||
#define ISDN_CTYPE_MIC 17
|
||||
#define ISDN_CTYPE_ELSA_PCI 18
|
||||
#define ISDN_CTYPE_COMPAQ_ISA 19
|
||||
#define ISDN_CTYPE_NETJET_S 20
|
||||
#define ISDN_CTYPE_TELESPCI 21
|
||||
#define ISDN_CTYPE_SEDLBAUER_PCMCIA 22
|
||||
#define ISDN_CTYPE_AMD7930 23
|
||||
#define ISDN_CTYPE_NICCY 24
|
||||
#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_ISURF 29
|
||||
#define ISDN_CTYPE_ACERP10 30
|
||||
#define ISDN_CTYPE_HSTSAPHIR 31
|
||||
#define ISDN_CTYPE_BKM_A4T 32
|
||||
#define ISDN_CTYPE_SCT_QUADRO 33
|
||||
#define ISDN_CTYPE_GAZEL 34
|
||||
#define ISDN_CTYPE_HFC_PCI 35
|
||||
#define ISDN_CTYPE_W6692 36
|
||||
#define ISDN_CTYPE_HFC_SX 37
|
||||
#define ISDN_CTYPE_NETJET_U 38
|
||||
#define ISDN_CTYPE_HFC_SP_PCMCIA 39
|
||||
#define ISDN_CTYPE_DYNAMIC 40
|
||||
#define ISDN_CTYPE_ENTERNOW 41
|
||||
#define ISDN_CTYPE_COUNT 41
|
||||
|
||||
typedef struct IsdnCardState IsdnCardState_t;
|
||||
typedef struct IsdnCard IsdnCard_t;
|
||||
|
||||
struct IsdnCard {
|
||||
int typ;
|
||||
int protocol; /* EDSS1, 1TR6 or NI1 */
|
||||
unsigned long para[4];
|
||||
IsdnCardState_t *cs;
|
||||
};
|
||||
|
||||
typedef int (*hisax_setup_func_t)(struct IsdnCard *card);
|
||||
|
||||
extern void HiSax_closecard(int);
|
||||
extern int hisax_init_pcmcia(void *, int *, IsdnCard_t *);
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Common debugging macros for use with the hisax driver
|
||||
*
|
||||
* Author Frode Isaksen
|
||||
* Copyright 2001 by Frode Isaksen <fisaksen@bewan.com>
|
||||
* 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* How to use:
|
||||
*
|
||||
* Before including this file, you need to
|
||||
* #define __debug_variable my_debug
|
||||
* where my_debug is a variable in your code which
|
||||
* determines the debug bitmask.
|
||||
*
|
||||
* If CONFIG_HISAX_DEBUG is not set, all macros evaluate to nothing
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __HISAX_DEBUG_H__
|
||||
#define __HISAX_DEBUG_H__
|
||||
|
||||
|
||||
#ifdef CONFIG_HISAX_DEBUG
|
||||
|
||||
#define DBG(level, format, arg...) do { \
|
||||
if (level & __debug_variable) \
|
||||
printk(KERN_DEBUG "%s: " format "\n" , __func__ , ## arg); \
|
||||
} while (0)
|
||||
|
||||
#define DBG_PACKET(level, data, count) \
|
||||
if (level & __debug_variable) dump_packet(__func__, data, count)
|
||||
|
||||
#define DBG_SKB(level, skb) \
|
||||
if ((level & __debug_variable) && skb) dump_packet(__func__, skb->data, skb->len)
|
||||
|
||||
|
||||
static void __attribute__((unused))
|
||||
dump_packet(const char *name, const u_char *data, int pkt_len)
|
||||
{
|
||||
#define DUMP_HDR_SIZE 20
|
||||
#define DUMP_TLR_SIZE 8
|
||||
if (pkt_len) {
|
||||
int i, len1, len2;
|
||||
|
||||
printk(KERN_DEBUG "%s: length=%d,data=", name, pkt_len);
|
||||
|
||||
if (pkt_len > DUMP_HDR_SIZE + DUMP_TLR_SIZE) {
|
||||
len1 = DUMP_HDR_SIZE;
|
||||
len2 = DUMP_TLR_SIZE;
|
||||
} else {
|
||||
len1 = pkt_len > DUMP_HDR_SIZE ? DUMP_HDR_SIZE : pkt_len;
|
||||
len2 = 0;
|
||||
}
|
||||
for (i = 0; i < len1; ++i) {
|
||||
printk("%.2x", data[i]);
|
||||
}
|
||||
if (len2) {
|
||||
printk("..");
|
||||
for (i = pkt_len-DUMP_TLR_SIZE; i < pkt_len; ++i) {
|
||||
printk("%.2x", data[i]);
|
||||
}
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
#undef DUMP_HDR_SIZE
|
||||
#undef DUMP_TLR_SIZE
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define DBG(level, format, arg...) do {} while (0)
|
||||
#define DBG_PACKET(level, data, count) do {} while (0)
|
||||
#define DBG_SKB(level, skb) do {} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,58 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include "hisax_if.h"
|
||||
#include "hisax_isac.h"
|
||||
#include <linux/pci.h>
|
||||
|
||||
#define HSCX_BUFMAX 4096
|
||||
|
||||
enum {
|
||||
AVM_FRITZ_PCI,
|
||||
AVM_FRITZ_PNP,
|
||||
AVM_FRITZ_PCIV2,
|
||||
};
|
||||
|
||||
struct hdlc_stat_reg {
|
||||
#ifdef __BIG_ENDIAN
|
||||
u_char fill;
|
||||
u_char mode;
|
||||
u_char xml;
|
||||
u_char cmd;
|
||||
#else
|
||||
u_char cmd;
|
||||
u_char xml;
|
||||
u_char mode;
|
||||
u_char fill;
|
||||
#endif
|
||||
} __attribute__((packed));
|
||||
|
||||
struct fritz_bcs {
|
||||
struct hisax_b_if b_if;
|
||||
struct fritz_adapter *adapter;
|
||||
int mode;
|
||||
int channel;
|
||||
|
||||
union {
|
||||
u_int ctrl;
|
||||
struct hdlc_stat_reg sr;
|
||||
} ctrl;
|
||||
u_int stat;
|
||||
int rcvidx;
|
||||
int fifo_size;
|
||||
u_char rcvbuf[HSCX_BUFMAX]; /* B-Channel receive Buffer */
|
||||
|
||||
int tx_cnt; /* B-Channel transmit counter */
|
||||
struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
|
||||
};
|
||||
|
||||
struct fritz_adapter {
|
||||
int type;
|
||||
spinlock_t hw_lock;
|
||||
unsigned int io;
|
||||
unsigned int irq;
|
||||
struct isac isac;
|
||||
|
||||
struct fritz_bcs bcs[2];
|
||||
|
||||
u32 (*read_hdlc_status) (struct fritz_adapter *adapter, int nr);
|
||||
void (*write_ctrl) (struct fritz_bcs *bcs, int which);
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* Interface between low level (hardware) drivers and
|
||||
* HiSax protocol stack
|
||||
*
|
||||
* Author Kai Germaschewski
|
||||
* Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __HISAX_IF_H__
|
||||
#define __HISAX_IF_H__
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#define REQUEST 0
|
||||
#define CONFIRM 1
|
||||
#define INDICATION 2
|
||||
#define RESPONSE 3
|
||||
|
||||
#define PH_ACTIVATE 0x0100
|
||||
#define PH_DEACTIVATE 0x0110
|
||||
#define PH_DATA 0x0120
|
||||
#define PH_PULL 0x0130
|
||||
#define PH_DATA_E 0x0140
|
||||
|
||||
#define L1_MODE_NULL 0
|
||||
#define L1_MODE_TRANS 1
|
||||
#define L1_MODE_HDLC 2
|
||||
#define L1_MODE_EXTRN 3
|
||||
#define L1_MODE_HDLC_56K 4
|
||||
#define L1_MODE_MODEM 7
|
||||
#define L1_MODE_V32 8
|
||||
#define L1_MODE_FAX 9
|
||||
|
||||
struct hisax_if {
|
||||
void *priv; // private to driver
|
||||
void (*l1l2)(struct hisax_if *, int pr, void *arg);
|
||||
void (*l2l1)(struct hisax_if *, int pr, void *arg);
|
||||
};
|
||||
|
||||
struct hisax_b_if {
|
||||
struct hisax_if ifc;
|
||||
|
||||
// private to hisax
|
||||
struct BCState *bcs;
|
||||
};
|
||||
|
||||
struct hisax_d_if {
|
||||
struct hisax_if ifc;
|
||||
|
||||
// private to hisax
|
||||
struct module *owner;
|
||||
struct IsdnCardState *cs;
|
||||
struct hisax_b_if *b_if[2];
|
||||
struct sk_buff_head erq;
|
||||
unsigned long ph_state;
|
||||
};
|
||||
|
||||
int hisax_register(struct hisax_d_if *hisax_if, struct hisax_b_if *b_if[],
|
||||
char *name, int protocol);
|
||||
void hisax_unregister(struct hisax_d_if *hisax_if);
|
||||
|
||||
#endif
|
|
@ -1,895 +0,0 @@
|
|||
/*
|
||||
* Driver for ISAC-S and ISAC-SX
|
||||
* ISDN Subscriber Access Controller for Terminals
|
||||
*
|
||||
* Author Kai Germaschewski
|
||||
* Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
|
||||
* 2001 by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* based upon Karsten Keil's original isac.c driver
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* Thanks to Wizard Computersysteme GmbH, Bremervoerde and
|
||||
* SoHaNet Technology GmbH, Berlin
|
||||
* for supporting the development of this driver
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
* specifically handle level vs edge triggered?
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include "hisax_isac.h"
|
||||
|
||||
// debugging cruft
|
||||
|
||||
#define __debug_variable debug
|
||||
#include "hisax_debug.h"
|
||||
|
||||
#ifdef CONFIG_HISAX_DEBUG
|
||||
static int debug = 1;
|
||||
module_param(debug, int, 0);
|
||||
|
||||
static char *ISACVer[] = {
|
||||
"2086/2186 V1.1",
|
||||
"2085 B1",
|
||||
"2085 B2",
|
||||
"2085 V2.3"
|
||||
};
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
|
||||
MODULE_DESCRIPTION("ISAC/ISAC-SX driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define DBG_WARN 0x0001
|
||||
#define DBG_IRQ 0x0002
|
||||
#define DBG_L1M 0x0004
|
||||
#define DBG_PR 0x0008
|
||||
#define DBG_RFIFO 0x0100
|
||||
#define DBG_RPACKET 0x0200
|
||||
#define DBG_XFIFO 0x1000
|
||||
#define DBG_XPACKET 0x2000
|
||||
|
||||
// we need to distinguish ISAC-S and ISAC-SX
|
||||
#define TYPE_ISAC 0x00
|
||||
#define TYPE_ISACSX 0x01
|
||||
|
||||
// registers etc.
|
||||
#define ISAC_MASK 0x20
|
||||
#define ISAC_ISTA 0x20
|
||||
#define ISAC_ISTA_EXI 0x01
|
||||
#define ISAC_ISTA_SIN 0x02
|
||||
#define ISAC_ISTA_CISQ 0x04
|
||||
#define ISAC_ISTA_XPR 0x10
|
||||
#define ISAC_ISTA_RSC 0x20
|
||||
#define ISAC_ISTA_RPF 0x40
|
||||
#define ISAC_ISTA_RME 0x80
|
||||
|
||||
#define ISAC_STAR 0x21
|
||||
#define ISAC_CMDR 0x21
|
||||
#define ISAC_CMDR_XRES 0x01
|
||||
#define ISAC_CMDR_XME 0x02
|
||||
#define ISAC_CMDR_XTF 0x08
|
||||
#define ISAC_CMDR_RRES 0x40
|
||||
#define ISAC_CMDR_RMC 0x80
|
||||
|
||||
#define ISAC_EXIR 0x24
|
||||
#define ISAC_EXIR_MOS 0x04
|
||||
#define ISAC_EXIR_XDU 0x40
|
||||
#define ISAC_EXIR_XMR 0x80
|
||||
|
||||
#define ISAC_ADF2 0x39
|
||||
#define ISAC_SPCR 0x30
|
||||
#define ISAC_ADF1 0x38
|
||||
|
||||
#define ISAC_CIR0 0x31
|
||||
#define ISAC_CIX0 0x31
|
||||
#define ISAC_CIR0_CIC0 0x02
|
||||
#define ISAC_CIR0_CIC1 0x01
|
||||
|
||||
#define ISAC_CIR1 0x33
|
||||
#define ISAC_CIX1 0x33
|
||||
#define ISAC_STCR 0x37
|
||||
#define ISAC_MODE 0x22
|
||||
|
||||
#define ISAC_RSTA 0x27
|
||||
#define ISAC_RSTA_RDO 0x40
|
||||
#define ISAC_RSTA_CRC 0x20
|
||||
#define ISAC_RSTA_RAB 0x10
|
||||
|
||||
#define ISAC_RBCL 0x25
|
||||
#define ISAC_RBCH 0x2A
|
||||
#define ISAC_TIMR 0x23
|
||||
#define ISAC_SQXR 0x3b
|
||||
#define ISAC_MOSR 0x3a
|
||||
#define ISAC_MOCR 0x3a
|
||||
#define ISAC_MOR0 0x32
|
||||
#define ISAC_MOX0 0x32
|
||||
#define ISAC_MOR1 0x34
|
||||
#define ISAC_MOX1 0x34
|
||||
|
||||
#define ISAC_RBCH_XAC 0x80
|
||||
|
||||
#define ISAC_CMD_TIM 0x0
|
||||
#define ISAC_CMD_RES 0x1
|
||||
#define ISAC_CMD_SSP 0x2
|
||||
#define ISAC_CMD_SCP 0x3
|
||||
#define ISAC_CMD_AR8 0x8
|
||||
#define ISAC_CMD_AR10 0x9
|
||||
#define ISAC_CMD_ARL 0xa
|
||||
#define ISAC_CMD_DI 0xf
|
||||
|
||||
#define ISACSX_MASK 0x60
|
||||
#define ISACSX_ISTA 0x60
|
||||
#define ISACSX_ISTA_ICD 0x01
|
||||
#define ISACSX_ISTA_CIC 0x10
|
||||
|
||||
#define ISACSX_MASKD 0x20
|
||||
#define ISACSX_ISTAD 0x20
|
||||
#define ISACSX_ISTAD_XDU 0x04
|
||||
#define ISACSX_ISTAD_XMR 0x08
|
||||
#define ISACSX_ISTAD_XPR 0x10
|
||||
#define ISACSX_ISTAD_RFO 0x20
|
||||
#define ISACSX_ISTAD_RPF 0x40
|
||||
#define ISACSX_ISTAD_RME 0x80
|
||||
|
||||
#define ISACSX_CMDRD 0x21
|
||||
#define ISACSX_CMDRD_XRES 0x01
|
||||
#define ISACSX_CMDRD_XME 0x02
|
||||
#define ISACSX_CMDRD_XTF 0x08
|
||||
#define ISACSX_CMDRD_RRES 0x40
|
||||
#define ISACSX_CMDRD_RMC 0x80
|
||||
|
||||
#define ISACSX_MODED 0x22
|
||||
|
||||
#define ISACSX_RBCLD 0x26
|
||||
|
||||
#define ISACSX_RSTAD 0x28
|
||||
#define ISACSX_RSTAD_RAB 0x10
|
||||
#define ISACSX_RSTAD_CRC 0x20
|
||||
#define ISACSX_RSTAD_RDO 0x40
|
||||
#define ISACSX_RSTAD_VFR 0x80
|
||||
|
||||
#define ISACSX_CIR0 0x2e
|
||||
#define ISACSX_CIR0_CIC0 0x08
|
||||
#define ISACSX_CIX0 0x2e
|
||||
|
||||
#define ISACSX_TR_CONF0 0x30
|
||||
|
||||
#define ISACSX_TR_CONF2 0x32
|
||||
|
||||
static struct Fsm l1fsm;
|
||||
|
||||
enum {
|
||||
ST_L1_RESET,
|
||||
ST_L1_F3_PDOWN,
|
||||
ST_L1_F3_PUP,
|
||||
ST_L1_F3_PEND_DEACT,
|
||||
ST_L1_F4,
|
||||
ST_L1_F5,
|
||||
ST_L1_F6,
|
||||
ST_L1_F7,
|
||||
ST_L1_F8,
|
||||
};
|
||||
|
||||
#define L1_STATE_COUNT (ST_L1_F8 + 1)
|
||||
|
||||
static char *strL1State[] =
|
||||
{
|
||||
"ST_L1_RESET",
|
||||
"ST_L1_F3_PDOWN",
|
||||
"ST_L1_F3_PUP",
|
||||
"ST_L1_F3_PEND_DEACT",
|
||||
"ST_L1_F4",
|
||||
"ST_L1_F5",
|
||||
"ST_L1_F6",
|
||||
"ST_L1_F7",
|
||||
"ST_L1_F8",
|
||||
};
|
||||
|
||||
enum {
|
||||
EV_PH_DR, // 0000
|
||||
EV_PH_RES, // 0001
|
||||
EV_PH_TMA, // 0010
|
||||
EV_PH_SLD, // 0011
|
||||
EV_PH_RSY, // 0100
|
||||
EV_PH_DR6, // 0101
|
||||
EV_PH_EI, // 0110
|
||||
EV_PH_PU, // 0111
|
||||
EV_PH_AR, // 1000
|
||||
EV_PH_9, // 1001
|
||||
EV_PH_ARL, // 1010
|
||||
EV_PH_CVR, // 1011
|
||||
EV_PH_AI8, // 1100
|
||||
EV_PH_AI10, // 1101
|
||||
EV_PH_AIL, // 1110
|
||||
EV_PH_DC, // 1111
|
||||
EV_PH_ACTIVATE_REQ,
|
||||
EV_PH_DEACTIVATE_REQ,
|
||||
EV_TIMER3,
|
||||
};
|
||||
|
||||
#define L1_EVENT_COUNT (EV_TIMER3 + 1)
|
||||
|
||||
static char *strL1Event[] =
|
||||
{
|
||||
"EV_PH_DR", // 0000
|
||||
"EV_PH_RES", // 0001
|
||||
"EV_PH_TMA", // 0010
|
||||
"EV_PH_SLD", // 0011
|
||||
"EV_PH_RSY", // 0100
|
||||
"EV_PH_DR6", // 0101
|
||||
"EV_PH_EI", // 0110
|
||||
"EV_PH_PU", // 0111
|
||||
"EV_PH_AR", // 1000
|
||||
"EV_PH_9", // 1001
|
||||
"EV_PH_ARL", // 1010
|
||||
"EV_PH_CVR", // 1011
|
||||
"EV_PH_AI8", // 1100
|
||||
"EV_PH_AI10", // 1101
|
||||
"EV_PH_AIL", // 1110
|
||||
"EV_PH_DC", // 1111
|
||||
"EV_PH_ACTIVATE_REQ",
|
||||
"EV_PH_DEACTIVATE_REQ",
|
||||
"EV_TIMER3",
|
||||
};
|
||||
|
||||
static inline void D_L1L2(struct isac *isac, int pr, void *arg)
|
||||
{
|
||||
struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if;
|
||||
|
||||
DBG(DBG_PR, "pr %#x", pr);
|
||||
ifc->l1l2(ifc, pr, arg);
|
||||
}
|
||||
|
||||
static void ph_command(struct isac *isac, unsigned int command)
|
||||
{
|
||||
DBG(DBG_L1M, "ph_command %#x", command);
|
||||
switch (isac->type) {
|
||||
case TYPE_ISAC:
|
||||
isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3);
|
||||
break;
|
||||
case TYPE_ISACSX:
|
||||
isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
static void l1_di(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_RESET);
|
||||
ph_command(isac, ISAC_CMD_DI);
|
||||
}
|
||||
|
||||
static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_RESET);
|
||||
D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
|
||||
ph_command(isac, ISAC_CMD_DI);
|
||||
}
|
||||
|
||||
static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F3_PDOWN);
|
||||
}
|
||||
|
||||
static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
|
||||
D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
|
||||
ph_command(isac, ISAC_CMD_DI);
|
||||
}
|
||||
|
||||
static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
|
||||
ph_command(isac, ISAC_CMD_DI);
|
||||
}
|
||||
|
||||
static void l1_go_f4(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F4);
|
||||
}
|
||||
|
||||
static void l1_go_f5(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F5);
|
||||
}
|
||||
|
||||
static void l1_go_f6(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F6);
|
||||
}
|
||||
|
||||
static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_F6);
|
||||
D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
|
||||
}
|
||||
|
||||
static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmDelTimer(&isac->timer, 0);
|
||||
FsmChangeState(fi, ST_L1_F7);
|
||||
ph_command(isac, ISAC_CMD_AR8);
|
||||
D_L1L2(isac, PH_ACTIVATE | INDICATION, NULL);
|
||||
}
|
||||
|
||||
static void l1_go_f8(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F8);
|
||||
}
|
||||
|
||||
static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_F8);
|
||||
D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
|
||||
}
|
||||
|
||||
static void l1_ar8(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
|
||||
ph_command(isac, ISAC_CMD_AR8);
|
||||
}
|
||||
|
||||
static void l1_timer3(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct isac *isac = fi->userdata;
|
||||
|
||||
ph_command(isac, ISAC_CMD_DI);
|
||||
D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
|
||||
}
|
||||
|
||||
// state machines according to data sheet PSB 2186 / 3186
|
||||
|
||||
static struct FsmNode L1FnList[] __initdata =
|
||||
{
|
||||
{ST_L1_RESET, EV_PH_RES, l1_di},
|
||||
{ST_L1_RESET, EV_PH_EI, l1_di},
|
||||
{ST_L1_RESET, EV_PH_DC, l1_go_f3pdown},
|
||||
{ST_L1_RESET, EV_PH_AR, l1_go_f6},
|
||||
{ST_L1_RESET, EV_PH_AI8, l1_go_f7_act_ind},
|
||||
|
||||
{ST_L1_F3_PDOWN, EV_PH_RES, l1_di},
|
||||
{ST_L1_F3_PDOWN, EV_PH_EI, l1_di},
|
||||
{ST_L1_F3_PDOWN, EV_PH_AR, l1_go_f6},
|
||||
{ST_L1_F3_PDOWN, EV_PH_RSY, l1_go_f5},
|
||||
{ST_L1_F3_PDOWN, EV_PH_PU, l1_go_f4},
|
||||
{ST_L1_F3_PDOWN, EV_PH_AI8, l1_go_f7_act_ind},
|
||||
{ST_L1_F3_PDOWN, EV_PH_ACTIVATE_REQ, l1_ar8},
|
||||
{ST_L1_F3_PDOWN, EV_TIMER3, l1_timer3},
|
||||
|
||||
{ST_L1_F3_PEND_DEACT, EV_PH_RES, l1_di},
|
||||
{ST_L1_F3_PEND_DEACT, EV_PH_EI, l1_di},
|
||||
{ST_L1_F3_PEND_DEACT, EV_PH_DC, l1_go_f3pdown},
|
||||
{ST_L1_F3_PEND_DEACT, EV_PH_RSY, l1_go_f5},
|
||||
{ST_L1_F3_PEND_DEACT, EV_PH_AR, l1_go_f6},
|
||||
{ST_L1_F3_PEND_DEACT, EV_PH_AI8, l1_go_f7_act_ind},
|
||||
|
||||
{ST_L1_F4, EV_PH_RES, l1_di},
|
||||
{ST_L1_F4, EV_PH_EI, l1_di},
|
||||
{ST_L1_F4, EV_PH_RSY, l1_go_f5},
|
||||
{ST_L1_F4, EV_PH_AI8, l1_go_f7_act_ind},
|
||||
{ST_L1_F4, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F4, EV_PH_DC, l1_go_f3pdown},
|
||||
|
||||
{ST_L1_F5, EV_PH_RES, l1_di},
|
||||
{ST_L1_F5, EV_PH_EI, l1_di},
|
||||
{ST_L1_F5, EV_PH_AR, l1_go_f6},
|
||||
{ST_L1_F5, EV_PH_AI8, l1_go_f7_act_ind},
|
||||
{ST_L1_F5, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F5, EV_PH_DR, l1_go_f3pend},
|
||||
{ST_L1_F5, EV_PH_DC, l1_go_f3pdown},
|
||||
|
||||
{ST_L1_F6, EV_PH_RES, l1_di},
|
||||
{ST_L1_F6, EV_PH_EI, l1_di},
|
||||
{ST_L1_F6, EV_PH_RSY, l1_go_f8},
|
||||
{ST_L1_F6, EV_PH_AI8, l1_go_f7_act_ind},
|
||||
{ST_L1_F6, EV_PH_DR6, l1_go_f3pend},
|
||||
{ST_L1_F6, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F6, EV_PH_DC, l1_go_f3pdown},
|
||||
|
||||
{ST_L1_F7, EV_PH_RES, l1_di_deact_ind},
|
||||
{ST_L1_F7, EV_PH_EI, l1_di_deact_ind},
|
||||
{ST_L1_F7, EV_PH_AR, l1_go_f6_deact_ind},
|
||||
{ST_L1_F7, EV_PH_RSY, l1_go_f8_deact_ind},
|
||||
{ST_L1_F7, EV_PH_DR, l1_go_f3pend_deact_ind},
|
||||
|
||||
{ST_L1_F8, EV_PH_RES, l1_di},
|
||||
{ST_L1_F8, EV_PH_EI, l1_di},
|
||||
{ST_L1_F8, EV_PH_AR, l1_go_f6},
|
||||
{ST_L1_F8, EV_PH_DR, l1_go_f3pend},
|
||||
{ST_L1_F8, EV_PH_AI8, l1_go_f7_act_ind},
|
||||
{ST_L1_F8, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F8, EV_PH_DC, l1_go_f3pdown},
|
||||
};
|
||||
|
||||
static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buf[256];
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
DBG(DBG_L1M, "%s", buf);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void isac_version(struct isac *cs)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = cs->read_isac(cs, ISAC_RBCH);
|
||||
DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]);
|
||||
}
|
||||
|
||||
static void isac_empty_fifo(struct isac *isac, int count)
|
||||
{
|
||||
// this also works for isacsx, since
|
||||
// CMDR(D) register works the same
|
||||
u_char *ptr;
|
||||
|
||||
DBG(DBG_IRQ, "count %d", count);
|
||||
|
||||
if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
|
||||
DBG(DBG_WARN, "overrun %d", isac->rcvidx + count);
|
||||
isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
|
||||
isac->rcvidx = 0;
|
||||
return;
|
||||
}
|
||||
ptr = isac->rcvbuf + isac->rcvidx;
|
||||
isac->rcvidx += count;
|
||||
isac->read_isac_fifo(isac, ptr, count);
|
||||
isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
|
||||
DBG_PACKET(DBG_RFIFO, ptr, count);
|
||||
}
|
||||
|
||||
static void isac_fill_fifo(struct isac *isac)
|
||||
{
|
||||
// this also works for isacsx, since
|
||||
// CMDR(D) register works the same
|
||||
|
||||
int count;
|
||||
unsigned char cmd;
|
||||
u_char *ptr;
|
||||
|
||||
BUG_ON(!isac->tx_skb);
|
||||
|
||||
count = isac->tx_skb->len;
|
||||
BUG_ON(count <= 0);
|
||||
|
||||
DBG(DBG_IRQ, "count %d", count);
|
||||
|
||||
if (count > 0x20) {
|
||||
count = 0x20;
|
||||
cmd = ISAC_CMDR_XTF;
|
||||
} else {
|
||||
cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME;
|
||||
}
|
||||
|
||||
ptr = isac->tx_skb->data;
|
||||
skb_pull(isac->tx_skb, count);
|
||||
isac->tx_cnt += count;
|
||||
DBG_PACKET(DBG_XFIFO, ptr, count);
|
||||
isac->write_isac_fifo(isac, ptr, count);
|
||||
isac->write_isac(isac, ISAC_CMDR, cmd);
|
||||
}
|
||||
|
||||
static void isac_retransmit(struct isac *isac)
|
||||
{
|
||||
if (!isac->tx_skb) {
|
||||
DBG(DBG_WARN, "no skb");
|
||||
return;
|
||||
}
|
||||
skb_push(isac->tx_skb, isac->tx_cnt);
|
||||
isac->tx_cnt = 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void isac_cisq_interrupt(struct isac *isac)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = isac->read_isac(isac, ISAC_CIR0);
|
||||
DBG(DBG_IRQ, "CIR0 %#x", val);
|
||||
if (val & ISAC_CIR0_CIC0) {
|
||||
DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf);
|
||||
FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
|
||||
}
|
||||
if (val & ISAC_CIR0_CIC1) {
|
||||
val = isac->read_isac(isac, ISAC_CIR1);
|
||||
DBG(DBG_WARN, "ISAC CIR1 %#x", val);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void isac_rme_interrupt(struct isac *isac)
|
||||
{
|
||||
unsigned char val;
|
||||
int count;
|
||||
struct sk_buff *skb;
|
||||
|
||||
val = isac->read_isac(isac, ISAC_RSTA);
|
||||
if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB))
|
||||
!= ISAC_RSTA_CRC) {
|
||||
DBG(DBG_WARN, "RSTA %#x, dropped", val);
|
||||
isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
|
||||
goto out;
|
||||
}
|
||||
|
||||
count = isac->read_isac(isac, ISAC_RBCL) & 0x1f;
|
||||
DBG(DBG_IRQ, "RBCL %#x", count);
|
||||
if (count == 0)
|
||||
count = 0x20;
|
||||
|
||||
isac_empty_fifo(isac, count);
|
||||
count = isac->rcvidx;
|
||||
if (count < 1) {
|
||||
DBG(DBG_WARN, "count %d < 1", count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
skb = alloc_skb(count, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
DBG(DBG_WARN, "no memory, dropping\n");
|
||||
goto out;
|
||||
}
|
||||
skb_put_data(skb, isac->rcvbuf, count);
|
||||
DBG_SKB(DBG_RPACKET, skb);
|
||||
D_L1L2(isac, PH_DATA | INDICATION, skb);
|
||||
out:
|
||||
isac->rcvidx = 0;
|
||||
}
|
||||
|
||||
static inline void isac_xpr_interrupt(struct isac *isac)
|
||||
{
|
||||
if (!isac->tx_skb)
|
||||
return;
|
||||
|
||||
if (isac->tx_skb->len > 0) {
|
||||
isac_fill_fifo(isac);
|
||||
return;
|
||||
}
|
||||
dev_kfree_skb_irq(isac->tx_skb);
|
||||
isac->tx_cnt = 0;
|
||||
isac->tx_skb = NULL;
|
||||
D_L1L2(isac, PH_DATA | CONFIRM, NULL);
|
||||
}
|
||||
|
||||
static inline void isac_exi_interrupt(struct isac *isac)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = isac->read_isac(isac, ISAC_EXIR);
|
||||
DBG(2, "EXIR %#x", val);
|
||||
|
||||
if (val & ISAC_EXIR_XMR) {
|
||||
DBG(DBG_WARN, "ISAC XMR");
|
||||
isac_retransmit(isac);
|
||||
}
|
||||
if (val & ISAC_EXIR_XDU) {
|
||||
DBG(DBG_WARN, "ISAC XDU");
|
||||
isac_retransmit(isac);
|
||||
}
|
||||
if (val & ISAC_EXIR_MOS) { /* MOS */
|
||||
DBG(DBG_WARN, "MOS");
|
||||
val = isac->read_isac(isac, ISAC_MOSR);
|
||||
DBG(2, "ISAC MOSR %#x", val);
|
||||
}
|
||||
}
|
||||
|
||||
void isac_irq(struct isac *isac)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = isac->read_isac(isac, ISAC_ISTA);
|
||||
DBG(DBG_IRQ, "ISTA %#x", val);
|
||||
|
||||
if (val & ISAC_ISTA_EXI) {
|
||||
DBG(DBG_IRQ, "EXI");
|
||||
isac_exi_interrupt(isac);
|
||||
}
|
||||
if (val & ISAC_ISTA_XPR) {
|
||||
DBG(DBG_IRQ, "XPR");
|
||||
isac_xpr_interrupt(isac);
|
||||
}
|
||||
if (val & ISAC_ISTA_RME) {
|
||||
DBG(DBG_IRQ, "RME");
|
||||
isac_rme_interrupt(isac);
|
||||
}
|
||||
if (val & ISAC_ISTA_RPF) {
|
||||
DBG(DBG_IRQ, "RPF");
|
||||
isac_empty_fifo(isac, 0x20);
|
||||
}
|
||||
if (val & ISAC_ISTA_CISQ) {
|
||||
DBG(DBG_IRQ, "CISQ");
|
||||
isac_cisq_interrupt(isac);
|
||||
}
|
||||
if (val & ISAC_ISTA_RSC) {
|
||||
DBG(DBG_WARN, "RSC");
|
||||
}
|
||||
if (val & ISAC_ISTA_SIN) {
|
||||
DBG(DBG_WARN, "SIN");
|
||||
}
|
||||
isac->write_isac(isac, ISAC_MASK, 0xff);
|
||||
isac->write_isac(isac, ISAC_MASK, 0x00);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
|
||||
static inline void isacsx_cic_interrupt(struct isac *isac)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = isac->read_isac(isac, ISACSX_CIR0);
|
||||
DBG(DBG_IRQ, "CIR0 %#x", val);
|
||||
if (val & ISACSX_CIR0_CIC0) {
|
||||
DBG(DBG_IRQ, "CODR0 %#x", val >> 4);
|
||||
FsmEvent(&isac->l1m, val >> 4, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void isacsx_rme_interrupt(struct isac *isac)
|
||||
{
|
||||
int count;
|
||||
struct sk_buff *skb;
|
||||
unsigned char val;
|
||||
|
||||
val = isac->read_isac(isac, ISACSX_RSTAD);
|
||||
if ((val & (ISACSX_RSTAD_VFR |
|
||||
ISACSX_RSTAD_RDO |
|
||||
ISACSX_RSTAD_CRC |
|
||||
ISACSX_RSTAD_RAB))
|
||||
!= (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) {
|
||||
DBG(DBG_WARN, "RSTAD %#x, dropped", val);
|
||||
isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
|
||||
goto out;
|
||||
}
|
||||
|
||||
count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f;
|
||||
DBG(DBG_IRQ, "RBCLD %#x", count);
|
||||
if (count == 0)
|
||||
count = 0x20;
|
||||
|
||||
isac_empty_fifo(isac, count);
|
||||
// strip trailing status byte
|
||||
count = isac->rcvidx - 1;
|
||||
if (count < 1) {
|
||||
DBG(DBG_WARN, "count %d < 1", count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(count);
|
||||
if (!skb) {
|
||||
DBG(DBG_WARN, "no memory, dropping");
|
||||
goto out;
|
||||
}
|
||||
skb_put_data(skb, isac->rcvbuf, count);
|
||||
DBG_SKB(DBG_RPACKET, skb);
|
||||
D_L1L2(isac, PH_DATA | INDICATION, skb);
|
||||
out:
|
||||
isac->rcvidx = 0;
|
||||
}
|
||||
|
||||
static inline void isacsx_xpr_interrupt(struct isac *isac)
|
||||
{
|
||||
if (!isac->tx_skb)
|
||||
return;
|
||||
|
||||
if (isac->tx_skb->len > 0) {
|
||||
isac_fill_fifo(isac);
|
||||
return;
|
||||
}
|
||||
dev_kfree_skb_irq(isac->tx_skb);
|
||||
isac->tx_skb = NULL;
|
||||
isac->tx_cnt = 0;
|
||||
D_L1L2(isac, PH_DATA | CONFIRM, NULL);
|
||||
}
|
||||
|
||||
static inline void isacsx_icd_interrupt(struct isac *isac)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = isac->read_isac(isac, ISACSX_ISTAD);
|
||||
DBG(DBG_IRQ, "ISTAD %#x", val);
|
||||
if (val & ISACSX_ISTAD_XDU) {
|
||||
DBG(DBG_WARN, "ISTAD XDU");
|
||||
isac_retransmit(isac);
|
||||
}
|
||||
if (val & ISACSX_ISTAD_XMR) {
|
||||
DBG(DBG_WARN, "ISTAD XMR");
|
||||
isac_retransmit(isac);
|
||||
}
|
||||
if (val & ISACSX_ISTAD_XPR) {
|
||||
DBG(DBG_IRQ, "ISTAD XPR");
|
||||
isacsx_xpr_interrupt(isac);
|
||||
}
|
||||
if (val & ISACSX_ISTAD_RFO) {
|
||||
DBG(DBG_WARN, "ISTAD RFO");
|
||||
isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
|
||||
}
|
||||
if (val & ISACSX_ISTAD_RME) {
|
||||
DBG(DBG_IRQ, "ISTAD RME");
|
||||
isacsx_rme_interrupt(isac);
|
||||
}
|
||||
if (val & ISACSX_ISTAD_RPF) {
|
||||
DBG(DBG_IRQ, "ISTAD RPF");
|
||||
isac_empty_fifo(isac, 0x20);
|
||||
}
|
||||
}
|
||||
|
||||
void isacsx_irq(struct isac *isac)
|
||||
{
|
||||
unsigned char val;
|
||||
|
||||
val = isac->read_isac(isac, ISACSX_ISTA);
|
||||
DBG(DBG_IRQ, "ISTA %#x", val);
|
||||
|
||||
if (val & ISACSX_ISTA_ICD)
|
||||
isacsx_icd_interrupt(isac);
|
||||
if (val & ISACSX_ISTA_CIC)
|
||||
isacsx_cic_interrupt(isac);
|
||||
}
|
||||
|
||||
void isac_init(struct isac *isac)
|
||||
{
|
||||
isac->tx_skb = NULL;
|
||||
isac->l1m.fsm = &l1fsm;
|
||||
isac->l1m.state = ST_L1_RESET;
|
||||
#ifdef CONFIG_HISAX_DEBUG
|
||||
isac->l1m.debug = 1;
|
||||
#else
|
||||
isac->l1m.debug = 0;
|
||||
#endif
|
||||
isac->l1m.userdata = isac;
|
||||
isac->l1m.printdebug = l1m_debug;
|
||||
FsmInitTimer(&isac->l1m, &isac->timer);
|
||||
}
|
||||
|
||||
void isac_setup(struct isac *isac)
|
||||
{
|
||||
int val, eval;
|
||||
|
||||
isac->type = TYPE_ISAC;
|
||||
isac_version(isac);
|
||||
|
||||
ph_command(isac, ISAC_CMD_RES);
|
||||
|
||||
isac->write_isac(isac, ISAC_MASK, 0xff);
|
||||
isac->mocr = 0xaa;
|
||||
if (test_bit(ISAC_IOM1, &isac->flags)) {
|
||||
/* IOM 1 Mode */
|
||||
isac->write_isac(isac, ISAC_ADF2, 0x0);
|
||||
isac->write_isac(isac, ISAC_SPCR, 0xa);
|
||||
isac->write_isac(isac, ISAC_ADF1, 0x2);
|
||||
isac->write_isac(isac, ISAC_STCR, 0x70);
|
||||
isac->write_isac(isac, ISAC_MODE, 0xc9);
|
||||
} else {
|
||||
/* IOM 2 Mode */
|
||||
if (!isac->adf2)
|
||||
isac->adf2 = 0x80;
|
||||
isac->write_isac(isac, ISAC_ADF2, isac->adf2);
|
||||
isac->write_isac(isac, ISAC_SQXR, 0x2f);
|
||||
isac->write_isac(isac, ISAC_SPCR, 0x00);
|
||||
isac->write_isac(isac, ISAC_STCR, 0x70);
|
||||
isac->write_isac(isac, ISAC_MODE, 0xc9);
|
||||
isac->write_isac(isac, ISAC_TIMR, 0x00);
|
||||
isac->write_isac(isac, ISAC_ADF1, 0x00);
|
||||
}
|
||||
val = isac->read_isac(isac, ISAC_STAR);
|
||||
DBG(2, "ISAC STAR %x", val);
|
||||
val = isac->read_isac(isac, ISAC_MODE);
|
||||
DBG(2, "ISAC MODE %x", val);
|
||||
val = isac->read_isac(isac, ISAC_ADF2);
|
||||
DBG(2, "ISAC ADF2 %x", val);
|
||||
val = isac->read_isac(isac, ISAC_ISTA);
|
||||
DBG(2, "ISAC ISTA %x", val);
|
||||
if (val & 0x01) {
|
||||
eval = isac->read_isac(isac, ISAC_EXIR);
|
||||
DBG(2, "ISAC EXIR %x", eval);
|
||||
}
|
||||
val = isac->read_isac(isac, ISAC_CIR0);
|
||||
DBG(2, "ISAC CIR0 %x", val);
|
||||
FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
|
||||
|
||||
isac->write_isac(isac, ISAC_MASK, 0x0);
|
||||
// RESET Receiver and Transmitter
|
||||
isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES);
|
||||
}
|
||||
|
||||
void isacsx_setup(struct isac *isac)
|
||||
{
|
||||
isac->type = TYPE_ISACSX;
|
||||
// clear LDD
|
||||
isac->write_isac(isac, ISACSX_TR_CONF0, 0x00);
|
||||
// enable transmitter
|
||||
isac->write_isac(isac, ISACSX_TR_CONF2, 0x00);
|
||||
// transparent mode 0, RAC, stop/go
|
||||
isac->write_isac(isac, ISACSX_MODED, 0xc9);
|
||||
// all HDLC IRQ unmasked
|
||||
isac->write_isac(isac, ISACSX_MASKD, 0x03);
|
||||
// unmask ICD, CID IRQs
|
||||
isac->write_isac(isac, ISACSX_MASK,
|
||||
~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
|
||||
}
|
||||
|
||||
void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
|
||||
{
|
||||
struct isac *isac = hisax_d_if->priv;
|
||||
struct sk_buff *skb = arg;
|
||||
|
||||
DBG(DBG_PR, "pr %#x", pr);
|
||||
|
||||
switch (pr) {
|
||||
case PH_ACTIVATE | REQUEST:
|
||||
FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL);
|
||||
break;
|
||||
case PH_DEACTIVATE | REQUEST:
|
||||
FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL);
|
||||
break;
|
||||
case PH_DATA | REQUEST:
|
||||
DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len);
|
||||
DBG_SKB(DBG_XPACKET, skb);
|
||||
if (isac->l1m.state != ST_L1_F7) {
|
||||
DBG(1, "L1 wrong state %d\n", isac->l1m.state);
|
||||
dev_kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
BUG_ON(isac->tx_skb);
|
||||
|
||||
isac->tx_skb = skb;
|
||||
isac_fill_fifo(isac);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init hisax_isac_init(void)
|
||||
{
|
||||
printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n");
|
||||
|
||||
l1fsm.state_count = L1_STATE_COUNT;
|
||||
l1fsm.event_count = L1_EVENT_COUNT;
|
||||
l1fsm.strState = strL1State;
|
||||
l1fsm.strEvent = strL1Event;
|
||||
return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
|
||||
}
|
||||
|
||||
static void __exit hisax_isac_exit(void)
|
||||
{
|
||||
FsmFree(&l1fsm);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(isac_init);
|
||||
EXPORT_SYMBOL(isac_d_l2l1);
|
||||
|
||||
EXPORT_SYMBOL(isacsx_setup);
|
||||
EXPORT_SYMBOL(isacsx_irq);
|
||||
|
||||
EXPORT_SYMBOL(isac_setup);
|
||||
EXPORT_SYMBOL(isac_irq);
|
||||
|
||||
module_init(hisax_isac_init);
|
||||
module_exit(hisax_isac_exit);
|
|
@ -1,46 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __HISAX_ISAC_H__
|
||||
#define __HISAX_ISAC_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include "fsm.h"
|
||||
#include "hisax_if.h"
|
||||
|
||||
#define TIMER3_VALUE 7000
|
||||
#define MAX_DFRAME_LEN_L1 300
|
||||
|
||||
#define ISAC_IOM1 0
|
||||
|
||||
struct isac {
|
||||
void *priv;
|
||||
|
||||
u_long flags;
|
||||
struct hisax_d_if hisax_d_if;
|
||||
struct FsmInst l1m;
|
||||
struct FsmTimer timer;
|
||||
u_char mocr;
|
||||
u_char adf2;
|
||||
int type;
|
||||
|
||||
u_char rcvbuf[MAX_DFRAME_LEN_L1];
|
||||
int rcvidx;
|
||||
|
||||
struct sk_buff *tx_skb;
|
||||
int tx_cnt;
|
||||
|
||||
u_char (*read_isac) (struct isac *, u_char);
|
||||
void (*write_isac) (struct isac *, u_char, u_char);
|
||||
void (*read_isac_fifo) (struct isac *, u_char *, int);
|
||||
void (*write_isac_fifo)(struct isac *, u_char *, int);
|
||||
};
|
||||
|
||||
void isac_init(struct isac *isac);
|
||||
void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
|
||||
|
||||
void isac_setup(struct isac *isac);
|
||||
void isac_irq(struct isac *isac);
|
||||
|
||||
void isacsx_setup(struct isac *isac);
|
||||
void isacsx_irq(struct isac *isac);
|
||||
|
||||
#endif
|
|
@ -1,277 +0,0 @@
|
|||
/* $Id: hscx.c,v 1.24.2.4 2004/01/24 20:47:23 keil Exp $
|
||||
*
|
||||
* HSCX specific routines
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "hscx.h"
|
||||
#include "isac.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static char *HSCXVer[] =
|
||||
{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
|
||||
"?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
|
||||
|
||||
int
|
||||
HscxVersion(struct IsdnCardState *cs, char *s)
|
||||
{
|
||||
int verA, verB;
|
||||
|
||||
verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf;
|
||||
verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf;
|
||||
printk(KERN_INFO "%s HSCX version A: %s B: %s\n", s,
|
||||
HSCXVer[verA], HSCXVer[verB]);
|
||||
if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf))
|
||||
return (1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
modehscx(struct BCState *bcs, int mode, int bc)
|
||||
{
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int hscx = bcs->hw.hscx.hscx;
|
||||
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "hscx %c mode %d ichan %d",
|
||||
'A' + hscx, mode, bc);
|
||||
bcs->mode = mode;
|
||||
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 IOM 1 SSI */
|
||||
if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0))
|
||||
bc = 1 - bc;
|
||||
|
||||
if (bc == 0) {
|
||||
cs->BC_Write_Reg(cs, hscx, HSCX_TSAX,
|
||||
test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
|
||||
cs->BC_Write_Reg(cs, hscx, HSCX_TSAR,
|
||||
test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
|
||||
} else {
|
||||
cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, bcs->hw.hscx.tsaxr1);
|
||||
cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, bcs->hw.hscx.tsaxr1);
|
||||
}
|
||||
switch (mode) {
|
||||
case (L1_MODE_NULL):
|
||||
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;
|
||||
}
|
||||
if (mode)
|
||||
cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41);
|
||||
cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00);
|
||||
}
|
||||
|
||||
void
|
||||
hscx_l2l1(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct BCState *bcs = st->l1.bcs;
|
||||
u_long flags;
|
||||
struct sk_buff *skb = arg;
|
||||
|
||||
switch (pr) {
|
||||
case (PH_DATA | REQUEST):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
skb_queue_tail(&bcs->squeue, skb);
|
||||
} else {
|
||||
bcs->tx_skb = skb;
|
||||
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->hw.hscx.count = 0;
|
||||
bcs->cs->BC_Send_Data(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | INDICATION):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
printk(KERN_WARNING "hscx_l2l1: this shouldn't happen\n");
|
||||
} else {
|
||||
test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->tx_skb = skb;
|
||||
bcs->hw.hscx.count = 0;
|
||||
bcs->cs->BC_Send_Data(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | REQUEST):
|
||||
if (!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):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
modehscx(bcs, st->l1.mode, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | REQUEST):
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | CONFIRM):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
modehscx(bcs, 0, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
close_hscxstate(struct BCState *bcs)
|
||||
{
|
||||
modehscx(bcs, 0, bcs->channel);
|
||||
if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
|
||||
kfree(bcs->hw.hscx.rcvbuf);
|
||||
bcs->hw.hscx.rcvbuf = NULL;
|
||||
kfree(bcs->blog);
|
||||
bcs->blog = NULL;
|
||||
skb_queue_purge(&bcs->rqueue);
|
||||
skb_queue_purge(&bcs->squeue);
|
||||
if (bcs->tx_skb) {
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs)
|
||||
{
|
||||
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");
|
||||
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->tx_skb = NULL;
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->event = 0;
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
bcs->tx_cnt = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
setstack_hscx(struct PStack *st, struct BCState *bcs)
|
||||
{
|
||||
bcs->channel = st->l1.bc;
|
||||
if (open_hscxstate(st->l1.hardware, bcs))
|
||||
return (-1);
|
||||
st->l1.bcs = bcs;
|
||||
st->l2.l2l1 = hscx_l2l1;
|
||||
setstack_manager(st);
|
||||
bcs->st = st;
|
||||
setstack_l1_B(st);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
clear_pending_hscx_ints(struct IsdnCardState *cs)
|
||||
{
|
||||
int val, eval;
|
||||
|
||||
val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
|
||||
debugl1(cs, "HSCX B ISTA %x", val);
|
||||
if (val & 0x01) {
|
||||
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);
|
||||
debugl1(cs, "HSCX A ISTA %x", val);
|
||||
val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
|
||||
debugl1(cs, "HSCX B STAR %x", val);
|
||||
val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
cs->bcs[0].hw.hscx.tsaxr0 = 0x2f;
|
||||
cs->bcs[0].hw.hscx.tsaxr1 = 3;
|
||||
cs->bcs[1].hw.hscx.tsaxr0 = 0x2f;
|
||||
cs->bcs[1].hw.hscx.tsaxr1 = 3;
|
||||
modehscx(cs->bcs, 0, 0);
|
||||
modehscx(cs->bcs + 1, 0, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/* $Id: hscx.h,v 1.8.2.2 2004/01/12 22:52:26 keil Exp $
|
||||
*
|
||||
* HSCX specific defines
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/* All Registers original Siemens Spec */
|
||||
|
||||
#define HSCX_ISTA 0x20
|
||||
#define HSCX_CCR1 0x2f
|
||||
#define HSCX_CCR2 0x2c
|
||||
#define HSCX_TSAR 0x31
|
||||
#define HSCX_TSAX 0x30
|
||||
#define HSCX_XCCR 0x32
|
||||
#define HSCX_RCCR 0x33
|
||||
#define HSCX_MODE 0x22
|
||||
#define HSCX_CMDR 0x21
|
||||
#define HSCX_EXIR 0x24
|
||||
#define HSCX_XAD1 0x24
|
||||
#define HSCX_XAD2 0x25
|
||||
#define HSCX_RAH2 0x27
|
||||
#define HSCX_RSTA 0x27
|
||||
#define HSCX_TIMR 0x23
|
||||
#define HSCX_STAR 0x21
|
||||
#define HSCX_RBCL 0x25
|
||||
#define HSCX_XBCH 0x2d
|
||||
#define HSCX_VSTR 0x2e
|
||||
#define HSCX_RLCR 0x2e
|
||||
#define HSCX_MASK 0x20
|
||||
|
||||
extern int HscxVersion(struct IsdnCardState *cs, char *s);
|
||||
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);
|
|
@ -1,294 +0,0 @@
|
|||
/* $Id: hscx_irq.c,v 1.18.2.3 2004/02/11 13:21:34 keil Exp $
|
||||
*
|
||||
* low level b-channel stuff for Siemens HSCX
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* This is an include file for fast inline IRQ stuff
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
static inline void
|
||||
waitforCEC(struct IsdnCardState *cs, int hscx)
|
||||
{
|
||||
int to = 50;
|
||||
|
||||
while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
|
||||
udelay(1);
|
||||
to--;
|
||||
}
|
||||
if (!to)
|
||||
printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
waitforXFW(struct IsdnCardState *cs, int hscx)
|
||||
{
|
||||
int to = 50;
|
||||
|
||||
while (((READHSCX(cs, hscx, HSCX_STAR) & 0x44) != 0x40) && to) {
|
||||
udelay(1);
|
||||
to--;
|
||||
}
|
||||
if (!to)
|
||||
printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
|
||||
}
|
||||
|
||||
static inline void
|
||||
WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
|
||||
{
|
||||
waitforCEC(cs, hscx);
|
||||
WRITEHSCX(cs, hscx, HSCX_CMDR, data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
hscx_empty_fifo(struct BCState *bcs, int count)
|
||||
{
|
||||
u_char *ptr;
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
|
||||
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
|
||||
debugl1(cs, "hscx_empty_fifo");
|
||||
|
||||
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->hw.hscx.hscx, 0x80);
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
return;
|
||||
}
|
||||
ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
|
||||
bcs->hw.hscx.rcvidx += count;
|
||||
READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
|
||||
WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
|
||||
if (cs->debug & L1_DEB_HSCX_FIFO) {
|
||||
char *t = bcs->blog;
|
||||
|
||||
t += sprintf(t, "hscx_empty_fifo %c cnt %d",
|
||||
bcs->hw.hscx.hscx ? 'B' : 'A', count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", bcs->blog);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hscx_fill_fifo(struct BCState *bcs)
|
||||
{
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int more, count;
|
||||
int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags) ? 64 : 32;
|
||||
u_char *ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
|
||||
debugl1(cs, "hscx_fill_fifo");
|
||||
|
||||
if (!bcs->tx_skb)
|
||||
return;
|
||||
if (bcs->tx_skb->len <= 0)
|
||||
return;
|
||||
|
||||
more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
|
||||
if (bcs->tx_skb->len > fifo_size) {
|
||||
more = !0;
|
||||
count = fifo_size;
|
||||
} else
|
||||
count = bcs->tx_skb->len;
|
||||
|
||||
waitforXFW(cs, bcs->hw.hscx.hscx);
|
||||
ptr = bcs->tx_skb->data;
|
||||
skb_pull(bcs->tx_skb, count);
|
||||
bcs->tx_cnt -= count;
|
||||
bcs->hw.hscx.count += count;
|
||||
WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
|
||||
WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
|
||||
if (cs->debug & L1_DEB_HSCX_FIFO) {
|
||||
char *t = bcs->blog;
|
||||
|
||||
t += sprintf(t, "hscx_fill_fifo %c cnt %d",
|
||||
bcs->hw.hscx.hscx ? 'B' : 'A', count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", bcs->blog);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
|
||||
{
|
||||
u_char r;
|
||||
struct BCState *bcs = cs->bcs + hscx;
|
||||
struct sk_buff *skb;
|
||||
int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags) ? 64 : 32;
|
||||
int count;
|
||||
|
||||
if (!test_bit(BC_FLG_INIT, &bcs->Flag))
|
||||
return;
|
||||
|
||||
if (val & 0x80) { /* RME */
|
||||
r = READHSCX(cs, hscx, HSCX_RSTA);
|
||||
if ((r & 0xf0) != 0xa0) {
|
||||
if (!(r & 0x80)) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "HSCX invalid frame");
|
||||
#ifdef ERROR_STATISTIC
|
||||
bcs->err_inv++;
|
||||
#endif
|
||||
}
|
||||
if ((r & 0x40) && bcs->mode) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "HSCX RDO mode=%d",
|
||||
bcs->mode);
|
||||
#ifdef ERROR_STATISTIC
|
||||
bcs->err_rdo++;
|
||||
#endif
|
||||
}
|
||||
if (!(r & 0x20)) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "HSCX CRC error");
|
||||
#ifdef ERROR_STATISTIC
|
||||
bcs->err_crc++;
|
||||
#endif
|
||||
}
|
||||
WriteHSCXCMDR(cs, hscx, 0x80);
|
||||
} else {
|
||||
count = READHSCX(cs, hscx, HSCX_RBCL) & (
|
||||
test_bit(HW_IPAC, &cs->HW_Flags) ? 0x3f : 0x1f);
|
||||
if (count == 0)
|
||||
count = fifo_size;
|
||||
hscx_empty_fifo(bcs, count);
|
||||
if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
|
||||
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 {
|
||||
skb_put_data(skb, bcs->hw.hscx.rcvbuf,
|
||||
count);
|
||||
skb_queue_tail(&bcs->rqueue, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
schedule_event(bcs, B_RCVBUFREADY);
|
||||
}
|
||||
if (val & 0x40) { /* RPF */
|
||||
hscx_empty_fifo(bcs, fifo_size);
|
||||
if (bcs->mode == L1_MODE_TRANS) {
|
||||
/* receive audio data */
|
||||
if (!(skb = dev_alloc_skb(fifo_size)))
|
||||
printk(KERN_WARNING "HiSax: receive out of memory\n");
|
||||
else {
|
||||
skb_put_data(skb, bcs->hw.hscx.rcvbuf,
|
||||
fifo_size);
|
||||
skb_queue_tail(&bcs->rqueue, skb);
|
||||
}
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
schedule_event(bcs, B_RCVBUFREADY);
|
||||
}
|
||||
}
|
||||
if (val & 0x10) { /* XPR */
|
||||
if (bcs->tx_skb) {
|
||||
if (bcs->tx_skb->len) {
|
||||
hscx_fill_fifo(bcs);
|
||||
return;
|
||||
} else {
|
||||
if (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) &&
|
||||
(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
|
||||
u_long flags;
|
||||
spin_lock_irqsave(&bcs->aclock, flags);
|
||||
bcs->ackcnt += bcs->hw.hscx.count;
|
||||
spin_unlock_irqrestore(&bcs->aclock, flags);
|
||||
schedule_event(bcs, B_ACKPENDING);
|
||||
}
|
||||
dev_kfree_skb_irq(bcs->tx_skb);
|
||||
bcs->hw.hscx.count = 0;
|
||||
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);
|
||||
hscx_fill_fifo(bcs);
|
||||
} else {
|
||||
test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
schedule_event(bcs, B_XMTBUFREADY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hscx_int_main(struct IsdnCardState *cs, u_char val)
|
||||
{
|
||||
|
||||
u_char exval;
|
||||
struct BCState *bcs;
|
||||
|
||||
if (val & 0x01) {
|
||||
bcs = cs->bcs + 1;
|
||||
exval = READHSCX(cs, 1, HSCX_EXIR);
|
||||
if (exval & 0x40) {
|
||||
if (bcs->mode == 1)
|
||||
hscx_fill_fifo(bcs);
|
||||
else {
|
||||
#ifdef ERROR_STATISTIC
|
||||
bcs->err_tx++;
|
||||
#endif
|
||||
/* Here we lost an TX interrupt, so
|
||||
* restart transmitting the whole frame.
|
||||
*/
|
||||
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->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)
|
||||
debugl1(cs, "HSCX B EXIR %x", exval);
|
||||
}
|
||||
if (val & 0xf8) {
|
||||
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 (bcs->mode == L1_MODE_TRANS)
|
||||
hscx_fill_fifo(bcs);
|
||||
else {
|
||||
/* Here we lost an TX interrupt, so
|
||||
* restart transmitting the whole frame.
|
||||
*/
|
||||
#ifdef ERROR_STATISTIC
|
||||
bcs->err_tx++;
|
||||
#endif
|
||||
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->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)
|
||||
debugl1(cs, "HSCX A EXIR %x", exval);
|
||||
}
|
||||
if (val & 0x04) {
|
||||
exval = READHSCX(cs, 0, HSCX_ISTA);
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "HSCX A interrupt %x", exval);
|
||||
hscx_interrupt(cs, exval, 0);
|
||||
}
|
||||
}
|
|
@ -1,680 +0,0 @@
|
|||
/* $Id: icc.c,v 1.8.2.3 2004/01/13 14:31:25 keil Exp $
|
||||
*
|
||||
* ICC specific routines
|
||||
*
|
||||
* Author Matt Henderson & Guy Ellis
|
||||
* Copyright by Traverse Technologies Pty Ltd, www.travers.com.au
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* 1999.6.25 Initial implementation of routines for Siemens ISDN
|
||||
* Communication Controller PEB 2070 based on the ISAC routines
|
||||
* written by Karsten Keil.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "icc.h"
|
||||
// #include "arcofi.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DBUSY_TIMER_VALUE 80
|
||||
#define ARCOFI_USE 0
|
||||
|
||||
static char *ICCVer[] =
|
||||
{"2070 A1/A3", "2070 B1", "2070 B2/B3", "2070 V2.4"};
|
||||
|
||||
void
|
||||
ICCVersion(struct IsdnCardState *cs, char *s)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = cs->readisac(cs, ICC_RBCH);
|
||||
printk(KERN_INFO "%s ICC version (%x): %s\n", s, val, ICCVer[(val >> 5) & 3]);
|
||||
}
|
||||
|
||||
static void
|
||||
ph_command(struct IsdnCardState *cs, unsigned int command)
|
||||
{
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ph_command %x", command);
|
||||
cs->writeisac(cs, ICC_CIX0, (command << 2) | 3);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
icc_new_ph(struct IsdnCardState *cs)
|
||||
{
|
||||
switch (cs->dc.icc.ph_state) {
|
||||
case (ICC_IND_EI1):
|
||||
ph_command(cs, ICC_CMD_DI);
|
||||
l1_msg(cs, HW_RESET | INDICATION, NULL);
|
||||
break;
|
||||
case (ICC_IND_DC):
|
||||
l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
|
||||
break;
|
||||
case (ICC_IND_DR):
|
||||
l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
|
||||
break;
|
||||
case (ICC_IND_PU):
|
||||
l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
|
||||
break;
|
||||
case (ICC_IND_FJ):
|
||||
l1_msg(cs, HW_RSYNC | INDICATION, NULL);
|
||||
break;
|
||||
case (ICC_IND_AR):
|
||||
l1_msg(cs, HW_INFO2 | INDICATION, NULL);
|
||||
break;
|
||||
case (ICC_IND_AI):
|
||||
l1_msg(cs, HW_INFO4 | INDICATION, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
icc_bh(struct work_struct *work)
|
||||
{
|
||||
struct IsdnCardState *cs =
|
||||
container_of(work, struct IsdnCardState, tqueue);
|
||||
struct PStack *stptr;
|
||||
|
||||
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 | CONFIRM, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
}
|
||||
if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
|
||||
icc_new_ph(cs);
|
||||
if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
|
||||
DChannel_proc_rcv(cs);
|
||||
if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
|
||||
DChannel_proc_xmt(cs);
|
||||
#if ARCOFI_USE
|
||||
if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
|
||||
return;
|
||||
if (test_and_clear_bit(D_RX_MON1, &cs->event))
|
||||
arcofi_fsm(cs, ARCOFI_RX_END, NULL);
|
||||
if (test_and_clear_bit(D_TX_MON1, &cs->event))
|
||||
arcofi_fsm(cs, ARCOFI_TX_END, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
icc_empty_fifo(struct IsdnCardState *cs, int count)
|
||||
{
|
||||
u_char *ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "icc_empty_fifo");
|
||||
|
||||
if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "icc_empty_fifo overrun %d",
|
||||
cs->rcvidx + count);
|
||||
cs->writeisac(cs, ICC_CMDR, 0x80);
|
||||
cs->rcvidx = 0;
|
||||
return;
|
||||
}
|
||||
ptr = cs->rcvbuf + cs->rcvidx;
|
||||
cs->rcvidx += count;
|
||||
cs->readisacfifo(cs, ptr, count);
|
||||
cs->writeisac(cs, ICC_CMDR, 0x80);
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "icc_empty_fifo cnt %d", count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
icc_fill_fifo(struct IsdnCardState *cs)
|
||||
{
|
||||
int count, more;
|
||||
u_char *ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "icc_fill_fifo");
|
||||
|
||||
if (!cs->tx_skb)
|
||||
return;
|
||||
|
||||
count = cs->tx_skb->len;
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
more = 0;
|
||||
if (count > 32) {
|
||||
more = !0;
|
||||
count = 32;
|
||||
}
|
||||
ptr = cs->tx_skb->data;
|
||||
skb_pull(cs->tx_skb, count);
|
||||
cs->tx_cnt += count;
|
||||
cs->writeisacfifo(cs, ptr, count);
|
||||
cs->writeisac(cs, ICC_CMDR, more ? 0x8 : 0xa);
|
||||
if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
debugl1(cs, "icc_fill_fifo dbusytimer running");
|
||||
del_timer(&cs->dbusytimer);
|
||||
}
|
||||
cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
|
||||
add_timer(&cs->dbusytimer);
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "icc_fill_fifo cnt %d", count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
icc_interrupt(struct IsdnCardState *cs, u_char val)
|
||||
{
|
||||
u_char exval, v1;
|
||||
struct sk_buff *skb;
|
||||
unsigned int count;
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ICC interrupt %x", val);
|
||||
if (val & 0x80) { /* RME */
|
||||
exval = cs->readisac(cs, ICC_RSTA);
|
||||
if ((exval & 0x70) != 0x20) {
|
||||
if (exval & 0x40) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC RDO");
|
||||
#ifdef ERROR_STATISTIC
|
||||
cs->err_rx++;
|
||||
#endif
|
||||
}
|
||||
if (!(exval & 0x20)) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC CRC error");
|
||||
#ifdef ERROR_STATISTIC
|
||||
cs->err_crc++;
|
||||
#endif
|
||||
}
|
||||
cs->writeisac(cs, ICC_CMDR, 0x80);
|
||||
} else {
|
||||
count = cs->readisac(cs, ICC_RBCL) & 0x1f;
|
||||
if (count == 0)
|
||||
count = 32;
|
||||
icc_empty_fifo(cs, count);
|
||||
if ((count = cs->rcvidx) > 0) {
|
||||
cs->rcvidx = 0;
|
||||
if (!(skb = alloc_skb(count, GFP_ATOMIC)))
|
||||
printk(KERN_WARNING "HiSax: D receive out of memory\n");
|
||||
else {
|
||||
skb_put_data(skb, cs->rcvbuf, count);
|
||||
skb_queue_tail(&cs->rq, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
cs->rcvidx = 0;
|
||||
schedule_event(cs, D_RCVBUFREADY);
|
||||
}
|
||||
if (val & 0x40) { /* RPF */
|
||||
icc_empty_fifo(cs, 32);
|
||||
}
|
||||
if (val & 0x20) { /* RSC */
|
||||
/* never */
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC RSC interrupt");
|
||||
}
|
||||
if (val & 0x10) { /* XPR */
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
if (cs->tx_skb) {
|
||||
if (cs->tx_skb->len) {
|
||||
icc_fill_fifo(cs);
|
||||
goto afterXPR;
|
||||
} else {
|
||||
dev_kfree_skb_irq(cs->tx_skb);
|
||||
cs->tx_cnt = 0;
|
||||
cs->tx_skb = NULL;
|
||||
}
|
||||
}
|
||||
if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
|
||||
cs->tx_cnt = 0;
|
||||
icc_fill_fifo(cs);
|
||||
} else
|
||||
schedule_event(cs, D_XMTBUFREADY);
|
||||
}
|
||||
afterXPR:
|
||||
if (val & 0x04) { /* CISQ */
|
||||
exval = cs->readisac(cs, ICC_CIR0);
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ICC CIR0 %02X", exval);
|
||||
if (exval & 2) {
|
||||
cs->dc.icc.ph_state = (exval >> 2) & 0xf;
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ph_state change %x", cs->dc.icc.ph_state);
|
||||
schedule_event(cs, D_L1STATECHANGE);
|
||||
}
|
||||
if (exval & 1) {
|
||||
exval = cs->readisac(cs, ICC_CIR1);
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ICC CIR1 %02X", exval);
|
||||
}
|
||||
}
|
||||
if (val & 0x02) { /* SIN */
|
||||
/* never */
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC SIN interrupt");
|
||||
}
|
||||
if (val & 0x01) { /* EXI */
|
||||
exval = cs->readisac(cs, ICC_EXIR);
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC EXIR %02x", exval);
|
||||
if (exval & 0x80) { /* XMR */
|
||||
debugl1(cs, "ICC XMR");
|
||||
printk(KERN_WARNING "HiSax: ICC XMR\n");
|
||||
}
|
||||
if (exval & 0x40) { /* XDU */
|
||||
debugl1(cs, "ICC XDU");
|
||||
printk(KERN_WARNING "HiSax: ICC XDU\n");
|
||||
#ifdef ERROR_STATISTIC
|
||||
cs->err_tx++;
|
||||
#endif
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
if (cs->tx_skb) { /* Restart frame */
|
||||
skb_push(cs->tx_skb, cs->tx_cnt);
|
||||
cs->tx_cnt = 0;
|
||||
icc_fill_fifo(cs);
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: ICC XDU no skb\n");
|
||||
debugl1(cs, "ICC XDU no skb");
|
||||
}
|
||||
}
|
||||
if (exval & 0x04) { /* MOS */
|
||||
v1 = cs->readisac(cs, ICC_MOSR);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ICC MOSR %02x", v1);
|
||||
#if ARCOFI_USE
|
||||
if (v1 & 0x08) {
|
||||
if (!cs->dc.icc.mon_rx) {
|
||||
if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC MON RX out of memory!");
|
||||
cs->dc.icc.mocr &= 0xf0;
|
||||
cs->dc.icc.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
goto afterMONR0;
|
||||
} else
|
||||
cs->dc.icc.mon_rxp = 0;
|
||||
}
|
||||
if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
|
||||
cs->dc.icc.mocr &= 0xf0;
|
||||
cs->dc.icc.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
cs->dc.icc.mon_rxp = 0;
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC MON RX overflow!");
|
||||
goto afterMONR0;
|
||||
}
|
||||
cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR0);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ICC MOR0 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp - 1]);
|
||||
if (cs->dc.icc.mon_rxp == 1) {
|
||||
cs->dc.icc.mocr |= 0x04;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
}
|
||||
}
|
||||
afterMONR0:
|
||||
if (v1 & 0x80) {
|
||||
if (!cs->dc.icc.mon_rx) {
|
||||
if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC MON RX out of memory!");
|
||||
cs->dc.icc.mocr &= 0x0f;
|
||||
cs->dc.icc.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
goto afterMONR1;
|
||||
} else
|
||||
cs->dc.icc.mon_rxp = 0;
|
||||
}
|
||||
if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
|
||||
cs->dc.icc.mocr &= 0x0f;
|
||||
cs->dc.icc.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
cs->dc.icc.mon_rxp = 0;
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ICC MON RX overflow!");
|
||||
goto afterMONR1;
|
||||
}
|
||||
cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR1);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ICC MOR1 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp - 1]);
|
||||
cs->dc.icc.mocr |= 0x40;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
}
|
||||
afterMONR1:
|
||||
if (v1 & 0x04) {
|
||||
cs->dc.icc.mocr &= 0xf0;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
cs->dc.icc.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
schedule_event(cs, D_RX_MON0);
|
||||
}
|
||||
if (v1 & 0x40) {
|
||||
cs->dc.icc.mocr &= 0x0f;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
cs->dc.icc.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
schedule_event(cs, D_RX_MON1);
|
||||
}
|
||||
if (v1 & 0x02) {
|
||||
if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc &&
|
||||
(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) &&
|
||||
!(v1 & 0x08))) {
|
||||
cs->dc.icc.mocr &= 0xf0;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
cs->dc.icc.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
if (cs->dc.icc.mon_txc &&
|
||||
(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
|
||||
schedule_event(cs, D_TX_MON0);
|
||||
goto AfterMOX0;
|
||||
}
|
||||
if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
|
||||
schedule_event(cs, D_TX_MON0);
|
||||
goto AfterMOX0;
|
||||
}
|
||||
cs->writeisac(cs, ICC_MOX0,
|
||||
cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ICC %02x -> MOX0", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp - 1]);
|
||||
}
|
||||
AfterMOX0:
|
||||
if (v1 & 0x20) {
|
||||
if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc &&
|
||||
(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) &&
|
||||
!(v1 & 0x80))) {
|
||||
cs->dc.icc.mocr &= 0x0f;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
cs->dc.icc.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
|
||||
if (cs->dc.icc.mon_txc &&
|
||||
(cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
|
||||
schedule_event(cs, D_TX_MON1);
|
||||
goto AfterMOX1;
|
||||
}
|
||||
if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
|
||||
schedule_event(cs, D_TX_MON1);
|
||||
goto AfterMOX1;
|
||||
}
|
||||
cs->writeisac(cs, ICC_MOX1,
|
||||
cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ICC %02x -> MOX1", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp - 1]);
|
||||
}
|
||||
AfterMOX1: ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ICC_l1hw(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
int val;
|
||||
|
||||
switch (pr) {
|
||||
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);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->tx_skb) {
|
||||
skb_queue_tail(&cs->sq, skb);
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "PH_DATA Queued", 0);
|
||||
#endif
|
||||
} else {
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "PH_DATA", 0);
|
||||
#endif
|
||||
icc_fill_fifo(cs);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | INDICATION):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
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);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
}
|
||||
if (cs->debug & DEB_DLOG_HEX)
|
||||
LogFrame(cs, skb->data, skb->len);
|
||||
if (cs->debug & DEB_DLOG_VERBOSE)
|
||||
dlogframe(cs, skb, 0);
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
|
||||
#endif
|
||||
icc_fill_fifo(cs);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
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 | CONFIRM, NULL);
|
||||
} else
|
||||
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
||||
break;
|
||||
case (HW_RESET | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if ((cs->dc.icc.ph_state == ICC_IND_EI1) ||
|
||||
(cs->dc.icc.ph_state == ICC_IND_DR))
|
||||
ph_command(cs, ICC_CMD_DI);
|
||||
else
|
||||
ph_command(cs, ICC_CMD_RES);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_ENABLE | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
ph_command(cs, ICC_CMD_DI);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_INFO1 | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
ph_command(cs, ICC_CMD_AR);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_INFO3 | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
ph_command(cs, ICC_CMD_AI);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_TESTLOOP | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
val = 0;
|
||||
if (1 & (long) arg)
|
||||
val |= 0x0c;
|
||||
if (2 & (long) arg)
|
||||
val |= 0x3;
|
||||
if (test_bit(HW_IOM1, &cs->HW_Flags)) {
|
||||
/* IOM 1 Mode */
|
||||
if (!val) {
|
||||
cs->writeisac(cs, ICC_SPCR, 0xa);
|
||||
cs->writeisac(cs, ICC_ADF1, 0x2);
|
||||
} else {
|
||||
cs->writeisac(cs, ICC_SPCR, val);
|
||||
cs->writeisac(cs, ICC_ADF1, 0xa);
|
||||
}
|
||||
} else {
|
||||
/* IOM 2 Mode */
|
||||
cs->writeisac(cs, ICC_SPCR, val);
|
||||
if (val)
|
||||
cs->writeisac(cs, ICC_ADF1, 0x8);
|
||||
else
|
||||
cs->writeisac(cs, ICC_ADF1, 0x0);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_DEACTIVATE | RESPONSE):
|
||||
skb_queue_purge(&cs->rq);
|
||||
skb_queue_purge(&cs->sq);
|
||||
if (cs->tx_skb) {
|
||||
dev_kfree_skb_any(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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
break;
|
||||
default:
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "icc_l1hw unknown %04x", pr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setstack_icc(struct PStack *st, struct IsdnCardState *cs)
|
||||
{
|
||||
st->l1.l1hw = ICC_l1hw;
|
||||
}
|
||||
|
||||
static void
|
||||
DC_Close_icc(struct IsdnCardState *cs) {
|
||||
kfree(cs->dc.icc.mon_rx);
|
||||
cs->dc.icc.mon_rx = NULL;
|
||||
kfree(cs->dc.icc.mon_tx);
|
||||
cs->dc.icc.mon_tx = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
dbusy_timer_handler(struct timer_list *t)
|
||||
{
|
||||
struct IsdnCardState *cs = from_timer(cs, t, dbusytimer);
|
||||
struct PStack *stptr;
|
||||
int rbch, star;
|
||||
|
||||
if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
rbch = cs->readisac(cs, ICC_RBCH);
|
||||
star = cs->readisac(cs, ICC_STAR);
|
||||
if (cs->debug)
|
||||
debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
|
||||
rbch, star);
|
||||
if (rbch & ICC_RBCH_XAC) { /* D-Channel Busy */
|
||||
test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
|
||||
stptr = cs->stlist;
|
||||
while (stptr != NULL) {
|
||||
stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
} else {
|
||||
/* discard frame; reset transceiver */
|
||||
test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
|
||||
if (cs->tx_skb) {
|
||||
dev_kfree_skb_any(cs->tx_skb);
|
||||
cs->tx_cnt = 0;
|
||||
cs->tx_skb = NULL;
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: ICC D-Channel Busy no skb\n");
|
||||
debugl1(cs, "D-Channel Busy no skb");
|
||||
}
|
||||
cs->writeisac(cs, ICC_CMDR, 0x01); /* Transmitter reset */
|
||||
cs->irq_func(cs->irq, cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
initicc(struct IsdnCardState *cs)
|
||||
{
|
||||
cs->setstack_d = setstack_icc;
|
||||
cs->DC_Close = DC_Close_icc;
|
||||
cs->dc.icc.mon_tx = NULL;
|
||||
cs->dc.icc.mon_rx = NULL;
|
||||
cs->writeisac(cs, ICC_MASK, 0xff);
|
||||
cs->dc.icc.mocr = 0xaa;
|
||||
if (test_bit(HW_IOM1, &cs->HW_Flags)) {
|
||||
/* IOM 1 Mode */
|
||||
cs->writeisac(cs, ICC_ADF2, 0x0);
|
||||
cs->writeisac(cs, ICC_SPCR, 0xa);
|
||||
cs->writeisac(cs, ICC_ADF1, 0x2);
|
||||
cs->writeisac(cs, ICC_STCR, 0x70);
|
||||
cs->writeisac(cs, ICC_MODE, 0xc9);
|
||||
} else {
|
||||
/* IOM 2 Mode */
|
||||
if (!cs->dc.icc.adf2)
|
||||
cs->dc.icc.adf2 = 0x80;
|
||||
cs->writeisac(cs, ICC_ADF2, cs->dc.icc.adf2);
|
||||
cs->writeisac(cs, ICC_SQXR, 0xa0);
|
||||
cs->writeisac(cs, ICC_SPCR, 0x20);
|
||||
cs->writeisac(cs, ICC_STCR, 0x70);
|
||||
cs->writeisac(cs, ICC_MODE, 0xca);
|
||||
cs->writeisac(cs, ICC_TIMR, 0x00);
|
||||
cs->writeisac(cs, ICC_ADF1, 0x20);
|
||||
}
|
||||
ph_command(cs, ICC_CMD_RES);
|
||||
cs->writeisac(cs, ICC_MASK, 0x0);
|
||||
ph_command(cs, ICC_CMD_DI);
|
||||
}
|
||||
|
||||
void
|
||||
clear_pending_icc_ints(struct IsdnCardState *cs)
|
||||
{
|
||||
int val, eval;
|
||||
|
||||
val = cs->readisac(cs, ICC_STAR);
|
||||
debugl1(cs, "ICC STAR %x", val);
|
||||
val = cs->readisac(cs, ICC_MODE);
|
||||
debugl1(cs, "ICC MODE %x", val);
|
||||
val = cs->readisac(cs, ICC_ADF2);
|
||||
debugl1(cs, "ICC ADF2 %x", val);
|
||||
val = cs->readisac(cs, ICC_ISTA);
|
||||
debugl1(cs, "ICC ISTA %x", val);
|
||||
if (val & 0x01) {
|
||||
eval = cs->readisac(cs, ICC_EXIR);
|
||||
debugl1(cs, "ICC EXIR %x", eval);
|
||||
}
|
||||
val = cs->readisac(cs, ICC_CIR0);
|
||||
debugl1(cs, "ICC CIR0 %x", val);
|
||||
cs->dc.icc.ph_state = (val >> 2) & 0xf;
|
||||
schedule_event(cs, D_L1STATECHANGE);
|
||||
/* Disable all IRQ */
|
||||
cs->writeisac(cs, ICC_MASK, 0xFF);
|
||||
}
|
||||
|
||||
void setup_icc(struct IsdnCardState *cs)
|
||||
{
|
||||
INIT_WORK(&cs->tqueue, icc_bh);
|
||||
timer_setup(&cs->dbusytimer, dbusy_timer_handler, 0);
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/* $Id: icc.h,v 1.4.2.2 2004/01/12 22:52:26 keil Exp $
|
||||
*
|
||||
* ICC specific routines
|
||||
*
|
||||
* Author Matt Henderson & Guy Ellis
|
||||
* Copyright by Traverse Technologies Pty Ltd, www.travers.com.au
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* 1999.7.14 Initial implementation of routines for Siemens ISDN
|
||||
* Communication Controller PEB 2070 based on the ISAC routines
|
||||
* written by Karsten Keil.
|
||||
*/
|
||||
|
||||
/* All Registers original Siemens Spec */
|
||||
|
||||
#define ICC_MASK 0x20
|
||||
#define ICC_ISTA 0x20
|
||||
#define ICC_STAR 0x21
|
||||
#define ICC_CMDR 0x21
|
||||
#define ICC_EXIR 0x24
|
||||
#define ICC_ADF2 0x39
|
||||
#define ICC_SPCR 0x30
|
||||
#define ICC_ADF1 0x38
|
||||
#define ICC_CIR0 0x31
|
||||
#define ICC_CIX0 0x31
|
||||
#define ICC_CIR1 0x33
|
||||
#define ICC_CIX1 0x33
|
||||
#define ICC_STCR 0x37
|
||||
#define ICC_MODE 0x22
|
||||
#define ICC_RSTA 0x27
|
||||
#define ICC_RBCL 0x25
|
||||
#define ICC_RBCH 0x2A
|
||||
#define ICC_TIMR 0x23
|
||||
#define ICC_SQXR 0x3b
|
||||
#define ICC_MOSR 0x3a
|
||||
#define ICC_MOCR 0x3a
|
||||
#define ICC_MOR0 0x32
|
||||
#define ICC_MOX0 0x32
|
||||
#define ICC_MOR1 0x34
|
||||
#define ICC_MOX1 0x34
|
||||
|
||||
#define ICC_RBCH_XAC 0x80
|
||||
|
||||
#define ICC_CMD_TIM 0x0
|
||||
#define ICC_CMD_RES 0x1
|
||||
#define ICC_CMD_DU 0x3
|
||||
#define ICC_CMD_EI1 0x4
|
||||
#define ICC_CMD_SSP 0x5
|
||||
#define ICC_CMD_DT 0x6
|
||||
#define ICC_CMD_AR 0x8
|
||||
#define ICC_CMD_ARL 0xA
|
||||
#define ICC_CMD_AI 0xC
|
||||
#define ICC_CMD_DI 0xF
|
||||
|
||||
#define ICC_IND_DR 0x0
|
||||
#define ICC_IND_FJ 0x2
|
||||
#define ICC_IND_EI1 0x4
|
||||
#define ICC_IND_INT 0x6
|
||||
#define ICC_IND_PU 0x7
|
||||
#define ICC_IND_AR 0x8
|
||||
#define ICC_IND_ARL 0xA
|
||||
#define ICC_IND_AI 0xC
|
||||
#define ICC_IND_AIL 0xE
|
||||
#define ICC_IND_DC 0xF
|
||||
|
||||
extern void ICCVersion(struct IsdnCardState *cs, char *s);
|
||||
extern void initicc(struct IsdnCardState *cs);
|
||||
extern void icc_interrupt(struct IsdnCardState *cs, u_char val);
|
||||
extern void clear_pending_icc_ints(struct IsdnCardState *cs);
|
||||
extern void setup_icc(struct IsdnCardState *);
|
|
@ -1,29 +0,0 @@
|
|||
/* $Id: ipac.h,v 1.7.2.2 2004/01/12 22:52:26 keil Exp $
|
||||
*
|
||||
* IPAC specific defines
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/* All Registers original Siemens Spec */
|
||||
|
||||
#define IPAC_CONF 0xC0
|
||||
#define IPAC_MASK 0xC1
|
||||
#define IPAC_ISTA 0xC1
|
||||
#define IPAC_ID 0xC2
|
||||
#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
|
||||
#define IPAC_POTA2 0xC9
|
||||
#define IPAC_PCFG 0xCA
|
||||
#define IPAC_SCFG 0xCB
|
||||
#define IPAC_TIMR2 0xCC
|
|
@ -1,913 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* IPACX specific routines
|
||||
*
|
||||
* Author Joerg Petersohn
|
||||
* Derived from hisax_isac.c, isac.c, hscx.c and others
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include "hisax_if.h"
|
||||
#include "hisax.h"
|
||||
#include "isdnl1.h"
|
||||
#include "ipacx.h"
|
||||
|
||||
#define DBUSY_TIMER_VALUE 80
|
||||
#define TIMER3_VALUE 7000
|
||||
#define MAX_DFRAME_LEN_L1 300
|
||||
#define B_FIFO_SIZE 64
|
||||
#define D_FIFO_SIZE 32
|
||||
|
||||
|
||||
// ipacx interrupt mask values
|
||||
#define _MASK_IMASK 0x2E // global mask
|
||||
#define _MASKB_IMASK 0x0B
|
||||
#define _MASKD_IMASK 0x03 // all on
|
||||
|
||||
//----------------------------------------------------------
|
||||
// local function declarations
|
||||
//----------------------------------------------------------
|
||||
static void ph_command(struct IsdnCardState *cs, unsigned int command);
|
||||
static inline void cic_int(struct IsdnCardState *cs);
|
||||
static void dch_l2l1(struct PStack *st, int pr, void *arg);
|
||||
static void dbusy_timer_handler(struct timer_list *t);
|
||||
static void dch_empty_fifo(struct IsdnCardState *cs, int count);
|
||||
static void dch_fill_fifo(struct IsdnCardState *cs);
|
||||
static inline void dch_int(struct IsdnCardState *cs);
|
||||
static void dch_setstack(struct PStack *st, struct IsdnCardState *cs);
|
||||
static void dch_init(struct IsdnCardState *cs);
|
||||
static void bch_l2l1(struct PStack *st, int pr, void *arg);
|
||||
static void bch_empty_fifo(struct BCState *bcs, int count);
|
||||
static void bch_fill_fifo(struct BCState *bcs);
|
||||
static void bch_int(struct IsdnCardState *cs, u_char hscx);
|
||||
static void bch_mode(struct BCState *bcs, int mode, int bc);
|
||||
static void bch_close_state(struct BCState *bcs);
|
||||
static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs);
|
||||
static int bch_setstack(struct PStack *st, struct BCState *bcs);
|
||||
static void bch_init(struct IsdnCardState *cs, int hscx);
|
||||
static void clear_pending_ints(struct IsdnCardState *cs);
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Issue Layer 1 command to chip
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
ph_command(struct IsdnCardState *cs, unsigned int command)
|
||||
{
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ph_command (%#x) in (%#x)", command,
|
||||
cs->dc.isac.ph_state);
|
||||
//###################################
|
||||
// printk(KERN_INFO "ph_command (%#x)\n", command);
|
||||
//###################################
|
||||
cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Transceiver interrupt handler
|
||||
//----------------------------------------------------------
|
||||
static inline void
|
||||
cic_int(struct IsdnCardState *cs)
|
||||
{
|
||||
u_char event;
|
||||
|
||||
event = cs->readisac(cs, IPACX_CIR0) >> 4;
|
||||
if (cs->debug & L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event);
|
||||
//#########################################
|
||||
// printk(KERN_INFO "cic_int(%x)\n", event);
|
||||
//#########################################
|
||||
cs->dc.isac.ph_state = event;
|
||||
schedule_event(cs, D_L1STATECHANGE);
|
||||
}
|
||||
|
||||
//==========================================================
|
||||
// D channel functions
|
||||
//==========================================================
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Command entry point
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
dch_l2l1(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
|
||||
struct sk_buff *skb = arg;
|
||||
u_char cda1_cr;
|
||||
|
||||
switch (pr) {
|
||||
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
|
||||
if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA Queued", 0);
|
||||
#endif
|
||||
} else {
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
#ifdef L2FRAME_DEBUG
|
||||
if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA", 0);
|
||||
#endif
|
||||
dch_fill_fifo(cs);
|
||||
}
|
||||
break;
|
||||
|
||||
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->debug & DEB_DLOG_HEX) LogFrame(cs, skb->data, skb->len);
|
||||
if (cs->debug & DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
#ifdef L2FRAME_DEBUG
|
||||
if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
|
||||
#endif
|
||||
dch_fill_fifo(cs);
|
||||
break;
|
||||
|
||||
case (PH_PULL | REQUEST):
|
||||
#ifdef L2FRAME_DEBUG
|
||||
if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL");
|
||||
#endif
|
||||
if (!cs->tx_skb) {
|
||||
clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
||||
st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
|
||||
} else
|
||||
set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
||||
break;
|
||||
|
||||
case (HW_RESET | REQUEST):
|
||||
case (HW_ENABLE | REQUEST):
|
||||
if ((cs->dc.isac.ph_state == IPACX_IND_RES) ||
|
||||
(cs->dc.isac.ph_state == IPACX_IND_DR) ||
|
||||
(cs->dc.isac.ph_state == IPACX_IND_DC))
|
||||
ph_command(cs, IPACX_CMD_TIM);
|
||||
else
|
||||
ph_command(cs, IPACX_CMD_RES);
|
||||
break;
|
||||
|
||||
case (HW_INFO3 | REQUEST):
|
||||
ph_command(cs, IPACX_CMD_AR8);
|
||||
break;
|
||||
|
||||
case (HW_TESTLOOP | REQUEST):
|
||||
cs->writeisac(cs, IPACX_CDA_TSDP10, 0x80); // Timeslot 0 is B1
|
||||
cs->writeisac(cs, IPACX_CDA_TSDP11, 0x81); // Timeslot 0 is B1
|
||||
cda1_cr = cs->readisac(cs, IPACX_CDA1_CR);
|
||||
(void) cs->readisac(cs, IPACX_CDA2_CR);
|
||||
if ((long)arg & 1) { // loop B1
|
||||
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr | 0x0a);
|
||||
}
|
||||
else { // B1 off
|
||||
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr & ~0x0a);
|
||||
}
|
||||
if ((long)arg & 2) { // loop B2
|
||||
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr | 0x14);
|
||||
}
|
||||
else { // B2 off
|
||||
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr & ~0x14);
|
||||
}
|
||||
break;
|
||||
|
||||
case (HW_DEACTIVATE | RESPONSE):
|
||||
skb_queue_purge(&cs->rq);
|
||||
skb_queue_purge(&cs->sq);
|
||||
if (cs->tx_skb) {
|
||||
dev_kfree_skb_any(cs->tx_skb);
|
||||
cs->tx_skb = NULL;
|
||||
}
|
||||
if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
|
||||
del_timer(&cs->dbusytimer);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (cs->debug & L1_DEB_WARN) debugl1(cs, "dch_l2l1 unknown %04x", pr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
dbusy_timer_handler(struct timer_list *t)
|
||||
{
|
||||
struct IsdnCardState *cs = from_timer(cs, t, dbusytimer);
|
||||
struct PStack *st;
|
||||
int rbchd, stard;
|
||||
|
||||
if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
rbchd = cs->readisac(cs, IPACX_RBCHD);
|
||||
stard = cs->readisac(cs, IPACX_STARD);
|
||||
if (cs->debug)
|
||||
debugl1(cs, "D-Channel Busy RBCHD %02x STARD %02x", rbchd, stard);
|
||||
if (!(stard & 0x40)) { // D-Channel Busy
|
||||
set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
|
||||
for (st = cs->stlist; st; st = st->next) {
|
||||
st->l1.l1l2(st, PH_PAUSE | INDICATION, NULL); // flow control on
|
||||
}
|
||||
} else {
|
||||
// seems we lost an interrupt; reset transceiver */
|
||||
clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
|
||||
if (cs->tx_skb) {
|
||||
dev_kfree_skb_any(cs->tx_skb);
|
||||
cs->tx_cnt = 0;
|
||||
cs->tx_skb = NULL;
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
|
||||
debugl1(cs, "D-Channel Busy no skb");
|
||||
}
|
||||
cs->writeisac(cs, IPACX_CMDRD, 0x01); // Tx reset, generates XPR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Fill buffer from receive FIFO
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
dch_empty_fifo(struct IsdnCardState *cs, int count)
|
||||
{
|
||||
u_char *ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "dch_empty_fifo()");
|
||||
|
||||
// message too large, remove
|
||||
if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "dch_empty_fifo() incoming message too large");
|
||||
cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
|
||||
cs->rcvidx = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = cs->rcvbuf + cs->rcvidx;
|
||||
cs->rcvidx += count;
|
||||
|
||||
cs->readisacfifo(cs, ptr, count);
|
||||
cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "dch_empty_fifo() cnt %d", count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Fill transmit FIFO
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
dch_fill_fifo(struct IsdnCardState *cs)
|
||||
{
|
||||
int count;
|
||||
u_char cmd, *ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "dch_fill_fifo()");
|
||||
|
||||
if (!cs->tx_skb) return;
|
||||
count = cs->tx_skb->len;
|
||||
if (count <= 0) return;
|
||||
|
||||
if (count > D_FIFO_SIZE) {
|
||||
count = D_FIFO_SIZE;
|
||||
cmd = 0x08; // XTF
|
||||
} else {
|
||||
cmd = 0x0A; // XTF | XME
|
||||
}
|
||||
|
||||
ptr = cs->tx_skb->data;
|
||||
skb_pull(cs->tx_skb, count);
|
||||
cs->tx_cnt += count;
|
||||
cs->writeisacfifo(cs, ptr, count);
|
||||
cs->writeisac(cs, IPACX_CMDRD, cmd);
|
||||
|
||||
// set timeout for transmission contol
|
||||
if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
debugl1(cs, "dch_fill_fifo dbusytimer running");
|
||||
del_timer(&cs->dbusytimer);
|
||||
}
|
||||
cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
|
||||
add_timer(&cs->dbusytimer);
|
||||
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "dch_fill_fifo() cnt %d", count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// D channel interrupt handler
|
||||
//----------------------------------------------------------
|
||||
static inline void
|
||||
dch_int(struct IsdnCardState *cs)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u_char istad, rstad;
|
||||
int count;
|
||||
|
||||
istad = cs->readisac(cs, IPACX_ISTAD);
|
||||
//##############################################
|
||||
// printk(KERN_WARNING "dch_int(istad=%02x)\n", istad);
|
||||
//##############################################
|
||||
|
||||
if (istad & 0x80) { // RME
|
||||
rstad = cs->readisac(cs, IPACX_RSTAD);
|
||||
if ((rstad & 0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
|
||||
if (!(rstad & 0x80))
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "dch_int(): invalid frame");
|
||||
if ((rstad & 0x40))
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "dch_int(): RDO");
|
||||
if (!(rstad & 0x20))
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "dch_int(): CRC error");
|
||||
cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
|
||||
} else { // received frame ok
|
||||
count = cs->readisac(cs, IPACX_RBCLD);
|
||||
if (count) count--; // RSTAB is last byte
|
||||
count &= D_FIFO_SIZE - 1;
|
||||
if (count == 0) count = D_FIFO_SIZE;
|
||||
dch_empty_fifo(cs, count);
|
||||
if ((count = cs->rcvidx) > 0) {
|
||||
cs->rcvidx = 0;
|
||||
if (!(skb = dev_alloc_skb(count)))
|
||||
printk(KERN_WARNING "HiSax dch_int(): receive out of memory\n");
|
||||
else {
|
||||
skb_put_data(skb, cs->rcvbuf, count);
|
||||
skb_queue_tail(&cs->rq, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
cs->rcvidx = 0;
|
||||
schedule_event(cs, D_RCVBUFREADY);
|
||||
}
|
||||
|
||||
if (istad & 0x40) { // RPF
|
||||
dch_empty_fifo(cs, D_FIFO_SIZE);
|
||||
}
|
||||
|
||||
if (istad & 0x20) { // RFO
|
||||
if (cs->debug & L1_DEB_WARN) debugl1(cs, "dch_int(): RFO");
|
||||
cs->writeisac(cs, IPACX_CMDRD, 0x40); //RRES
|
||||
}
|
||||
|
||||
if (istad & 0x10) { // XPR
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
if (cs->tx_skb) {
|
||||
if (cs->tx_skb->len) {
|
||||
dch_fill_fifo(cs);
|
||||
goto afterXPR;
|
||||
}
|
||||
else {
|
||||
dev_kfree_skb_irq(cs->tx_skb);
|
||||
cs->tx_skb = NULL;
|
||||
cs->tx_cnt = 0;
|
||||
}
|
||||
}
|
||||
if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
|
||||
cs->tx_cnt = 0;
|
||||
dch_fill_fifo(cs);
|
||||
}
|
||||
else {
|
||||
schedule_event(cs, D_XMTBUFREADY);
|
||||
}
|
||||
}
|
||||
afterXPR:
|
||||
|
||||
if (istad & 0x0C) { // XDU or XMR
|
||||
if (cs->debug & L1_DEB_WARN) debugl1(cs, "dch_int(): XDU");
|
||||
if (cs->tx_skb) {
|
||||
skb_push(cs->tx_skb, cs->tx_cnt); // retransmit
|
||||
cs->tx_cnt = 0;
|
||||
dch_fill_fifo(cs);
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
|
||||
debugl1(cs, "ISAC XDU no skb");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
dch_setstack(struct PStack *st, struct IsdnCardState *cs)
|
||||
{
|
||||
st->l1.l1hw = dch_l2l1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
dch_init(struct IsdnCardState *cs)
|
||||
{
|
||||
printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n");
|
||||
|
||||
cs->setstack_d = dch_setstack;
|
||||
|
||||
timer_setup(&cs->dbusytimer, dbusy_timer_handler, 0);
|
||||
|
||||
cs->writeisac(cs, IPACX_TR_CONF0, 0x00); // clear LDD
|
||||
cs->writeisac(cs, IPACX_TR_CONF2, 0x00); // enable transmitter
|
||||
cs->writeisac(cs, IPACX_MODED, 0xC9); // transparent mode 0, RAC, stop/go
|
||||
cs->writeisac(cs, IPACX_MON_CR, 0x00); // disable monitor channel
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
// B channel functions
|
||||
//==========================================================
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Entry point for commands
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
bch_l2l1(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct BCState *bcs = st->l1.bcs;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
|
||||
switch (pr) {
|
||||
case (PH_DATA | REQUEST):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
skb_queue_tail(&bcs->squeue, skb);
|
||||
} else {
|
||||
bcs->tx_skb = skb;
|
||||
set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->hw.hscx.count = 0;
|
||||
bch_fill_fifo(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | INDICATION):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
if (bcs->tx_skb) {
|
||||
printk(KERN_WARNING "HiSax bch_l2l1(): this shouldn't happen\n");
|
||||
} else {
|
||||
set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->tx_skb = skb;
|
||||
bcs->hw.hscx.count = 0;
|
||||
bch_fill_fifo(bcs);
|
||||
}
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | REQUEST):
|
||||
if (!bcs->tx_skb) {
|
||||
clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
||||
st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
|
||||
} else
|
||||
set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
||||
break;
|
||||
case (PH_ACTIVATE | REQUEST):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
set_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
bch_mode(bcs, st->l1.mode, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | REQUEST):
|
||||
l1_msg_b(st, pr, arg);
|
||||
break;
|
||||
case (PH_DEACTIVATE | CONFIRM):
|
||||
spin_lock_irqsave(&bcs->cs->lock, flags);
|
||||
clear_bit(BC_FLG_ACTIV, &bcs->Flag);
|
||||
clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bch_mode(bcs, 0, st->l1.bc);
|
||||
spin_unlock_irqrestore(&bcs->cs->lock, flags);
|
||||
st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Read B channel fifo to receive buffer
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
bch_empty_fifo(struct BCState *bcs, int count)
|
||||
{
|
||||
u_char *ptr, hscx;
|
||||
struct IsdnCardState *cs;
|
||||
int cnt;
|
||||
|
||||
cs = bcs->cs;
|
||||
hscx = bcs->hw.hscx.hscx;
|
||||
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
|
||||
debugl1(cs, "bch_empty_fifo()");
|
||||
|
||||
// message too large, remove
|
||||
if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "bch_empty_fifo() incoming packet too large");
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
|
||||
cnt = count;
|
||||
while (cnt--) *ptr++ = cs->BC_Read_Reg(cs, hscx, IPACX_RFIFOB);
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
|
||||
|
||||
ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
|
||||
bcs->hw.hscx.rcvidx += count;
|
||||
|
||||
if (cs->debug & L1_DEB_HSCX_FIFO) {
|
||||
char *t = bcs->blog;
|
||||
|
||||
t += sprintf(t, "bch_empty_fifo() B-%d cnt %d", hscx, count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", bcs->blog);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Fill buffer to transmit FIFO
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
bch_fill_fifo(struct BCState *bcs)
|
||||
{
|
||||
struct IsdnCardState *cs;
|
||||
int more, count, cnt;
|
||||
u_char *ptr, *p, hscx;
|
||||
|
||||
cs = bcs->cs;
|
||||
if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
|
||||
debugl1(cs, "bch_fill_fifo()");
|
||||
|
||||
if (!bcs->tx_skb) return;
|
||||
if (bcs->tx_skb->len <= 0) return;
|
||||
|
||||
hscx = bcs->hw.hscx.hscx;
|
||||
more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
|
||||
if (bcs->tx_skb->len > B_FIFO_SIZE) {
|
||||
more = 1;
|
||||
count = B_FIFO_SIZE;
|
||||
} else {
|
||||
count = bcs->tx_skb->len;
|
||||
}
|
||||
cnt = count;
|
||||
|
||||
p = ptr = bcs->tx_skb->data;
|
||||
skb_pull(bcs->tx_skb, count);
|
||||
bcs->tx_cnt -= count;
|
||||
bcs->hw.hscx.count += count;
|
||||
while (cnt--) cs->BC_Write_Reg(cs, hscx, IPACX_XFIFOB, *p++);
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, (more ? 0x08 : 0x0a));
|
||||
|
||||
if (cs->debug & L1_DEB_HSCX_FIFO) {
|
||||
char *t = bcs->blog;
|
||||
|
||||
t += sprintf(t, "%s() B-%d cnt %d", __func__, hscx, count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", bcs->blog);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// B channel interrupt handler
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
bch_int(struct IsdnCardState *cs, u_char hscx)
|
||||
{
|
||||
u_char istab;
|
||||
struct BCState *bcs;
|
||||
struct sk_buff *skb;
|
||||
int count;
|
||||
u_char rstab;
|
||||
|
||||
bcs = cs->bcs + hscx;
|
||||
istab = cs->BC_Read_Reg(cs, hscx, IPACX_ISTAB);
|
||||
//##############################################
|
||||
// printk(KERN_WARNING "bch_int(istab=%02x)\n", istab);
|
||||
//##############################################
|
||||
if (!test_bit(BC_FLG_INIT, &bcs->Flag)) return;
|
||||
|
||||
if (istab & 0x80) { // RME
|
||||
rstab = cs->BC_Read_Reg(cs, hscx, IPACX_RSTAB);
|
||||
if ((rstab & 0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
|
||||
if (!(rstab & 0x80))
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "bch_int() B-%d: invalid frame", hscx);
|
||||
if ((rstab & 0x40) && (bcs->mode != L1_MODE_NULL))
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "bch_int() B-%d: RDO mode=%d", hscx, bcs->mode);
|
||||
if (!(rstab & 0x20))
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "bch_int() B-%d: CRC error", hscx);
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
|
||||
}
|
||||
else { // received frame ok
|
||||
count = cs->BC_Read_Reg(cs, hscx, IPACX_RBCLB) & (B_FIFO_SIZE - 1);
|
||||
if (count == 0) count = B_FIFO_SIZE;
|
||||
bch_empty_fifo(bcs, count);
|
||||
if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
|
||||
if (cs->debug & L1_DEB_HSCX_FIFO)
|
||||
debugl1(cs, "bch_int Frame %d", count);
|
||||
if (!(skb = dev_alloc_skb(count)))
|
||||
printk(KERN_WARNING "HiSax bch_int(): receive frame out of memory\n");
|
||||
else {
|
||||
skb_put_data(skb, bcs->hw.hscx.rcvbuf,
|
||||
count);
|
||||
skb_queue_tail(&bcs->rqueue, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
schedule_event(bcs, B_RCVBUFREADY);
|
||||
}
|
||||
|
||||
if (istab & 0x40) { // RPF
|
||||
bch_empty_fifo(bcs, B_FIFO_SIZE);
|
||||
|
||||
if (bcs->mode == L1_MODE_TRANS) { // queue every chunk
|
||||
// receive transparent audio data
|
||||
if (!(skb = dev_alloc_skb(B_FIFO_SIZE)))
|
||||
printk(KERN_WARNING "HiSax bch_int(): receive transparent out of memory\n");
|
||||
else {
|
||||
skb_put_data(skb, bcs->hw.hscx.rcvbuf,
|
||||
B_FIFO_SIZE);
|
||||
skb_queue_tail(&bcs->rqueue, skb);
|
||||
}
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
schedule_event(bcs, B_RCVBUFREADY);
|
||||
}
|
||||
}
|
||||
|
||||
if (istab & 0x20) { // RFO
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "bch_int() B-%d: RFO error", hscx);
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x40); // RRES
|
||||
}
|
||||
|
||||
if (istab & 0x10) { // XPR
|
||||
if (bcs->tx_skb) {
|
||||
if (bcs->tx_skb->len) {
|
||||
bch_fill_fifo(bcs);
|
||||
goto afterXPR;
|
||||
} else {
|
||||
if (test_bit(FLG_LLI_L1WAKEUP, &bcs->st->lli.flag) &&
|
||||
(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
|
||||
u_long flags;
|
||||
spin_lock_irqsave(&bcs->aclock, flags);
|
||||
bcs->ackcnt += bcs->hw.hscx.count;
|
||||
spin_unlock_irqrestore(&bcs->aclock, flags);
|
||||
schedule_event(bcs, B_ACKPENDING);
|
||||
}
|
||||
}
|
||||
dev_kfree_skb_irq(bcs->tx_skb);
|
||||
bcs->hw.hscx.count = 0;
|
||||
bcs->tx_skb = NULL;
|
||||
}
|
||||
if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
|
||||
bcs->hw.hscx.count = 0;
|
||||
set_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bch_fill_fifo(bcs);
|
||||
} else {
|
||||
clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
schedule_event(bcs, B_XMTBUFREADY);
|
||||
}
|
||||
}
|
||||
afterXPR:
|
||||
|
||||
if (istab & 0x04) { // XDU
|
||||
if (bcs->mode == L1_MODE_TRANS) {
|
||||
bch_fill_fifo(bcs);
|
||||
}
|
||||
else {
|
||||
if (bcs->tx_skb) { // restart transmitting the whole frame
|
||||
skb_push(bcs->tx_skb, bcs->hw.hscx.count);
|
||||
bcs->tx_cnt += bcs->hw.hscx.count;
|
||||
bcs->hw.hscx.count = 0;
|
||||
}
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x01); // XRES
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "bch_int() B-%d XDU error", hscx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
bch_mode(struct BCState *bcs, int mode, int bc)
|
||||
{
|
||||
struct IsdnCardState *cs = bcs->cs;
|
||||
int hscx = bcs->hw.hscx.hscx;
|
||||
|
||||
bc = bc ? 1 : 0; // in case bc is greater than 1
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "mode_bch() switch B-%d mode %d chan %d", hscx, mode, bc);
|
||||
bcs->mode = mode;
|
||||
bcs->channel = bc;
|
||||
|
||||
// map controller to according timeslot
|
||||
if (!hscx)
|
||||
{
|
||||
cs->writeisac(cs, IPACX_BCHA_TSDP_BC1, 0x80 | bc);
|
||||
cs->writeisac(cs, IPACX_BCHA_CR, 0x88);
|
||||
}
|
||||
else
|
||||
{
|
||||
cs->writeisac(cs, IPACX_BCHB_TSDP_BC1, 0x80 | bc);
|
||||
cs->writeisac(cs, IPACX_BCHB_CR, 0x88);
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case (L1_MODE_NULL):
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC0); // rec off
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x30); // std adj.
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, 0xFF); // ints off
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
|
||||
break;
|
||||
case (L1_MODE_TRANS):
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0x88); // ext transp mode
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x00); // xxx00000
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
|
||||
break;
|
||||
case (L1_MODE_HDLC):
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC8); // transp mode 0
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x01); // idle=hdlc flags crc enabled
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
|
||||
cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
bch_close_state(struct BCState *bcs)
|
||||
{
|
||||
bch_mode(bcs, 0, bcs->channel);
|
||||
if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
|
||||
kfree(bcs->hw.hscx.rcvbuf);
|
||||
bcs->hw.hscx.rcvbuf = NULL;
|
||||
kfree(bcs->blog);
|
||||
bcs->blog = NULL;
|
||||
skb_queue_purge(&bcs->rqueue);
|
||||
skb_queue_purge(&bcs->squeue);
|
||||
if (bcs->tx_skb) {
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static int
|
||||
bch_open_state(struct IsdnCardState *cs, struct BCState *bcs)
|
||||
{
|
||||
if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
|
||||
if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax open_bchstate(): No memory for hscx.rcvbuf\n");
|
||||
clear_bit(BC_FLG_INIT, &bcs->Flag);
|
||||
return (1);
|
||||
}
|
||||
if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax open_bchstate: No memory for bcs->blog\n");
|
||||
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->tx_skb = NULL;
|
||||
clear_bit(BC_FLG_BUSY, &bcs->Flag);
|
||||
bcs->event = 0;
|
||||
bcs->hw.hscx.rcvidx = 0;
|
||||
bcs->tx_cnt = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static int
|
||||
bch_setstack(struct PStack *st, struct BCState *bcs)
|
||||
{
|
||||
bcs->channel = st->l1.bc;
|
||||
if (bch_open_state(st->l1.hardware, bcs)) return (-1);
|
||||
st->l1.bcs = bcs;
|
||||
st->l2.l2l1 = bch_l2l1;
|
||||
setstack_manager(st);
|
||||
bcs->st = st;
|
||||
setstack_l1_B(st);
|
||||
return (0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
bch_init(struct IsdnCardState *cs, int hscx)
|
||||
{
|
||||
cs->bcs[hscx].BC_SetStack = bch_setstack;
|
||||
cs->bcs[hscx].BC_Close = bch_close_state;
|
||||
cs->bcs[hscx].hw.hscx.hscx = hscx;
|
||||
cs->bcs[hscx].cs = cs;
|
||||
bch_mode(cs->bcs + hscx, 0, hscx);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================
|
||||
// Shared functions
|
||||
//==========================================================
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Main interrupt handler
|
||||
//----------------------------------------------------------
|
||||
void
|
||||
interrupt_ipacx(struct IsdnCardState *cs)
|
||||
{
|
||||
u_char ista;
|
||||
|
||||
while ((ista = cs->readisac(cs, IPACX_ISTA))) {
|
||||
//#################################################
|
||||
// printk(KERN_WARNING "interrupt_ipacx(ista=%02x)\n", ista);
|
||||
//#################################################
|
||||
if (ista & 0x80) bch_int(cs, 0); // B channel interrupts
|
||||
if (ista & 0x40) bch_int(cs, 1);
|
||||
|
||||
if (ista & 0x01) dch_int(cs); // D channel
|
||||
if (ista & 0x10) cic_int(cs); // Layer 1 state
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Clears chip interrupt status
|
||||
//----------------------------------------------------------
|
||||
static void
|
||||
clear_pending_ints(struct IsdnCardState *cs)
|
||||
{
|
||||
int ista;
|
||||
|
||||
// all interrupts off
|
||||
cs->writeisac(cs, IPACX_MASK, 0xff);
|
||||
cs->writeisac(cs, IPACX_MASKD, 0xff);
|
||||
cs->BC_Write_Reg(cs, 0, IPACX_MASKB, 0xff);
|
||||
cs->BC_Write_Reg(cs, 1, IPACX_MASKB, 0xff);
|
||||
|
||||
ista = cs->readisac(cs, IPACX_ISTA);
|
||||
if (ista & 0x80) cs->BC_Read_Reg(cs, 0, IPACX_ISTAB);
|
||||
if (ista & 0x40) cs->BC_Read_Reg(cs, 1, IPACX_ISTAB);
|
||||
if (ista & 0x10) cs->readisac(cs, IPACX_CIR0);
|
||||
if (ista & 0x01) cs->readisac(cs, IPACX_ISTAD);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------
|
||||
// Does chip configuration work
|
||||
// Work to do depends on bit mask in part
|
||||
//----------------------------------------------------------
|
||||
void
|
||||
init_ipacx(struct IsdnCardState *cs, int part)
|
||||
{
|
||||
if (part & 1) { // initialise chip
|
||||
//##################################################
|
||||
// printk(KERN_INFO "init_ipacx(%x)\n", part);
|
||||
//##################################################
|
||||
clear_pending_ints(cs);
|
||||
bch_init(cs, 0);
|
||||
bch_init(cs, 1);
|
||||
dch_init(cs);
|
||||
}
|
||||
if (part & 2) { // reenable all interrupts and start chip
|
||||
cs->BC_Write_Reg(cs, 0, IPACX_MASKB, _MASKB_IMASK);
|
||||
cs->BC_Write_Reg(cs, 1, IPACX_MASKB, _MASKB_IMASK);
|
||||
cs->writeisac(cs, IPACX_MASKD, _MASKD_IMASK);
|
||||
cs->writeisac(cs, IPACX_MASK, _MASK_IMASK); // global mask register
|
||||
|
||||
// reset HDLC Transmitters/receivers
|
||||
cs->writeisac(cs, IPACX_CMDRD, 0x41);
|
||||
cs->BC_Write_Reg(cs, 0, IPACX_CMDRB, 0x41);
|
||||
cs->BC_Write_Reg(cs, 1, IPACX_CMDRB, 0x41);
|
||||
ph_command(cs, IPACX_CMD_RES);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------- end of file -----------------------
|
|
@ -1,162 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* IPACX specific defines
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/* All Registers original Siemens Spec */
|
||||
|
||||
#ifndef INCLUDE_IPACX_H
|
||||
#define INCLUDE_IPACX_H
|
||||
|
||||
/* D-channel registers */
|
||||
#define IPACX_RFIFOD 0x00 /* RD */
|
||||
#define IPACX_XFIFOD 0x00 /* WR */
|
||||
#define IPACX_ISTAD 0x20 /* RD */
|
||||
#define IPACX_MASKD 0x20 /* WR */
|
||||
#define IPACX_STARD 0x21 /* RD */
|
||||
#define IPACX_CMDRD 0x21 /* WR */
|
||||
#define IPACX_MODED 0x22 /* RD/WR */
|
||||
#define IPACX_EXMD1 0x23 /* RD/WR */
|
||||
#define IPACX_TIMR1 0x24 /* RD/WR */
|
||||
#define IPACX_SAP1 0x25 /* WR */
|
||||
#define IPACX_SAP2 0x26 /* WR */
|
||||
#define IPACX_RBCLD 0x26 /* RD */
|
||||
#define IPACX_RBCHD 0x27 /* RD */
|
||||
#define IPACX_TEI1 0x27 /* WR */
|
||||
#define IPACX_TEI2 0x28 /* WR */
|
||||
#define IPACX_RSTAD 0x28 /* RD */
|
||||
#define IPACX_TMD 0x29 /* RD/WR */
|
||||
#define IPACX_CIR0 0x2E /* RD */
|
||||
#define IPACX_CIX0 0x2E /* WR */
|
||||
#define IPACX_CIR1 0x2F /* RD */
|
||||
#define IPACX_CIX1 0x2F /* WR */
|
||||
|
||||
/* Transceiver registers */
|
||||
#define IPACX_TR_CONF0 0x30 /* RD/WR */
|
||||
#define IPACX_TR_CONF1 0x31 /* RD/WR */
|
||||
#define IPACX_TR_CONF2 0x32 /* RD/WR */
|
||||
#define IPACX_TR_STA 0x33 /* RD */
|
||||
#define IPACX_TR_CMD 0x34 /* RD/WR */
|
||||
#define IPACX_SQRR1 0x35 /* RD */
|
||||
#define IPACX_SQXR1 0x35 /* WR */
|
||||
#define IPACX_SQRR2 0x36 /* RD */
|
||||
#define IPACX_SQXR2 0x36 /* WR */
|
||||
#define IPACX_SQRR3 0x37 /* RD */
|
||||
#define IPACX_SQXR3 0x37 /* WR */
|
||||
#define IPACX_ISTATR 0x38 /* RD */
|
||||
#define IPACX_MASKTR 0x39 /* RD/WR */
|
||||
#define IPACX_TR_MODE 0x3A /* RD/WR */
|
||||
#define IPACX_ACFG1 0x3C /* RD/WR */
|
||||
#define IPACX_ACFG2 0x3D /* RD/WR */
|
||||
#define IPACX_AOE 0x3E /* RD/WR */
|
||||
#define IPACX_ARX 0x3F /* RD */
|
||||
#define IPACX_ATX 0x3F /* WR */
|
||||
|
||||
/* IOM: Timeslot, DPS, CDA */
|
||||
#define IPACX_CDA10 0x40 /* RD/WR */
|
||||
#define IPACX_CDA11 0x41 /* RD/WR */
|
||||
#define IPACX_CDA20 0x42 /* RD/WR */
|
||||
#define IPACX_CDA21 0x43 /* RD/WR */
|
||||
#define IPACX_CDA_TSDP10 0x44 /* RD/WR */
|
||||
#define IPACX_CDA_TSDP11 0x45 /* RD/WR */
|
||||
#define IPACX_CDA_TSDP20 0x46 /* RD/WR */
|
||||
#define IPACX_CDA_TSDP21 0x47 /* RD/WR */
|
||||
#define IPACX_BCHA_TSDP_BC1 0x48 /* RD/WR */
|
||||
#define IPACX_BCHA_TSDP_BC2 0x49 /* RD/WR */
|
||||
#define IPACX_BCHB_TSDP_BC1 0x4A /* RD/WR */
|
||||
#define IPACX_BCHB_TSDP_BC2 0x4B /* RD/WR */
|
||||
#define IPACX_TR_TSDP_BC1 0x4C /* RD/WR */
|
||||
#define IPACX_TR_TSDP_BC2 0x4D /* RD/WR */
|
||||
#define IPACX_CDA1_CR 0x4E /* RD/WR */
|
||||
#define IPACX_CDA2_CR 0x4F /* RD/WR */
|
||||
|
||||
/* IOM: Contol, Sync transfer, Monitor */
|
||||
#define IPACX_TR_CR 0x50 /* RD/WR */
|
||||
#define IPACX_TRC_CR 0x50 /* RD/WR */
|
||||
#define IPACX_BCHA_CR 0x51 /* RD/WR */
|
||||
#define IPACX_BCHB_CR 0x52 /* RD/WR */
|
||||
#define IPACX_DCI_CR 0x53 /* RD/WR */
|
||||
#define IPACX_DCIC_CR 0x53 /* RD/WR */
|
||||
#define IPACX_MON_CR 0x54 /* RD/WR */
|
||||
#define IPACX_SDS1_CR 0x55 /* RD/WR */
|
||||
#define IPACX_SDS2_CR 0x56 /* RD/WR */
|
||||
#define IPACX_IOM_CR 0x57 /* RD/WR */
|
||||
#define IPACX_STI 0x58 /* RD */
|
||||
#define IPACX_ASTI 0x58 /* WR */
|
||||
#define IPACX_MSTI 0x59 /* RD/WR */
|
||||
#define IPACX_SDS_CONF 0x5A /* RD/WR */
|
||||
#define IPACX_MCDA 0x5B /* RD */
|
||||
#define IPACX_MOR 0x5C /* RD */
|
||||
#define IPACX_MOX 0x5C /* WR */
|
||||
#define IPACX_MOSR 0x5D /* RD */
|
||||
#define IPACX_MOCR 0x5E /* RD/WR */
|
||||
#define IPACX_MSTA 0x5F /* RD */
|
||||
#define IPACX_MCONF 0x5F /* WR */
|
||||
|
||||
/* Interrupt and general registers */
|
||||
#define IPACX_ISTA 0x60 /* RD */
|
||||
#define IPACX_MASK 0x60 /* WR */
|
||||
#define IPACX_AUXI 0x61 /* RD */
|
||||
#define IPACX_AUXM 0x61 /* WR */
|
||||
#define IPACX_MODE1 0x62 /* RD/WR */
|
||||
#define IPACX_MODE2 0x63 /* RD/WR */
|
||||
#define IPACX_ID 0x64 /* RD */
|
||||
#define IPACX_SRES 0x64 /* WR */
|
||||
#define IPACX_TIMR2 0x65 /* RD/WR */
|
||||
|
||||
/* B-channel registers */
|
||||
#define IPACX_OFF_B1 0x70
|
||||
#define IPACX_OFF_B2 0x80
|
||||
|
||||
#define IPACX_ISTAB 0x00 /* RD */
|
||||
#define IPACX_MASKB 0x00 /* WR */
|
||||
#define IPACX_STARB 0x01 /* RD */
|
||||
#define IPACX_CMDRB 0x01 /* WR */
|
||||
#define IPACX_MODEB 0x02 /* RD/WR */
|
||||
#define IPACX_EXMB 0x03 /* RD/WR */
|
||||
#define IPACX_RAH1 0x05 /* WR */
|
||||
#define IPACX_RAH2 0x06 /* WR */
|
||||
#define IPACX_RBCLB 0x06 /* RD */
|
||||
#define IPACX_RBCHB 0x07 /* RD */
|
||||
#define IPACX_RAL1 0x07 /* WR */
|
||||
#define IPACX_RAL2 0x08 /* WR */
|
||||
#define IPACX_RSTAB 0x08 /* RD */
|
||||
#define IPACX_TMB 0x09 /* RD/WR */
|
||||
#define IPACX_RFIFOB 0x0A /*- RD */
|
||||
#define IPACX_XFIFOB 0x0A /*- WR */
|
||||
|
||||
/* Layer 1 Commands */
|
||||
#define IPACX_CMD_TIM 0x0
|
||||
#define IPACX_CMD_RES 0x1
|
||||
#define IPACX_CMD_SSP 0x2
|
||||
#define IPACX_CMD_SCP 0x3
|
||||
#define IPACX_CMD_AR8 0x8
|
||||
#define IPACX_CMD_AR10 0x9
|
||||
#define IPACX_CMD_ARL 0xa
|
||||
#define IPACX_CMD_DI 0xf
|
||||
|
||||
/* Layer 1 Indications */
|
||||
#define IPACX_IND_DR 0x0
|
||||
#define IPACX_IND_RES 0x1
|
||||
#define IPACX_IND_TMA 0x2
|
||||
#define IPACX_IND_SLD 0x3
|
||||
#define IPACX_IND_RSY 0x4
|
||||
#define IPACX_IND_DR6 0x5
|
||||
#define IPACX_IND_PU 0x7
|
||||
#define IPACX_IND_AR 0x8
|
||||
#define IPACX_IND_ARL 0xa
|
||||
#define IPACX_IND_CVR 0xb
|
||||
#define IPACX_IND_AI8 0xc
|
||||
#define IPACX_IND_AI10 0xd
|
||||
#define IPACX_IND_AIL 0xe
|
||||
#define IPACX_IND_DC 0xf
|
||||
|
||||
extern void init_ipacx(struct IsdnCardState *, int);
|
||||
extern void interrupt_ipacx(struct IsdnCardState *);
|
||||
extern void setup_isac(struct IsdnCardState *);
|
||||
|
||||
#endif
|
|
@ -1,681 +0,0 @@
|
|||
/* $Id: isac.c,v 1.31.2.3 2004/01/13 14:31:25 keil Exp $
|
||||
*
|
||||
* ISAC specific routines
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* For changes and modifications please read
|
||||
* Documentation/isdn/HiSax.cert
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "arcofi.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DBUSY_TIMER_VALUE 80
|
||||
#define ARCOFI_USE 1
|
||||
|
||||
static char *ISACVer[] =
|
||||
{"2086/2186 V1.1", "2085 B1", "2085 B2",
|
||||
"2085 V2.3"};
|
||||
|
||||
void ISACVersion(struct IsdnCardState *cs, char *s)
|
||||
{
|
||||
int val;
|
||||
|
||||
val = cs->readisac(cs, ISAC_RBCH);
|
||||
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)
|
||||
debugl1(cs, "ph_command %x", command);
|
||||
cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
isac_new_ph(struct IsdnCardState *cs)
|
||||
{
|
||||
switch (cs->dc.isac.ph_state) {
|
||||
case (ISAC_IND_RS):
|
||||
case (ISAC_IND_EI):
|
||||
ph_command(cs, ISAC_CMD_DUI);
|
||||
l1_msg(cs, HW_RESET | INDICATION, NULL);
|
||||
break;
|
||||
case (ISAC_IND_DID):
|
||||
l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
|
||||
break;
|
||||
case (ISAC_IND_DR):
|
||||
l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
|
||||
break;
|
||||
case (ISAC_IND_PU):
|
||||
l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
|
||||
break;
|
||||
case (ISAC_IND_RSY):
|
||||
l1_msg(cs, HW_RSYNC | INDICATION, NULL);
|
||||
break;
|
||||
case (ISAC_IND_ARD):
|
||||
l1_msg(cs, HW_INFO2 | INDICATION, NULL);
|
||||
break;
|
||||
case (ISAC_IND_AI8):
|
||||
l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
|
||||
break;
|
||||
case (ISAC_IND_AI10):
|
||||
l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isac_bh(struct work_struct *work)
|
||||
{
|
||||
struct IsdnCardState *cs =
|
||||
container_of(work, struct IsdnCardState, tqueue);
|
||||
struct PStack *stptr;
|
||||
|
||||
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 | CONFIRM, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
}
|
||||
if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
|
||||
isac_new_ph(cs);
|
||||
if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
|
||||
DChannel_proc_rcv(cs);
|
||||
if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
|
||||
DChannel_proc_xmt(cs);
|
||||
#if ARCOFI_USE
|
||||
if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
|
||||
return;
|
||||
if (test_and_clear_bit(D_RX_MON1, &cs->event))
|
||||
arcofi_fsm(cs, ARCOFI_RX_END, NULL);
|
||||
if (test_and_clear_bit(D_TX_MON1, &cs->event))
|
||||
arcofi_fsm(cs, ARCOFI_TX_END, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
isac_empty_fifo(struct IsdnCardState *cs, int count)
|
||||
{
|
||||
u_char *ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "isac_empty_fifo");
|
||||
|
||||
if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "isac_empty_fifo overrun %d",
|
||||
cs->rcvidx + count);
|
||||
cs->writeisac(cs, ISAC_CMDR, 0x80);
|
||||
cs->rcvidx = 0;
|
||||
return;
|
||||
}
|
||||
ptr = cs->rcvbuf + cs->rcvidx;
|
||||
cs->rcvidx += count;
|
||||
cs->readisacfifo(cs, ptr, count);
|
||||
cs->writeisac(cs, ISAC_CMDR, 0x80);
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "isac_empty_fifo cnt %d", count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isac_fill_fifo(struct IsdnCardState *cs)
|
||||
{
|
||||
int count, more;
|
||||
u_char *ptr;
|
||||
|
||||
if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
|
||||
debugl1(cs, "isac_fill_fifo");
|
||||
|
||||
if (!cs->tx_skb)
|
||||
return;
|
||||
|
||||
count = cs->tx_skb->len;
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
more = 0;
|
||||
if (count > 32) {
|
||||
more = !0;
|
||||
count = 32;
|
||||
}
|
||||
ptr = cs->tx_skb->data;
|
||||
skb_pull(cs->tx_skb, count);
|
||||
cs->tx_cnt += count;
|
||||
cs->writeisacfifo(cs, ptr, count);
|
||||
cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa);
|
||||
if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
debugl1(cs, "isac_fill_fifo dbusytimer running");
|
||||
del_timer(&cs->dbusytimer);
|
||||
}
|
||||
cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
|
||||
add_timer(&cs->dbusytimer);
|
||||
if (cs->debug & L1_DEB_ISAC_FIFO) {
|
||||
char *t = cs->dlog;
|
||||
|
||||
t += sprintf(t, "isac_fill_fifo cnt %d", count);
|
||||
QuickHex(t, ptr, count);
|
||||
debugl1(cs, "%s", cs->dlog);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isac_interrupt(struct IsdnCardState *cs, u_char val)
|
||||
{
|
||||
u_char exval, v1;
|
||||
struct sk_buff *skb;
|
||||
unsigned int count;
|
||||
|
||||
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) {
|
||||
if (exval & 0x40) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC RDO");
|
||||
#ifdef ERROR_STATISTIC
|
||||
cs->err_rx++;
|
||||
#endif
|
||||
}
|
||||
if (!(exval & 0x20)) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC CRC error");
|
||||
#ifdef ERROR_STATISTIC
|
||||
cs->err_crc++;
|
||||
#endif
|
||||
}
|
||||
cs->writeisac(cs, ISAC_CMDR, 0x80);
|
||||
} else {
|
||||
count = cs->readisac(cs, ISAC_RBCL) & 0x1f;
|
||||
if (count == 0)
|
||||
count = 32;
|
||||
isac_empty_fifo(cs, count);
|
||||
count = cs->rcvidx;
|
||||
if (count > 0) {
|
||||
cs->rcvidx = 0;
|
||||
skb = alloc_skb(count, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
printk(KERN_WARNING "HiSax: D receive out of memory\n");
|
||||
else {
|
||||
skb_put_data(skb, cs->rcvbuf, count);
|
||||
skb_queue_tail(&cs->rq, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
cs->rcvidx = 0;
|
||||
schedule_event(cs, D_RCVBUFREADY);
|
||||
}
|
||||
if (val & 0x40) { /* RPF */
|
||||
isac_empty_fifo(cs, 32);
|
||||
}
|
||||
if (val & 0x20) { /* RSC */
|
||||
/* never */
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC RSC interrupt");
|
||||
}
|
||||
if (val & 0x10) { /* XPR */
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
if (cs->tx_skb) {
|
||||
if (cs->tx_skb->len) {
|
||||
isac_fill_fifo(cs);
|
||||
goto afterXPR;
|
||||
} else {
|
||||
dev_kfree_skb_irq(cs->tx_skb);
|
||||
cs->tx_cnt = 0;
|
||||
cs->tx_skb = NULL;
|
||||
}
|
||||
}
|
||||
cs->tx_skb = skb_dequeue(&cs->sq);
|
||||
if (cs->tx_skb) {
|
||||
cs->tx_cnt = 0;
|
||||
isac_fill_fifo(cs);
|
||||
} else
|
||||
schedule_event(cs, D_XMTBUFREADY);
|
||||
}
|
||||
afterXPR:
|
||||
if (val & 0x04) { /* CISQ */
|
||||
exval = cs->readisac(cs, ISAC_CIR0);
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ISAC CIR0 %02X", exval);
|
||||
if (exval & 2) {
|
||||
cs->dc.isac.ph_state = (exval >> 2) & 0xf;
|
||||
if (cs->debug & L1_DEB_ISAC)
|
||||
debugl1(cs, "ph_state change %x", cs->dc.isac.ph_state);
|
||||
schedule_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);
|
||||
}
|
||||
}
|
||||
if (val & 0x02) { /* SIN */
|
||||
/* never */
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC SIN interrupt");
|
||||
}
|
||||
if (val & 0x01) { /* EXI */
|
||||
exval = cs->readisac(cs, ISAC_EXIR);
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC EXIR %02x", exval);
|
||||
if (exval & 0x80) { /* XMR */
|
||||
debugl1(cs, "ISAC XMR");
|
||||
printk(KERN_WARNING "HiSax: ISAC XMR\n");
|
||||
}
|
||||
if (exval & 0x40) { /* XDU */
|
||||
debugl1(cs, "ISAC XDU");
|
||||
printk(KERN_WARNING "HiSax: ISAC XDU\n");
|
||||
#ifdef ERROR_STATISTIC
|
||||
cs->err_tx++;
|
||||
#endif
|
||||
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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
if (cs->tx_skb) { /* Restart frame */
|
||||
skb_push(cs->tx_skb, cs->tx_cnt);
|
||||
cs->tx_cnt = 0;
|
||||
isac_fill_fifo(cs);
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
|
||||
debugl1(cs, "ISAC XDU no skb");
|
||||
}
|
||||
}
|
||||
if (exval & 0x04) { /* MOS */
|
||||
v1 = cs->readisac(cs, ISAC_MOSR);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ISAC MOSR %02x", v1);
|
||||
#if ARCOFI_USE
|
||||
if (v1 & 0x08) {
|
||||
if (!cs->dc.isac.mon_rx) {
|
||||
cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
|
||||
if (!cs->dc.isac.mon_rx) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC MON RX out of memory!");
|
||||
cs->dc.isac.mocr &= 0xf0;
|
||||
cs->dc.isac.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
goto afterMONR0;
|
||||
} else
|
||||
cs->dc.isac.mon_rxp = 0;
|
||||
}
|
||||
if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
|
||||
cs->dc.isac.mocr &= 0xf0;
|
||||
cs->dc.isac.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
cs->dc.isac.mon_rxp = 0;
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC MON RX overflow!");
|
||||
goto afterMONR0;
|
||||
}
|
||||
cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ISAC MOR0 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp - 1]);
|
||||
if (cs->dc.isac.mon_rxp == 1) {
|
||||
cs->dc.isac.mocr |= 0x04;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
}
|
||||
}
|
||||
afterMONR0:
|
||||
if (v1 & 0x80) {
|
||||
if (!cs->dc.isac.mon_rx) {
|
||||
cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
|
||||
if (!cs->dc.isac.mon_rx) {
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC MON RX out of memory!");
|
||||
cs->dc.isac.mocr &= 0x0f;
|
||||
cs->dc.isac.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
goto afterMONR1;
|
||||
} else
|
||||
cs->dc.isac.mon_rxp = 0;
|
||||
}
|
||||
if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
|
||||
cs->dc.isac.mocr &= 0x0f;
|
||||
cs->dc.isac.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
cs->dc.isac.mon_rxp = 0;
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "ISAC MON RX overflow!");
|
||||
goto afterMONR1;
|
||||
}
|
||||
cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ISAC MOR1 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp - 1]);
|
||||
cs->dc.isac.mocr |= 0x40;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
}
|
||||
afterMONR1:
|
||||
if (v1 & 0x04) {
|
||||
cs->dc.isac.mocr &= 0xf0;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
cs->dc.isac.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
schedule_event(cs, D_RX_MON0);
|
||||
}
|
||||
if (v1 & 0x40) {
|
||||
cs->dc.isac.mocr &= 0x0f;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
cs->dc.isac.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
schedule_event(cs, D_RX_MON1);
|
||||
}
|
||||
if (v1 & 0x02) {
|
||||
if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc &&
|
||||
(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) &&
|
||||
!(v1 & 0x08))) {
|
||||
cs->dc.isac.mocr &= 0xf0;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
cs->dc.isac.mocr |= 0x0a;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
if (cs->dc.isac.mon_txc &&
|
||||
(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
|
||||
schedule_event(cs, D_TX_MON0);
|
||||
goto AfterMOX0;
|
||||
}
|
||||
if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
|
||||
schedule_event(cs, D_TX_MON0);
|
||||
goto AfterMOX0;
|
||||
}
|
||||
cs->writeisac(cs, ISAC_MOX0,
|
||||
cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ISAC %02x -> MOX0", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp - 1]);
|
||||
}
|
||||
AfterMOX0:
|
||||
if (v1 & 0x20) {
|
||||
if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc &&
|
||||
(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) &&
|
||||
!(v1 & 0x80))) {
|
||||
cs->dc.isac.mocr &= 0x0f;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
cs->dc.isac.mocr |= 0xa0;
|
||||
cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
|
||||
if (cs->dc.isac.mon_txc &&
|
||||
(cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
|
||||
schedule_event(cs, D_TX_MON1);
|
||||
goto AfterMOX1;
|
||||
}
|
||||
if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
|
||||
schedule_event(cs, D_TX_MON1);
|
||||
goto AfterMOX1;
|
||||
}
|
||||
cs->writeisac(cs, ISAC_MOX1,
|
||||
cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
|
||||
if (cs->debug & L1_DEB_MONITOR)
|
||||
debugl1(cs, "ISAC %02x -> MOX1", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp - 1]);
|
||||
}
|
||||
AfterMOX1:;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ISAC_l1hw(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
|
||||
struct sk_buff *skb = arg;
|
||||
u_long flags;
|
||||
int val;
|
||||
|
||||
switch (pr) {
|
||||
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);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->tx_skb) {
|
||||
skb_queue_tail(&cs->sq, skb);
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "PH_DATA Queued", 0);
|
||||
#endif
|
||||
} else {
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "PH_DATA", 0);
|
||||
#endif
|
||||
isac_fill_fifo(cs);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (PH_PULL | INDICATION):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
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);
|
||||
} else {
|
||||
if (cs->debug & DEB_DLOG_HEX)
|
||||
LogFrame(cs, skb->data, skb->len);
|
||||
if (cs->debug & DEB_DLOG_VERBOSE)
|
||||
dlogframe(cs, skb, 0);
|
||||
cs->tx_skb = skb;
|
||||
cs->tx_cnt = 0;
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
|
||||
#endif
|
||||
isac_fill_fifo(cs);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
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 | CONFIRM, NULL);
|
||||
} else
|
||||
test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
|
||||
break;
|
||||
case (HW_RESET | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if ((cs->dc.isac.ph_state == ISAC_IND_EI) ||
|
||||
(cs->dc.isac.ph_state == ISAC_IND_DR) ||
|
||||
(cs->dc.isac.ph_state == ISAC_IND_RS))
|
||||
ph_command(cs, ISAC_CMD_TIM);
|
||||
else
|
||||
ph_command(cs, ISAC_CMD_RS);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_ENABLE | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
ph_command(cs, ISAC_CMD_TIM);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_INFO3 | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
ph_command(cs, ISAC_CMD_AR8);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_TESTLOOP | REQUEST):
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
val = 0;
|
||||
if (1 & (long) arg)
|
||||
val |= 0x0c;
|
||||
if (2 & (long) arg)
|
||||
val |= 0x3;
|
||||
if (test_bit(HW_IOM1, &cs->HW_Flags)) {
|
||||
/* IOM 1 Mode */
|
||||
if (!val) {
|
||||
cs->writeisac(cs, ISAC_SPCR, 0xa);
|
||||
cs->writeisac(cs, ISAC_ADF1, 0x2);
|
||||
} else {
|
||||
cs->writeisac(cs, ISAC_SPCR, val);
|
||||
cs->writeisac(cs, ISAC_ADF1, 0xa);
|
||||
}
|
||||
} else {
|
||||
/* IOM 2 Mode */
|
||||
cs->writeisac(cs, ISAC_SPCR, val);
|
||||
if (val)
|
||||
cs->writeisac(cs, ISAC_ADF1, 0x8);
|
||||
else
|
||||
cs->writeisac(cs, ISAC_ADF1, 0x0);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
break;
|
||||
case (HW_DEACTIVATE | RESPONSE):
|
||||
skb_queue_purge(&cs->rq);
|
||||
skb_queue_purge(&cs->sq);
|
||||
if (cs->tx_skb) {
|
||||
dev_kfree_skb_any(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))
|
||||
schedule_event(cs, D_CLEARBUSY);
|
||||
break;
|
||||
default:
|
||||
if (cs->debug & L1_DEB_WARN)
|
||||
debugl1(cs, "isac_l1hw unknown %04x", pr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
setstack_isac(struct PStack *st, struct IsdnCardState *cs)
|
||||
{
|
||||
st->l1.l1hw = ISAC_l1hw;
|
||||
}
|
||||
|
||||
static void
|
||||
DC_Close_isac(struct IsdnCardState *cs)
|
||||
{
|
||||
kfree(cs->dc.isac.mon_rx);
|
||||
cs->dc.isac.mon_rx = NULL;
|
||||
kfree(cs->dc.isac.mon_tx);
|
||||
cs->dc.isac.mon_tx = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
dbusy_timer_handler(struct timer_list *t)
|
||||
{
|
||||
struct IsdnCardState *cs = from_timer(cs, t, dbusytimer);
|
||||
struct PStack *stptr;
|
||||
int rbch, star;
|
||||
|
||||
if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
|
||||
rbch = cs->readisac(cs, ISAC_RBCH);
|
||||
star = cs->readisac(cs, ISAC_STAR);
|
||||
if (cs->debug)
|
||||
debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
|
||||
rbch, star);
|
||||
if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */
|
||||
test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
|
||||
stptr = cs->stlist;
|
||||
while (stptr != NULL) {
|
||||
stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
|
||||
stptr = stptr->next;
|
||||
}
|
||||
} else {
|
||||
/* discard frame; reset transceiver */
|
||||
test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
|
||||
if (cs->tx_skb) {
|
||||
dev_kfree_skb_any(cs->tx_skb);
|
||||
cs->tx_cnt = 0;
|
||||
cs->tx_skb = NULL;
|
||||
} else {
|
||||
printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
|
||||
debugl1(cs, "D-Channel Busy no skb");
|
||||
}
|
||||
cs->writeisac(cs, ISAC_CMDR, 0x01); /* Transmitter reset */
|
||||
cs->irq_func(cs->irq, cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initisac(struct IsdnCardState *cs)
|
||||
{
|
||||
cs->setstack_d = setstack_isac;
|
||||
cs->DC_Close = DC_Close_isac;
|
||||
cs->dc.isac.mon_tx = NULL;
|
||||
cs->dc.isac.mon_rx = NULL;
|
||||
cs->writeisac(cs, ISAC_MASK, 0xff);
|
||||
cs->dc.isac.mocr = 0xaa;
|
||||
if (test_bit(HW_IOM1, &cs->HW_Flags)) {
|
||||
/* IOM 1 Mode */
|
||||
cs->writeisac(cs, ISAC_ADF2, 0x0);
|
||||
cs->writeisac(cs, ISAC_SPCR, 0xa);
|
||||
cs->writeisac(cs, ISAC_ADF1, 0x2);
|
||||
cs->writeisac(cs, ISAC_STCR, 0x70);
|
||||
cs->writeisac(cs, ISAC_MODE, 0xc9);
|
||||
} else {
|
||||
/* IOM 2 Mode */
|
||||
if (!cs->dc.isac.adf2)
|
||||
cs->dc.isac.adf2 = 0x80;
|
||||
cs->writeisac(cs, ISAC_ADF2, cs->dc.isac.adf2);
|
||||
cs->writeisac(cs, ISAC_SQXR, 0x2f);
|
||||
cs->writeisac(cs, ISAC_SPCR, 0x00);
|
||||
cs->writeisac(cs, ISAC_STCR, 0x70);
|
||||
cs->writeisac(cs, ISAC_MODE, 0xc9);
|
||||
cs->writeisac(cs, ISAC_TIMR, 0x00);
|
||||
cs->writeisac(cs, ISAC_ADF1, 0x00);
|
||||
}
|
||||
ph_command(cs, ISAC_CMD_RS);
|
||||
cs->writeisac(cs, ISAC_MASK, 0x0);
|
||||
}
|
||||
|
||||
void clear_pending_isac_ints(struct IsdnCardState *cs)
|
||||
{
|
||||
int val, eval;
|
||||
|
||||
val = cs->readisac(cs, ISAC_STAR);
|
||||
debugl1(cs, "ISAC STAR %x", val);
|
||||
val = cs->readisac(cs, ISAC_MODE);
|
||||
debugl1(cs, "ISAC MODE %x", val);
|
||||
val = cs->readisac(cs, ISAC_ADF2);
|
||||
debugl1(cs, "ISAC ADF2 %x", val);
|
||||
val = cs->readisac(cs, ISAC_ISTA);
|
||||
debugl1(cs, "ISAC ISTA %x", val);
|
||||
if (val & 0x01) {
|
||||
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->dc.isac.ph_state = (val >> 2) & 0xf;
|
||||
schedule_event(cs, D_L1STATECHANGE);
|
||||
/* Disable all IRQ */
|
||||
cs->writeisac(cs, ISAC_MASK, 0xFF);
|
||||
}
|
||||
|
||||
void setup_isac(struct IsdnCardState *cs)
|
||||
{
|
||||
INIT_WORK(&cs->tqueue, isac_bh);
|
||||
timer_setup(&cs->dbusytimer, dbusy_timer_handler, 0);
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/* $Id: isac.h,v 1.9.2.2 2004/01/12 22:52:27 keil Exp $
|
||||
*
|
||||
* ISAC specific defines
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/* All Registers original Siemens Spec */
|
||||
|
||||
#define ISAC_MASK 0x20
|
||||
#define ISAC_ISTA 0x20
|
||||
#define ISAC_STAR 0x21
|
||||
#define ISAC_CMDR 0x21
|
||||
#define ISAC_EXIR 0x24
|
||||
#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_MOCR 0x3a
|
||||
#define ISAC_MOR0 0x32
|
||||
#define ISAC_MOX0 0x32
|
||||
#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
|
||||
#define ISAC_CMD_SSZ 0x2
|
||||
#define ISAC_CMD_AR8 0x8
|
||||
#define ISAC_CMD_AR10 0x9
|
||||
#define ISAC_CMD_ARL 0xA
|
||||
#define ISAC_CMD_DUI 0xF
|
||||
|
||||
#define ISAC_IND_RS 0x1
|
||||
#define ISAC_IND_PU 0x7
|
||||
#define ISAC_IND_DR 0x0
|
||||
#define ISAC_IND_SD 0x2
|
||||
#define ISAC_IND_DIS 0x3
|
||||
#define ISAC_IND_EI 0x6
|
||||
#define ISAC_IND_RSY 0x4
|
||||
#define ISAC_IND_ARD 0x8
|
||||
#define ISAC_IND_TI 0xA
|
||||
#define ISAC_IND_ATI 0xB
|
||||
#define ISAC_IND_AI8 0xC
|
||||
#define ISAC_IND_AI10 0xD
|
||||
#define ISAC_IND_DID 0xF
|
||||
|
||||
extern void ISACVersion(struct IsdnCardState *, char *);
|
||||
extern void setup_isac(struct IsdnCardState *);
|
||||
extern void initisac(struct IsdnCardState *);
|
||||
extern void isac_interrupt(struct IsdnCardState *, u_char);
|
||||
extern void clear_pending_isac_ints(struct IsdnCardState *);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,222 +0,0 @@
|
|||
/* $Id: isar.h,v 1.11.2.2 2004/01/12 22:52:27 keil Exp $
|
||||
*
|
||||
* ISAR (Siemens PSB 7110) specific defines
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#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_WAITSTATE 0x27
|
||||
#define ISAR_HIS_TIMERIRQ 0x25
|
||||
#define ISAR_HIS_P0CFG 0x3c
|
||||
#define ISAR_HIS_P12CFG 0x24
|
||||
#define ISAR_HIS_SARTCFG 0x25
|
||||
#define ISAR_HIS_PUMPCFG 0x26
|
||||
#define ISAR_HIS_PUMPCTRL 0x2a
|
||||
#define ISAR_HIS_IOM2CFG 0x27
|
||||
#define ISAR_HIS_IOM2REQ 0x07
|
||||
#define ISAR_HIS_IOM2CTRL 0x2b
|
||||
#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_CMD_TIMERIRQ_OFF 0x20
|
||||
#define ISAR_CMD_TIMERIRQ_ON 0x21
|
||||
|
||||
|
||||
#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 0x00
|
||||
#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_IIS_INVMSG 0x3f
|
||||
|
||||
#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 ISAR_RATE_REQ 3
|
||||
|
||||
#define PMOD_DISABLE 0
|
||||
#define PMOD_FAX 1
|
||||
#define PMOD_DATAMODEM 2
|
||||
#define PMOD_HALFDUPLEX 3
|
||||
#define PMOD_V110 4
|
||||
#define PMOD_DTMF 5
|
||||
#define PMOD_DTMF_TRANS 6
|
||||
#define PMOD_BYPASS 7
|
||||
|
||||
#define PCTRL_ORIG 0x80
|
||||
#define PV32P2_V23R 0x40
|
||||
#define PV32P2_V22A 0x20
|
||||
#define PV32P2_V22B 0x10
|
||||
#define PV32P2_V22C 0x08
|
||||
#define PV32P2_V21 0x02
|
||||
#define PV32P2_BEL 0x01
|
||||
|
||||
// LSB MSB in ISAR doc wrong !!! Arghhh
|
||||
#define PV32P3_AMOD 0x80
|
||||
#define PV32P3_V32B 0x02
|
||||
#define PV32P3_V23B 0x01
|
||||
#define PV32P4_48 0x11
|
||||
#define PV32P5_48 0x05
|
||||
#define PV32P4_UT48 0x11
|
||||
#define PV32P5_UT48 0x0d
|
||||
#define PV32P4_96 0x11
|
||||
#define PV32P5_96 0x03
|
||||
#define PV32P4_UT96 0x11
|
||||
#define PV32P5_UT96 0x0f
|
||||
#define PV32P4_B96 0x91
|
||||
#define PV32P5_B96 0x0b
|
||||
#define PV32P4_UTB96 0xd1
|
||||
#define PV32P5_UTB96 0x0f
|
||||
#define PV32P4_120 0xb1
|
||||
#define PV32P5_120 0x09
|
||||
#define PV32P4_UT120 0xf1
|
||||
#define PV32P5_UT120 0x0f
|
||||
#define PV32P4_144 0x99
|
||||
#define PV32P5_144 0x09
|
||||
#define PV32P4_UT144 0xf9
|
||||
#define PV32P5_UT144 0x0f
|
||||
#define PV32P6_CTN 0x01
|
||||
#define PV32P6_ATN 0x02
|
||||
|
||||
#define PFAXP2_CTN 0x01
|
||||
#define PFAXP2_ATN 0x04
|
||||
|
||||
#define PSEV_10MS_TIMER 0x02
|
||||
#define PSEV_CON_ON 0x18
|
||||
#define PSEV_CON_OFF 0x19
|
||||
#define PSEV_V24_OFF 0x20
|
||||
#define PSEV_CTS_ON 0x21
|
||||
#define PSEV_CTS_OFF 0x22
|
||||
#define PSEV_DCD_ON 0x23
|
||||
#define PSEV_DCD_OFF 0x24
|
||||
#define PSEV_DSR_ON 0x25
|
||||
#define PSEV_DSR_OFF 0x26
|
||||
#define PSEV_REM_RET 0xcc
|
||||
#define PSEV_REM_REN 0xcd
|
||||
#define PSEV_GSTN_CLR 0xd4
|
||||
|
||||
#define PSEV_RSP_READY 0xbc
|
||||
#define PSEV_LINE_TX_H 0xb3
|
||||
#define PSEV_LINE_TX_B 0xb2
|
||||
#define PSEV_LINE_RX_H 0xb1
|
||||
#define PSEV_LINE_RX_B 0xb0
|
||||
#define PSEV_RSP_CONN 0xb5
|
||||
#define PSEV_RSP_DISC 0xb7
|
||||
#define PSEV_RSP_FCERR 0xb9
|
||||
#define PSEV_RSP_SILDET 0xbe
|
||||
#define PSEV_RSP_SILOFF 0xab
|
||||
#define PSEV_FLAGS_DET 0xba
|
||||
|
||||
#define PCTRL_CMD_FTH 0xa7
|
||||
#define PCTRL_CMD_FRH 0xa5
|
||||
#define PCTRL_CMD_FTM 0xa8
|
||||
#define PCTRL_CMD_FRM 0xa6
|
||||
#define PCTRL_CMD_SILON 0xac
|
||||
#define PCTRL_CMD_CONT 0xa2
|
||||
#define PCTRL_CMD_ESC 0xa4
|
||||
#define PCTRL_CMD_SILOFF 0xab
|
||||
#define PCTRL_CMD_HALT 0xa9
|
||||
|
||||
#define PCTRL_LOC_RET 0xcf
|
||||
#define PCTRL_LOC_REN 0xce
|
||||
|
||||
#define SMODE_DISABLE 0
|
||||
#define SMODE_V14 2
|
||||
#define SMODE_HDLC 3
|
||||
#define SMODE_BINARY 4
|
||||
#define SMODE_FSK_V14 5
|
||||
|
||||
#define SCTRL_HDMC_BOTH 0x00
|
||||
#define SCTRL_HDMC_DTX 0x80
|
||||
#define SCTRL_HDMC_DRX 0x40
|
||||
#define S_P1_OVSP 0x40
|
||||
#define S_P1_SNP 0x20
|
||||
#define S_P1_EOP 0x10
|
||||
#define S_P1_EDP 0x08
|
||||
#define S_P1_NSB 0x04
|
||||
#define S_P1_CHS_8 0x03
|
||||
#define S_P1_CHS_7 0x02
|
||||
#define S_P1_CHS_6 0x01
|
||||
#define S_P1_CHS_5 0x00
|
||||
|
||||
#define S_P2_BFT_DEF 0x10
|
||||
|
||||
#define IOM_CTRL_ENA 0x80
|
||||
#define IOM_CTRL_NOPCM 0x00
|
||||
#define IOM_CTRL_ALAW 0x02
|
||||
#define IOM_CTRL_ULAW 0x04
|
||||
#define IOM_CTRL_RCV 0x01
|
||||
|
||||
#define IOM_P1_TXD 0x10
|
||||
|
||||
#define HDLC_FED 0x40
|
||||
#define HDLC_FSD 0x20
|
||||
#define HDLC_FST 0x20
|
||||
#define HDLC_ERROR 0x1c
|
||||
#define HDLC_ERR_FAD 0x10
|
||||
#define HDLC_ERR_RER 0x08
|
||||
#define HDLC_ERR_CER 0x04
|
||||
#define SART_NMD 0x01
|
||||
|
||||
#define BSTAT_RDM0 0x1
|
||||
#define BSTAT_RDM1 0x2
|
||||
#define BSTAT_RDM2 0x4
|
||||
#define BSTAT_RDM3 0x8
|
||||
#define BSTEV_TBO 0x1f
|
||||
#define BSTEV_RBO 0x2f
|
||||
|
||||
/* FAX State Machine */
|
||||
#define STFAX_NULL 0
|
||||
#define STFAX_READY 1
|
||||
#define STFAX_LINE 2
|
||||
#define STFAX_CONT 3
|
||||
#define STFAX_ACTIV 4
|
||||
#define STFAX_ESCAPE 5
|
||||
#define STFAX_SILDET 6
|
||||
|
||||
#define ISDN_FAXPUMP_HALT 100
|
||||
|
||||
extern int ISARVersion(struct IsdnCardState *cs, char *s);
|
||||
extern void isar_int_main(struct IsdnCardState *cs);
|
||||
extern void initisar(struct IsdnCardState *cs);
|
||||
extern void isar_fill_fifo(struct BCState *bcs);
|
||||
extern int isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic);
|
|
@ -1,930 +0,0 @@
|
|||
/* $Id: isdnl1.c,v 2.46.2.5 2004/02/11 13:21:34 keil Exp $
|
||||
*
|
||||
* common low level stuff for Siemens Chipsetbased isdn cards
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* based on the teles driver from Jan den Ouden
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* For changes and modifications please read
|
||||
* Documentation/isdn/HiSax.cert
|
||||
*
|
||||
* Thanks to Jan den Ouden
|
||||
* Fritz Elfert
|
||||
* Beat Doebeli
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/gfp.h>
|
||||
#include "hisax.h"
|
||||
#include "isdnl1.h"
|
||||
|
||||
const char *l1_revision = "$Revision: 2.46.2.5 $";
|
||||
|
||||
#define TIMER3_VALUE 7000
|
||||
|
||||
static struct Fsm l1fsm_b;
|
||||
static struct Fsm l1fsm_s;
|
||||
|
||||
enum {
|
||||
ST_L1_F2,
|
||||
ST_L1_F3,
|
||||
ST_L1_F4,
|
||||
ST_L1_F5,
|
||||
ST_L1_F6,
|
||||
ST_L1_F7,
|
||||
ST_L1_F8,
|
||||
};
|
||||
|
||||
#define L1S_STATE_COUNT (ST_L1_F8 + 1)
|
||||
|
||||
static char *strL1SState[] =
|
||||
{
|
||||
"ST_L1_F2",
|
||||
"ST_L1_F3",
|
||||
"ST_L1_F4",
|
||||
"ST_L1_F5",
|
||||
"ST_L1_F6",
|
||||
"ST_L1_F7",
|
||||
"ST_L1_F8",
|
||||
};
|
||||
|
||||
#ifdef HISAX_UINTERFACE
|
||||
static
|
||||
struct Fsm l1fsm_u =
|
||||
{NULL, 0, 0, NULL, NULL};
|
||||
|
||||
enum {
|
||||
ST_L1_RESET,
|
||||
ST_L1_DEACT,
|
||||
ST_L1_SYNC2,
|
||||
ST_L1_TRANS,
|
||||
};
|
||||
|
||||
#define L1U_STATE_COUNT (ST_L1_TRANS + 1)
|
||||
|
||||
static char *strL1UState[] =
|
||||
{
|
||||
"ST_L1_RESET",
|
||||
"ST_L1_DEACT",
|
||||
"ST_L1_SYNC2",
|
||||
"ST_L1_TRANS",
|
||||
};
|
||||
#endif
|
||||
|
||||
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,
|
||||
EV_POWER_UP,
|
||||
EV_RSYNC_IND,
|
||||
EV_INFO2_IND,
|
||||
EV_INFO4_IND,
|
||||
EV_TIMER_DEACT,
|
||||
EV_TIMER_ACT,
|
||||
EV_TIMER3,
|
||||
};
|
||||
|
||||
#define L1_EVENT_COUNT (EV_TIMER3 + 1)
|
||||
|
||||
static char *strL1Event[] =
|
||||
{
|
||||
"EV_PH_ACTIVATE",
|
||||
"EV_PH_DEACTIVATE",
|
||||
"EV_RESET_IND",
|
||||
"EV_DEACT_CNF",
|
||||
"EV_DEACT_IND",
|
||||
"EV_POWER_UP",
|
||||
"EV_RSYNC_IND",
|
||||
"EV_INFO2_IND",
|
||||
"EV_INFO4_IND",
|
||||
"EV_TIMER_DEACT",
|
||||
"EV_TIMER_ACT",
|
||||
"EV_TIMER3",
|
||||
};
|
||||
|
||||
void
|
||||
debugl1(struct IsdnCardState *cs, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
char tmp[8];
|
||||
|
||||
va_start(args, fmt);
|
||||
sprintf(tmp, "Card%d ", cs->cardnr + 1);
|
||||
VHiSax_putstatus(cs, tmp, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void
|
||||
l1m_debug(struct FsmInst *fi, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
struct PStack *st = fi->userdata;
|
||||
struct IsdnCardState *cs = st->l1.hardware;
|
||||
char tmp[8];
|
||||
|
||||
va_start(args, fmt);
|
||||
sprintf(tmp, "Card%d ", cs->cardnr + 1);
|
||||
VHiSax_putstatus(cs, tmp, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void
|
||||
L1activated(struct IsdnCardState *cs)
|
||||
{
|
||||
struct PStack *st;
|
||||
|
||||
st = cs->stlist;
|
||||
while (st) {
|
||||
if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
|
||||
st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
|
||||
else
|
||||
st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
|
||||
st = st->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
L1deactivated(struct IsdnCardState *cs)
|
||||
{
|
||||
struct PStack *st;
|
||||
|
||||
st = cs->stlist;
|
||||
while (st) {
|
||||
if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
DChannel_proc_xmt(struct IsdnCardState *cs)
|
||||
{
|
||||
struct PStack *stptr;
|
||||
|
||||
if (cs->tx_skb)
|
||||
return;
|
||||
|
||||
stptr = cs->stlist;
|
||||
while (stptr != NULL) {
|
||||
if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
|
||||
stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
|
||||
break;
|
||||
} else
|
||||
stptr = stptr->next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DChannel_proc_rcv(struct IsdnCardState *cs)
|
||||
{
|
||||
struct sk_buff *skb, *nskb;
|
||||
struct PStack *stptr = cs->stlist;
|
||||
int found, tei, sapi;
|
||||
|
||||
if (stptr)
|
||||
if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
|
||||
FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL);
|
||||
while ((skb = skb_dequeue(&cs->rq))) {
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
if (cs->debug & L1_DEB_LAPD)
|
||||
Logl2Frame(cs, skb, "PH_DATA", 1);
|
||||
#endif
|
||||
stptr = cs->stlist;
|
||||
if (skb->len < 3) {
|
||||
debugl1(cs, "D-channel frame too short(%d)", skb->len);
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
if ((skb->data[0] & 1) || !(skb->data[1] & 1)) {
|
||||
debugl1(cs, "D-channel frame wrong EA0/EA1");
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
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 */
|
||||
while (stptr != NULL) {
|
||||
if ((nskb = skb_clone(skb, GFP_ATOMIC)))
|
||||
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 | INDICATION, nskb);
|
||||
else
|
||||
printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
|
||||
stptr = stptr->next;
|
||||
}
|
||||
}
|
||||
dev_kfree_skb(skb);
|
||||
} else if (sapi == CTRL_SAPI) { /* sapi 0 */
|
||||
found = 0;
|
||||
while (stptr != NULL)
|
||||
if (tei == stptr->l2.tei) {
|
||||
stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
|
||||
found = !0;
|
||||
break;
|
||||
} else
|
||||
stptr = stptr->next;
|
||||
if (!found)
|
||||
dev_kfree_skb(skb);
|
||||
} else
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
BChannel_proc_xmt(struct BCState *bcs)
|
||||
{
|
||||
struct PStack *st = bcs->st;
|
||||
|
||||
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 | CONFIRM, NULL);
|
||||
if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
|
||||
if (!test_bit(BC_FLG_BUSY, &bcs->Flag) &&
|
||||
skb_queue_empty(&bcs->squeue)) {
|
||||
st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
BChannel_proc_rcv(struct BCState *bcs)
|
||||
{
|
||||
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 | INDICATION, skb);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
BChannel_proc_ack(struct BCState *bcs)
|
||||
{
|
||||
u_long flags;
|
||||
int ack;
|
||||
|
||||
spin_lock_irqsave(&bcs->aclock, flags);
|
||||
ack = bcs->ackcnt;
|
||||
bcs->ackcnt = 0;
|
||||
spin_unlock_irqrestore(&bcs->aclock, flags);
|
||||
if (ack)
|
||||
lli_writewakeup(bcs->st, ack);
|
||||
}
|
||||
|
||||
void
|
||||
BChannel_bh(struct work_struct *work)
|
||||
{
|
||||
struct BCState *bcs = container_of(work, struct BCState, tqueue);
|
||||
|
||||
if (!bcs)
|
||||
return;
|
||||
if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event))
|
||||
BChannel_proc_rcv(bcs);
|
||||
if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event))
|
||||
BChannel_proc_xmt(bcs);
|
||||
if (test_and_clear_bit(B_ACKPENDING, &bcs->event))
|
||||
BChannel_proc_ack(bcs);
|
||||
}
|
||||
|
||||
void
|
||||
HiSax_addlist(struct IsdnCardState *cs,
|
||||
struct PStack *st)
|
||||
{
|
||||
st->next = cs->stlist;
|
||||
cs->stlist = st;
|
||||
}
|
||||
|
||||
void
|
||||
HiSax_rmlist(struct IsdnCardState *cs,
|
||||
struct PStack *st)
|
||||
{
|
||||
struct PStack *p;
|
||||
|
||||
FsmDelTimer(&st->l1.timer, 0);
|
||||
if (cs->stlist == st)
|
||||
cs->stlist = st->next;
|
||||
else {
|
||||
p = cs->stlist;
|
||||
while (p)
|
||||
if (p->next == st) {
|
||||
p->next = st->next;
|
||||
return;
|
||||
} else
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
init_bcstate(struct IsdnCardState *cs, int bc)
|
||||
{
|
||||
struct BCState *bcs = cs->bcs + bc;
|
||||
|
||||
bcs->cs = cs;
|
||||
bcs->channel = bc;
|
||||
INIT_WORK(&bcs->tqueue, BChannel_bh);
|
||||
spin_lock_init(&bcs->aclock);
|
||||
bcs->BC_SetStack = NULL;
|
||||
bcs->BC_Close = NULL;
|
||||
bcs->Flag = 0;
|
||||
}
|
||||
|
||||
#ifdef L2FRAME_DEBUG /* psa */
|
||||
|
||||
static char *
|
||||
l2cmd(u_char cmd)
|
||||
{
|
||||
switch (cmd & ~0x10) {
|
||||
case 1:
|
||||
return "RR";
|
||||
case 5:
|
||||
return "RNR";
|
||||
case 9:
|
||||
return "REJ";
|
||||
case 0x6f:
|
||||
return "SABME";
|
||||
case 0x0f:
|
||||
return "DM";
|
||||
case 3:
|
||||
return "UI";
|
||||
case 0x43:
|
||||
return "DISC";
|
||||
case 0x63:
|
||||
return "UA";
|
||||
case 0x87:
|
||||
return "FRMR";
|
||||
case 0xaf:
|
||||
return "XID";
|
||||
default:
|
||||
if (!(cmd & 1))
|
||||
return "I";
|
||||
else
|
||||
return "invalid command";
|
||||
}
|
||||
}
|
||||
|
||||
static char tmpdeb[32];
|
||||
|
||||
static char *
|
||||
l2frames(u_char *ptr)
|
||||
{
|
||||
switch (ptr[2] & ~0x10) {
|
||||
case 1:
|
||||
case 5:
|
||||
case 9:
|
||||
sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
|
||||
break;
|
||||
case 0x6f:
|
||||
case 0x0f:
|
||||
case 3:
|
||||
case 0x43:
|
||||
case 0x63:
|
||||
case 0x87:
|
||||
case 0xaf:
|
||||
sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
|
||||
break;
|
||||
default:
|
||||
if (!(ptr[2] & 1)) {
|
||||
sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
|
||||
break;
|
||||
} else
|
||||
return "invalid command";
|
||||
}
|
||||
|
||||
|
||||
return tmpdeb;
|
||||
}
|
||||
|
||||
void
|
||||
Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
|
||||
{
|
||||
u_char *ptr;
|
||||
|
||||
ptr = skb->data;
|
||||
|
||||
if (ptr[0] & 1 || !(ptr[1] & 1))
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
l1_reset(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F3);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_F3);
|
||||
if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
|
||||
st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_F3);
|
||||
FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
|
||||
test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_power_up_s(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
|
||||
FsmChangeState(fi, ST_L1_F4);
|
||||
st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
|
||||
FsmRestartTimer(&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);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_go_F5(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F5);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_go_F8(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_F8);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_info2_ind(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
#ifdef HISAX_UINTERFACE
|
||||
if (test_bit(FLG_L1_UINT, &st->l1.Flags))
|
||||
FsmChangeState(fi, ST_L1_SYNC2);
|
||||
else
|
||||
#endif
|
||||
FsmChangeState(fi, ST_L1_F6);
|
||||
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;
|
||||
|
||||
#ifdef HISAX_UINTERFACE
|
||||
if (test_bit(FLG_L1_UINT, &st->l1.Flags))
|
||||
FsmChangeState(fi, ST_L1_TRANS);
|
||||
else
|
||||
#endif
|
||||
FsmChangeState(fi, ST_L1_F7);
|
||||
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)) {
|
||||
if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags))
|
||||
FsmDelTimer(&st->l1.timer, 3);
|
||||
FsmRestartTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
|
||||
test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
l1_timer3(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);
|
||||
if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
|
||||
L1deactivated(st->l1.hardware);
|
||||
|
||||
#ifdef HISAX_UINTERFACE
|
||||
if (!test_bit(FLG_L1_UINT, &st->l1.Flags))
|
||||
#endif
|
||||
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;
|
||||
|
||||
test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
|
||||
test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
|
||||
L1activated(st->l1.hardware);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_timer_deact(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
|
||||
test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
|
||||
L1deactivated(st->l1.hardware);
|
||||
st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_activate_s(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
st->l1.l1hw(st, HW_RESET | REQUEST, NULL);
|
||||
}
|
||||
|
||||
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 L1SFnList[] __initdata =
|
||||
{
|
||||
{ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
|
||||
{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_F6, EV_RESET_IND, l1_reset},
|
||||
{ST_L1_F7, EV_RESET_IND, l1_reset},
|
||||
{ST_L1_F8, EV_RESET_IND, l1_reset},
|
||||
{ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
|
||||
{ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
|
||||
{ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
|
||||
{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
|
||||
{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
|
||||
{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
|
||||
{ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
|
||||
{ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
|
||||
{ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
|
||||
{ST_L1_F3, EV_POWER_UP, l1_power_up_s},
|
||||
{ST_L1_F4, EV_RSYNC_IND, l1_go_F5},
|
||||
{ST_L1_F6, EV_RSYNC_IND, l1_go_F8},
|
||||
{ST_L1_F7, EV_RSYNC_IND, l1_go_F8},
|
||||
{ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
|
||||
{ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
|
||||
{ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
|
||||
{ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
|
||||
{ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
|
||||
{ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_F3, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F4, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F5, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F6, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F8, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
|
||||
{ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
|
||||
{ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
|
||||
{ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
|
||||
{ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
|
||||
{ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
|
||||
{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
|
||||
};
|
||||
|
||||
#ifdef HISAX_UINTERFACE
|
||||
static void
|
||||
l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_RESET);
|
||||
FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
|
||||
test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
|
||||
st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_power_up_u(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
|
||||
test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_info0_ind(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
FsmChangeState(fi, ST_L1_DEACT);
|
||||
}
|
||||
|
||||
static void
|
||||
l1_activate_u(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
st->l1.l1hw(st, HW_INFO1 | REQUEST, NULL);
|
||||
}
|
||||
|
||||
static struct FsmNode L1UFnList[] __initdata =
|
||||
{
|
||||
{ST_L1_RESET, EV_DEACT_IND, l1_deact_req_u},
|
||||
{ST_L1_DEACT, EV_DEACT_IND, l1_deact_req_u},
|
||||
{ST_L1_SYNC2, EV_DEACT_IND, l1_deact_req_u},
|
||||
{ST_L1_TRANS, EV_DEACT_IND, l1_deact_req_u},
|
||||
{ST_L1_DEACT, EV_PH_ACTIVATE, l1_activate_u},
|
||||
{ST_L1_DEACT, EV_POWER_UP, l1_power_up_u},
|
||||
{ST_L1_DEACT, EV_INFO2_IND, l1_info2_ind},
|
||||
{ST_L1_TRANS, EV_INFO2_IND, l1_info2_ind},
|
||||
{ST_L1_RESET, EV_DEACT_CNF, l1_info0_ind},
|
||||
{ST_L1_DEACT, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_SYNC2, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_RESET, EV_INFO4_IND, l1_info4_ind},
|
||||
{ST_L1_DEACT, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_SYNC2, EV_TIMER3, l1_timer3},
|
||||
{ST_L1_TRANS, EV_TIMER_ACT, l1_timer_act},
|
||||
{ST_L1_DEACT, EV_TIMER_DEACT, l1_timer_deact},
|
||||
{ST_L1_SYNC2, EV_TIMER_DEACT, l1_timer_deact},
|
||||
{ST_L1_RESET, EV_TIMER_DEACT, l1_timer_deact},
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
l1b_activate(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L1_WAIT_ACT);
|
||||
FsmRestartTimer(&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);
|
||||
FsmRestartTimer(&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[] __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},
|
||||
};
|
||||
|
||||
int __init
|
||||
Isdnl1New(void)
|
||||
{
|
||||
int retval;
|
||||
|
||||
l1fsm_s.state_count = L1S_STATE_COUNT;
|
||||
l1fsm_s.event_count = L1_EVENT_COUNT;
|
||||
l1fsm_s.strEvent = strL1Event;
|
||||
l1fsm_s.strState = strL1SState;
|
||||
retval = FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
l1fsm_b.state_count = L1B_STATE_COUNT;
|
||||
l1fsm_b.event_count = L1_EVENT_COUNT;
|
||||
l1fsm_b.strEvent = strL1Event;
|
||||
l1fsm_b.strState = strL1BState;
|
||||
retval = FsmNew(&l1fsm_b, L1BFnList, ARRAY_SIZE(L1BFnList));
|
||||
if (retval) {
|
||||
FsmFree(&l1fsm_s);
|
||||
return retval;
|
||||
}
|
||||
#ifdef HISAX_UINTERFACE
|
||||
l1fsm_u.state_count = L1U_STATE_COUNT;
|
||||
l1fsm_u.event_count = L1_EVENT_COUNT;
|
||||
l1fsm_u.strEvent = strL1Event;
|
||||
l1fsm_u.strState = strL1UState;
|
||||
retval = FsmNew(&l1fsm_u, L1UFnList, ARRAY_SIZE(L1UFnList));
|
||||
if (retval) {
|
||||
FsmFree(&l1fsm_s);
|
||||
FsmFree(&l1fsm_b);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Isdnl1Free(void)
|
||||
{
|
||||
#ifdef HISAX_UINTERFACE
|
||||
FsmFree(&l1fsm_u);
|
||||
#endif
|
||||
FsmFree(&l1fsm_s);
|
||||
FsmFree(&l1fsm_b);
|
||||
}
|
||||
|
||||
static void
|
||||
dch_l2l1(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
|
||||
|
||||
switch (pr) {
|
||||
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",
|
||||
st->l1.l1m.fsm->strState[st->l1.l1m.state]);
|
||||
if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
|
||||
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_TESTLOOP | REQUEST):
|
||||
if (1 & (long) arg)
|
||||
debugl1(cs, "PH_TEST_LOOP B1");
|
||||
if (2 & (long) arg)
|
||||
debugl1(cs, "PH_TEST_LOOP B2");
|
||||
if (!(3 & (long) arg))
|
||||
debugl1(cs, "PH_TEST_LOOP DISABLED");
|
||||
st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
|
||||
break;
|
||||
default:
|
||||
if (cs->debug)
|
||||
debugl1(cs, "dch_l2l1 msg %04X unhandled", pr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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, "%s %04X unhandled", __func__, 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;
|
||||
case (PH_DEACTIVATE | REQUEST):
|
||||
FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
setstack_HiSax(struct PStack *st, struct IsdnCardState *cs)
|
||||
{
|
||||
st->l1.hardware = cs;
|
||||
st->protocol = cs->protocol;
|
||||
st->l1.l1m.fsm = &l1fsm_s;
|
||||
st->l1.l1m.state = ST_L1_F3;
|
||||
st->l1.Flags = 0;
|
||||
#ifdef HISAX_UINTERFACE
|
||||
if (test_bit(FLG_HW_L1_UINT, &cs->HW_Flags)) {
|
||||
st->l1.l1m.fsm = &l1fsm_u;
|
||||
st->l1.l1m.state = ST_L1_RESET;
|
||||
st->l1.Flags = FLG_L1_UINT;
|
||||
}
|
||||
#endif
|
||||
st->l1.l1m.debug = cs->debug;
|
||||
st->l1.l1m.userdata = st;
|
||||
st->l1.l1m.userint = 0;
|
||||
st->l1.l1m.printdebug = l1m_debug;
|
||||
FsmInitTimer(&st->l1.l1m, &st->l1.timer);
|
||||
setstack_tei(st);
|
||||
setstack_manager(st);
|
||||
st->l1.stlistp = &(cs->stlist);
|
||||
st->l2.l2l1 = dch_l2l1;
|
||||
if (cs->setstack_d)
|
||||
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);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/* $Id: isdnl1.h,v 2.12.2.3 2004/02/11 13:21:34 keil Exp $
|
||||
*
|
||||
* Layer 1 defines
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#define D_RCVBUFREADY 0
|
||||
#define D_XMTBUFREADY 1
|
||||
#define D_L1STATECHANGE 2
|
||||
#define D_CLEARBUSY 3
|
||||
#define D_RX_MON0 4
|
||||
#define D_RX_MON1 5
|
||||
#define D_TX_MON0 6
|
||||
#define D_TX_MON1 7
|
||||
#define E_RCVBUFREADY 8
|
||||
|
||||
#define B_RCVBUFREADY 0
|
||||
#define B_XMTBUFREADY 1
|
||||
#define B_ACKPENDING 2
|
||||
|
||||
__printf(2, 3)
|
||||
void debugl1(struct IsdnCardState *cs, char *fmt, ...);
|
||||
void DChannel_proc_xmt(struct IsdnCardState *cs);
|
||||
void DChannel_proc_rcv(struct IsdnCardState *cs);
|
||||
void l1_msg(struct IsdnCardState *cs, int pr, void *arg);
|
||||
void l1_msg_b(struct PStack *st, int pr, void *arg);
|
||||
void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf,
|
||||
int dir);
|
||||
void BChannel_bh(struct work_struct *work);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,25 +0,0 @@
|
|||
/* $Id: isdnl2.h,v 1.3.6.2 2001/09/23 22:24:49 kai Exp $
|
||||
*
|
||||
* Layer 2 defines
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#define RR 0x01
|
||||
#define RNR 0x05
|
||||
#define REJ 0x09
|
||||
#define SABME 0x6f
|
||||
#define SABM 0x2f
|
||||
#define DM 0x0f
|
||||
#define UI 0x03
|
||||
#define DISC 0x43
|
||||
#define UA 0x63
|
||||
#define FRMR 0x87
|
||||
#define XID 0xaf
|
||||
|
||||
#define CMD 0
|
||||
#define RSP 1
|
||||
|
||||
#define LC_FLUSH_WAIT 1
|
|
@ -1,594 +0,0 @@
|
|||
/* $Id: isdnl3.c,v 2.22.2.3 2004/01/13 14:31:25 keil Exp $
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* based on the teles driver from Jan den Ouden
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* For changes and modifications please read
|
||||
* Documentation/isdn/HiSax.cert
|
||||
*
|
||||
* Thanks to Jan den Ouden
|
||||
* Fritz Elfert
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include "hisax.h"
|
||||
#include "isdnl3.h"
|
||||
|
||||
const char *l3_revision = "$Revision: 2.22.2.3 $";
|
||||
|
||||
static struct Fsm l3fsm;
|
||||
|
||||
enum {
|
||||
ST_L3_LC_REL,
|
||||
ST_L3_LC_ESTAB_WAIT,
|
||||
ST_L3_LC_REL_DELAY,
|
||||
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_DELAY",
|
||||
"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,
|
||||
EV_TIMEOUT,
|
||||
};
|
||||
|
||||
#define L3_EVENT_COUNT (EV_TIMEOUT + 1)
|
||||
|
||||
static char *strL3Event[] =
|
||||
{
|
||||
"EV_ESTABLISH_REQ",
|
||||
"EV_ESTABLISH_IND",
|
||||
"EV_ESTABLISH_CNF",
|
||||
"EV_RELEASE_REQ",
|
||||
"EV_RELEASE_CNF",
|
||||
"EV_RELEASE_IND",
|
||||
"EV_TIMEOUT",
|
||||
};
|
||||
|
||||
static __printf(2, 3) 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)
|
||||
{
|
||||
int l, codeset, maincodeset;
|
||||
u_char *pend = p + size;
|
||||
|
||||
/* skip protocol discriminator, callref and message type */
|
||||
p++;
|
||||
l = (*p++) & 0xf;
|
||||
p += l;
|
||||
p++;
|
||||
codeset = 0;
|
||||
maincodeset = 0;
|
||||
/* while there are bytes left... */
|
||||
while (p < pend) {
|
||||
if ((*p & 0xf0) == 0x90) {
|
||||
codeset = *p & 0x07;
|
||||
if (!(*p & 0x08))
|
||||
maincodeset = codeset;
|
||||
}
|
||||
if (*p & 0x80)
|
||||
p++;
|
||||
else {
|
||||
if (codeset == wanted_set) {
|
||||
if (*p == ie)
|
||||
{ /* improved length check (Werner Cornelius) */
|
||||
if ((pend - p) < 2)
|
||||
return (NULL);
|
||||
if (*(p + 1) > (pend - (p + 2)))
|
||||
return (NULL);
|
||||
return (p);
|
||||
}
|
||||
|
||||
if (*p > ie)
|
||||
return (NULL);
|
||||
}
|
||||
p++;
|
||||
l = *p++;
|
||||
p += l;
|
||||
codeset = maincodeset;
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
int
|
||||
getcallref(u_char *p)
|
||||
{
|
||||
int l, cr = 0;
|
||||
|
||||
p++; /* prot discr */
|
||||
if (*p & 0xfe) /* wrong callref BRI only 1 octet*/
|
||||
return (-2);
|
||||
l = 0xf & *p++; /* callref length */
|
||||
if (!l) /* dummy CallRef */
|
||||
return (-1);
|
||||
cr = *p++;
|
||||
return (cr);
|
||||
}
|
||||
|
||||
static int OrigCallRef = 0;
|
||||
|
||||
int
|
||||
newcallref(void)
|
||||
{
|
||||
if (OrigCallRef == 127)
|
||||
OrigCallRef = 1;
|
||||
else
|
||||
OrigCallRef++;
|
||||
return (OrigCallRef);
|
||||
}
|
||||
|
||||
void
|
||||
newl3state(struct l3_process *pc, int state)
|
||||
{
|
||||
if (pc->debug & L3_DEB_STATE)
|
||||
l3_debug(pc->st, "%s cr %d %d --> %d", __func__,
|
||||
pc->callref & 0x7F,
|
||||
pc->state, state);
|
||||
pc->state = state;
|
||||
}
|
||||
|
||||
static void
|
||||
L3ExpireTimer(struct timer_list *timer)
|
||||
{
|
||||
struct L3Timer *t = from_timer(t, timer, tl);
|
||||
t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
|
||||
}
|
||||
|
||||
void
|
||||
L3InitTimer(struct l3_process *pc, struct L3Timer *t)
|
||||
{
|
||||
t->pc = pc;
|
||||
timer_setup(&t->tl, L3ExpireTimer, 0);
|
||||
}
|
||||
|
||||
void
|
||||
L3DelTimer(struct L3Timer *t)
|
||||
{
|
||||
del_timer(&t->tl);
|
||||
}
|
||||
|
||||
int
|
||||
L3AddTimer(struct L3Timer *t,
|
||||
int millisec, int event)
|
||||
{
|
||||
if (timer_pending(&t->tl)) {
|
||||
printk(KERN_WARNING "L3AddTimer: timer already active!\n");
|
||||
return -1;
|
||||
}
|
||||
t->event = event;
|
||||
t->tl.expires = jiffies + (millisec * HZ) / 1000;
|
||||
add_timer(&t->tl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
StopAllL3Timer(struct l3_process *pc)
|
||||
{
|
||||
L3DelTimer(&pc->timer);
|
||||
}
|
||||
|
||||
struct sk_buff *
|
||||
l3_alloc_skb(int len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) {
|
||||
printk(KERN_WARNING "HiSax: No skb for D-channel\n");
|
||||
return (NULL);
|
||||
}
|
||||
skb_reserve(skb, MAX_HEADER_LEN);
|
||||
return (skb);
|
||||
}
|
||||
|
||||
static void
|
||||
no_l3_proto(struct PStack *st, int pr, void *arg)
|
||||
{
|
||||
struct sk_buff *skb = arg;
|
||||
|
||||
HiSax_putstatus(st->l1.hardware, "L3", "no D protocol");
|
||||
if (skb) {
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
no_l3_proto_spec(struct PStack *st, isdn_ctrl *ic)
|
||||
{
|
||||
printk(KERN_WARNING "HiSax: no specific protocol handler for proto %lu\n", ic->arg & 0xFF);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
struct l3_process
|
||||
*getl3proc(struct PStack *st, int cr)
|
||||
{
|
||||
struct l3_process *p = st->l3.proc;
|
||||
|
||||
while (p)
|
||||
if (p->callref == cr)
|
||||
return (p);
|
||||
else
|
||||
p = p->next;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
struct l3_process
|
||||
*new_l3_process(struct PStack *st, int cr)
|
||||
{
|
||||
struct l3_process *p, *np;
|
||||
|
||||
if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr);
|
||||
return (NULL);
|
||||
}
|
||||
if (!st->l3.proc)
|
||||
st->l3.proc = p;
|
||||
else {
|
||||
np = st->l3.proc;
|
||||
while (np->next)
|
||||
np = np->next;
|
||||
np->next = p;
|
||||
}
|
||||
p->next = NULL;
|
||||
p->debug = st->l3.debug;
|
||||
p->callref = cr;
|
||||
p->state = 0;
|
||||
p->chan = NULL;
|
||||
p->st = st;
|
||||
p->N303 = st->l3.N303;
|
||||
L3InitTimer(p, &p->timer);
|
||||
return (p);
|
||||
};
|
||||
|
||||
void
|
||||
release_l3_process(struct l3_process *p)
|
||||
{
|
||||
struct l3_process *np, *pp = NULL;
|
||||
|
||||
if (!p)
|
||||
return;
|
||||
np = p->st->l3.proc;
|
||||
while (np) {
|
||||
if (np == p) {
|
||||
StopAllL3Timer(p);
|
||||
if (pp)
|
||||
pp->next = np->next;
|
||||
else if (!(p->st->l3.proc = np->next) &&
|
||||
!test_bit(FLG_PTP, &p->st->l2.flag)) {
|
||||
if (p->debug)
|
||||
l3_debug(p->st, "release_l3_process: last process");
|
||||
if (skb_queue_empty(&p->st->l3.squeue)) {
|
||||
if (p->debug)
|
||||
l3_debug(p->st, "release_l3_process: release link");
|
||||
if (p->st->protocol != ISDN_PTYPE_NI1)
|
||||
FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL);
|
||||
else
|
||||
FsmEvent(&p->st->l3.l3m, EV_RELEASE_IND, NULL);
|
||||
} else {
|
||||
if (p->debug)
|
||||
l3_debug(p->st, "release_l3_process: not release link");
|
||||
}
|
||||
}
|
||||
kfree(p);
|
||||
return;
|
||||
}
|
||||
pp = np;
|
||||
np = np->next;
|
||||
}
|
||||
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);
|
||||
};
|
||||
|
||||
static void
|
||||
l3ml3p(struct PStack *st, int pr)
|
||||
{
|
||||
struct l3_process *p = st->l3.proc;
|
||||
struct l3_process *np;
|
||||
|
||||
while (p) {
|
||||
/* p might be kfreed under us, so we need to save where we want to go on */
|
||||
np = p->next;
|
||||
st->l3.l3ml3(st, pr, p);
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
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;
|
||||
FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer);
|
||||
strcpy(st->l3.debug_id, "L3DC ");
|
||||
st->lli.l4l3_proto = no_l3_proto_spec;
|
||||
|
||||
#ifdef CONFIG_HISAX_EURO
|
||||
if (st->protocol == ISDN_PTYPE_EURO) {
|
||||
setstack_dss1(st);
|
||||
} else
|
||||
#endif
|
||||
#ifdef CONFIG_HISAX_NI1
|
||||
if (st->protocol == ISDN_PTYPE_NI1) {
|
||||
setstack_ni1(st);
|
||||
} else
|
||||
#endif
|
||||
#ifdef CONFIG_HISAX_1TR6
|
||||
if (st->protocol == ISDN_PTYPE_1TR6) {
|
||||
setstack_1tr6(st);
|
||||
} else
|
||||
#endif
|
||||
if (st->protocol == ISDN_PTYPE_LEASED) {
|
||||
st->lli.l4l3 = no_l3_proto;
|
||||
st->l2.l2l3 = no_l3_proto;
|
||||
st->l3.l3ml3 = no_l3_proto;
|
||||
printk(KERN_INFO "HiSax: Leased line mode\n");
|
||||
} else {
|
||||
st->lli.l4l3 = no_l3_proto;
|
||||
st->l2.l2l3 = no_l3_proto;
|
||||
st->l3.l3ml3 = no_l3_proto;
|
||||
sprintf(tmp, "protocol %s not supported",
|
||||
(st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
|
||||
(st->protocol == ISDN_PTYPE_EURO) ? "euro" :
|
||||
(st->protocol == ISDN_PTYPE_NI1) ? "ni1" :
|
||||
"unknown");
|
||||
printk(KERN_WARNING "HiSax: %s\n", tmp);
|
||||
st->protocol = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
isdnl3_trans(struct PStack *st, int pr, void *arg) {
|
||||
st->l3.l3l2(st, pr, arg);
|
||||
}
|
||||
|
||||
void
|
||||
releasestack_isdnl3(struct PStack *st)
|
||||
{
|
||||
while (st->l3.proc)
|
||||
release_l3_process(st->l3.proc);
|
||||
if (st->l3.global) {
|
||||
StopAllL3Timer(st->l3.global);
|
||||
kfree(st->l3.global);
|
||||
st->l3.global = NULL;
|
||||
}
|
||||
FsmDelTimer(&st->l3.l3m_timer, 54);
|
||||
skb_queue_purge(&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;
|
||||
}
|
||||
|
||||
#define DREL_TIMER_VALUE 40000
|
||||
|
||||
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;
|
||||
int dequeued = 0;
|
||||
|
||||
FsmChangeState(fi, ST_L3_LC_ESTAB);
|
||||
while ((skb = skb_dequeue(&st->l3.squeue))) {
|
||||
st->l3.l3l2(st, DL_DATA | REQUEST, skb);
|
||||
dequeued++;
|
||||
}
|
||||
if ((!st->l3.proc) && dequeued) {
|
||||
if (st->l3.debug)
|
||||
l3_debug(st, "lc_connect: release link");
|
||||
FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
|
||||
} else
|
||||
l3ml3p(st, DL_ESTABLISH | INDICATION);
|
||||
}
|
||||
|
||||
static void
|
||||
lc_connected(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
struct sk_buff *skb = arg;
|
||||
int dequeued = 0;
|
||||
|
||||
FsmDelTimer(&st->l3.l3m_timer, 51);
|
||||
FsmChangeState(fi, ST_L3_LC_ESTAB);
|
||||
while ((skb = skb_dequeue(&st->l3.squeue))) {
|
||||
st->l3.l3l2(st, DL_DATA | REQUEST, skb);
|
||||
dequeued++;
|
||||
}
|
||||
if ((!st->l3.proc) && dequeued) {
|
||||
if (st->l3.debug)
|
||||
l3_debug(st, "lc_connected: release link");
|
||||
FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
|
||||
} else
|
||||
l3ml3p(st, DL_ESTABLISH | CONFIRM);
|
||||
}
|
||||
|
||||
static void
|
||||
lc_start_delay(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L3_LC_REL_DELAY);
|
||||
FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
|
||||
}
|
||||
|
||||
static void
|
||||
lc_start_delay_check(struct FsmInst *fi, int event, void *arg)
|
||||
/* 20/09/00 - GE timer not user for NI-1 as layer 2 should stay up */
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L3_LC_REL_DELAY);
|
||||
/* 19/09/00 - GE timer not user for NI-1 */
|
||||
if (st->protocol != ISDN_PTYPE_NI1)
|
||||
FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
|
||||
}
|
||||
|
||||
static void
|
||||
lc_release_req(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
if (test_bit(FLG_L2BLOCK, &st->l2.flag)) {
|
||||
if (st->l3.debug)
|
||||
l3_debug(st, "lc_release_req: l2 blocked");
|
||||
/* restart release timer */
|
||||
FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51);
|
||||
} 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;
|
||||
|
||||
FsmDelTimer(&st->l3.l3m_timer, 52);
|
||||
FsmChangeState(fi, ST_L3_LC_REL);
|
||||
skb_queue_purge(&st->l3.squeue);
|
||||
l3ml3p(st, DL_RELEASE | INDICATION);
|
||||
}
|
||||
|
||||
static void
|
||||
lc_release_cnf(struct FsmInst *fi, int event, void *arg)
|
||||
{
|
||||
struct PStack *st = fi->userdata;
|
||||
|
||||
FsmChangeState(fi, ST_L3_LC_REL);
|
||||
skb_queue_purge(&st->l3.squeue);
|
||||
l3ml3p(st, DL_RELEASE | CONFIRM);
|
||||
}
|
||||
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
static struct FsmNode L3FnList[] __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_connected},
|
||||
{ST_L3_LC_ESTAB_WAIT, EV_RELEASE_REQ, lc_start_delay},
|
||||
{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_start_delay_check},
|
||||
{ST_L3_LC_REL_DELAY, EV_RELEASE_IND, lc_release_ind},
|
||||
{ST_L3_LC_REL_DELAY, EV_ESTABLISH_REQ, lc_connected},
|
||||
{ST_L3_LC_REL_DELAY, EV_TIMEOUT, lc_release_req},
|
||||
{ST_L3_LC_REL_WAIT, EV_RELEASE_CNF, lc_release_cnf},
|
||||
{ST_L3_LC_REL_WAIT, EV_ESTABLISH_REQ, lc_activate},
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
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_tail(&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;
|
||||
}
|
||||
}
|
||||
|
||||
int __init
|
||||
Isdnl3New(void)
|
||||
{
|
||||
l3fsm.state_count = L3_STATE_COUNT;
|
||||
l3fsm.event_count = L3_EVENT_COUNT;
|
||||
l3fsm.strEvent = strL3Event;
|
||||
l3fsm.strState = strL3State;
|
||||
return FsmNew(&l3fsm, L3FnList, ARRAY_SIZE(L3FnList));
|
||||
}
|
||||
|
||||
void
|
||||
Isdnl3Free(void)
|
||||
{
|
||||
FsmFree(&l3fsm);
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/* $Id: isdnl3.h,v 2.6.6.2 2001/09/23 22:24:49 kai Exp $
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#define SBIT(state) (1 << state)
|
||||
#define ALL_STATES 0x03ffffff
|
||||
|
||||
#define PROTO_DIS_EURO 0x08
|
||||
|
||||
#define L3_DEB_WARN 0x01
|
||||
#define L3_DEB_PROTERR 0x02
|
||||
#define L3_DEB_STATE 0x04
|
||||
#define L3_DEB_CHARGE 0x08
|
||||
#define L3_DEB_CHECK 0x10
|
||||
#define L3_DEB_SI 0x20
|
||||
|
||||
struct stateentry {
|
||||
int state;
|
||||
int primitive;
|
||||
void (*rout) (struct l3_process *, u8, void *);
|
||||
};
|
||||
|
||||
#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args)
|
||||
|
||||
struct PStack;
|
||||
|
||||
void newl3state(struct l3_process *pc, int state);
|
||||
void L3InitTimer(struct l3_process *pc, struct L3Timer *t);
|
||||
void L3DelTimer(struct L3Timer *t);
|
||||
int L3AddTimer(struct L3Timer *t, int millisec, int event);
|
||||
void StopAllL3Timer(struct l3_process *pc);
|
||||
struct sk_buff *l3_alloc_skb(int len);
|
||||
struct l3_process *new_l3_process(struct PStack *st, int cr);
|
||||
void release_l3_process(struct l3_process *p);
|
||||
struct l3_process *getl3proc(struct PStack *st, int cr);
|
||||
void l3_msg(struct PStack *st, int pr, void *arg);
|
||||
void setstack_dss1(struct PStack *st);
|
||||
void setstack_ni1(struct PStack *st);
|
||||
void setstack_1tr6(struct PStack *st);
|
|
@ -1,305 +0,0 @@
|
|||
/* $Id: isurf.c,v 1.12.2.4 2004/01/13 21:46:03 keil Exp $
|
||||
*
|
||||
* low level stuff for Siemens I-Surf/I-Talk cards
|
||||
*
|
||||
* Author Karsten Keil
|
||||
* Copyright by Karsten Keil <keil@isdn4linux.de>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "hisax.h"
|
||||
#include "isac.h"
|
||||
#include "isar.h"
|
||||
#include "isdnl1.h"
|
||||
#include <linux/isapnp.h>
|
||||
|
||||
static const char *ISurf_revision = "$Revision: 1.12.2.4 $";
|
||||
|
||||
#define byteout(addr, val) outb(val, addr)
|
||||
#define bytein(addr) inb(addr)
|
||||
|
||||
#define ISURF_ISAR_RESET 1
|
||||
#define ISURF_ISAC_RESET 2
|
||||
#define ISURF_ISAR_EA 4
|
||||
#define ISURF_ARCOFI_RESET 8
|
||||
#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET)
|
||||
|
||||
#define ISURF_ISAR_OFFSET 0
|
||||
#define ISURF_ISAC_OFFSET 0x100
|
||||
#define ISURF_IOMEM_SIZE 0x400
|
||||
/* Interface functions */
|
||||
|
||||
static u_char
|
||||
ReadISAC(struct IsdnCardState *cs, u_char offset)
|
||||
{
|
||||
return (readb(cs->hw.isurf.isac + offset));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
|
||||
{
|
||||
writeb(value, cs->hw.isurf.isac + offset); mb();
|
||||
}
|
||||
|
||||
static void
|
||||
ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
register int i;
|
||||
for (i = 0; i < size; i++)
|
||||
data[i] = readb(cs->hw.isurf.isac);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
|
||||
{
|
||||
register int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
writeb(data[i], cs->hw.isurf.isac); mb();
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
return (readb(cs->hw.isurf.isar + offset));
|
||||
}
|
||||
|
||||
static void
|
||||
WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
|
||||
{
|
||||
writeb(value, cs->hw.isurf.isar + offset); mb();
|
||||
}
|
||||
|
||||
static irqreturn_t
|
||||
isurf_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
struct IsdnCardState *cs = dev_id;
|
||||
u_char val;
|
||||
int cnt = 5;
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
|
||||
Start_ISAR:
|
||||
if (val & ISAR_IRQSTA)
|
||||
isar_int_main(cs);
|
||||
val = readb(cs->hw.isurf.isac + ISAC_ISTA);
|
||||
Start_ISAC:
|
||||
if (val)
|
||||
isac_interrupt(cs, val);
|
||||
val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
|
||||
if ((val & ISAR_IRQSTA) && --cnt) {
|
||||
if (cs->debug & L1_DEB_HSCX)
|
||||
debugl1(cs, "ISAR IntStat after IntRoutine");
|
||||
goto Start_ISAR;
|
||||
}
|
||||
val = readb(cs->hw.isurf.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 "ISurf IRQ LOOP\n");
|
||||
|
||||
writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
|
||||
writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK); mb();
|
||||
writeb(0, cs->hw.isurf.isac + ISAC_MASK); mb();
|
||||
writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
release_io_isurf(struct IsdnCardState *cs)
|
||||
{
|
||||
release_region(cs->hw.isurf.reset, 1);
|
||||
iounmap(cs->hw.isurf.isar);
|
||||
release_mem_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_isurf(struct IsdnCardState *cs, u_char chips)
|
||||
{
|
||||
printk(KERN_INFO "ISurf: resetting card\n");
|
||||
|
||||
byteout(cs->hw.isurf.reset, chips); /* Reset On */
|
||||
mdelay(10);
|
||||
byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
static int
|
||||
ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
switch (mt) {
|
||||
case CARD_RESET:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_isurf(cs, ISURF_RESET);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_RELEASE:
|
||||
release_io_isurf(cs);
|
||||
return (0);
|
||||
case CARD_INIT:
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
reset_isurf(cs, ISURF_RESET);
|
||||
clear_pending_isac_ints(cs);
|
||||
writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
|
||||
initisac(cs);
|
||||
initisar(cs);
|
||||
/* Reenable ISAC IRQ */
|
||||
cs->writeisac(cs, ISAC_MASK, 0);
|
||||
/* RESET Receiver and Transmitter */
|
||||
cs->writeisac(cs, ISAC_CMDR, 0x41);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (0);
|
||||
case CARD_TEST:
|
||||
return (0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
|
||||
int ret;
|
||||
u_long flags;
|
||||
|
||||
if ((ic->command == ISDN_CMD_IOCTL) && (ic->arg == 9)) {
|
||||
ret = isar_auxcmd(cs, ic);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (!ret) {
|
||||
reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET |
|
||||
ISURF_ARCOFI_RESET);
|
||||
initisac(cs);
|
||||
cs->writeisac(cs, ISAC_MASK, 0);
|
||||
cs->writeisac(cs, ISAC_CMDR, 0x41);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return (ret);
|
||||
}
|
||||
return (isar_auxcmd(cs, ic));
|
||||
}
|
||||
|
||||
#ifdef __ISAPNP__
|
||||
static struct pnp_card *pnp_c = NULL;
|
||||
#endif
|
||||
|
||||
int setup_isurf(struct IsdnCard *card)
|
||||
{
|
||||
int ver;
|
||||
struct IsdnCardState *cs = card->cs;
|
||||
char tmp[64];
|
||||
|
||||
strcpy(tmp, ISurf_revision);
|
||||
printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp));
|
||||
|
||||
if (cs->typ != ISDN_CTYPE_ISURF)
|
||||
return (0);
|
||||
if (card->para[1] && card->para[2]) {
|
||||
cs->hw.isurf.reset = card->para[1];
|
||||
cs->hw.isurf.phymem = card->para[2];
|
||||
cs->irq = card->para[0];
|
||||
} else {
|
||||
#ifdef __ISAPNP__
|
||||
if (isapnp_present()) {
|
||||
struct pnp_dev *pnp_d = NULL;
|
||||
int err;
|
||||
|
||||
cs->subtyp = 0;
|
||||
if ((pnp_c = pnp_find_card(
|
||||
ISAPNP_VENDOR('S', 'I', 'E'),
|
||||
ISAPNP_FUNCTION(0x0010), pnp_c))) {
|
||||
if (!(pnp_d = pnp_find_dev(pnp_c,
|
||||
ISAPNP_VENDOR('S', 'I', 'E'),
|
||||
ISAPNP_FUNCTION(0x0010), pnp_d))) {
|
||||
printk(KERN_ERR "ISurfPnP: PnP error card found, no device\n");
|
||||
return (0);
|
||||
}
|
||||
pnp_disable_dev(pnp_d);
|
||||
err = pnp_activate_dev(pnp_d);
|
||||
if (err < 0) {
|
||||
pr_warn("%s: pnp_activate_dev ret=%d\n",
|
||||
__func__, err);
|
||||
return 0;
|
||||
}
|
||||
cs->hw.isurf.reset = pnp_port_start(pnp_d, 0);
|
||||
cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1);
|
||||
cs->irq = pnp_irq(pnp_d, 0);
|
||||
if (cs->irq == -1 || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) {
|
||||
printk(KERN_ERR "ISurfPnP:some resources are missing %d/%x/%lx\n",
|
||||
cs->irq, cs->hw.isurf.reset, cs->hw.isurf.phymem);
|
||||
pnp_disable_dev(pnp_d);
|
||||
return (0);
|
||||
}
|
||||
} else {
|
||||
printk(KERN_INFO "ISurfPnP: no ISAPnP card found\n");
|
||||
return (0);
|
||||
}
|
||||
} else {
|
||||
printk(KERN_INFO "ISurfPnP: no ISAPnP bus found\n");
|
||||
return (0);
|
||||
}
|
||||
#else
|
||||
printk(KERN_WARNING "HiSax: Siemens I-Surf port/mem not set\n");
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
if (!request_region(cs->hw.isurf.reset, 1, "isurf isdn")) {
|
||||
printk(KERN_WARNING
|
||||
"HiSax: Siemens I-Surf config port %x already in use\n",
|
||||
cs->hw.isurf.reset);
|
||||
return (0);
|
||||
}
|
||||
if (!request_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE, "isurf iomem")) {
|
||||
printk(KERN_WARNING "HiSax: Siemens I-Surf memory region "
|
||||
"%lx-%lx already in use\n",
|
||||
cs->hw.isurf.phymem,
|
||||
cs->hw.isurf.phymem + ISURF_IOMEM_SIZE);
|
||||
release_region(cs->hw.isurf.reset, 1);
|
||||
return (0);
|
||||
}
|
||||
cs->hw.isurf.isar = ioremap(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
|
||||
cs->hw.isurf.isac = cs->hw.isurf.isar + ISURF_ISAC_OFFSET;
|
||||
printk(KERN_INFO
|
||||
"ISurf: defined at 0x%x 0x%lx IRQ %d\n",
|
||||
cs->hw.isurf.reset,
|
||||
cs->hw.isurf.phymem,
|
||||
cs->irq);
|
||||
|
||||
setup_isac(cs);
|
||||
cs->cardmsg = &ISurf_card_msg;
|
||||
cs->irq_func = &isurf_interrupt;
|
||||
cs->auxcmd = &isurf_auxcmd;
|
||||
cs->readisac = &ReadISAC;
|
||||
cs->writeisac = &WriteISAC;
|
||||
cs->readisacfifo = &ReadISACfifo;
|
||||
cs->writeisacfifo = &WriteISACfifo;
|
||||
cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r;
|
||||
cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r;
|
||||
test_and_set_bit(HW_ISAR, &cs->HW_Flags);
|
||||
ISACVersion(cs, "ISurf:");
|
||||
cs->BC_Read_Reg = &ReadISAR;
|
||||
cs->BC_Write_Reg = &WriteISAR;
|
||||
cs->BC_Send_Data = &isar_fill_fifo;
|
||||
ver = ISARVersion(cs, "ISurf:");
|
||||
if (ver < 0) {
|
||||
printk(KERN_WARNING
|
||||
"ISurf: wrong ISAR version (ret = %d)\n", ver);
|
||||
release_io_isurf(cs);
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче