mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
staging: remove isdn capi drivers
As described in drivers/staging/isdn/TODO, the drivers are all assumed to be unmaintained and unused now, with gigaset being the last one to stop being maintained after Paul Bolle lost access to an ISDN network. The CAPI subsystem remains for now, as it is still required by bluetooth/cmtp. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Link: https://lore.kernel.org/r/20191210210455.3475361-1-arnd@arndb.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
4d17363db0
commit
f10870b05d
@ -1,246 +0,0 @@
|
||||
================================
|
||||
Driver for active AVM Controller
|
||||
================================
|
||||
|
||||
The driver provides a kernel capi2.0 Interface (kernelcapi) and
|
||||
on top of this a User-Level-CAPI2.0-interface (capi)
|
||||
and a driver to connect isdn4linux with CAPI2.0 (capidrv).
|
||||
The lowlevel interface can be used to implement a CAPI2.0
|
||||
also for passive cards since July 1999.
|
||||
|
||||
The author can be reached at calle@calle.in-berlin.de.
|
||||
The command avmcapictrl is part of the isdn4k-utils.
|
||||
t4-files can be found at ftp://ftp.avm.de/cardware/b1/linux/firmware
|
||||
|
||||
Currently supported cards:
|
||||
|
||||
- B1 ISA (all versions)
|
||||
- B1 PCI
|
||||
- T1/T1B (HEMA card)
|
||||
- M1
|
||||
- M2
|
||||
- B1 PCMCIA
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
You need at least /dev/capi20 to load the firmware.
|
||||
|
||||
::
|
||||
|
||||
mknod /dev/capi20 c 68 0
|
||||
mknod /dev/capi20.00 c 68 1
|
||||
mknod /dev/capi20.01 c 68 2
|
||||
.
|
||||
.
|
||||
.
|
||||
mknod /dev/capi20.19 c 68 20
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
To use the card you need the t4-files to download the firmware.
|
||||
AVM GmbH provides several t4-files for the different D-channel
|
||||
protocols (b1.t4 for Euro-ISDN). Install these file in /lib/isdn.
|
||||
|
||||
if you configure as modules load the modules this way::
|
||||
|
||||
insmod /lib/modules/current/misc/capiutil.o
|
||||
insmod /lib/modules/current/misc/b1.o
|
||||
insmod /lib/modules/current/misc/kernelcapi.o
|
||||
insmod /lib/modules/current/misc/capidrv.o
|
||||
insmod /lib/modules/current/misc/capi.o
|
||||
|
||||
if you have an B1-PCI card load the module b1pci.o::
|
||||
|
||||
insmod /lib/modules/current/misc/b1pci.o
|
||||
|
||||
and load the firmware with::
|
||||
|
||||
avmcapictrl load /lib/isdn/b1.t4 1
|
||||
|
||||
if you have an B1-ISA card load the module b1isa.o
|
||||
and add the card by calling::
|
||||
|
||||
avmcapictrl add 0x150 15
|
||||
|
||||
and load the firmware by calling::
|
||||
|
||||
avmcapictrl load /lib/isdn/b1.t4 1
|
||||
|
||||
if you have an T1-ISA card load the module t1isa.o
|
||||
and add the card by calling::
|
||||
|
||||
avmcapictrl add 0x450 15 T1 0
|
||||
|
||||
and load the firmware by calling::
|
||||
|
||||
avmcapictrl load /lib/isdn/t1.t4 1
|
||||
|
||||
if you have an PCMCIA card (B1/M1/M2) load the module b1pcmcia.o
|
||||
before you insert the card.
|
||||
|
||||
Leased Lines with B1
|
||||
--------------------
|
||||
|
||||
Init card and load firmware.
|
||||
|
||||
For an D64S use "FV: 1" as phone number
|
||||
|
||||
For an D64S2 use "FV: 1" and "FV: 2" for multilink
|
||||
or "FV: 1,2" to use CAPI channel bundling.
|
||||
|
||||
/proc-Interface
|
||||
-----------------
|
||||
|
||||
/proc/capi::
|
||||
|
||||
dr-xr-xr-x 2 root root 0 Jul 1 14:03 .
|
||||
dr-xr-xr-x 82 root root 0 Jun 30 19:08 ..
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 applications
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 applstats
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 capi20
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 capidrv
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 controller
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 contrstats
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 driver
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 ncci
|
||||
-r--r--r-- 1 root root 0 Jul 1 14:03 users
|
||||
|
||||
/proc/capi/applications:
|
||||
applid level3cnt datablkcnt datablklen ncci-cnt recvqueuelen
|
||||
level3cnt:
|
||||
capi_register parameter
|
||||
datablkcnt:
|
||||
capi_register parameter
|
||||
ncci-cnt:
|
||||
current number of nccis (connections)
|
||||
recvqueuelen:
|
||||
number of messages on receive queue
|
||||
|
||||
for example::
|
||||
|
||||
1 -2 16 2048 1 0
|
||||
2 2 7 2048 1 0
|
||||
|
||||
/proc/capi/applstats:
|
||||
applid recvctlmsg nrecvdatamsg nsentctlmsg nsentdatamsg
|
||||
recvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
recvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
sentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
sentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example::
|
||||
|
||||
1 2057 1699 1721 1699
|
||||
|
||||
/proc/capi/capi20: statistics of capi.o (/dev/capi20)
|
||||
minor nopen nrecvdropmsg nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg
|
||||
minor:
|
||||
minor device number of capi device
|
||||
nopen:
|
||||
number of calls to devices open
|
||||
nrecvdropmsg:
|
||||
capi messages dropped (messages in recvqueue in close)
|
||||
nrecvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
nrecvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
nsentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
nsentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example::
|
||||
|
||||
1 2 18 0 16 2
|
||||
|
||||
/proc/capi/capidrv: statistics of capidrv.o (capi messages)
|
||||
nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg
|
||||
nrecvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
nrecvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
nsentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
nsentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example:
|
||||
2780 2226 2256 2226
|
||||
|
||||
/proc/capi/controller:
|
||||
controller drivername state cardname controllerinfo
|
||||
|
||||
for example::
|
||||
|
||||
1 b1pci running b1pci-e000 B1 3.07-01 0xe000 19
|
||||
2 t1isa running t1isa-450 B1 3.07-01 0x450 11 0
|
||||
3 b1pcmcia running m2-150 B1 3.07-01 0x150 5
|
||||
|
||||
/proc/capi/contrstats:
|
||||
controller nrecvctlmsg nrecvdatamsg sentctlmsg sentdatamsg
|
||||
nrecvctlmsg:
|
||||
capi messages received without DATA_B3_IND
|
||||
nrecvdatamsg:
|
||||
capi DATA_B3_IND received
|
||||
nsentctlmsg:
|
||||
capi messages sent without DATA_B3_REQ
|
||||
nsentdatamsg:
|
||||
capi DATA_B3_REQ sent
|
||||
|
||||
for example::
|
||||
|
||||
1 2845 2272 2310 2274
|
||||
2 2 0 2 0
|
||||
3 2 0 2 0
|
||||
|
||||
/proc/capi/driver:
|
||||
drivername ncontroller
|
||||
|
||||
for example::
|
||||
|
||||
b1pci 1
|
||||
t1isa 1
|
||||
b1pcmcia 1
|
||||
b1isa 0
|
||||
|
||||
/proc/capi/ncci:
|
||||
apllid ncci winsize sendwindow
|
||||
|
||||
for example::
|
||||
|
||||
1 0x10101 8 0
|
||||
|
||||
/proc/capi/users: kernelmodules that use the kernelcapi.
|
||||
name
|
||||
|
||||
for example::
|
||||
|
||||
capidrv
|
||||
capi20
|
||||
|
||||
Questions
|
||||
---------
|
||||
|
||||
Check out the FAQ (ftp.isdn4linux.de) or subscribe to the
|
||||
linux-avmb1@calle.in-berlin.de mailing list by sending
|
||||
a mail to majordomo@calle.in-berlin.de with
|
||||
subscribe linux-avmb1
|
||||
in the body.
|
||||
|
||||
German documentation and several scripts can be found at
|
||||
ftp://ftp.avm.de/cardware/b1/linux/
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
If you find any please let me know.
|
||||
|
||||
Enjoy,
|
||||
|
||||
Carsten Paeth (calle@calle.in-berlin.de)
|
@ -1,465 +0,0 @@
|
||||
==========================
|
||||
GigaSet 307x Device Driver
|
||||
==========================
|
||||
|
||||
1. Requirements
|
||||
=================
|
||||
|
||||
1.1. Hardware
|
||||
-------------
|
||||
|
||||
This driver supports the connection of the Gigaset 307x/417x family of
|
||||
ISDN DECT bases via Gigaset M101 Data, Gigaset M105 Data or direct USB
|
||||
connection. The following devices are reported to be compatible:
|
||||
|
||||
Bases:
|
||||
- Siemens Gigaset 3070/3075 isdn
|
||||
- Siemens Gigaset 4170/4175 isdn
|
||||
- Siemens Gigaset SX205/255
|
||||
- Siemens Gigaset SX353
|
||||
- T-Com Sinus 45 [AB] isdn
|
||||
- T-Com Sinus 721X[A] [SE]
|
||||
- Vox Chicago 390 ISDN (KPN Telecom)
|
||||
|
||||
RS232 data boxes:
|
||||
- Siemens Gigaset M101 Data
|
||||
- T-Com Sinus 45 Data 1
|
||||
|
||||
USB data boxes:
|
||||
- Siemens Gigaset M105 Data
|
||||
- Siemens Gigaset USB Adapter DECT
|
||||
- T-Com Sinus 45 Data 2
|
||||
- T-Com Sinus 721 data
|
||||
- Chicago 390 USB (KPN)
|
||||
|
||||
See also http://www.erbze.info/sinus_gigaset.htm
|
||||
(archived at https://web.archive.org/web/20100717020421/http://www.erbze.info:80/sinus_gigaset.htm ) and
|
||||
http://gigaset307x.sourceforge.net/
|
||||
|
||||
We had also reports from users of Gigaset M105 who could use the drivers
|
||||
with SX 100 and CX 100 ISDN bases (only in unimodem mode, see section 2.5.)
|
||||
If you have another device that works with our driver, please let us know.
|
||||
|
||||
Chances of getting an USB device to work are good if the output of::
|
||||
|
||||
lsusb
|
||||
|
||||
at the command line contains one of the following::
|
||||
|
||||
ID 0681:0001
|
||||
ID 0681:0002
|
||||
ID 0681:0009
|
||||
ID 0681:0021
|
||||
ID 0681:0022
|
||||
|
||||
1.2. Software
|
||||
-------------
|
||||
|
||||
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/
|
||||
which provide access to additional device specific functions like SMS,
|
||||
phonebook or call journal.
|
||||
|
||||
|
||||
2. How to use the driver
|
||||
==========================
|
||||
|
||||
2.1. Modules
|
||||
------------
|
||||
|
||||
For the devices to work, the proper kernel modules have to be loaded.
|
||||
This normally happens automatically when the system detects the USB
|
||||
device (base, M105) or when the line discipline is attached (M101). It
|
||||
can also be triggered manually using the modprobe(8) command, for example
|
||||
for troubleshooting or to pass module parameters.
|
||||
|
||||
The module ser_gigaset provides a serial line discipline N_GIGASET_M101
|
||||
which uses the regular serial port driver to access the device, and must
|
||||
therefore be attached to the serial device to which the M101 is connected.
|
||||
The ldattach(8) command (included in util-linux-ng release 2.14 or later)
|
||||
can be used for that purpose, for example::
|
||||
|
||||
ldattach GIGASET_M101 /dev/ttyS1
|
||||
|
||||
This will open the device file, attach the line discipline to it, and
|
||||
then sleep in the background, keeping the device open so that the line
|
||||
discipline remains active. To deactivate it, kill the daemon, for example
|
||||
with::
|
||||
|
||||
killall ldattach
|
||||
|
||||
before disconnecting the device. To have this happen automatically at
|
||||
system startup/shutdown on an LSB compatible system, create and activate
|
||||
an appropriate LSB startup script /etc/init.d/gigaset. (The init name
|
||||
'gigaset' is officially assigned to this project by LANANA.)
|
||||
Alternatively, just add the 'ldattach' command line to /etc/rc.local.
|
||||
|
||||
The modules accept the following parameters:
|
||||
|
||||
=============== ========== ==========================================
|
||||
Module Parameter Meaning
|
||||
|
||||
gigaset debug debug level (see section 3.2.)
|
||||
|
||||
startmode initial operation mode (see section 2.5.):
|
||||
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
|
||||
|
||||
=============== ========== ==========================================
|
||||
|
||||
Depending on your distribution you may want to create a separate module
|
||||
configuration file like /etc/modprobe.d/gigaset.conf for these.
|
||||
|
||||
2.2. Device nodes for user space programs
|
||||
-----------------------------------------
|
||||
|
||||
The device can be accessed from user space (eg. by the user space tools
|
||||
mentioned in 1.2.) through the device nodes:
|
||||
|
||||
- /dev/ttyGS0 for M101 (RS232 data boxes)
|
||||
- /dev/ttyGU0 for M105 (USB data boxes)
|
||||
- /dev/ttyGB0 for the base driver (direct USB connection)
|
||||
|
||||
If you connect more than one device of a type, they will get consecutive
|
||||
device nodes, eg. /dev/ttyGU1 for a second M105.
|
||||
|
||||
You can also set a "default device" for the user space tools to use when
|
||||
no device node is given as parameter, by creating a symlink /dev/ttyG to
|
||||
one of them, eg.::
|
||||
|
||||
ln -s /dev/ttyGB0 /dev/ttyG
|
||||
|
||||
The devices accept the following device specific ioctl calls
|
||||
(defined in gigaset_dev.h):
|
||||
|
||||
``ioctl(int fd, GIGASET_REDIR, int *cmd);``
|
||||
|
||||
If cmd==1, the device is set to be controlled exclusively through the
|
||||
character device node; access from the ISDN subsystem is blocked.
|
||||
|
||||
If cmd==0, the device is set to be used from the ISDN subsystem and does
|
||||
not communicate through the character device node.
|
||||
|
||||
``ioctl(int fd, GIGASET_CONFIG, int *cmd);``
|
||||
|
||||
(ser_gigaset and usb_gigaset only)
|
||||
|
||||
If cmd==1, the device is set to adapter configuration mode where commands
|
||||
are interpreted by the M10x DECT adapter itself instead of being
|
||||
forwarded to the base station. In this mode, the device accepts the
|
||||
commands described in Siemens document "AT-Kommando Alignment M10x Data"
|
||||
for setting the operation mode, associating with a base station and
|
||||
querying parameters like field strengh and signal quality.
|
||||
|
||||
Note that there is no ioctl command for leaving adapter configuration
|
||||
mode and returning to regular operation. In order to leave adapter
|
||||
configuration mode, write the command ATO to the device.
|
||||
|
||||
``ioctl(int fd, GIGASET_BRKCHARS, unsigned char brkchars[6]);``
|
||||
|
||||
(usb_gigaset only)
|
||||
|
||||
Set the break characters on an M105's internal serial adapter to the six
|
||||
bytes stored in brkchars[]. Unused bytes should be set to zero.
|
||||
|
||||
ioctl(int fd, GIGASET_VERSION, unsigned version[4]);
|
||||
Retrieve version information from the driver. version[0] must be set to
|
||||
one of:
|
||||
|
||||
- GIGVER_DRIVER: retrieve driver version
|
||||
- GIGVER_COMPAT: retrieve interface compatibility version
|
||||
- GIGVER_FWBASE: retrieve the firmware version of the base
|
||||
|
||||
Upon return, version[] is filled with the requested version information.
|
||||
|
||||
2.3. 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.
|
||||
|
||||
Most distributions handle loading and unloading of the various CAPI
|
||||
modules automatically via the command capiinit(1) from the capi4k-utils
|
||||
package or a similar mechanism. Note that capiinit(1) cannot unload the
|
||||
Gigaset drivers because it doesn't support more than one module per
|
||||
driver.
|
||||
|
||||
2.5. Unimodem mode
|
||||
------------------
|
||||
|
||||
In this mode the device works like a modem connected to a serial port
|
||||
(the /dev/ttyGU0, ... mentioned above) which understands the commands::
|
||||
|
||||
ATZ init, reset
|
||||
=> OK or ERROR
|
||||
ATD
|
||||
ATDT dial
|
||||
=> OK, CONNECT,
|
||||
BUSY,
|
||||
NO DIAL TONE,
|
||||
NO CARRIER,
|
||||
NO ANSWER
|
||||
<pause>+++<pause> change to command mode when connected
|
||||
ATH hangup
|
||||
|
||||
You can use some configuration tool of your distribution to configure this
|
||||
"modem" or configure pppd/wvdial manually. There are some example ppp
|
||||
configuration files and chat scripts in the gigaset-VERSION/ppp directory
|
||||
in the driver packages from https://sourceforge.net/projects/gigaset307x/.
|
||||
Please note that the USB drivers are not able to change the state of the
|
||||
control lines. This means you must use "Stupid Mode" if you are using
|
||||
wvdial or you should use the nocrtscts option of pppd.
|
||||
You must also assure that the ppp_async module is loaded with the parameter
|
||||
flag_time=0. You can do this e.g. by adding a line like::
|
||||
|
||||
options ppp_async flag_time=0
|
||||
|
||||
to an appropriate module configuration file, like::
|
||||
|
||||
/etc/modprobe.d/gigaset.conf.
|
||||
|
||||
Unimodem mode is needed for making some devices [e.g. SX100] work which
|
||||
do not support the regular Gigaset command set. If debug output (see
|
||||
section 3.2.) shows something like this when dialing::
|
||||
|
||||
CMD Received: ERROR
|
||||
Available Params: 0
|
||||
Connection State: 0, Response: -1
|
||||
gigaset_process_response: resp_code -1 in ConState 0 !
|
||||
Timeout occurred
|
||||
|
||||
then switching to unimodem mode may help.
|
||||
|
||||
If you have installed the command line tool gigacontr, you can enter
|
||||
unimodem mode using::
|
||||
|
||||
gigacontr --mode unimodem
|
||||
|
||||
You can switch back using::
|
||||
|
||||
gigacontr --mode isdn
|
||||
|
||||
You can also put the driver directly into Unimodem mode when it's loaded,
|
||||
by passing the module parameter startmode=0 to the hardware specific
|
||||
module, e.g.::
|
||||
|
||||
modprobe usb_gigaset startmode=0
|
||||
|
||||
or by adding a line like::
|
||||
|
||||
options usb_gigaset startmode=0
|
||||
|
||||
to an appropriate module configuration file, like::
|
||||
|
||||
/etc/modprobe.d/gigaset.conf
|
||||
|
||||
2.6. Call-ID (CID) mode
|
||||
-----------------------
|
||||
|
||||
Call-IDs are numbers used to tag commands to, and responses from, the
|
||||
Gigaset base in order to support the simultaneous handling of multiple
|
||||
ISDN calls. Their use can be enabled ("CID mode") or disabled ("Unimodem
|
||||
mode"). Without Call-IDs (in Unimodem mode), only a very limited set of
|
||||
functions is available. It allows outgoing data connections only, but
|
||||
does not signal incoming calls or other base events.
|
||||
|
||||
DECT cordless data devices (M10x) permanently occupy the cordless
|
||||
connection to the base while Call-IDs are activated. As the Gigaset
|
||||
bases only support one DECT data connection at a time, this prevents
|
||||
other DECT cordless data devices from accessing the base.
|
||||
|
||||
During active operation, the driver switches to the necessary mode
|
||||
automatically. However, for the reasons above, the mode chosen when
|
||||
the device is not in use (idle) can be selected by the user.
|
||||
|
||||
- If you want to receive incoming calls, you can use the default
|
||||
settings (CID mode).
|
||||
- If you have several DECT data devices (M10x) which you want to use
|
||||
in turn, select Unimodem mode by passing the parameter "cidmode=0" to
|
||||
the appropriate driver module (ser_gigaset or usb_gigaset).
|
||||
|
||||
If you want both of these at once, you are out of luck.
|
||||
|
||||
You can also use the tty class parameter "cidmode" of the device to
|
||||
change its CID mode while the driver is loaded, eg.::
|
||||
|
||||
echo 0 > /sys/class/tty/ttyGU0/cidmode
|
||||
|
||||
2.7. Dialing Numbers
|
||||
--------------------
|
||||
provided by an application for dialing out must
|
||||
be a public network number according to the local dialing plan, without
|
||||
any dial prefix for getting an outside line.
|
||||
|
||||
Internal calls can be made by providing an internal extension number
|
||||
prefixed with ``**`` (two asterisks) as the called party number. So to dial
|
||||
eg. the first registered DECT handset, give ``**11`` as the called party
|
||||
number. Dialing ``***`` (three asterisks) calls all extensions
|
||||
simultaneously (global call).
|
||||
|
||||
Unimodem mode does not support internal calls.
|
||||
|
||||
2.8. Unregistered Wireless Devices (M101/M105)
|
||||
----------------------------------------------
|
||||
|
||||
The main purpose of the ser_gigaset and usb_gigaset drivers is to allow
|
||||
the M101 and M105 wireless devices to be used as ISDN devices for ISDN
|
||||
connections through a Gigaset base. Therefore they assume that the device
|
||||
is registered to a DECT base.
|
||||
|
||||
If the M101/M105 device is not registered to a base, initialization of
|
||||
the device fails, and a corresponding error message is logged by the
|
||||
driver. In that situation, a restricted set of functions is available
|
||||
which includes, in particular, those necessary for registering the device
|
||||
to a base or for switching it between Fixed Part and Portable Part
|
||||
modes. See the gigacontr(8) manpage for details.
|
||||
|
||||
3. Troubleshooting
|
||||
====================
|
||||
|
||||
3.1. Solutions to frequently reported problems
|
||||
----------------------------------------------
|
||||
|
||||
Problem:
|
||||
You have a slow provider and isdn4linux gives up dialing too early.
|
||||
Solution:
|
||||
Load the isdn module using the dialtimeout option. You can do this e.g.
|
||||
by adding a line like::
|
||||
|
||||
options isdn dialtimeout=15
|
||||
|
||||
to /etc/modprobe.d/gigaset.conf or a similar file.
|
||||
|
||||
Problem:
|
||||
The isdnlog program emits error messages or just doesn't work.
|
||||
Solution:
|
||||
Isdnlog supports only the HiSax driver. Do not attempt to use it with
|
||||
other drivers such as Gigaset.
|
||||
|
||||
Problem:
|
||||
You have two or more DECT data adapters (M101/M105) and only the
|
||||
first one you turn on works.
|
||||
Solution:
|
||||
Select Unimodem mode for all DECT data adapters. (see section 2.5.)
|
||||
|
||||
Problem:
|
||||
Messages like this::
|
||||
|
||||
usb_gigaset 3-2:1.0: Could not initialize the device.
|
||||
|
||||
appear in your syslog.
|
||||
Solution:
|
||||
Check whether your M10x wireless device is correctly registered to the
|
||||
Gigaset base. (see section 2.7.)
|
||||
|
||||
3.2. Telling the driver to provide more information
|
||||
---------------------------------------------------
|
||||
Building the driver with the "Gigaset debugging" kernel configuration
|
||||
option (CONFIG_GIGASET_DEBUG) gives it the ability to produce additional
|
||||
information useful for debugging.
|
||||
|
||||
You can control the amount of debugging information the driver produces by
|
||||
writing an appropriate value to /sys/module/gigaset/parameters/debug,
|
||||
e.g.::
|
||||
|
||||
echo 0 > /sys/module/gigaset/parameters/debug
|
||||
|
||||
switches off debugging output completely,
|
||||
|
||||
::
|
||||
|
||||
echo 0x302020 > /sys/module/gigaset/parameters/debug
|
||||
|
||||
enables a reasonable set of debugging output messages. These values are
|
||||
bit patterns where every bit controls a certain type of debugging output.
|
||||
See the constants DEBUG_* in the source file gigaset.h for details.
|
||||
|
||||
The initial value can be set using the debug parameter when loading the
|
||||
module "gigaset", e.g. by adding a line::
|
||||
|
||||
options gigaset debug=0
|
||||
|
||||
to your module configuration file, eg. /etc/modprobe.d/gigaset.conf
|
||||
|
||||
Generated debugging information can be found
|
||||
- as output of the command::
|
||||
|
||||
dmesg
|
||||
|
||||
- in system log files written by your syslog daemon, usually
|
||||
in /var/log/, e.g. /var/log/messages.
|
||||
|
||||
3.3. Reporting problems and bugs
|
||||
--------------------------------
|
||||
If you can't solve problems with the driver on your own, feel free to
|
||||
use one of the forums, bug trackers, or mailing lists on
|
||||
|
||||
https://sourceforge.net/projects/gigaset307x
|
||||
|
||||
or write an electronic mail to the maintainers.
|
||||
|
||||
Try to provide as much information as possible, such as
|
||||
|
||||
- distribution
|
||||
- kernel version (uname -r)
|
||||
- gcc version (gcc --version)
|
||||
- hardware architecture (uname -m, ...)
|
||||
- type and firmware version of your device (base and wireless module,
|
||||
if any)
|
||||
- output of "lsusb -v" (if using an USB device)
|
||||
- error messages
|
||||
- relevant system log messages (it would help if you activate debug
|
||||
output as described in 3.2.)
|
||||
|
||||
For help with general configuration problems not specific to our driver,
|
||||
such as isdn4linux and network configuration issues, please refer to the
|
||||
appropriate forums and newsgroups.
|
||||
|
||||
3.4. Reporting problem solutions
|
||||
--------------------------------
|
||||
If you solved a problem with our drivers, wrote startup scripts for your
|
||||
distribution, ... feel free to contact us (using one of the places
|
||||
mentioned in 3.3.). We'd like to add scripts, hints, documentation
|
||||
to the driver and/or the project web page.
|
||||
|
||||
|
||||
4. Links, other software
|
||||
==========================
|
||||
|
||||
- Sourceforge project developing this driver and associated tools
|
||||
https://sourceforge.net/projects/gigaset307x
|
||||
- Yahoo! Group on the Siemens Gigaset family of devices
|
||||
https://de.groups.yahoo.com/group/Siemens-Gigaset
|
||||
- Siemens Gigaset/T-Sinus compatibility table
|
||||
http://www.erbze.info/sinus_gigaset.htm
|
||||
(archived at https://web.archive.org/web/20100717020421/http://www.erbze.info:80/sinus_gigaset.htm )
|
||||
|
||||
|
||||
5. Credits
|
||||
============
|
||||
|
||||
Thanks to
|
||||
|
||||
Karsten Keil
|
||||
for his help with isdn4linux
|
||||
Deti Fliegl
|
||||
for his base driver code
|
||||
Dennis Dietrich
|
||||
for his kernel 2.6 patches
|
||||
Andreas Rummel
|
||||
for his work and logs to get unimodem mode working
|
||||
Andreas Degert
|
||||
for his logs and patches to get cx 100 working
|
||||
Dietrich Feist
|
||||
for his generous donation of one M105 and two M101 cordless adapters
|
||||
Christoph Schweers
|
||||
for his generous donation of a M34 device
|
||||
|
||||
and all the other people who sent logs and other information.
|
@ -1,196 +0,0 @@
|
||||
============
|
||||
Hysdn Driver
|
||||
============
|
||||
|
||||
The hysdn driver has been written by
|
||||
Werner Cornelius (werner@isdn4linux.de or werner@titro.de)
|
||||
for Hypercope GmbH Aachen Germany. Hypercope agreed to publish this driver
|
||||
under the GNU General Public License.
|
||||
|
||||
The CAPI 2.0-support was added by Ulrich Albrecht (ualbrecht@hypercope.de)
|
||||
for Hypercope GmbH Aachen, Germany.
|
||||
|
||||
|
||||
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. About the driver
|
||||
|
||||
2. Loading/Unloading the driver
|
||||
|
||||
3. Entries in the /proc filesystem
|
||||
|
||||
4. The /proc/net/hysdn/cardconfX file
|
||||
|
||||
5. The /proc/net/hysdn/cardlogX file
|
||||
|
||||
6. Where to get additional info and help
|
||||
|
||||
|
||||
1. About the driver
|
||||
===================
|
||||
|
||||
The drivers/isdn/hysdn subdir contains a driver for HYPERCOPEs active
|
||||
PCI isdn cards Champ, Ergo and Metro. To enable support for this cards
|
||||
enable ISDN support in the kernel config and support for HYSDN cards in
|
||||
the active cards submenu. The driver may only be compiled and used if
|
||||
support for loadable modules and the process filesystem have been enabled.
|
||||
|
||||
These cards provide two different interfaces to the kernel. Without the
|
||||
optional CAPI 2.0 support, they register as ethernet card. IP-routing
|
||||
to a ISDN-destination is performed on the card itself. All necessary
|
||||
handlers for various protocols like ppp and others as well as config info
|
||||
and firmware may be fetched from Hypercopes WWW-Site www.hypercope.de.
|
||||
|
||||
With CAPI 2.0 support enabled, the card can also be used as a CAPI 2.0
|
||||
compliant devices with either CAPI 2.0 applications
|
||||
(check isdn4k-utils) or -using the capidrv module- as a regular
|
||||
isdn4linux device. This is done via the same mechanism as with the
|
||||
active AVM cards and in fact uses the same module.
|
||||
|
||||
|
||||
2. Loading/Unloading the driver
|
||||
===============================
|
||||
|
||||
The module has no command line parameters and auto detects up to 10 cards
|
||||
in the id-range 0-9.
|
||||
If a loaded driver shall be unloaded all open files in the /proc/net/hysdn
|
||||
subdir need to be closed and all ethernet interfaces allocated by this
|
||||
driver must be shut down. Otherwise the module counter will avoid a module
|
||||
unload.
|
||||
|
||||
If you are using the CAPI 2.0-interface, make sure to load/modprobe the
|
||||
kernelcapi-module first.
|
||||
|
||||
If you plan to use the capidrv-link to isdn4linux, make sure to load
|
||||
capidrv.o after all modules using this driver (i.e. after hysdn and
|
||||
any avm-specific modules).
|
||||
|
||||
3. Entries in the /proc filesystem
|
||||
==================================
|
||||
|
||||
When the module has been loaded it adds the directory hysdn in the
|
||||
/proc/net tree. This directory contains exactly 2 file entries for each
|
||||
card. One is called cardconfX and the other cardlogX, where X is the
|
||||
card id number from 0 to 9.
|
||||
The cards are numbered in the order found in the PCI config data.
|
||||
|
||||
4. The /proc/net/hysdn/cardconfX file
|
||||
=====================================
|
||||
|
||||
This file may be read to get by everyone to get info about the cards type,
|
||||
actual state, available features and used resources.
|
||||
The first 3 entries (id, bus and slot) are PCI info fields, the following
|
||||
type field gives the information about the cards type:
|
||||
|
||||
- 4 -> Ergo card (server card with 2 b-chans)
|
||||
- 5 -> Metro card (server card with 4 or 8 b-chans)
|
||||
- 6 -> Champ card (client card with 2 b-chans)
|
||||
|
||||
The following 3 fields show the hardware assignments for irq, iobase and the
|
||||
dual ported memory (dp-mem).
|
||||
|
||||
The fields b-chans and fax-chans announce the available card resources of
|
||||
this types for the user.
|
||||
|
||||
The state variable indicates the actual drivers state for this card with the
|
||||
following assignments.
|
||||
|
||||
- 0 -> card has not been booted since driver load
|
||||
- 1 -> card booting is actually in progess
|
||||
- 2 -> card is in an error state due to a previous boot failure
|
||||
- 3 -> card is booted and active
|
||||
|
||||
And the last field (device) shows the name of the ethernet device assigned
|
||||
to this card. Up to the first successful boot this field only shows a -
|
||||
to tell that no net device has been allocated up to now. Once a net device
|
||||
has been allocated it remains assigned to this card, even if a card is
|
||||
rebooted and an boot error occurs.
|
||||
|
||||
Writing to the cardconfX file boots the card or transfers config lines to
|
||||
the cards firmware. The type of data is automatically detected when the
|
||||
first data is written. Only root has write access to this file.
|
||||
The firmware boot files are normally called hyclient.pof for client cards
|
||||
and hyserver.pof for server cards.
|
||||
After successfully writing the boot file, complete config files or single
|
||||
config lines may be copied to this file.
|
||||
If an error occurs the return value given to the writing process has the
|
||||
following additional codes (decimal):
|
||||
|
||||
==== ============================================
|
||||
1000 Another process is currently bootng the card
|
||||
1001 Invalid firmware header
|
||||
1002 Boards dual-port RAM test failed
|
||||
1003 Internal firmware handler error
|
||||
1004 Boot image size invalid
|
||||
1005 First boot stage (bootstrap loader) failed
|
||||
1006 Second boot stage failure
|
||||
1007 Timeout waiting for card ready during boot
|
||||
1008 Operation only allowed in booted state
|
||||
1009 Config line too long
|
||||
1010 Invalid channel number
|
||||
1011 Timeout sending config data
|
||||
==== ============================================
|
||||
|
||||
Additional info about error reasons may be fetched from the log output.
|
||||
|
||||
5. The /proc/net/hysdn/cardlogX file
|
||||
====================================
|
||||
|
||||
The cardlogX file entry may be opened multiple for reading by everyone to
|
||||
get the cards and drivers log data. Card messages always start with the
|
||||
keyword LOG. All other lines are output from the driver.
|
||||
The driver log data may be redirected to the syslog by selecting the
|
||||
appropriate bitmask. The cards log messages will always be send to this
|
||||
interface but never to the syslog.
|
||||
|
||||
A root user may write a decimal or hex (with 0x) value t this file to select
|
||||
desired output options. As mentioned above the cards log dat is always
|
||||
written to the cardlog file independent of the following options only used
|
||||
to check and debug the driver itself:
|
||||
|
||||
For example::
|
||||
|
||||
echo "0x34560078" > /proc/net/hysdn/cardlog0
|
||||
|
||||
to output the hex log mask 34560078 for card 0.
|
||||
|
||||
The written value is regarded as an unsigned 32-Bit value, bit ored for
|
||||
desired output. The following bits are already assigned:
|
||||
|
||||
========== ============================================================
|
||||
0x80000000 All driver log data is alternatively via syslog
|
||||
0x00000001 Log memory allocation errors
|
||||
0x00000010 Firmware load start and close are logged
|
||||
0x00000020 Log firmware record parser
|
||||
0x00000040 Log every firmware write actions
|
||||
0x00000080 Log all card related boot messages
|
||||
0x00000100 Output all config data sent for debugging purposes
|
||||
0x00000200 Only non comment config lines are shown wth channel
|
||||
0x00000400 Additional conf log output
|
||||
0x00001000 Log the asynchronous scheduler actions (config and log)
|
||||
0x00100000 Log all open and close actions to /proc/net/hysdn/card files
|
||||
0x00200000 Log all actions from /proc file entries
|
||||
0x00010000 Log network interface init and deinit
|
||||
========== ============================================================
|
||||
|
||||
6. Where to get additional info and help
|
||||
========================================
|
||||
|
||||
If you have any problems concerning the driver or configuration contact
|
||||
the Hypercope support team (support@hypercope.de) and or the authors
|
||||
Werner Cornelius (werner@isdn4linux or cornelius@titro.de) or
|
||||
Ulrich Albrecht (ualbrecht@hypercope.de).
|
@ -9,9 +9,6 @@ ISDN
|
||||
|
||||
interface_capi
|
||||
|
||||
avmb1
|
||||
gigaset
|
||||
hysdn
|
||||
m_isdn
|
||||
|
||||
credits
|
||||
|
@ -132,7 +132,6 @@ Code Seq# Include File Comments
|
||||
'F' 80-8F linux/arcfb.h conflict!
|
||||
'F' DD video/sstfb.h conflict!
|
||||
'G' 00-3F drivers/misc/sgi-gru/grulib.h conflict!
|
||||
'G' 00-0F linux/gigaset_dev.h conflict!
|
||||
'H' 00-7F linux/hiddev.h conflict!
|
||||
'H' 00-0F linux/hidraw.h conflict!
|
||||
'H' 01 linux/mei.h conflict!
|
||||
|
@ -8804,7 +8804,7 @@ S: Maintained
|
||||
F: drivers/isdn/mISDN
|
||||
F: drivers/isdn/hardware
|
||||
|
||||
ISDN/CAPI SUBSYSTEM
|
||||
ISDN/CMTP OVER BLUETOOTH
|
||||
M: Karsten Keil <isdn@linux-pingi.de>
|
||||
L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
|
||||
L: netdev@vger.kernel.org
|
||||
@ -8812,7 +8812,6 @@ W: http://www.isdn4linux.de
|
||||
S: Odd Fixes
|
||||
F: Documentation/isdn/
|
||||
F: drivers/isdn/capi/
|
||||
F: drivers/staging/isdn/
|
||||
F: net/bluetooth/cmtp/
|
||||
F: include/linux/isdn/
|
||||
F: include/uapi/linux/isdn/
|
||||
|
@ -116,8 +116,6 @@ source "drivers/staging/fieldbus/Kconfig"
|
||||
|
||||
source "drivers/staging/kpc2000/Kconfig"
|
||||
|
||||
source "drivers/staging/isdn/Kconfig"
|
||||
|
||||
source "drivers/staging/wusbcore/Kconfig"
|
||||
source "drivers/staging/uwb/Kconfig"
|
||||
|
||||
|
@ -48,7 +48,6 @@ obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
|
||||
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
|
||||
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
|
||||
obj-$(CONFIG_KPC2000) += kpc2000/
|
||||
obj-$(CONFIG_ISDN_CAPI) += isdn/
|
||||
obj-$(CONFIG_UWB) += uwb/
|
||||
obj-$(CONFIG_USB_WUSB) += wusbcore/
|
||||
obj-$(CONFIG_EXFAT_FS) += exfat/
|
||||
|
@ -1,12 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menu "ISDN CAPI drivers"
|
||||
depends on ISDN_CAPI
|
||||
|
||||
source "drivers/staging/isdn/avm/Kconfig"
|
||||
|
||||
source "drivers/staging/isdn/gigaset/Kconfig"
|
||||
|
||||
source "drivers/staging/isdn/hysdn/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -1,8 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for the kernel ISDN subsystem and device drivers.
|
||||
|
||||
# Object files in subdirectories
|
||||
|
||||
obj-$(CONFIG_CAPI_AVM) += avm/
|
||||
obj-$(CONFIG_HYSDN) += hysdn/
|
||||
obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset/
|
@ -1,22 +0,0 @@
|
||||
TODO: Remove in late 2019 unless there are users
|
||||
|
||||
|
||||
I tried to find any indication of whether the capi drivers are
|
||||
still in use, and have not found anything from a long time ago.
|
||||
|
||||
With public ISDN networks almost completely shut down over the past 12
|
||||
months, there is very little you can actually do with this hardware. The
|
||||
main remaining use case would be to connect ISDN voice phones to an
|
||||
in-house installation with Asterisk or LCR, but anyone trying this in
|
||||
turn seems to be using either the mISDN driver stack, or out-of-tree
|
||||
drivers from the hardware vendors.
|
||||
|
||||
I may of course have missed something, so I would suggest moving
|
||||
these into drivers/staging/ just in case someone still uses one
|
||||
of the three remaining in-kernel drivers (avm, hysdn, gigaset).
|
||||
|
||||
If nobody complains, we can remove them entirely in six months,
|
||||
or otherwise move the core code and any drivers that are still
|
||||
needed back into drivers/isdn.
|
||||
|
||||
Arnd Bergmann <arnd@arndb.de>
|
@ -1,65 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# ISDN AVM drivers
|
||||
#
|
||||
|
||||
menuconfig CAPI_AVM
|
||||
bool "Active AVM cards"
|
||||
help
|
||||
Enable support for AVM active ISDN cards.
|
||||
|
||||
if CAPI_AVM
|
||||
|
||||
config ISDN_DRV_AVMB1_B1ISA
|
||||
tristate "AVM B1 ISA support"
|
||||
depends on ISA
|
||||
help
|
||||
Enable support for the ISA version of the AVM B1 card.
|
||||
|
||||
config ISDN_DRV_AVMB1_B1PCI
|
||||
tristate "AVM B1 PCI support"
|
||||
depends on PCI
|
||||
help
|
||||
Enable support for the PCI version of the AVM B1 card.
|
||||
|
||||
config ISDN_DRV_AVMB1_B1PCIV4
|
||||
bool "AVM B1 PCI V4 support"
|
||||
depends on ISDN_DRV_AVMB1_B1PCI
|
||||
help
|
||||
Enable support for the V4 version of AVM B1 PCI card.
|
||||
|
||||
config ISDN_DRV_AVMB1_T1ISA
|
||||
tristate "AVM T1/T1-B ISA support"
|
||||
depends on ISA
|
||||
help
|
||||
Enable support for the AVM T1 T1B card.
|
||||
Note: This is a PRI card and handle 30 B-channels.
|
||||
|
||||
config ISDN_DRV_AVMB1_B1PCMCIA
|
||||
tristate "AVM B1/M1/M2 PCMCIA support"
|
||||
depends on PCMCIA
|
||||
help
|
||||
Enable support for the PCMCIA version of the AVM B1 card.
|
||||
|
||||
config ISDN_DRV_AVMB1_AVM_CS
|
||||
tristate "AVM B1/M1/M2 PCMCIA cs module"
|
||||
depends on ISDN_DRV_AVMB1_B1PCMCIA
|
||||
help
|
||||
Enable the PCMCIA client driver for the AVM B1/M1/M2
|
||||
PCMCIA cards.
|
||||
|
||||
config ISDN_DRV_AVMB1_T1PCI
|
||||
tristate "AVM T1/T1-B PCI support"
|
||||
depends on PCI
|
||||
help
|
||||
Enable support for the AVM T1 T1B card.
|
||||
Note: This is a PRI card and handle 30 B-channels.
|
||||
|
||||
config ISDN_DRV_AVMB1_C4
|
||||
tristate "AVM C4/C2 support"
|
||||
depends on PCI
|
||||
help
|
||||
Enable support for the AVM C4/C2 PCI cards.
|
||||
These cards handle 4/2 BRI ISDN lines (8/4 channels).
|
||||
|
||||
endif # CAPI_AVM
|
@ -1,12 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Makefile for the AVM ISDN device drivers
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_ISDN_DRV_AVMB1_B1ISA) += b1isa.o b1.o
|
||||
obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCI) += b1pci.o b1.o b1dma.o
|
||||
obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCMCIA) += b1pcmcia.o b1.o
|
||||
obj-$(CONFIG_ISDN_DRV_AVMB1_AVM_CS) += avm_cs.o
|
||||
obj-$(CONFIG_ISDN_DRV_AVMB1_T1ISA) += t1isa.o b1.o
|
||||
obj-$(CONFIG_ISDN_DRV_AVMB1_T1PCI) += t1pci.o b1.o b1dma.o
|
||||
obj-$(CONFIG_ISDN_DRV_AVMB1_C4) += c4.o b1.o
|
@ -1,166 +0,0 @@
|
||||
/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
|
||||
*
|
||||
* A PCMCIA client driver for AVM B1/M1/M2
|
||||
*
|
||||
* Copyright 1999 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/major.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ciscode.h>
|
||||
#include <pcmcia/ds.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/capi.h>
|
||||
#include <linux/b1lli.h>
|
||||
#include <linux/b1pcmcia.h>
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*====================================================================*/
|
||||
|
||||
static int avmcs_config(struct pcmcia_device *link);
|
||||
static void avmcs_release(struct pcmcia_device *link);
|
||||
static void avmcs_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
static int avmcs_probe(struct pcmcia_device *p_dev)
|
||||
{
|
||||
/* 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 avmcs_config(p_dev);
|
||||
} /* avmcs_attach */
|
||||
|
||||
|
||||
static void avmcs_detach(struct pcmcia_device *link)
|
||||
{
|
||||
avmcs_release(link);
|
||||
} /* avmcs_detach */
|
||||
|
||||
static int avmcs_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;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
}
|
||||
|
||||
static int avmcs_config(struct pcmcia_device *link)
|
||||
{
|
||||
int i = -1;
|
||||
char devname[128];
|
||||
int cardtype;
|
||||
int (*addcard)(unsigned int port, unsigned irq);
|
||||
|
||||
devname[0] = 0;
|
||||
if (link->prod_id[1])
|
||||
strlcpy(devname, link->prod_id[1], sizeof(devname));
|
||||
|
||||
/*
|
||||
* find IO port
|
||||
*/
|
||||
if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
|
||||
return -ENODEV;
|
||||
|
||||
do {
|
||||
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 (devname[0]) {
|
||||
char *s = strrchr(devname, ' ');
|
||||
if (!s)
|
||||
s = devname;
|
||||
else s++;
|
||||
if (strcmp("M1", s) == 0) {
|
||||
cardtype = AVM_CARDTYPE_M1;
|
||||
} else if (strcmp("M2", s) == 0) {
|
||||
cardtype = AVM_CARDTYPE_M2;
|
||||
} else {
|
||||
cardtype = AVM_CARDTYPE_B1;
|
||||
}
|
||||
} else
|
||||
cardtype = AVM_CARDTYPE_B1;
|
||||
|
||||
/* If any step failed, release any partially configured state */
|
||||
if (i != 0) {
|
||||
avmcs_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
switch (cardtype) {
|
||||
case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
|
||||
case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
|
||||
default:
|
||||
case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
|
||||
}
|
||||
if ((i = (*addcard)(link->resource[0]->start, link->irq)) < 0) {
|
||||
dev_err(&link->dev,
|
||||
"avm_cs: failed to add AVM-Controller at i/o %#x, irq %d\n",
|
||||
(unsigned int) link->resource[0]->start, link->irq);
|
||||
avmcs_release(link);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
|
||||
} /* avmcs_config */
|
||||
|
||||
|
||||
static void avmcs_release(struct pcmcia_device *link)
|
||||
{
|
||||
b1pcmcia_delcard(link->resource[0]->start, link->irq);
|
||||
pcmcia_disable_device(link);
|
||||
} /* avmcs_release */
|
||||
|
||||
|
||||
static const struct pcmcia_device_id avmcs_ids[] = {
|
||||
PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
|
||||
PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
|
||||
PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
|
||||
|
||||
static struct pcmcia_driver avmcs_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "avm_cs",
|
||||
.probe = avmcs_probe,
|
||||
.remove = avmcs_detach,
|
||||
.id_table = avmcs_ids,
|
||||
};
|
||||
module_pcmcia_driver(avmcs_driver);
|
@ -1,581 +0,0 @@
|
||||
/* $Id: avmcard.h,v 1.1.4.1.2.1 2001/12/21 15:00:17 kai Exp $
|
||||
*
|
||||
* Copyright 1999 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 _AVMCARD_H_
|
||||
#define _AVMCARD_H_
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define AVMB1_PORTLEN 0x1f
|
||||
#define AVM_MAXVERSION 8
|
||||
#define AVM_NCCI_PER_CHANNEL 4
|
||||
|
||||
/*
|
||||
* Versions
|
||||
*/
|
||||
|
||||
#define VER_DRIVER 0
|
||||
#define VER_CARDTYPE 1
|
||||
#define VER_HWID 2
|
||||
#define VER_SERIAL 3
|
||||
#define VER_OPTION 4
|
||||
#define VER_PROTO 5
|
||||
#define VER_PROFILE 6
|
||||
#define VER_CAPI 7
|
||||
|
||||
enum avmcardtype {
|
||||
avm_b1isa,
|
||||
avm_b1pci,
|
||||
avm_b1pcmcia,
|
||||
avm_m1,
|
||||
avm_m2,
|
||||
avm_t1isa,
|
||||
avm_t1pci,
|
||||
avm_c4,
|
||||
avm_c2
|
||||
};
|
||||
|
||||
typedef struct avmcard_dmabuf {
|
||||
long size;
|
||||
u8 *dmabuf;
|
||||
dma_addr_t dmaaddr;
|
||||
} avmcard_dmabuf;
|
||||
|
||||
typedef struct avmcard_dmainfo {
|
||||
u32 recvlen;
|
||||
avmcard_dmabuf recvbuf;
|
||||
|
||||
avmcard_dmabuf sendbuf;
|
||||
struct sk_buff_head send_queue;
|
||||
|
||||
struct pci_dev *pcidev;
|
||||
} avmcard_dmainfo;
|
||||
|
||||
typedef struct avmctrl_info {
|
||||
char cardname[32];
|
||||
|
||||
int versionlen;
|
||||
char versionbuf[1024];
|
||||
char *version[AVM_MAXVERSION];
|
||||
|
||||
char infobuf[128]; /* for function procinfo */
|
||||
|
||||
struct avmcard *card;
|
||||
struct capi_ctr capi_ctrl;
|
||||
|
||||
struct list_head ncci_head;
|
||||
} avmctrl_info;
|
||||
|
||||
typedef struct avmcard {
|
||||
char name[32];
|
||||
|
||||
spinlock_t lock;
|
||||
unsigned int port;
|
||||
unsigned irq;
|
||||
unsigned long membase;
|
||||
enum avmcardtype cardtype;
|
||||
unsigned char revision;
|
||||
unsigned char class;
|
||||
int cardnr; /* for t1isa */
|
||||
|
||||
char msgbuf[128]; /* capimsg msg part */
|
||||
char databuf[2048]; /* capimsg data part */
|
||||
|
||||
void __iomem *mbase;
|
||||
volatile u32 csr;
|
||||
avmcard_dmainfo *dma;
|
||||
|
||||
struct avmctrl_info *ctrlinfo;
|
||||
|
||||
u_int nr_controllers;
|
||||
u_int nlogcontr;
|
||||
struct list_head list;
|
||||
} avmcard;
|
||||
|
||||
extern int b1_irq_table[16];
|
||||
|
||||
/*
|
||||
* LLI Messages to the ISDN-ControllerISDN Controller
|
||||
*/
|
||||
|
||||
#define SEND_POLL 0x72 /*
|
||||
* after load <- RECEIVE_POLL
|
||||
*/
|
||||
#define SEND_INIT 0x11 /*
|
||||
* first message <- RECEIVE_INIT
|
||||
* int32 NumApplications int32
|
||||
* NumNCCIs int32 BoardNumber
|
||||
*/
|
||||
#define SEND_REGISTER 0x12 /*
|
||||
* register an application int32
|
||||
* ApplIDId int32 NumMessages
|
||||
* int32 NumB3Connections int32
|
||||
* NumB3Blocks int32 B3Size
|
||||
*
|
||||
* AnzB3Connection != 0 &&
|
||||
* AnzB3Blocks >= 1 && B3Size >= 1
|
||||
*/
|
||||
#define SEND_RELEASE 0x14 /*
|
||||
* deregister an application int32
|
||||
* ApplID
|
||||
*/
|
||||
#define SEND_MESSAGE 0x15 /*
|
||||
* send capi-message int32 length
|
||||
* capi-data ...
|
||||
*/
|
||||
#define SEND_DATA_B3_REQ 0x13 /*
|
||||
* send capi-data-message int32
|
||||
* MsgLength capi-data ... int32
|
||||
* B3Length data ....
|
||||
*/
|
||||
|
||||
#define SEND_CONFIG 0x21 /*
|
||||
*/
|
||||
|
||||
#define SEND_POLLACK 0x73 /* T1 Watchdog */
|
||||
|
||||
/*
|
||||
* LLI Messages from the ISDN-ControllerISDN Controller
|
||||
*/
|
||||
|
||||
#define RECEIVE_POLL 0x32 /*
|
||||
* <- after SEND_POLL
|
||||
*/
|
||||
#define RECEIVE_INIT 0x27 /*
|
||||
* <- after SEND_INIT int32 length
|
||||
* byte total length b1struct board
|
||||
* driver revision b1struct card
|
||||
* type b1struct reserved b1struct
|
||||
* serial number b1struct driver
|
||||
* capability b1struct d-channel
|
||||
* protocol b1struct CAPI-2.0
|
||||
* profile b1struct capi version
|
||||
*/
|
||||
#define RECEIVE_MESSAGE 0x21 /*
|
||||
* <- after SEND_MESSAGE int32
|
||||
* AppllID int32 Length capi-data
|
||||
* ....
|
||||
*/
|
||||
#define RECEIVE_DATA_B3_IND 0x22 /*
|
||||
* received data int32 AppllID
|
||||
* int32 Length capi-data ...
|
||||
* int32 B3Length data ...
|
||||
*/
|
||||
#define RECEIVE_START 0x23 /*
|
||||
* Handshake
|
||||
*/
|
||||
#define RECEIVE_STOP 0x24 /*
|
||||
* Handshake
|
||||
*/
|
||||
#define RECEIVE_NEW_NCCI 0x25 /*
|
||||
* int32 AppllID int32 NCCI int32
|
||||
* WindowSize
|
||||
*/
|
||||
#define RECEIVE_FREE_NCCI 0x26 /*
|
||||
* int32 AppllID int32 NCCI
|
||||
*/
|
||||
#define RECEIVE_RELEASE 0x26 /*
|
||||
* int32 AppllID int32 0xffffffff
|
||||
*/
|
||||
#define RECEIVE_TASK_READY 0x31 /*
|
||||
* int32 tasknr
|
||||
* int32 Length Taskname ...
|
||||
*/
|
||||
#define RECEIVE_DEBUGMSG 0x71 /*
|
||||
* int32 Length message
|
||||
*
|
||||
*/
|
||||
#define RECEIVE_POLLDWORD 0x75 /* t1pci in dword mode */
|
||||
|
||||
#define WRITE_REGISTER 0x00
|
||||
#define READ_REGISTER 0x01
|
||||
|
||||
/*
|
||||
* port offsets
|
||||
*/
|
||||
|
||||
#define B1_READ 0x00
|
||||
#define B1_WRITE 0x01
|
||||
#define B1_INSTAT 0x02
|
||||
#define B1_OUTSTAT 0x03
|
||||
#define B1_ANALYSE 0x04
|
||||
#define B1_REVISION 0x05
|
||||
#define B1_RESET 0x10
|
||||
|
||||
|
||||
#define B1_STAT0(cardtype) ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l)
|
||||
#define B1_STAT1(cardtype) (0x80E00000l)
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
static inline unsigned char b1outp(unsigned int base,
|
||||
unsigned short offset,
|
||||
unsigned char value)
|
||||
{
|
||||
outb(value, base + offset);
|
||||
return inb(base + B1_ANALYSE);
|
||||
}
|
||||
|
||||
|
||||
static inline int b1_rx_full(unsigned int base)
|
||||
{
|
||||
return inb(base + B1_INSTAT) & 0x1;
|
||||
}
|
||||
|
||||
static inline unsigned char b1_get_byte(unsigned int base)
|
||||
{
|
||||
unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */
|
||||
while (!b1_rx_full(base) && time_before(jiffies, stop));
|
||||
if (b1_rx_full(base))
|
||||
return inb(base + B1_READ);
|
||||
printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int b1_get_word(unsigned int base)
|
||||
{
|
||||
unsigned int val = 0;
|
||||
val |= b1_get_byte(base);
|
||||
val |= (b1_get_byte(base) << 8);
|
||||
val |= (b1_get_byte(base) << 16);
|
||||
val |= (b1_get_byte(base) << 24);
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline int b1_tx_empty(unsigned int base)
|
||||
{
|
||||
return inb(base + B1_OUTSTAT) & 0x1;
|
||||
}
|
||||
|
||||
static inline void b1_put_byte(unsigned int base, unsigned char val)
|
||||
{
|
||||
while (!b1_tx_empty(base));
|
||||
b1outp(base, B1_WRITE, val);
|
||||
}
|
||||
|
||||
static inline int b1_save_put_byte(unsigned int base, unsigned char val)
|
||||
{
|
||||
unsigned long stop = jiffies + 2 * HZ;
|
||||
while (!b1_tx_empty(base) && time_before(jiffies, stop));
|
||||
if (!b1_tx_empty(base)) return -1;
|
||||
b1outp(base, B1_WRITE, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void b1_put_word(unsigned int base, unsigned int val)
|
||||
{
|
||||
b1_put_byte(base, val & 0xff);
|
||||
b1_put_byte(base, (val >> 8) & 0xff);
|
||||
b1_put_byte(base, (val >> 16) & 0xff);
|
||||
b1_put_byte(base, (val >> 24) & 0xff);
|
||||
}
|
||||
|
||||
static inline unsigned int b1_get_slice(unsigned int base,
|
||||
unsigned char *dp)
|
||||
{
|
||||
unsigned int len, i;
|
||||
|
||||
len = i = b1_get_word(base);
|
||||
while (i-- > 0) *dp++ = b1_get_byte(base);
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline void b1_put_slice(unsigned int base,
|
||||
unsigned char *dp, unsigned int len)
|
||||
{
|
||||
unsigned i = len;
|
||||
b1_put_word(base, i);
|
||||
while (i-- > 0)
|
||||
b1_put_byte(base, *dp++);
|
||||
}
|
||||
|
||||
static void b1_wr_reg(unsigned int base,
|
||||
unsigned int reg,
|
||||
unsigned int value)
|
||||
{
|
||||
b1_put_byte(base, WRITE_REGISTER);
|
||||
b1_put_word(base, reg);
|
||||
b1_put_word(base, value);
|
||||
}
|
||||
|
||||
static inline unsigned int b1_rd_reg(unsigned int base,
|
||||
unsigned int reg)
|
||||
{
|
||||
b1_put_byte(base, READ_REGISTER);
|
||||
b1_put_word(base, reg);
|
||||
return b1_get_word(base);
|
||||
|
||||
}
|
||||
|
||||
static inline void b1_reset(unsigned int base)
|
||||
{
|
||||
b1outp(base, B1_RESET, 0);
|
||||
mdelay(55 * 2); /* 2 TIC's */
|
||||
|
||||
b1outp(base, B1_RESET, 1);
|
||||
mdelay(55 * 2); /* 2 TIC's */
|
||||
|
||||
b1outp(base, B1_RESET, 0);
|
||||
mdelay(55 * 2); /* 2 TIC's */
|
||||
}
|
||||
|
||||
static inline unsigned char b1_disable_irq(unsigned int base)
|
||||
{
|
||||
return b1outp(base, B1_INSTAT, 0x00);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
static inline void b1_set_test_bit(unsigned int base,
|
||||
enum avmcardtype cardtype,
|
||||
int onoff)
|
||||
{
|
||||
b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
|
||||
}
|
||||
|
||||
static inline int b1_get_test_bit(unsigned int base,
|
||||
enum avmcardtype cardtype)
|
||||
{
|
||||
return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
|
||||
#define T1_FASTLINK 0x00
|
||||
#define T1_SLOWLINK 0x08
|
||||
|
||||
#define T1_READ B1_READ
|
||||
#define T1_WRITE B1_WRITE
|
||||
#define T1_INSTAT B1_INSTAT
|
||||
#define T1_OUTSTAT B1_OUTSTAT
|
||||
#define T1_IRQENABLE 0x05
|
||||
#define T1_FIFOSTAT 0x06
|
||||
#define T1_RESETLINK 0x10
|
||||
#define T1_ANALYSE 0x11
|
||||
#define T1_IRQMASTER 0x12
|
||||
#define T1_IDENT 0x17
|
||||
#define T1_RESETBOARD 0x1f
|
||||
|
||||
#define T1F_IREADY 0x01
|
||||
#define T1F_IHALF 0x02
|
||||
#define T1F_IFULL 0x04
|
||||
#define T1F_IEMPTY 0x08
|
||||
#define T1F_IFLAGS 0xF0
|
||||
|
||||
#define T1F_OREADY 0x10
|
||||
#define T1F_OHALF 0x20
|
||||
#define T1F_OEMPTY 0x40
|
||||
#define T1F_OFULL 0x80
|
||||
#define T1F_OFLAGS 0xF0
|
||||
|
||||
/* there are HEMA cards with 1k and 4k FIFO out */
|
||||
#define FIFO_OUTBSIZE 256
|
||||
#define FIFO_INPBSIZE 512
|
||||
|
||||
#define HEMA_VERSION_ID 0
|
||||
#define HEMA_PAL_ID 0
|
||||
|
||||
static inline void t1outp(unsigned int base,
|
||||
unsigned short offset,
|
||||
unsigned char value)
|
||||
{
|
||||
outb(value, base + offset);
|
||||
}
|
||||
|
||||
static inline unsigned char t1inp(unsigned int base,
|
||||
unsigned short offset)
|
||||
{
|
||||
return inb(base + offset);
|
||||
}
|
||||
|
||||
static inline int t1_isfastlink(unsigned int base)
|
||||
{
|
||||
return (inb(base + T1_IDENT) & ~0x82) == 1;
|
||||
}
|
||||
|
||||
static inline unsigned char t1_fifostatus(unsigned int base)
|
||||
{
|
||||
return inb(base + T1_FIFOSTAT);
|
||||
}
|
||||
|
||||
static inline unsigned int t1_get_slice(unsigned int base,
|
||||
unsigned char *dp)
|
||||
{
|
||||
unsigned int len, i;
|
||||
#ifdef FASTLINK_DEBUG
|
||||
unsigned wcnt = 0, bcnt = 0;
|
||||
#endif
|
||||
|
||||
len = i = b1_get_word(base);
|
||||
if (t1_isfastlink(base)) {
|
||||
int status;
|
||||
while (i > 0) {
|
||||
status = t1_fifostatus(base) & (T1F_IREADY | T1F_IHALF);
|
||||
if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
|
||||
|
||||
switch (status) {
|
||||
case T1F_IREADY | T1F_IHALF | T1F_IFULL:
|
||||
insb(base + B1_READ, dp, FIFO_INPBSIZE);
|
||||
dp += FIFO_INPBSIZE;
|
||||
i -= FIFO_INPBSIZE;
|
||||
#ifdef FASTLINK_DEBUG
|
||||
wcnt += FIFO_INPBSIZE;
|
||||
#endif
|
||||
break;
|
||||
case T1F_IREADY | T1F_IHALF:
|
||||
insb(base + B1_READ, dp, i);
|
||||
#ifdef FASTLINK_DEBUG
|
||||
wcnt += i;
|
||||
#endif
|
||||
dp += i;
|
||||
i = 0;
|
||||
break;
|
||||
default:
|
||||
*dp++ = b1_get_byte(base);
|
||||
i--;
|
||||
#ifdef FASTLINK_DEBUG
|
||||
bcnt++;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef FASTLINK_DEBUG
|
||||
if (wcnt)
|
||||
printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
|
||||
base, len, wcnt, bcnt);
|
||||
#endif
|
||||
} else {
|
||||
while (i-- > 0)
|
||||
*dp++ = b1_get_byte(base);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline void t1_put_slice(unsigned int base,
|
||||
unsigned char *dp, unsigned int len)
|
||||
{
|
||||
unsigned i = len;
|
||||
b1_put_word(base, i);
|
||||
if (t1_isfastlink(base)) {
|
||||
int status;
|
||||
while (i > 0) {
|
||||
status = t1_fifostatus(base) & (T1F_OREADY | T1F_OHALF);
|
||||
if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
|
||||
switch (status) {
|
||||
case T1F_OREADY | T1F_OHALF | T1F_OEMPTY:
|
||||
outsb(base + B1_WRITE, dp, FIFO_OUTBSIZE);
|
||||
dp += FIFO_OUTBSIZE;
|
||||
i -= FIFO_OUTBSIZE;
|
||||
break;
|
||||
case T1F_OREADY | T1F_OHALF:
|
||||
outsb(base + B1_WRITE, dp, i);
|
||||
dp += i;
|
||||
i = 0;
|
||||
break;
|
||||
default:
|
||||
b1_put_byte(base, *dp++);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (i-- > 0)
|
||||
b1_put_byte(base, *dp++);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void t1_disable_irq(unsigned int base)
|
||||
{
|
||||
t1outp(base, T1_IRQMASTER, 0x00);
|
||||
}
|
||||
|
||||
static inline void t1_reset(unsigned int base)
|
||||
{
|
||||
/* reset T1 Controller */
|
||||
b1_reset(base);
|
||||
/* disable irq on HEMA */
|
||||
t1outp(base, B1_INSTAT, 0x00);
|
||||
t1outp(base, B1_OUTSTAT, 0x00);
|
||||
t1outp(base, T1_IRQMASTER, 0x00);
|
||||
/* reset HEMA board configuration */
|
||||
t1outp(base, T1_RESETBOARD, 0xf);
|
||||
}
|
||||
|
||||
static inline void b1_setinterrupt(unsigned int base, unsigned irq,
|
||||
enum avmcardtype cardtype)
|
||||
{
|
||||
switch (cardtype) {
|
||||
case avm_t1isa:
|
||||
t1outp(base, B1_INSTAT, 0x00);
|
||||
t1outp(base, B1_INSTAT, 0x02);
|
||||
t1outp(base, T1_IRQMASTER, 0x08);
|
||||
break;
|
||||
case avm_b1isa:
|
||||
b1outp(base, B1_INSTAT, 0x00);
|
||||
b1outp(base, B1_RESET, b1_irq_table[irq]);
|
||||
b1outp(base, B1_INSTAT, 0x02);
|
||||
break;
|
||||
default:
|
||||
case avm_m1:
|
||||
case avm_m2:
|
||||
case avm_b1pci:
|
||||
b1outp(base, B1_INSTAT, 0x00);
|
||||
b1outp(base, B1_RESET, 0xf0);
|
||||
b1outp(base, B1_INSTAT, 0x02);
|
||||
break;
|
||||
case avm_c4:
|
||||
case avm_t1pci:
|
||||
b1outp(base, B1_RESET, 0xf0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* b1.c */
|
||||
avmcard *b1_alloc_card(int nr_controllers);
|
||||
void b1_free_card(avmcard *card);
|
||||
int b1_detect(unsigned int base, enum avmcardtype cardtype);
|
||||
void b1_getrevision(avmcard *card);
|
||||
int b1_load_t4file(avmcard *card, capiloaddatapart *t4file);
|
||||
int b1_load_config(avmcard *card, capiloaddatapart *config);
|
||||
int b1_loaded(avmcard *card);
|
||||
|
||||
int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
|
||||
void b1_reset_ctr(struct capi_ctr *ctrl);
|
||||
void b1_register_appl(struct capi_ctr *ctrl, u16 appl,
|
||||
capi_register_params *rp);
|
||||
void b1_release_appl(struct capi_ctr *ctrl, u16 appl);
|
||||
u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
|
||||
void b1_parse_version(avmctrl_info *card);
|
||||
irqreturn_t b1_interrupt(int interrupt, void *devptr);
|
||||
|
||||
int b1_proc_show(struct seq_file *m, void *v);
|
||||
|
||||
avmcard_dmainfo *avmcard_dma_alloc(char *name, struct pci_dev *,
|
||||
long rsize, long ssize);
|
||||
void avmcard_dma_free(avmcard_dmainfo *);
|
||||
|
||||
/* b1dma.c */
|
||||
int b1pciv4_detect(avmcard *card);
|
||||
int t1pci_detect(avmcard *card);
|
||||
void b1dma_reset(avmcard *card);
|
||||
irqreturn_t b1dma_interrupt(int interrupt, void *devptr);
|
||||
|
||||
int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
|
||||
void b1dma_reset_ctr(struct capi_ctr *ctrl);
|
||||
void b1dma_remove_ctr(struct capi_ctr *ctrl);
|
||||
void b1dma_register_appl(struct capi_ctr *ctrl,
|
||||
u16 appl,
|
||||
capi_register_params *rp);
|
||||
void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl);
|
||||
u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
|
||||
int b1dma_proc_show(struct seq_file *m, void *v);
|
||||
|
||||
#endif /* _AVMCARD_H_ */
|
@ -1,819 +0,0 @@
|
||||
/* $Id: b1.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
|
||||
*
|
||||
* Common module for AVM B1 cards.
|
||||
*
|
||||
* Copyright 1999 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/capi.h>
|
||||
#include <linux/kernelcapi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
#include "avmcard.h"
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
|
||||
static char *revision = "$Revision: 1.1.2.2 $";
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
int b1_irq_table[16] =
|
||||
{0,
|
||||
0,
|
||||
0,
|
||||
192, /* irq 3 */
|
||||
32, /* irq 4 */
|
||||
160, /* irq 5 */
|
||||
96, /* irq 6 */
|
||||
224, /* irq 7 */
|
||||
0,
|
||||
64, /* irq 9 */
|
||||
80, /* irq 10 */
|
||||
208, /* irq 11 */
|
||||
48, /* irq 12 */
|
||||
0,
|
||||
0,
|
||||
112, /* irq 15 */
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
avmcard *b1_alloc_card(int nr_controllers)
|
||||
{
|
||||
avmcard *card;
|
||||
avmctrl_info *cinfo;
|
||||
int i;
|
||||
|
||||
card = kzalloc(sizeof(*card), GFP_KERNEL);
|
||||
if (!card)
|
||||
return NULL;
|
||||
|
||||
cinfo = kcalloc(nr_controllers, sizeof(*cinfo), GFP_KERNEL);
|
||||
if (!cinfo) {
|
||||
kfree(card);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
card->ctrlinfo = cinfo;
|
||||
for (i = 0; i < nr_controllers; i++) {
|
||||
INIT_LIST_HEAD(&cinfo[i].ncci_head);
|
||||
cinfo[i].card = card;
|
||||
}
|
||||
spin_lock_init(&card->lock);
|
||||
card->nr_controllers = nr_controllers;
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
void b1_free_card(avmcard *card)
|
||||
{
|
||||
kfree(card->ctrlinfo);
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
int b1_detect(unsigned int base, enum avmcardtype cardtype)
|
||||
{
|
||||
int onoff, i;
|
||||
|
||||
/*
|
||||
* Statusregister 0000 00xx
|
||||
*/
|
||||
if ((inb(base + B1_INSTAT) & 0xfc)
|
||||
|| (inb(base + B1_OUTSTAT) & 0xfc))
|
||||
return 1;
|
||||
/*
|
||||
* Statusregister 0000 001x
|
||||
*/
|
||||
b1outp(base, B1_INSTAT, 0x2); /* enable irq */
|
||||
/* b1outp(base, B1_OUTSTAT, 0x2); */
|
||||
if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
|
||||
/* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
|
||||
return 2;
|
||||
/*
|
||||
* Statusregister 0000 000x
|
||||
*/
|
||||
b1outp(base, B1_INSTAT, 0x0); /* disable irq */
|
||||
b1outp(base, B1_OUTSTAT, 0x0);
|
||||
if ((inb(base + B1_INSTAT) & 0xfe)
|
||||
|| (inb(base + B1_OUTSTAT) & 0xfe))
|
||||
return 3;
|
||||
|
||||
for (onoff = !0, i = 0; i < 10; i++) {
|
||||
b1_set_test_bit(base, cardtype, onoff);
|
||||
if (b1_get_test_bit(base, cardtype) != onoff)
|
||||
return 4;
|
||||
onoff = !onoff;
|
||||
}
|
||||
|
||||
if (cardtype == avm_m1)
|
||||
return 0;
|
||||
|
||||
if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
|
||||
return 5;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void b1_getrevision(avmcard *card)
|
||||
{
|
||||
card->class = inb(card->port + B1_ANALYSE);
|
||||
card->revision = inb(card->port + B1_REVISION);
|
||||
}
|
||||
|
||||
#define FWBUF_SIZE 256
|
||||
int b1_load_t4file(avmcard *card, capiloaddatapart *t4file)
|
||||
{
|
||||
unsigned char buf[FWBUF_SIZE];
|
||||
unsigned char *dp;
|
||||
int i, left;
|
||||
unsigned int base = card->port;
|
||||
|
||||
dp = t4file->data;
|
||||
left = t4file->len;
|
||||
while (left > FWBUF_SIZE) {
|
||||
if (t4file->user) {
|
||||
if (copy_from_user(buf, dp, FWBUF_SIZE))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
memcpy(buf, dp, FWBUF_SIZE);
|
||||
}
|
||||
for (i = 0; i < FWBUF_SIZE; i++)
|
||||
if (b1_save_put_byte(base, buf[i]) < 0) {
|
||||
printk(KERN_ERR "%s: corrupted firmware file ?\n",
|
||||
card->name);
|
||||
return -EIO;
|
||||
}
|
||||
left -= FWBUF_SIZE;
|
||||
dp += FWBUF_SIZE;
|
||||
}
|
||||
if (left) {
|
||||
if (t4file->user) {
|
||||
if (copy_from_user(buf, dp, left))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
memcpy(buf, dp, left);
|
||||
}
|
||||
for (i = 0; i < left; i++)
|
||||
if (b1_save_put_byte(base, buf[i]) < 0) {
|
||||
printk(KERN_ERR "%s: corrupted firmware file ?\n",
|
||||
card->name);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int b1_load_config(avmcard *card, capiloaddatapart *config)
|
||||
{
|
||||
unsigned char buf[FWBUF_SIZE];
|
||||
unsigned char *dp;
|
||||
unsigned int base = card->port;
|
||||
int i, j, left;
|
||||
|
||||
dp = config->data;
|
||||
left = config->len;
|
||||
if (left) {
|
||||
b1_put_byte(base, SEND_CONFIG);
|
||||
b1_put_word(base, 1);
|
||||
b1_put_byte(base, SEND_CONFIG);
|
||||
b1_put_word(base, left);
|
||||
}
|
||||
while (left > FWBUF_SIZE) {
|
||||
if (config->user) {
|
||||
if (copy_from_user(buf, dp, FWBUF_SIZE))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
memcpy(buf, dp, FWBUF_SIZE);
|
||||
}
|
||||
for (i = 0; i < FWBUF_SIZE; ) {
|
||||
b1_put_byte(base, SEND_CONFIG);
|
||||
for (j = 0; j < 4; j++) {
|
||||
b1_put_byte(base, buf[i++]);
|
||||
}
|
||||
}
|
||||
left -= FWBUF_SIZE;
|
||||
dp += FWBUF_SIZE;
|
||||
}
|
||||
if (left) {
|
||||
if (config->user) {
|
||||
if (copy_from_user(buf, dp, left))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
memcpy(buf, dp, left);
|
||||
}
|
||||
for (i = 0; i < left; ) {
|
||||
b1_put_byte(base, SEND_CONFIG);
|
||||
for (j = 0; j < 4; j++) {
|
||||
if (i < left)
|
||||
b1_put_byte(base, buf[i++]);
|
||||
else
|
||||
b1_put_byte(base, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int b1_loaded(avmcard *card)
|
||||
{
|
||||
unsigned int base = card->port;
|
||||
unsigned long stop;
|
||||
unsigned char ans;
|
||||
unsigned long tout = 2;
|
||||
|
||||
for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
|
||||
if (b1_tx_empty(base))
|
||||
break;
|
||||
}
|
||||
if (!b1_tx_empty(base)) {
|
||||
printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n",
|
||||
card->name);
|
||||
return 0;
|
||||
}
|
||||
b1_put_byte(base, SEND_POLL);
|
||||
for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
|
||||
if (b1_rx_full(base)) {
|
||||
ans = b1_get_byte(base);
|
||||
if (ans == RECEIVE_POLL)
|
||||
return 1;
|
||||
|
||||
printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n",
|
||||
card->name, ans);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
b1_reset(port);
|
||||
retval = b1_load_t4file(card, &data->firmware);
|
||||
|
||||
if (retval) {
|
||||
b1_reset(port);
|
||||
printk(KERN_ERR "%s: failed to load t4file!!\n",
|
||||
card->name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
b1_disable_irq(port);
|
||||
|
||||
if (data->configuration.len > 0 && data->configuration.data) {
|
||||
retval = b1_load_config(card, &data->configuration);
|
||||
if (retval) {
|
||||
b1_reset(port);
|
||||
printk(KERN_ERR "%s: failed to load config!!\n",
|
||||
card->name);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (!b1_loaded(card)) {
|
||||
printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
b1_setinterrupt(port, card->irq, card->cardtype);
|
||||
b1_put_byte(port, SEND_INIT);
|
||||
b1_put_word(port, CAPI_MAXAPPL);
|
||||
b1_put_word(port, AVM_NCCI_PER_CHANNEL * 2);
|
||||
b1_put_word(port, ctrl->cnr - 1);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void b1_reset_ctr(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
|
||||
b1_reset(port);
|
||||
b1_reset(port);
|
||||
|
||||
memset(cinfo->version, 0, sizeof(cinfo->version));
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
capilib_release(&cinfo->ncci_head);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_down(ctrl);
|
||||
}
|
||||
|
||||
void b1_register_appl(struct capi_ctr *ctrl,
|
||||
u16 appl,
|
||||
capi_register_params *rp)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
int nconn, want = rp->level3cnt;
|
||||
|
||||
if (want > 0) nconn = want;
|
||||
else nconn = ctrl->profile.nbchannel * -want;
|
||||
if (nconn == 0) nconn = ctrl->profile.nbchannel;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
b1_put_byte(port, SEND_REGISTER);
|
||||
b1_put_word(port, appl);
|
||||
b1_put_word(port, 1024 * (nconn + 1));
|
||||
b1_put_word(port, nconn);
|
||||
b1_put_word(port, rp->datablkcnt);
|
||||
b1_put_word(port, rp->datablklen);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
void b1_release_appl(struct capi_ctr *ctrl, u16 appl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
capilib_release_appl(&cinfo->ncci_head, appl);
|
||||
b1_put_byte(port, SEND_RELEASE);
|
||||
b1_put_word(port, appl);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
u16 len = CAPIMSG_LEN(skb->data);
|
||||
u8 cmd = CAPIMSG_COMMAND(skb->data);
|
||||
u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
|
||||
u16 dlen, retval;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
|
||||
retval = capilib_data_b3_req(&cinfo->ncci_head,
|
||||
CAPIMSG_APPID(skb->data),
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
if (retval != CAPI_NOERROR) {
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
dlen = CAPIMSG_DATALEN(skb->data);
|
||||
|
||||
b1_put_byte(port, SEND_DATA_B3_REQ);
|
||||
b1_put_slice(port, skb->data, len);
|
||||
b1_put_slice(port, skb->data + len, dlen);
|
||||
} else {
|
||||
b1_put_byte(port, SEND_MESSAGE);
|
||||
b1_put_slice(port, skb->data, len);
|
||||
}
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
return CAPI_NOERROR;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
void b1_parse_version(avmctrl_info *cinfo)
|
||||
{
|
||||
struct capi_ctr *ctrl = &cinfo->capi_ctrl;
|
||||
avmcard *card = cinfo->card;
|
||||
capi_profile *profp;
|
||||
u8 *dversion;
|
||||
u8 flag;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < AVM_MAXVERSION; j++)
|
||||
cinfo->version[j] = "";
|
||||
for (i = 0, j = 0;
|
||||
j < AVM_MAXVERSION && i < cinfo->versionlen;
|
||||
j++, i += cinfo->versionbuf[i] + 1)
|
||||
cinfo->version[j] = &cinfo->versionbuf[i + 1];
|
||||
|
||||
strlcpy(ctrl->serial, cinfo->version[VER_SERIAL], sizeof(ctrl->serial));
|
||||
memcpy(&ctrl->profile, cinfo->version[VER_PROFILE], sizeof(capi_profile));
|
||||
strlcpy(ctrl->manu, "AVM GmbH", sizeof(ctrl->manu));
|
||||
dversion = cinfo->version[VER_DRIVER];
|
||||
ctrl->version.majorversion = 2;
|
||||
ctrl->version.minorversion = 0;
|
||||
ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
|
||||
ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf);
|
||||
ctrl->version.minormanuversion = (dversion[3] - '0') << 4;
|
||||
ctrl->version.minormanuversion |=
|
||||
(dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
|
||||
|
||||
profp = &ctrl->profile;
|
||||
|
||||
flag = ((u8 *)(profp->manu))[1];
|
||||
switch (flag) {
|
||||
case 0: if (cinfo->version[VER_CARDTYPE])
|
||||
strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]);
|
||||
else strcpy(cinfo->cardname, "B1");
|
||||
break;
|
||||
case 3: strcpy(cinfo->cardname, "PCMCIA B"); break;
|
||||
case 4: strcpy(cinfo->cardname, "PCMCIA M1"); break;
|
||||
case 5: strcpy(cinfo->cardname, "PCMCIA M2"); break;
|
||||
case 6: strcpy(cinfo->cardname, "B1 V3.0"); break;
|
||||
case 7: strcpy(cinfo->cardname, "B1 PCI"); break;
|
||||
default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break;
|
||||
}
|
||||
printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n",
|
||||
card->name, ctrl->cnr, cinfo->cardname);
|
||||
|
||||
flag = ((u8 *)(profp->manu))[3];
|
||||
if (flag)
|
||||
printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n",
|
||||
card->name,
|
||||
ctrl->cnr,
|
||||
(flag & 0x01) ? " DSS1" : "",
|
||||
(flag & 0x02) ? " CT1" : "",
|
||||
(flag & 0x04) ? " VN3" : "",
|
||||
(flag & 0x08) ? " NI1" : "",
|
||||
(flag & 0x10) ? " AUSTEL" : "",
|
||||
(flag & 0x20) ? " ESS" : "",
|
||||
(flag & 0x40) ? " 1TR6" : ""
|
||||
);
|
||||
|
||||
flag = ((u8 *)(profp->manu))[5];
|
||||
if (flag)
|
||||
printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n",
|
||||
card->name,
|
||||
ctrl->cnr,
|
||||
(flag & 0x01) ? " point to point" : "",
|
||||
(flag & 0x02) ? " point to multipoint" : "",
|
||||
(flag & 0x08) ? " leased line without D-channel" : "",
|
||||
(flag & 0x04) ? " leased line with D-channel" : ""
|
||||
);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
irqreturn_t b1_interrupt(int interrupt, void *devptr)
|
||||
{
|
||||
avmcard *card = devptr;
|
||||
avmctrl_info *cinfo = &card->ctrlinfo[0];
|
||||
struct capi_ctr *ctrl = &cinfo->capi_ctrl;
|
||||
unsigned char b1cmd;
|
||||
struct sk_buff *skb;
|
||||
|
||||
unsigned ApplId;
|
||||
unsigned MsgLen;
|
||||
unsigned DataB3Len;
|
||||
unsigned NCCI;
|
||||
unsigned WindowSize;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
|
||||
if (!b1_rx_full(card->port)) {
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
b1cmd = b1_get_byte(card->port);
|
||||
|
||||
switch (b1cmd) {
|
||||
|
||||
case RECEIVE_DATA_B3_IND:
|
||||
|
||||
ApplId = (unsigned) b1_get_word(card->port);
|
||||
MsgLen = b1_get_slice(card->port, card->msgbuf);
|
||||
DataB3Len = b1_get_slice(card->port, card->databuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
if (MsgLen < 30) { /* not CAPI 64Bit */
|
||||
memset(card->msgbuf + MsgLen, 0, 30-MsgLen);
|
||||
MsgLen = 30;
|
||||
CAPIMSG_SETLEN(card->msgbuf, 30);
|
||||
}
|
||||
|
||||
skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
printk(KERN_ERR "%s: incoming packet dropped\n",
|
||||
card->name);
|
||||
} else {
|
||||
skb_put_data(skb, card->msgbuf, MsgLen);
|
||||
skb_put_data(skb, card->databuf, DataB3Len);
|
||||
capi_ctr_handle_message(ctrl, ApplId, skb);
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_MESSAGE:
|
||||
|
||||
ApplId = (unsigned) b1_get_word(card->port);
|
||||
MsgLen = b1_get_slice(card->port, card->msgbuf);
|
||||
skb = alloc_skb(MsgLen, GFP_ATOMIC);
|
||||
|
||||
if (!skb) {
|
||||
printk(KERN_ERR "%s: incoming packet dropped\n",
|
||||
card->name);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
} else {
|
||||
skb_put_data(skb, card->msgbuf, MsgLen);
|
||||
if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
|
||||
capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_handle_message(ctrl, ApplId, skb);
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_NEW_NCCI:
|
||||
|
||||
ApplId = b1_get_word(card->port);
|
||||
NCCI = b1_get_word(card->port);
|
||||
WindowSize = b1_get_word(card->port);
|
||||
capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
break;
|
||||
|
||||
case RECEIVE_FREE_NCCI:
|
||||
|
||||
ApplId = b1_get_word(card->port);
|
||||
NCCI = b1_get_word(card->port);
|
||||
if (NCCI != 0xffffffff)
|
||||
capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
break;
|
||||
|
||||
case RECEIVE_START:
|
||||
/* b1_put_byte(card->port, SEND_POLLACK); */
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_resume_output(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_STOP:
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_suspend_output(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_INIT:
|
||||
|
||||
cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
b1_parse_version(cinfo);
|
||||
printk(KERN_INFO "%s: %s-card (%s) now active\n",
|
||||
card->name,
|
||||
cinfo->version[VER_CARDTYPE],
|
||||
cinfo->version[VER_DRIVER]);
|
||||
capi_ctr_ready(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_TASK_READY:
|
||||
ApplId = (unsigned) b1_get_word(card->port);
|
||||
MsgLen = b1_get_slice(card->port, card->msgbuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
card->msgbuf[MsgLen] = 0;
|
||||
while (MsgLen > 0
|
||||
&& (card->msgbuf[MsgLen - 1] == '\n'
|
||||
|| card->msgbuf[MsgLen - 1] == '\r')) {
|
||||
card->msgbuf[MsgLen - 1] = 0;
|
||||
MsgLen--;
|
||||
}
|
||||
printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
|
||||
card->name, ApplId, card->msgbuf);
|
||||
break;
|
||||
|
||||
case RECEIVE_DEBUGMSG:
|
||||
MsgLen = b1_get_slice(card->port, card->msgbuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
card->msgbuf[MsgLen] = 0;
|
||||
while (MsgLen > 0
|
||||
&& (card->msgbuf[MsgLen - 1] == '\n'
|
||||
|| card->msgbuf[MsgLen - 1] == '\r')) {
|
||||
card->msgbuf[MsgLen - 1] = 0;
|
||||
MsgLen--;
|
||||
}
|
||||
printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
|
||||
break;
|
||||
|
||||
case 0xff:
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_ERR "%s: card removed ?\n", card->name);
|
||||
return IRQ_NONE;
|
||||
default:
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
|
||||
card->name, b1cmd);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
int b1_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct capi_ctr *ctrl = m->private;
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
u8 flag;
|
||||
char *s;
|
||||
|
||||
seq_printf(m, "%-16s %s\n", "name", card->name);
|
||||
seq_printf(m, "%-16s 0x%x\n", "io", card->port);
|
||||
seq_printf(m, "%-16s %d\n", "irq", card->irq);
|
||||
switch (card->cardtype) {
|
||||
case avm_b1isa: s = "B1 ISA"; break;
|
||||
case avm_b1pci: s = "B1 PCI"; break;
|
||||
case avm_b1pcmcia: s = "B1 PCMCIA"; break;
|
||||
case avm_m1: s = "M1"; break;
|
||||
case avm_m2: s = "M2"; break;
|
||||
case avm_t1isa: s = "T1 ISA (HEMA)"; break;
|
||||
case avm_t1pci: s = "T1 PCI"; break;
|
||||
case avm_c4: s = "C4"; break;
|
||||
case avm_c2: s = "C2"; break;
|
||||
default: s = "???"; break;
|
||||
}
|
||||
seq_printf(m, "%-16s %s\n", "type", s);
|
||||
if (card->cardtype == avm_t1isa)
|
||||
seq_printf(m, "%-16s %d\n", "cardnr", card->cardnr);
|
||||
|
||||
s = cinfo->version[VER_DRIVER];
|
||||
if (s)
|
||||
seq_printf(m, "%-16s %s\n", "ver_driver", s);
|
||||
|
||||
s = cinfo->version[VER_CARDTYPE];
|
||||
if (s)
|
||||
seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
|
||||
|
||||
s = cinfo->version[VER_SERIAL];
|
||||
if (s)
|
||||
seq_printf(m, "%-16s %s\n", "ver_serial", s);
|
||||
|
||||
if (card->cardtype != avm_m1) {
|
||||
flag = ((u8 *)(ctrl->profile.manu))[3];
|
||||
if (flag)
|
||||
seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
|
||||
"protocol",
|
||||
(flag & 0x01) ? " DSS1" : "",
|
||||
(flag & 0x02) ? " CT1" : "",
|
||||
(flag & 0x04) ? " VN3" : "",
|
||||
(flag & 0x08) ? " NI1" : "",
|
||||
(flag & 0x10) ? " AUSTEL" : "",
|
||||
(flag & 0x20) ? " ESS" : "",
|
||||
(flag & 0x40) ? " 1TR6" : ""
|
||||
);
|
||||
}
|
||||
if (card->cardtype != avm_m1) {
|
||||
flag = ((u8 *)(ctrl->profile.manu))[5];
|
||||
if (flag)
|
||||
seq_printf(m, "%-16s%s%s%s%s\n",
|
||||
"linetype",
|
||||
(flag & 0x01) ? " point to point" : "",
|
||||
(flag & 0x02) ? " point to multipoint" : "",
|
||||
(flag & 0x08) ? " leased line without D-channel" : "",
|
||||
(flag & 0x04) ? " leased line with D-channel" : ""
|
||||
);
|
||||
}
|
||||
seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(b1_proc_show);
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
|
||||
avmcard_dmainfo *
|
||||
avmcard_dma_alloc(char *name, struct pci_dev *pdev, long rsize, long ssize)
|
||||
{
|
||||
avmcard_dmainfo *p;
|
||||
void *buf;
|
||||
|
||||
p = kzalloc(sizeof(avmcard_dmainfo), GFP_KERNEL);
|
||||
if (!p) {
|
||||
printk(KERN_WARNING "%s: no memory.\n", name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
p->recvbuf.size = rsize;
|
||||
buf = pci_alloc_consistent(pdev, rsize, &p->recvbuf.dmaaddr);
|
||||
if (!buf) {
|
||||
printk(KERN_WARNING "%s: allocation of receive dma buffer failed.\n", name);
|
||||
goto err_kfree;
|
||||
}
|
||||
p->recvbuf.dmabuf = buf;
|
||||
|
||||
p->sendbuf.size = ssize;
|
||||
buf = pci_alloc_consistent(pdev, ssize, &p->sendbuf.dmaaddr);
|
||||
if (!buf) {
|
||||
printk(KERN_WARNING "%s: allocation of send dma buffer failed.\n", name);
|
||||
goto err_free_consistent;
|
||||
}
|
||||
|
||||
p->sendbuf.dmabuf = buf;
|
||||
skb_queue_head_init(&p->send_queue);
|
||||
|
||||
return p;
|
||||
|
||||
err_free_consistent:
|
||||
pci_free_consistent(p->pcidev, p->recvbuf.size,
|
||||
p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
|
||||
err_kfree:
|
||||
kfree(p);
|
||||
err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void avmcard_dma_free(avmcard_dmainfo *p)
|
||||
{
|
||||
pci_free_consistent(p->pcidev, p->recvbuf.size,
|
||||
p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
|
||||
pci_free_consistent(p->pcidev, p->sendbuf.size,
|
||||
p->sendbuf.dmabuf, p->sendbuf.dmaaddr);
|
||||
skb_queue_purge(&p->send_queue);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(avmcard_dma_alloc);
|
||||
EXPORT_SYMBOL(avmcard_dma_free);
|
||||
|
||||
#endif
|
||||
|
||||
EXPORT_SYMBOL(b1_irq_table);
|
||||
|
||||
EXPORT_SYMBOL(b1_alloc_card);
|
||||
EXPORT_SYMBOL(b1_free_card);
|
||||
EXPORT_SYMBOL(b1_detect);
|
||||
EXPORT_SYMBOL(b1_getrevision);
|
||||
EXPORT_SYMBOL(b1_load_t4file);
|
||||
EXPORT_SYMBOL(b1_load_config);
|
||||
EXPORT_SYMBOL(b1_loaded);
|
||||
EXPORT_SYMBOL(b1_load_firmware);
|
||||
EXPORT_SYMBOL(b1_reset_ctr);
|
||||
EXPORT_SYMBOL(b1_register_appl);
|
||||
EXPORT_SYMBOL(b1_release_appl);
|
||||
EXPORT_SYMBOL(b1_send_message);
|
||||
|
||||
EXPORT_SYMBOL(b1_parse_version);
|
||||
EXPORT_SYMBOL(b1_interrupt);
|
||||
|
||||
static int __init b1_init(void)
|
||||
{
|
||||
char *p;
|
||||
char rev[32];
|
||||
|
||||
p = strchr(revision, ':');
|
||||
if (p && p[1]) {
|
||||
strlcpy(rev, p + 2, 32);
|
||||
p = strchr(rev, '$');
|
||||
if (p && p > rev)
|
||||
*(p - 1) = 0;
|
||||
} else {
|
||||
strcpy(rev, "1.0");
|
||||
}
|
||||
printk(KERN_INFO "b1: revision %s\n", rev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit b1_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(b1_init);
|
||||
module_exit(b1_exit);
|
@ -1,981 +0,0 @@
|
||||
/* $Id: b1dma.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
|
||||
*
|
||||
* Common module for AVM B1 cards that support dma with AMCC
|
||||
*
|
||||
* Copyright 2000 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/capi.h>
|
||||
#include <linux/kernelcapi.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
#include "avmcard.h"
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
|
||||
static char *revision = "$Revision: 1.1.2.3 $";
|
||||
|
||||
#undef AVM_B1DMA_DEBUG
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
MODULE_DESCRIPTION("CAPI4Linux: DMA support for active AVM cards");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static bool suppress_pollack = 0;
|
||||
module_param(suppress_pollack, bool, 0);
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void b1dma_dispatch_tx(avmcard *card);
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
/* S5933 */
|
||||
|
||||
#define AMCC_RXPTR 0x24
|
||||
#define AMCC_RXLEN 0x28
|
||||
#define AMCC_TXPTR 0x2c
|
||||
#define AMCC_TXLEN 0x30
|
||||
|
||||
#define AMCC_INTCSR 0x38
|
||||
# define EN_READ_TC_INT 0x00008000L
|
||||
# define EN_WRITE_TC_INT 0x00004000L
|
||||
# define EN_TX_TC_INT EN_READ_TC_INT
|
||||
# define EN_RX_TC_INT EN_WRITE_TC_INT
|
||||
# define AVM_FLAG 0x30000000L
|
||||
|
||||
# define ANY_S5933_INT 0x00800000L
|
||||
# define READ_TC_INT 0x00080000L
|
||||
# define WRITE_TC_INT 0x00040000L
|
||||
# define TX_TC_INT READ_TC_INT
|
||||
# define RX_TC_INT WRITE_TC_INT
|
||||
# define MASTER_ABORT_INT 0x00100000L
|
||||
# define TARGET_ABORT_INT 0x00200000L
|
||||
# define BUS_MASTER_INT 0x00200000L
|
||||
# define ALL_INT 0x000C0000L
|
||||
|
||||
#define AMCC_MCSR 0x3c
|
||||
# define A2P_HI_PRIORITY 0x00000100L
|
||||
# define EN_A2P_TRANSFERS 0x00000400L
|
||||
# define P2A_HI_PRIORITY 0x00001000L
|
||||
# define EN_P2A_TRANSFERS 0x00004000L
|
||||
# define RESET_A2P_FLAGS 0x04000000L
|
||||
# define RESET_P2A_FLAGS 0x02000000L
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static inline void b1dma_writel(avmcard *card, u32 value, int off)
|
||||
{
|
||||
writel(value, card->mbase + off);
|
||||
}
|
||||
|
||||
static inline u32 b1dma_readl(avmcard *card, int off)
|
||||
{
|
||||
return readl(card->mbase + off);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static inline int b1dma_tx_empty(unsigned int port)
|
||||
{
|
||||
return inb(port + 0x03) & 0x1;
|
||||
}
|
||||
|
||||
static inline int b1dma_rx_full(unsigned int port)
|
||||
{
|
||||
return inb(port + 0x02) & 0x1;
|
||||
}
|
||||
|
||||
static int b1dma_tolink(avmcard *card, void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */
|
||||
unsigned char *s = (unsigned char *)buf;
|
||||
while (len--) {
|
||||
while (!b1dma_tx_empty(card->port)
|
||||
&& time_before(jiffies, stop));
|
||||
if (!b1dma_tx_empty(card->port))
|
||||
return -1;
|
||||
t1outp(card->port, 0x01, *s++);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */
|
||||
unsigned char *s = (unsigned char *)buf;
|
||||
while (len--) {
|
||||
while (!b1dma_rx_full(card->port)
|
||||
&& time_before(jiffies, stop));
|
||||
if (!b1dma_rx_full(card->port))
|
||||
return -1;
|
||||
*s++ = t1inp(card->port, 0x00);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WriteReg(avmcard *card, u32 reg, u8 val)
|
||||
{
|
||||
u8 cmd = 0x00;
|
||||
if (b1dma_tolink(card, &cmd, 1) == 0
|
||||
&& b1dma_tolink(card, ®, 4) == 0) {
|
||||
u32 tmp = val;
|
||||
return b1dma_tolink(card, &tmp, 4);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static u8 ReadReg(avmcard *card, u32 reg)
|
||||
{
|
||||
u8 cmd = 0x01;
|
||||
if (b1dma_tolink(card, &cmd, 1) == 0
|
||||
&& b1dma_tolink(card, ®, 4) == 0) {
|
||||
u32 tmp;
|
||||
if (b1dma_fromlink(card, &tmp, 4) == 0)
|
||||
return (u8)tmp;
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static inline void _put_byte(void **pp, u8 val)
|
||||
{
|
||||
u8 *s = *pp;
|
||||
*s++ = val;
|
||||
*pp = s;
|
||||
}
|
||||
|
||||
static inline void _put_word(void **pp, u32 val)
|
||||
{
|
||||
u8 *s = *pp;
|
||||
*s++ = val & 0xff;
|
||||
*s++ = (val >> 8) & 0xff;
|
||||
*s++ = (val >> 16) & 0xff;
|
||||
*s++ = (val >> 24) & 0xff;
|
||||
*pp = s;
|
||||
}
|
||||
|
||||
static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
|
||||
{
|
||||
unsigned i = len;
|
||||
_put_word(pp, i);
|
||||
while (i-- > 0)
|
||||
_put_byte(pp, *dp++);
|
||||
}
|
||||
|
||||
static inline u8 _get_byte(void **pp)
|
||||
{
|
||||
u8 *s = *pp;
|
||||
u8 val;
|
||||
val = *s++;
|
||||
*pp = s;
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 _get_word(void **pp)
|
||||
{
|
||||
u8 *s = *pp;
|
||||
u32 val;
|
||||
val = *s++;
|
||||
val |= (*s++ << 8);
|
||||
val |= (*s++ << 16);
|
||||
val |= (*s++ << 24);
|
||||
*pp = s;
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 _get_slice(void **pp, unsigned char *dp)
|
||||
{
|
||||
unsigned int len, i;
|
||||
|
||||
len = i = _get_word(pp);
|
||||
while (i-- > 0) *dp++ = _get_byte(pp);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
void b1dma_reset(avmcard *card)
|
||||
{
|
||||
card->csr = 0x0;
|
||||
b1dma_writel(card, card->csr, AMCC_INTCSR);
|
||||
b1dma_writel(card, 0, AMCC_MCSR);
|
||||
b1dma_writel(card, 0, AMCC_RXLEN);
|
||||
b1dma_writel(card, 0, AMCC_TXLEN);
|
||||
|
||||
t1outp(card->port, 0x10, 0x00);
|
||||
t1outp(card->port, 0x07, 0x00);
|
||||
|
||||
b1dma_writel(card, 0, AMCC_MCSR);
|
||||
mdelay(10);
|
||||
b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
|
||||
mdelay(10);
|
||||
b1dma_writel(card, 0, AMCC_MCSR);
|
||||
if (card->cardtype == avm_t1pci)
|
||||
mdelay(42);
|
||||
else
|
||||
mdelay(10);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static int b1dma_detect(avmcard *card)
|
||||
{
|
||||
b1dma_writel(card, 0, AMCC_MCSR);
|
||||
mdelay(10);
|
||||
b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
|
||||
mdelay(10);
|
||||
b1dma_writel(card, 0, AMCC_MCSR);
|
||||
mdelay(42);
|
||||
|
||||
b1dma_writel(card, 0, AMCC_RXLEN);
|
||||
b1dma_writel(card, 0, AMCC_TXLEN);
|
||||
card->csr = 0x0;
|
||||
b1dma_writel(card, card->csr, AMCC_INTCSR);
|
||||
|
||||
if (b1dma_readl(card, AMCC_MCSR) != 0x000000E6)
|
||||
return 1;
|
||||
|
||||
b1dma_writel(card, 0xffffffff, AMCC_RXPTR);
|
||||
b1dma_writel(card, 0xffffffff, AMCC_TXPTR);
|
||||
if (b1dma_readl(card, AMCC_RXPTR) != 0xfffffffc
|
||||
|| b1dma_readl(card, AMCC_TXPTR) != 0xfffffffc)
|
||||
return 2;
|
||||
|
||||
b1dma_writel(card, 0x0, AMCC_RXPTR);
|
||||
b1dma_writel(card, 0x0, AMCC_TXPTR);
|
||||
if (b1dma_readl(card, AMCC_RXPTR) != 0x0
|
||||
|| b1dma_readl(card, AMCC_TXPTR) != 0x0)
|
||||
return 3;
|
||||
|
||||
t1outp(card->port, 0x10, 0x00);
|
||||
t1outp(card->port, 0x07, 0x00);
|
||||
|
||||
t1outp(card->port, 0x02, 0x02);
|
||||
t1outp(card->port, 0x03, 0x02);
|
||||
|
||||
if ((t1inp(card->port, 0x02) & 0xFE) != 0x02
|
||||
|| t1inp(card->port, 0x3) != 0x03)
|
||||
return 4;
|
||||
|
||||
t1outp(card->port, 0x02, 0x00);
|
||||
t1outp(card->port, 0x03, 0x00);
|
||||
|
||||
if ((t1inp(card->port, 0x02) & 0xFE) != 0x00
|
||||
|| t1inp(card->port, 0x3) != 0x01)
|
||||
return 5;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int t1pci_detect(avmcard *card)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = b1dma_detect(card)) != 0)
|
||||
return ret;
|
||||
|
||||
/* Transputer test */
|
||||
|
||||
if (WriteReg(card, 0x80001000, 0x11) != 0
|
||||
|| WriteReg(card, 0x80101000, 0x22) != 0
|
||||
|| WriteReg(card, 0x80201000, 0x33) != 0
|
||||
|| WriteReg(card, 0x80301000, 0x44) != 0)
|
||||
return 6;
|
||||
|
||||
if (ReadReg(card, 0x80001000) != 0x11
|
||||
|| ReadReg(card, 0x80101000) != 0x22
|
||||
|| ReadReg(card, 0x80201000) != 0x33
|
||||
|| ReadReg(card, 0x80301000) != 0x44)
|
||||
return 7;
|
||||
|
||||
if (WriteReg(card, 0x80001000, 0x55) != 0
|
||||
|| WriteReg(card, 0x80101000, 0x66) != 0
|
||||
|| WriteReg(card, 0x80201000, 0x77) != 0
|
||||
|| WriteReg(card, 0x80301000, 0x88) != 0)
|
||||
return 8;
|
||||
|
||||
if (ReadReg(card, 0x80001000) != 0x55
|
||||
|| ReadReg(card, 0x80101000) != 0x66
|
||||
|| ReadReg(card, 0x80201000) != 0x77
|
||||
|| ReadReg(card, 0x80301000) != 0x88)
|
||||
return 9;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int b1pciv4_detect(avmcard *card)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
if ((ret = b1dma_detect(card)) != 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (WriteReg(card, 0x80A00000, 0x21) != 0)
|
||||
return 6;
|
||||
if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01)
|
||||
return 7;
|
||||
}
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (WriteReg(card, 0x80A00000, 0x20) != 0)
|
||||
return 8;
|
||||
if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00)
|
||||
return 9;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void b1dma_queue_tx(avmcard *card, struct sk_buff *skb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
|
||||
skb_queue_tail(&card->dma->send_queue, skb);
|
||||
|
||||
if (!(card->csr & EN_TX_TC_INT)) {
|
||||
b1dma_dispatch_tx(card);
|
||||
b1dma_writel(card, card->csr, AMCC_INTCSR);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void b1dma_dispatch_tx(avmcard *card)
|
||||
{
|
||||
avmcard_dmainfo *dma = card->dma;
|
||||
struct sk_buff *skb;
|
||||
u8 cmd, subcmd;
|
||||
u16 len;
|
||||
u32 txlen;
|
||||
void *p;
|
||||
|
||||
skb = skb_dequeue(&dma->send_queue);
|
||||
|
||||
len = CAPIMSG_LEN(skb->data);
|
||||
|
||||
if (len) {
|
||||
cmd = CAPIMSG_COMMAND(skb->data);
|
||||
subcmd = CAPIMSG_SUBCOMMAND(skb->data);
|
||||
|
||||
p = dma->sendbuf.dmabuf;
|
||||
|
||||
if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
|
||||
u16 dlen = CAPIMSG_DATALEN(skb->data);
|
||||
_put_byte(&p, SEND_DATA_B3_REQ);
|
||||
_put_slice(&p, skb->data, len);
|
||||
_put_slice(&p, skb->data + len, dlen);
|
||||
} else {
|
||||
_put_byte(&p, SEND_MESSAGE);
|
||||
_put_slice(&p, skb->data, len);
|
||||
}
|
||||
txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
|
||||
#ifdef AVM_B1DMA_DEBUG
|
||||
printk(KERN_DEBUG "tx: put msg len=%d\n", txlen);
|
||||
#endif
|
||||
} else {
|
||||
txlen = skb->len - 2;
|
||||
#ifdef AVM_B1DMA_POLLDEBUG
|
||||
if (skb->data[2] == SEND_POLLACK)
|
||||
printk(KERN_INFO "%s: send ack\n", card->name);
|
||||
#endif
|
||||
#ifdef AVM_B1DMA_DEBUG
|
||||
printk(KERN_DEBUG "tx: put 0x%x len=%d\n",
|
||||
skb->data[2], txlen);
|
||||
#endif
|
||||
skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
|
||||
skb->len - 2);
|
||||
}
|
||||
txlen = (txlen + 3) & ~3;
|
||||
|
||||
b1dma_writel(card, dma->sendbuf.dmaaddr, AMCC_TXPTR);
|
||||
b1dma_writel(card, txlen, AMCC_TXLEN);
|
||||
|
||||
card->csr |= EN_TX_TC_INT;
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void queue_pollack(avmcard *card)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
void *p;
|
||||
|
||||
skb = alloc_skb(3, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
printk(KERN_CRIT "%s: no memory, lost poll ack\n",
|
||||
card->name);
|
||||
return;
|
||||
}
|
||||
p = skb->data;
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, SEND_POLLACK);
|
||||
skb_put(skb, (u8 *)p - (u8 *)skb->data);
|
||||
|
||||
b1dma_queue_tx(card, skb);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void b1dma_handle_rx(avmcard *card)
|
||||
{
|
||||
avmctrl_info *cinfo = &card->ctrlinfo[0];
|
||||
avmcard_dmainfo *dma = card->dma;
|
||||
struct capi_ctr *ctrl = &cinfo->capi_ctrl;
|
||||
struct sk_buff *skb;
|
||||
void *p = dma->recvbuf.dmabuf + 4;
|
||||
u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
|
||||
u8 b1cmd = _get_byte(&p);
|
||||
|
||||
#ifdef AVM_B1DMA_DEBUG
|
||||
printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
|
||||
#endif
|
||||
|
||||
switch (b1cmd) {
|
||||
case RECEIVE_DATA_B3_IND:
|
||||
|
||||
ApplId = (unsigned) _get_word(&p);
|
||||
MsgLen = _get_slice(&p, card->msgbuf);
|
||||
DataB3Len = _get_slice(&p, card->databuf);
|
||||
|
||||
if (MsgLen < 30) { /* not CAPI 64Bit */
|
||||
memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
|
||||
MsgLen = 30;
|
||||
CAPIMSG_SETLEN(card->msgbuf, 30);
|
||||
}
|
||||
if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "%s: incoming packet dropped\n",
|
||||
card->name);
|
||||
} else {
|
||||
skb_put_data(skb, card->msgbuf, MsgLen);
|
||||
skb_put_data(skb, card->databuf, DataB3Len);
|
||||
capi_ctr_handle_message(ctrl, ApplId, skb);
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_MESSAGE:
|
||||
|
||||
ApplId = (unsigned) _get_word(&p);
|
||||
MsgLen = _get_slice(&p, card->msgbuf);
|
||||
if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "%s: incoming packet dropped\n",
|
||||
card->name);
|
||||
} else {
|
||||
skb_put_data(skb, card->msgbuf, MsgLen);
|
||||
if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) {
|
||||
spin_lock(&card->lock);
|
||||
capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
spin_unlock(&card->lock);
|
||||
}
|
||||
capi_ctr_handle_message(ctrl, ApplId, skb);
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_NEW_NCCI:
|
||||
|
||||
ApplId = _get_word(&p);
|
||||
NCCI = _get_word(&p);
|
||||
WindowSize = _get_word(&p);
|
||||
spin_lock(&card->lock);
|
||||
capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
|
||||
spin_unlock(&card->lock);
|
||||
break;
|
||||
|
||||
case RECEIVE_FREE_NCCI:
|
||||
|
||||
ApplId = _get_word(&p);
|
||||
NCCI = _get_word(&p);
|
||||
|
||||
if (NCCI != 0xffffffff) {
|
||||
spin_lock(&card->lock);
|
||||
capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
|
||||
spin_unlock(&card->lock);
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_START:
|
||||
#ifdef AVM_B1DMA_POLLDEBUG
|
||||
printk(KERN_INFO "%s: receive poll\n", card->name);
|
||||
#endif
|
||||
if (!suppress_pollack)
|
||||
queue_pollack(card);
|
||||
capi_ctr_resume_output(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_STOP:
|
||||
capi_ctr_suspend_output(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_INIT:
|
||||
|
||||
cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
|
||||
b1_parse_version(cinfo);
|
||||
printk(KERN_INFO "%s: %s-card (%s) now active\n",
|
||||
card->name,
|
||||
cinfo->version[VER_CARDTYPE],
|
||||
cinfo->version[VER_DRIVER]);
|
||||
capi_ctr_ready(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_TASK_READY:
|
||||
ApplId = (unsigned) _get_word(&p);
|
||||
MsgLen = _get_slice(&p, card->msgbuf);
|
||||
card->msgbuf[MsgLen] = 0;
|
||||
while (MsgLen > 0
|
||||
&& (card->msgbuf[MsgLen - 1] == '\n'
|
||||
|| card->msgbuf[MsgLen - 1] == '\r')) {
|
||||
card->msgbuf[MsgLen - 1] = 0;
|
||||
MsgLen--;
|
||||
}
|
||||
printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
|
||||
card->name, ApplId, card->msgbuf);
|
||||
break;
|
||||
|
||||
case RECEIVE_DEBUGMSG:
|
||||
MsgLen = _get_slice(&p, card->msgbuf);
|
||||
card->msgbuf[MsgLen] = 0;
|
||||
while (MsgLen > 0
|
||||
&& (card->msgbuf[MsgLen - 1] == '\n'
|
||||
|| card->msgbuf[MsgLen - 1] == '\r')) {
|
||||
card->msgbuf[MsgLen - 1] = 0;
|
||||
MsgLen--;
|
||||
}
|
||||
printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n",
|
||||
card->name, b1cmd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void b1dma_handle_interrupt(avmcard *card)
|
||||
{
|
||||
u32 status;
|
||||
u32 newcsr;
|
||||
|
||||
spin_lock(&card->lock);
|
||||
|
||||
status = b1dma_readl(card, AMCC_INTCSR);
|
||||
if ((status & ANY_S5933_INT) == 0) {
|
||||
spin_unlock(&card->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
newcsr = card->csr | (status & ALL_INT);
|
||||
if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
|
||||
if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
|
||||
b1dma_writel(card, newcsr, AMCC_INTCSR);
|
||||
|
||||
if ((status & RX_TC_INT) != 0) {
|
||||
struct avmcard_dmainfo *dma = card->dma;
|
||||
u32 rxlen;
|
||||
if (card->dma->recvlen == 0) {
|
||||
rxlen = b1dma_readl(card, AMCC_RXLEN);
|
||||
if (rxlen == 0) {
|
||||
dma->recvlen = *((u32 *)dma->recvbuf.dmabuf);
|
||||
rxlen = (dma->recvlen + 3) & ~3;
|
||||
b1dma_writel(card, dma->recvbuf.dmaaddr + 4, AMCC_RXPTR);
|
||||
b1dma_writel(card, rxlen, AMCC_RXLEN);
|
||||
#ifdef AVM_B1DMA_DEBUG
|
||||
} else {
|
||||
printk(KERN_ERR "%s: rx not complete (%d).\n",
|
||||
card->name, rxlen);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
spin_unlock(&card->lock);
|
||||
b1dma_handle_rx(card);
|
||||
dma->recvlen = 0;
|
||||
spin_lock(&card->lock);
|
||||
b1dma_writel(card, dma->recvbuf.dmaaddr, AMCC_RXPTR);
|
||||
b1dma_writel(card, 4, AMCC_RXLEN);
|
||||
}
|
||||
}
|
||||
|
||||
if ((status & TX_TC_INT) != 0) {
|
||||
if (skb_queue_empty(&card->dma->send_queue))
|
||||
card->csr &= ~EN_TX_TC_INT;
|
||||
else
|
||||
b1dma_dispatch_tx(card);
|
||||
}
|
||||
b1dma_writel(card, card->csr, AMCC_INTCSR);
|
||||
|
||||
spin_unlock(&card->lock);
|
||||
}
|
||||
|
||||
irqreturn_t b1dma_interrupt(int interrupt, void *devptr)
|
||||
{
|
||||
avmcard *card = devptr;
|
||||
|
||||
b1dma_handle_interrupt(card);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static int b1dma_loaded(avmcard *card)
|
||||
{
|
||||
unsigned long stop;
|
||||
unsigned char ans;
|
||||
unsigned long tout = 2;
|
||||
unsigned int base = card->port;
|
||||
|
||||
for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
|
||||
if (b1_tx_empty(base))
|
||||
break;
|
||||
}
|
||||
if (!b1_tx_empty(base)) {
|
||||
printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n",
|
||||
card->name);
|
||||
return 0;
|
||||
}
|
||||
b1_put_byte(base, SEND_POLLACK);
|
||||
for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
|
||||
if (b1_rx_full(base)) {
|
||||
if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
|
||||
return 1;
|
||||
}
|
||||
printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void b1dma_send_init(avmcard *card)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
void *p;
|
||||
|
||||
skb = alloc_skb(15, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
printk(KERN_CRIT "%s: no memory, lost register appl.\n",
|
||||
card->name);
|
||||
return;
|
||||
}
|
||||
p = skb->data;
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, SEND_INIT);
|
||||
_put_word(&p, CAPI_MAXAPPL);
|
||||
_put_word(&p, AVM_NCCI_PER_CHANNEL * 30);
|
||||
_put_word(&p, card->cardnr - 1);
|
||||
skb_put(skb, (u8 *)p - (u8 *)skb->data);
|
||||
|
||||
b1dma_queue_tx(card, skb);
|
||||
}
|
||||
|
||||
int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
int retval;
|
||||
|
||||
b1dma_reset(card);
|
||||
|
||||
if ((retval = b1_load_t4file(card, &data->firmware))) {
|
||||
b1dma_reset(card);
|
||||
printk(KERN_ERR "%s: failed to load t4file!!\n",
|
||||
card->name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (data->configuration.len > 0 && data->configuration.data) {
|
||||
if ((retval = b1_load_config(card, &data->configuration))) {
|
||||
b1dma_reset(card);
|
||||
printk(KERN_ERR "%s: failed to load config!!\n",
|
||||
card->name);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (!b1dma_loaded(card)) {
|
||||
b1dma_reset(card);
|
||||
printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
card->csr = AVM_FLAG;
|
||||
b1dma_writel(card, card->csr, AMCC_INTCSR);
|
||||
b1dma_writel(card, EN_A2P_TRANSFERS | EN_P2A_TRANSFERS | A2P_HI_PRIORITY |
|
||||
P2A_HI_PRIORITY | RESET_A2P_FLAGS | RESET_P2A_FLAGS,
|
||||
AMCC_MCSR);
|
||||
t1outp(card->port, 0x07, 0x30);
|
||||
t1outp(card->port, 0x10, 0xF0);
|
||||
|
||||
card->dma->recvlen = 0;
|
||||
b1dma_writel(card, card->dma->recvbuf.dmaaddr, AMCC_RXPTR);
|
||||
b1dma_writel(card, 4, AMCC_RXLEN);
|
||||
card->csr |= EN_RX_TC_INT;
|
||||
b1dma_writel(card, card->csr, AMCC_INTCSR);
|
||||
|
||||
b1dma_send_init(card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void b1dma_reset_ctr(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
b1dma_reset(card);
|
||||
|
||||
memset(cinfo->version, 0, sizeof(cinfo->version));
|
||||
capilib_release(&cinfo->ncci_head);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_down(ctrl);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
void b1dma_register_appl(struct capi_ctr *ctrl,
|
||||
u16 appl,
|
||||
capi_register_params *rp)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
struct sk_buff *skb;
|
||||
int want = rp->level3cnt;
|
||||
int nconn;
|
||||
void *p;
|
||||
|
||||
if (want > 0) nconn = want;
|
||||
else nconn = ctrl->profile.nbchannel * -want;
|
||||
if (nconn == 0) nconn = ctrl->profile.nbchannel;
|
||||
|
||||
skb = alloc_skb(23, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
printk(KERN_CRIT "%s: no memory, lost register appl.\n",
|
||||
card->name);
|
||||
return;
|
||||
}
|
||||
p = skb->data;
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, SEND_REGISTER);
|
||||
_put_word(&p, appl);
|
||||
_put_word(&p, 1024 * (nconn + 1));
|
||||
_put_word(&p, nconn);
|
||||
_put_word(&p, rp->datablkcnt);
|
||||
_put_word(&p, rp->datablklen);
|
||||
skb_put(skb, (u8 *)p - (u8 *)skb->data);
|
||||
|
||||
b1dma_queue_tx(card, skb);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
struct sk_buff *skb;
|
||||
void *p;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
capilib_release_appl(&cinfo->ncci_head, appl);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
skb = alloc_skb(7, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
printk(KERN_CRIT "%s: no memory, lost release appl.\n",
|
||||
card->name);
|
||||
return;
|
||||
}
|
||||
p = skb->data;
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, 0);
|
||||
_put_byte(&p, SEND_RELEASE);
|
||||
_put_word(&p, appl);
|
||||
|
||||
skb_put(skb, (u8 *)p - (u8 *)skb->data);
|
||||
|
||||
b1dma_queue_tx(card, skb);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
u16 retval = CAPI_NOERROR;
|
||||
|
||||
if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
retval = capilib_data_b3_req(&cinfo->ncci_head,
|
||||
CAPIMSG_APPID(skb->data),
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
}
|
||||
if (retval == CAPI_NOERROR)
|
||||
b1dma_queue_tx(card, skb);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
int b1dma_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct capi_ctr *ctrl = m->private;
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
u8 flag;
|
||||
char *s;
|
||||
u32 txoff, txlen, rxoff, rxlen, csr;
|
||||
unsigned long flags;
|
||||
|
||||
seq_printf(m, "%-16s %s\n", "name", card->name);
|
||||
seq_printf(m, "%-16s 0x%x\n", "io", card->port);
|
||||
seq_printf(m, "%-16s %d\n", "irq", card->irq);
|
||||
seq_printf(m, "%-16s 0x%lx\n", "membase", card->membase);
|
||||
switch (card->cardtype) {
|
||||
case avm_b1isa: s = "B1 ISA"; break;
|
||||
case avm_b1pci: s = "B1 PCI"; break;
|
||||
case avm_b1pcmcia: s = "B1 PCMCIA"; break;
|
||||
case avm_m1: s = "M1"; break;
|
||||
case avm_m2: s = "M2"; break;
|
||||
case avm_t1isa: s = "T1 ISA (HEMA)"; break;
|
||||
case avm_t1pci: s = "T1 PCI"; break;
|
||||
case avm_c4: s = "C4"; break;
|
||||
case avm_c2: s = "C2"; break;
|
||||
default: s = "???"; break;
|
||||
}
|
||||
seq_printf(m, "%-16s %s\n", "type", s);
|
||||
if ((s = cinfo->version[VER_DRIVER]) != NULL)
|
||||
seq_printf(m, "%-16s %s\n", "ver_driver", s);
|
||||
if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
|
||||
seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
|
||||
if ((s = cinfo->version[VER_SERIAL]) != NULL)
|
||||
seq_printf(m, "%-16s %s\n", "ver_serial", s);
|
||||
|
||||
if (card->cardtype != avm_m1) {
|
||||
flag = ((u8 *)(ctrl->profile.manu))[3];
|
||||
if (flag)
|
||||
seq_printf(m, "%-16s%s%s%s%s%s%s%s\n",
|
||||
"protocol",
|
||||
(flag & 0x01) ? " DSS1" : "",
|
||||
(flag & 0x02) ? " CT1" : "",
|
||||
(flag & 0x04) ? " VN3" : "",
|
||||
(flag & 0x08) ? " NI1" : "",
|
||||
(flag & 0x10) ? " AUSTEL" : "",
|
||||
(flag & 0x20) ? " ESS" : "",
|
||||
(flag & 0x40) ? " 1TR6" : ""
|
||||
);
|
||||
}
|
||||
if (card->cardtype != avm_m1) {
|
||||
flag = ((u8 *)(ctrl->profile.manu))[5];
|
||||
if (flag)
|
||||
seq_printf(m, "%-16s%s%s%s%s\n",
|
||||
"linetype",
|
||||
(flag & 0x01) ? " point to point" : "",
|
||||
(flag & 0x02) ? " point to multipoint" : "",
|
||||
(flag & 0x08) ? " leased line without D-channel" : "",
|
||||
(flag & 0x04) ? " leased line with D-channel" : ""
|
||||
);
|
||||
}
|
||||
seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
|
||||
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
|
||||
txoff = (dma_addr_t)b1dma_readl(card, AMCC_TXPTR)-card->dma->sendbuf.dmaaddr;
|
||||
txlen = b1dma_readl(card, AMCC_TXLEN);
|
||||
|
||||
rxoff = (dma_addr_t)b1dma_readl(card, AMCC_RXPTR)-card->dma->recvbuf.dmaaddr;
|
||||
rxlen = b1dma_readl(card, AMCC_RXLEN);
|
||||
|
||||
csr = b1dma_readl(card, AMCC_INTCSR);
|
||||
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
seq_printf(m, "%-16s 0x%lx\n", "csr (cached)", (unsigned long)card->csr);
|
||||
seq_printf(m, "%-16s 0x%lx\n", "csr", (unsigned long)csr);
|
||||
seq_printf(m, "%-16s %lu\n", "txoff", (unsigned long)txoff);
|
||||
seq_printf(m, "%-16s %lu\n", "txlen", (unsigned long)txlen);
|
||||
seq_printf(m, "%-16s %lu\n", "rxoff", (unsigned long)rxoff);
|
||||
seq_printf(m, "%-16s %lu\n", "rxlen", (unsigned long)rxlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(b1dma_proc_show);
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
EXPORT_SYMBOL(b1dma_reset);
|
||||
EXPORT_SYMBOL(t1pci_detect);
|
||||
EXPORT_SYMBOL(b1pciv4_detect);
|
||||
EXPORT_SYMBOL(b1dma_interrupt);
|
||||
|
||||
EXPORT_SYMBOL(b1dma_load_firmware);
|
||||
EXPORT_SYMBOL(b1dma_reset_ctr);
|
||||
EXPORT_SYMBOL(b1dma_register_appl);
|
||||
EXPORT_SYMBOL(b1dma_release_appl);
|
||||
EXPORT_SYMBOL(b1dma_send_message);
|
||||
|
||||
static int __init b1dma_init(void)
|
||||
{
|
||||
char *p;
|
||||
char rev[32];
|
||||
|
||||
if ((p = strchr(revision, ':')) != NULL && p[1]) {
|
||||
strlcpy(rev, p + 2, sizeof(rev));
|
||||
if ((p = strchr(rev, '$')) != NULL && p > rev)
|
||||
*(p - 1) = 0;
|
||||
} else
|
||||
strcpy(rev, "1.0");
|
||||
|
||||
printk(KERN_INFO "b1dma: revision %s\n", rev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit b1dma_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
module_init(b1dma_init);
|
||||
module_exit(b1dma_exit);
|
@ -1,243 +0,0 @@
|
||||
/* $Id: b1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
|
||||
*
|
||||
* Module for AVM B1 ISA-card.
|
||||
*
|
||||
* Copyright 1999 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/capi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
#include "avmcard.h"
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *revision = "$Revision: 1.1.2.3 $";
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 ISA card");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void b1isa_remove(struct pci_dev *pdev)
|
||||
{
|
||||
avmctrl_info *cinfo = pci_get_drvdata(pdev);
|
||||
avmcard *card;
|
||||
|
||||
if (!cinfo)
|
||||
return;
|
||||
|
||||
card = cinfo->card;
|
||||
|
||||
b1_reset(card->port);
|
||||
b1_reset(card->port);
|
||||
|
||||
detach_capi_ctr(&cinfo->capi_ctrl);
|
||||
free_irq(card->irq, card);
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
b1_free_card(card);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *b1isa_procinfo(struct capi_ctr *ctrl);
|
||||
|
||||
static int b1isa_probe(struct pci_dev *pdev)
|
||||
{
|
||||
avmctrl_info *cinfo;
|
||||
avmcard *card;
|
||||
int retval;
|
||||
|
||||
card = b1_alloc_card(1);
|
||||
if (!card) {
|
||||
printk(KERN_WARNING "b1isa: no memory.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cinfo = card->ctrlinfo;
|
||||
|
||||
card->port = pci_resource_start(pdev, 0);
|
||||
card->irq = pdev->irq;
|
||||
card->cardtype = avm_b1isa;
|
||||
sprintf(card->name, "b1isa-%x", card->port);
|
||||
|
||||
if (card->port != 0x150 && card->port != 0x250
|
||||
&& card->port != 0x300 && card->port != 0x340) {
|
||||
printk(KERN_WARNING "b1isa: invalid port 0x%x.\n", card->port);
|
||||
retval = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
if (b1_irq_table[card->irq & 0xf] == 0) {
|
||||
printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq);
|
||||
retval = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
|
||||
printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n",
|
||||
card->port, card->port + AVMB1_PORTLEN);
|
||||
retval = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
retval = request_irq(card->irq, b1_interrupt, 0, card->name, card);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq);
|
||||
goto err_release_region;
|
||||
}
|
||||
b1_reset(card->port);
|
||||
if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
|
||||
printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n",
|
||||
card->port, retval);
|
||||
retval = -ENODEV;
|
||||
goto err_free_irq;
|
||||
}
|
||||
b1_reset(card->port);
|
||||
b1_getrevision(card);
|
||||
|
||||
cinfo->capi_ctrl.owner = THIS_MODULE;
|
||||
cinfo->capi_ctrl.driver_name = "b1isa";
|
||||
cinfo->capi_ctrl.driverdata = cinfo;
|
||||
cinfo->capi_ctrl.register_appl = b1_register_appl;
|
||||
cinfo->capi_ctrl.release_appl = b1_release_appl;
|
||||
cinfo->capi_ctrl.send_message = b1_send_message;
|
||||
cinfo->capi_ctrl.load_firmware = b1_load_firmware;
|
||||
cinfo->capi_ctrl.reset_ctr = b1_reset_ctr;
|
||||
cinfo->capi_ctrl.procinfo = b1isa_procinfo;
|
||||
cinfo->capi_ctrl.proc_show = b1_proc_show;
|
||||
strcpy(cinfo->capi_ctrl.name, card->name);
|
||||
|
||||
retval = attach_capi_ctr(&cinfo->capi_ctrl);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1isa: attach controller failed.\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "b1isa: AVM B1 ISA at i/o %#x, irq %d, revision %d\n",
|
||||
card->port, card->irq, card->revision);
|
||||
|
||||
pci_set_drvdata(pdev, cinfo);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(card->irq, card);
|
||||
err_release_region:
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
err_free:
|
||||
b1_free_card(card);
|
||||
err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static char *b1isa_procinfo(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
|
||||
if (!cinfo)
|
||||
return "";
|
||||
sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
|
||||
cinfo->cardname[0] ? cinfo->cardname : "-",
|
||||
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
|
||||
cinfo->card ? cinfo->card->port : 0x0,
|
||||
cinfo->card ? cinfo->card->irq : 0,
|
||||
cinfo->card ? cinfo->card->revision : 0
|
||||
);
|
||||
return cinfo->infobuf;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
#define MAX_CARDS 4
|
||||
static struct pci_dev isa_dev[MAX_CARDS];
|
||||
static int io[MAX_CARDS];
|
||||
static int irq[MAX_CARDS];
|
||||
|
||||
module_param_hw_array(io, int, ioport, NULL, 0);
|
||||
module_param_hw_array(irq, int, irq, NULL, 0);
|
||||
MODULE_PARM_DESC(io, "I/O base address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
|
||||
|
||||
static int b1isa_add_card(struct capi_driver *driver, capicardparams *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CARDS; i++) {
|
||||
if (isa_dev[i].resource[0].start)
|
||||
continue;
|
||||
|
||||
isa_dev[i].resource[0].start = data->port;
|
||||
isa_dev[i].irq = data->irq;
|
||||
|
||||
if (b1isa_probe(&isa_dev[i]) == 0)
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct capi_driver capi_driver_b1isa = {
|
||||
.name = "b1isa",
|
||||
.revision = "1.0",
|
||||
.add_card = b1isa_add_card,
|
||||
};
|
||||
|
||||
static int __init b1isa_init(void)
|
||||
{
|
||||
char *p;
|
||||
char rev[32];
|
||||
int i;
|
||||
|
||||
if ((p = strchr(revision, ':')) != NULL && p[1]) {
|
||||
strlcpy(rev, p + 2, 32);
|
||||
if ((p = strchr(rev, '$')) != NULL && p > rev)
|
||||
*(p - 1) = 0;
|
||||
} else
|
||||
strcpy(rev, "1.0");
|
||||
|
||||
for (i = 0; i < MAX_CARDS; i++) {
|
||||
if (!io[i])
|
||||
break;
|
||||
|
||||
isa_dev[i].resource[0].start = io[i];
|
||||
isa_dev[i].irq = irq[i];
|
||||
|
||||
if (b1isa_probe(&isa_dev[i]) != 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strlcpy(capi_driver_b1isa.revision, rev, 32);
|
||||
register_capi_driver(&capi_driver_b1isa);
|
||||
printk(KERN_INFO "b1isa: revision %s\n", rev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit b1isa_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CARDS; i++) {
|
||||
if (isa_dev[i].resource[0].start)
|
||||
b1isa_remove(&isa_dev[i]);
|
||||
}
|
||||
unregister_capi_driver(&capi_driver_b1isa);
|
||||
}
|
||||
|
||||
module_init(b1isa_init);
|
||||
module_exit(b1isa_exit);
|
@ -1,416 +0,0 @@
|
||||
/* $Id: b1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
|
||||
*
|
||||
* Module for AVM B1 PCI-card.
|
||||
*
|
||||
* Copyright 1999 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/capi.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
#include "avmcard.h"
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *revision = "$Revision: 1.1.2.2 $";
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static struct pci_device_id b1pci_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, PCI_ANY_ID, PCI_ANY_ID },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, b1pci_pci_tbl);
|
||||
MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 PCI card");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *b1pci_procinfo(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
|
||||
if (!cinfo)
|
||||
return "";
|
||||
sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
|
||||
cinfo->cardname[0] ? cinfo->cardname : "-",
|
||||
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
|
||||
cinfo->card ? cinfo->card->port : 0x0,
|
||||
cinfo->card ? cinfo->card->irq : 0,
|
||||
cinfo->card ? cinfo->card->revision : 0
|
||||
);
|
||||
return cinfo->infobuf;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static int b1pci_probe(struct capicardparams *p, struct pci_dev *pdev)
|
||||
{
|
||||
avmcard *card;
|
||||
avmctrl_info *cinfo;
|
||||
int retval;
|
||||
|
||||
card = b1_alloc_card(1);
|
||||
if (!card) {
|
||||
printk(KERN_WARNING "b1pci: no memory.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cinfo = card->ctrlinfo;
|
||||
sprintf(card->name, "b1pci-%x", p->port);
|
||||
card->port = p->port;
|
||||
card->irq = p->irq;
|
||||
card->cardtype = avm_b1pci;
|
||||
|
||||
if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
|
||||
printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
|
||||
card->port, card->port + AVMB1_PORTLEN);
|
||||
retval = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
b1_reset(card->port);
|
||||
retval = b1_detect(card->port, card->cardtype);
|
||||
if (retval) {
|
||||
printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
|
||||
card->port, retval);
|
||||
retval = -ENODEV;
|
||||
goto err_release_region;
|
||||
}
|
||||
b1_reset(card->port);
|
||||
b1_getrevision(card);
|
||||
|
||||
retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq);
|
||||
retval = -EBUSY;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
cinfo->capi_ctrl.driver_name = "b1pci";
|
||||
cinfo->capi_ctrl.driverdata = cinfo;
|
||||
cinfo->capi_ctrl.register_appl = b1_register_appl;
|
||||
cinfo->capi_ctrl.release_appl = b1_release_appl;
|
||||
cinfo->capi_ctrl.send_message = b1_send_message;
|
||||
cinfo->capi_ctrl.load_firmware = b1_load_firmware;
|
||||
cinfo->capi_ctrl.reset_ctr = b1_reset_ctr;
|
||||
cinfo->capi_ctrl.procinfo = b1pci_procinfo;
|
||||
cinfo->capi_ctrl.proc_show = b1_proc_show;
|
||||
strcpy(cinfo->capi_ctrl.name, card->name);
|
||||
cinfo->capi_ctrl.owner = THIS_MODULE;
|
||||
|
||||
retval = attach_capi_ctr(&cinfo->capi_ctrl);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1pci: attach controller failed.\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
if (card->revision >= 4) {
|
||||
printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n",
|
||||
card->port, card->irq, card->revision);
|
||||
} else {
|
||||
printk(KERN_INFO "b1pci: AVM B1 PCI at i/o %#x, irq %d, revision %d\n",
|
||||
card->port, card->irq, card->revision);
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, card);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(card->irq, card);
|
||||
err_release_region:
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
err_free:
|
||||
b1_free_card(card);
|
||||
err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void b1pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
avmcard *card = pci_get_drvdata(pdev);
|
||||
avmctrl_info *cinfo = card->ctrlinfo;
|
||||
unsigned int port = card->port;
|
||||
|
||||
b1_reset(port);
|
||||
b1_reset(port);
|
||||
|
||||
detach_capi_ctr(&cinfo->capi_ctrl);
|
||||
free_irq(card->irq, card);
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
b1_free_card(card);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *b1pciv4_procinfo(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
|
||||
if (!cinfo)
|
||||
return "";
|
||||
sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d",
|
||||
cinfo->cardname[0] ? cinfo->cardname : "-",
|
||||
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
|
||||
cinfo->card ? cinfo->card->port : 0x0,
|
||||
cinfo->card ? cinfo->card->irq : 0,
|
||||
cinfo->card ? cinfo->card->membase : 0,
|
||||
cinfo->card ? cinfo->card->revision : 0
|
||||
);
|
||||
return cinfo->infobuf;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static int b1pciv4_probe(struct capicardparams *p, struct pci_dev *pdev)
|
||||
{
|
||||
avmcard *card;
|
||||
avmctrl_info *cinfo;
|
||||
int retval;
|
||||
|
||||
card = b1_alloc_card(1);
|
||||
if (!card) {
|
||||
printk(KERN_WARNING "b1pci: no memory.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
card->dma = avmcard_dma_alloc("b1pci", pdev, 2048 + 128, 2048 + 128);
|
||||
if (!card->dma) {
|
||||
printk(KERN_WARNING "b1pci: dma alloc.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
cinfo = card->ctrlinfo;
|
||||
sprintf(card->name, "b1pciv4-%x", p->port);
|
||||
card->port = p->port;
|
||||
card->irq = p->irq;
|
||||
card->membase = p->membase;
|
||||
card->cardtype = avm_b1pci;
|
||||
|
||||
if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
|
||||
printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
|
||||
card->port, card->port + AVMB1_PORTLEN);
|
||||
retval = -EBUSY;
|
||||
goto err_free_dma;
|
||||
}
|
||||
|
||||
card->mbase = ioremap(card->membase, 64);
|
||||
if (!card->mbase) {
|
||||
printk(KERN_NOTICE "b1pci: can't remap memory at 0x%lx\n",
|
||||
card->membase);
|
||||
retval = -ENOMEM;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
b1dma_reset(card);
|
||||
|
||||
retval = b1pciv4_detect(card);
|
||||
if (retval) {
|
||||
printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
|
||||
card->port, retval);
|
||||
retval = -ENODEV;
|
||||
goto err_unmap;
|
||||
}
|
||||
b1dma_reset(card);
|
||||
b1_getrevision(card);
|
||||
|
||||
retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1pci: unable to get IRQ %d.\n",
|
||||
card->irq);
|
||||
retval = -EBUSY;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
cinfo->capi_ctrl.owner = THIS_MODULE;
|
||||
cinfo->capi_ctrl.driver_name = "b1pciv4";
|
||||
cinfo->capi_ctrl.driverdata = cinfo;
|
||||
cinfo->capi_ctrl.register_appl = b1dma_register_appl;
|
||||
cinfo->capi_ctrl.release_appl = b1dma_release_appl;
|
||||
cinfo->capi_ctrl.send_message = b1dma_send_message;
|
||||
cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
|
||||
cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr;
|
||||
cinfo->capi_ctrl.procinfo = b1pciv4_procinfo;
|
||||
cinfo->capi_ctrl.proc_show = b1dma_proc_show;
|
||||
strcpy(cinfo->capi_ctrl.name, card->name);
|
||||
|
||||
retval = attach_capi_ctr(&cinfo->capi_ctrl);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1pci: attach controller failed.\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
card->cardnr = cinfo->capi_ctrl.cnr;
|
||||
|
||||
printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n",
|
||||
card->port, card->irq, card->membase, card->revision);
|
||||
|
||||
pci_set_drvdata(pdev, card);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(card->irq, card);
|
||||
err_unmap:
|
||||
iounmap(card->mbase);
|
||||
err_release_region:
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
err_free_dma:
|
||||
avmcard_dma_free(card->dma);
|
||||
err_free:
|
||||
b1_free_card(card);
|
||||
err:
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
static void b1pciv4_remove(struct pci_dev *pdev)
|
||||
{
|
||||
avmcard *card = pci_get_drvdata(pdev);
|
||||
avmctrl_info *cinfo = card->ctrlinfo;
|
||||
|
||||
b1dma_reset(card);
|
||||
|
||||
detach_capi_ctr(&cinfo->capi_ctrl);
|
||||
free_irq(card->irq, card);
|
||||
iounmap(card->mbase);
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
avmcard_dma_free(card->dma);
|
||||
b1_free_card(card);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
|
||||
|
||||
static int b1pci_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
struct capicardparams param;
|
||||
int retval;
|
||||
|
||||
if (pci_enable_device(pdev) < 0) {
|
||||
printk(KERN_ERR "b1pci: failed to enable AVM-B1\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
param.irq = pdev->irq;
|
||||
|
||||
if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */
|
||||
#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
|
||||
pci_set_master(pdev);
|
||||
#endif
|
||||
param.membase = pci_resource_start(pdev, 0);
|
||||
param.port = pci_resource_start(pdev, 2);
|
||||
|
||||
printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
|
||||
param.port, param.irq, param.membase);
|
||||
#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
|
||||
retval = b1pciv4_probe(¶m, pdev);
|
||||
#else
|
||||
retval = b1pci_probe(¶m, pdev);
|
||||
#endif
|
||||
if (retval != 0) {
|
||||
printk(KERN_ERR "b1pci: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n",
|
||||
param.port, param.irq, param.membase);
|
||||
}
|
||||
} else {
|
||||
param.membase = 0;
|
||||
param.port = pci_resource_start(pdev, 1);
|
||||
|
||||
printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
|
||||
param.port, param.irq);
|
||||
retval = b1pci_probe(¶m, pdev);
|
||||
if (retval != 0) {
|
||||
printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
|
||||
param.port, param.irq);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void b1pci_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
|
||||
avmcard *card = pci_get_drvdata(pdev);
|
||||
|
||||
if (card->dma)
|
||||
b1pciv4_remove(pdev);
|
||||
else
|
||||
b1pci_remove(pdev);
|
||||
#else
|
||||
b1pci_remove(pdev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct pci_driver b1pci_pci_driver = {
|
||||
.name = "b1pci",
|
||||
.id_table = b1pci_pci_tbl,
|
||||
.probe = b1pci_pci_probe,
|
||||
.remove = b1pci_pci_remove,
|
||||
};
|
||||
|
||||
static struct capi_driver capi_driver_b1pci = {
|
||||
.name = "b1pci",
|
||||
.revision = "1.0",
|
||||
};
|
||||
#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
|
||||
static struct capi_driver capi_driver_b1pciv4 = {
|
||||
.name = "b1pciv4",
|
||||
.revision = "1.0",
|
||||
};
|
||||
#endif
|
||||
|
||||
static int __init b1pci_init(void)
|
||||
{
|
||||
char *p;
|
||||
char rev[32];
|
||||
int err;
|
||||
|
||||
if ((p = strchr(revision, ':')) != NULL && p[1]) {
|
||||
strlcpy(rev, p + 2, 32);
|
||||
if ((p = strchr(rev, '$')) != NULL && p > rev)
|
||||
*(p - 1) = 0;
|
||||
} else
|
||||
strcpy(rev, "1.0");
|
||||
|
||||
|
||||
err = pci_register_driver(&b1pci_pci_driver);
|
||||
if (!err) {
|
||||
strlcpy(capi_driver_b1pci.revision, rev, 32);
|
||||
register_capi_driver(&capi_driver_b1pci);
|
||||
#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
|
||||
strlcpy(capi_driver_b1pciv4.revision, rev, 32);
|
||||
register_capi_driver(&capi_driver_b1pciv4);
|
||||
#endif
|
||||
printk(KERN_INFO "b1pci: revision %s\n", rev);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit b1pci_exit(void)
|
||||
{
|
||||
unregister_capi_driver(&capi_driver_b1pci);
|
||||
#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
|
||||
unregister_capi_driver(&capi_driver_b1pciv4);
|
||||
#endif
|
||||
pci_unregister_driver(&b1pci_pci_driver);
|
||||
}
|
||||
|
||||
module_init(b1pci_init);
|
||||
module_exit(b1pci_exit);
|
@ -1,224 +0,0 @@
|
||||
/* $Id: b1pcmcia.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
|
||||
*
|
||||
* Module for AVM B1/M1/M2 PCMCIA-card.
|
||||
*
|
||||
* Copyright 1999 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/capi.h>
|
||||
#include <linux/b1pcmcia.h>
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
#include "avmcard.h"
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *revision = "$Revision: 1.1.2.2 $";
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM PCMCIA cards");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
|
||||
b1_reset(port);
|
||||
b1_reset(port);
|
||||
|
||||
detach_capi_ctr(ctrl);
|
||||
free_irq(card->irq, card);
|
||||
b1_free_card(card);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static LIST_HEAD(cards);
|
||||
|
||||
static char *b1pcmcia_procinfo(struct capi_ctr *ctrl);
|
||||
|
||||
static int b1pcmcia_add_card(unsigned int port, unsigned irq,
|
||||
enum avmcardtype cardtype)
|
||||
{
|
||||
avmctrl_info *cinfo;
|
||||
avmcard *card;
|
||||
char *cardname;
|
||||
int retval;
|
||||
|
||||
card = b1_alloc_card(1);
|
||||
if (!card) {
|
||||
printk(KERN_WARNING "b1pcmcia: no memory.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
cinfo = card->ctrlinfo;
|
||||
|
||||
switch (cardtype) {
|
||||
case avm_m1: sprintf(card->name, "m1-%x", port); break;
|
||||
case avm_m2: sprintf(card->name, "m2-%x", port); break;
|
||||
default: sprintf(card->name, "b1pcmcia-%x", port); break;
|
||||
}
|
||||
card->port = port;
|
||||
card->irq = irq;
|
||||
card->cardtype = cardtype;
|
||||
|
||||
retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n",
|
||||
card->irq);
|
||||
retval = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
b1_reset(card->port);
|
||||
if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
|
||||
printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n",
|
||||
card->port, retval);
|
||||
retval = -ENODEV;
|
||||
goto err_free_irq;
|
||||
}
|
||||
b1_reset(card->port);
|
||||
b1_getrevision(card);
|
||||
|
||||
cinfo->capi_ctrl.owner = THIS_MODULE;
|
||||
cinfo->capi_ctrl.driver_name = "b1pcmcia";
|
||||
cinfo->capi_ctrl.driverdata = cinfo;
|
||||
cinfo->capi_ctrl.register_appl = b1_register_appl;
|
||||
cinfo->capi_ctrl.release_appl = b1_release_appl;
|
||||
cinfo->capi_ctrl.send_message = b1_send_message;
|
||||
cinfo->capi_ctrl.load_firmware = b1_load_firmware;
|
||||
cinfo->capi_ctrl.reset_ctr = b1_reset_ctr;
|
||||
cinfo->capi_ctrl.procinfo = b1pcmcia_procinfo;
|
||||
cinfo->capi_ctrl.proc_show = b1_proc_show;
|
||||
strcpy(cinfo->capi_ctrl.name, card->name);
|
||||
|
||||
retval = attach_capi_ctr(&cinfo->capi_ctrl);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "b1pcmcia: attach controller failed.\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
switch (cardtype) {
|
||||
case avm_m1: cardname = "M1"; break;
|
||||
case avm_m2: cardname = "M2"; break;
|
||||
default: cardname = "B1 PCMCIA"; break;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "b1pcmcia: AVM %s at i/o %#x, irq %d, revision %d\n",
|
||||
cardname, card->port, card->irq, card->revision);
|
||||
|
||||
list_add(&card->list, &cards);
|
||||
return cinfo->capi_ctrl.cnr;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(card->irq, card);
|
||||
err_free:
|
||||
b1_free_card(card);
|
||||
err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *b1pcmcia_procinfo(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
|
||||
if (!cinfo)
|
||||
return "";
|
||||
sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
|
||||
cinfo->cardname[0] ? cinfo->cardname : "-",
|
||||
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
|
||||
cinfo->card ? cinfo->card->port : 0x0,
|
||||
cinfo->card ? cinfo->card->irq : 0,
|
||||
cinfo->card ? cinfo->card->revision : 0
|
||||
);
|
||||
return cinfo->infobuf;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
int b1pcmcia_addcard_b1(unsigned int port, unsigned irq)
|
||||
{
|
||||
return b1pcmcia_add_card(port, irq, avm_b1pcmcia);
|
||||
}
|
||||
|
||||
int b1pcmcia_addcard_m1(unsigned int port, unsigned irq)
|
||||
{
|
||||
return b1pcmcia_add_card(port, irq, avm_m1);
|
||||
}
|
||||
|
||||
int b1pcmcia_addcard_m2(unsigned int port, unsigned irq)
|
||||
{
|
||||
return b1pcmcia_add_card(port, irq, avm_m2);
|
||||
}
|
||||
|
||||
int b1pcmcia_delcard(unsigned int port, unsigned irq)
|
||||
{
|
||||
struct list_head *l;
|
||||
avmcard *card;
|
||||
|
||||
list_for_each(l, &cards) {
|
||||
card = list_entry(l, avmcard, list);
|
||||
if (card->port == port && card->irq == irq) {
|
||||
b1pcmcia_remove_ctr(&card->ctrlinfo[0].capi_ctrl);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(b1pcmcia_addcard_b1);
|
||||
EXPORT_SYMBOL(b1pcmcia_addcard_m1);
|
||||
EXPORT_SYMBOL(b1pcmcia_addcard_m2);
|
||||
EXPORT_SYMBOL(b1pcmcia_delcard);
|
||||
|
||||
static struct capi_driver capi_driver_b1pcmcia = {
|
||||
.name = "b1pcmcia",
|
||||
.revision = "1.0",
|
||||
};
|
||||
|
||||
static int __init b1pcmcia_init(void)
|
||||
{
|
||||
char *p;
|
||||
char rev[32];
|
||||
|
||||
if ((p = strchr(revision, ':')) != NULL && p[1]) {
|
||||
strlcpy(rev, p + 2, 32);
|
||||
if ((p = strchr(rev, '$')) != NULL && p > rev)
|
||||
*(p - 1) = 0;
|
||||
} else
|
||||
strcpy(rev, "1.0");
|
||||
|
||||
strlcpy(capi_driver_b1pcmcia.revision, rev, 32);
|
||||
register_capi_driver(&capi_driver_b1pcmcia);
|
||||
printk(KERN_INFO "b1pci: revision %s\n", rev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit b1pcmcia_exit(void)
|
||||
{
|
||||
unregister_capi_driver(&capi_driver_b1pcmcia);
|
||||
}
|
||||
|
||||
module_init(b1pcmcia_init);
|
||||
module_exit(b1pcmcia_exit);
|
File diff suppressed because it is too large
Load Diff
@ -1,594 +0,0 @@
|
||||
/* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
|
||||
*
|
||||
* Module for AVM T1 HEMA-card.
|
||||
*
|
||||
* Copyright 1999 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/capi.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/kernelcapi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
#include "avmcard.h"
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *revision = "$Revision: 1.1.2.3 $";
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static int hema_irq_table[16] =
|
||||
{0,
|
||||
0,
|
||||
0,
|
||||
0x80, /* irq 3 */
|
||||
0,
|
||||
0x90, /* irq 5 */
|
||||
0,
|
||||
0xA0, /* irq 7 */
|
||||
0,
|
||||
0xB0, /* irq 9 */
|
||||
0xC0, /* irq 10 */
|
||||
0xD0, /* irq 11 */
|
||||
0xE0, /* irq 12 */
|
||||
0,
|
||||
0,
|
||||
0xF0, /* irq 15 */
|
||||
};
|
||||
|
||||
static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr)
|
||||
{
|
||||
unsigned char cregs[8];
|
||||
unsigned char reverse_cardnr;
|
||||
unsigned char dummy;
|
||||
int i;
|
||||
|
||||
reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
|
||||
| ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
|
||||
cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
|
||||
cregs[1] = 0x00; /* fast & slow link connected to CON1 */
|
||||
cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
|
||||
cregs[3] = 0;
|
||||
cregs[4] = 0x11; /* zero wait state */
|
||||
cregs[5] = hema_irq_table[irq & 0xf];
|
||||
cregs[6] = 0;
|
||||
cregs[7] = 0;
|
||||
|
||||
/*
|
||||
* no one else should use the ISA bus in this moment,
|
||||
* but no function there to prevent this :-(
|
||||
* save_flags(flags); cli();
|
||||
*/
|
||||
|
||||
/* board reset */
|
||||
t1outp(base, T1_RESETBOARD, 0xf);
|
||||
mdelay(100);
|
||||
dummy = t1inp(base, T1_FASTLINK + T1_OUTSTAT); /* first read */
|
||||
|
||||
/* write config */
|
||||
dummy = (base >> 4) & 0xff;
|
||||
for (i = 1; i <= 0xf; i++) t1outp(base, i, dummy);
|
||||
t1outp(base, HEMA_PAL_ID & 0xf, dummy);
|
||||
t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
|
||||
for (i = 1; i < 7; i++) t1outp(base, 0, cregs[i]);
|
||||
t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
|
||||
/* restore_flags(flags); */
|
||||
|
||||
mdelay(100);
|
||||
t1outp(base, T1_FASTLINK + T1_RESETLINK, 0);
|
||||
t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0);
|
||||
mdelay(10);
|
||||
t1outp(base, T1_FASTLINK + T1_RESETLINK, 1);
|
||||
t1outp(base, T1_SLOWLINK + T1_RESETLINK, 1);
|
||||
mdelay(100);
|
||||
t1outp(base, T1_FASTLINK + T1_RESETLINK, 0);
|
||||
t1outp(base, T1_SLOWLINK + T1_RESETLINK, 0);
|
||||
mdelay(10);
|
||||
t1outp(base, T1_FASTLINK + T1_ANALYSE, 0);
|
||||
mdelay(5);
|
||||
t1outp(base, T1_SLOWLINK + T1_ANALYSE, 0);
|
||||
|
||||
if (t1inp(base, T1_FASTLINK + T1_OUTSTAT) != 0x1) /* tx empty */
|
||||
return 1;
|
||||
if (t1inp(base, T1_FASTLINK + T1_INSTAT) != 0x0) /* rx empty */
|
||||
return 2;
|
||||
if (t1inp(base, T1_FASTLINK + T1_IRQENABLE) != 0x0)
|
||||
return 3;
|
||||
if ((t1inp(base, T1_FASTLINK + T1_FIFOSTAT) & 0xf0) != 0x70)
|
||||
return 4;
|
||||
if ((t1inp(base, T1_FASTLINK + T1_IRQMASTER) & 0x0e) != 0)
|
||||
return 5;
|
||||
if ((t1inp(base, T1_FASTLINK + T1_IDENT) & 0x7d) != 1)
|
||||
return 6;
|
||||
if (t1inp(base, T1_SLOWLINK + T1_OUTSTAT) != 0x1) /* tx empty */
|
||||
return 7;
|
||||
if ((t1inp(base, T1_SLOWLINK + T1_IRQMASTER) & 0x0e) != 0)
|
||||
return 8;
|
||||
if ((t1inp(base, T1_SLOWLINK + T1_IDENT) & 0x7d) != 0)
|
||||
return 9;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t t1isa_interrupt(int interrupt, void *devptr)
|
||||
{
|
||||
avmcard *card = devptr;
|
||||
avmctrl_info *cinfo = &card->ctrlinfo[0];
|
||||
struct capi_ctr *ctrl = &cinfo->capi_ctrl;
|
||||
unsigned char b1cmd;
|
||||
struct sk_buff *skb;
|
||||
|
||||
unsigned ApplId;
|
||||
unsigned MsgLen;
|
||||
unsigned DataB3Len;
|
||||
unsigned NCCI;
|
||||
unsigned WindowSize;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
|
||||
while (b1_rx_full(card->port)) {
|
||||
|
||||
b1cmd = b1_get_byte(card->port);
|
||||
|
||||
switch (b1cmd) {
|
||||
|
||||
case RECEIVE_DATA_B3_IND:
|
||||
|
||||
ApplId = (unsigned) b1_get_word(card->port);
|
||||
MsgLen = t1_get_slice(card->port, card->msgbuf);
|
||||
DataB3Len = t1_get_slice(card->port, card->databuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
if (MsgLen < 30) { /* not CAPI 64Bit */
|
||||
memset(card->msgbuf + MsgLen, 0, 30 - MsgLen);
|
||||
MsgLen = 30;
|
||||
CAPIMSG_SETLEN(card->msgbuf, 30);
|
||||
}
|
||||
if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "%s: incoming packet dropped\n",
|
||||
card->name);
|
||||
} else {
|
||||
skb_put_data(skb, card->msgbuf, MsgLen);
|
||||
skb_put_data(skb, card->databuf, DataB3Len);
|
||||
capi_ctr_handle_message(ctrl, ApplId, skb);
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_MESSAGE:
|
||||
|
||||
ApplId = (unsigned) b1_get_word(card->port);
|
||||
MsgLen = t1_get_slice(card->port, card->msgbuf);
|
||||
if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_ERR "%s: incoming packet dropped\n",
|
||||
card->name);
|
||||
} else {
|
||||
skb_put_data(skb, card->msgbuf, MsgLen);
|
||||
if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3)
|
||||
capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_handle_message(ctrl, ApplId, skb);
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_NEW_NCCI:
|
||||
|
||||
ApplId = b1_get_word(card->port);
|
||||
NCCI = b1_get_word(card->port);
|
||||
WindowSize = b1_get_word(card->port);
|
||||
capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
break;
|
||||
|
||||
case RECEIVE_FREE_NCCI:
|
||||
|
||||
ApplId = b1_get_word(card->port);
|
||||
NCCI = b1_get_word(card->port);
|
||||
if (NCCI != 0xffffffff)
|
||||
capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
break;
|
||||
|
||||
case RECEIVE_START:
|
||||
b1_put_byte(card->port, SEND_POLLACK);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_resume_output(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_STOP:
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_suspend_output(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_INIT:
|
||||
|
||||
cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
b1_parse_version(cinfo);
|
||||
printk(KERN_INFO "%s: %s-card (%s) now active\n",
|
||||
card->name,
|
||||
cinfo->version[VER_CARDTYPE],
|
||||
cinfo->version[VER_DRIVER]);
|
||||
capi_ctr_ready(ctrl);
|
||||
break;
|
||||
|
||||
case RECEIVE_TASK_READY:
|
||||
ApplId = (unsigned) b1_get_word(card->port);
|
||||
MsgLen = t1_get_slice(card->port, card->msgbuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
card->msgbuf[MsgLen] = 0;
|
||||
while (MsgLen > 0
|
||||
&& (card->msgbuf[MsgLen - 1] == '\n'
|
||||
|| card->msgbuf[MsgLen - 1] == '\r')) {
|
||||
card->msgbuf[MsgLen - 1] = 0;
|
||||
MsgLen--;
|
||||
}
|
||||
printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
|
||||
card->name, ApplId, card->msgbuf);
|
||||
break;
|
||||
|
||||
case RECEIVE_DEBUGMSG:
|
||||
MsgLen = t1_get_slice(card->port, card->msgbuf);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
card->msgbuf[MsgLen] = 0;
|
||||
while (MsgLen > 0
|
||||
&& (card->msgbuf[MsgLen - 1] == '\n'
|
||||
|| card->msgbuf[MsgLen - 1] == '\r')) {
|
||||
card->msgbuf[MsgLen - 1] = 0;
|
||||
MsgLen--;
|
||||
}
|
||||
printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
|
||||
break;
|
||||
|
||||
|
||||
case 0xff:
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_ERR "%s: card reseted ?\n", card->name);
|
||||
return IRQ_HANDLED;
|
||||
default:
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
|
||||
card->name, b1cmd);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
t1_disable_irq(port);
|
||||
b1_reset(port);
|
||||
|
||||
if ((retval = b1_load_t4file(card, &data->firmware))) {
|
||||
b1_reset(port);
|
||||
printk(KERN_ERR "%s: failed to load t4file!!\n",
|
||||
card->name);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (data->configuration.len > 0 && data->configuration.data) {
|
||||
if ((retval = b1_load_config(card, &data->configuration))) {
|
||||
b1_reset(port);
|
||||
printk(KERN_ERR "%s: failed to load config!!\n",
|
||||
card->name);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
if (!b1_loaded(card)) {
|
||||
printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
b1_setinterrupt(port, card->irq, card->cardtype);
|
||||
b1_put_byte(port, SEND_INIT);
|
||||
b1_put_word(port, CAPI_MAXAPPL);
|
||||
b1_put_word(port, AVM_NCCI_PER_CHANNEL * 30);
|
||||
b1_put_word(port, ctrl->cnr - 1);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void t1isa_reset_ctr(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
|
||||
t1_disable_irq(port);
|
||||
b1_reset(port);
|
||||
b1_reset(port);
|
||||
|
||||
memset(cinfo->version, 0, sizeof(cinfo->version));
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
capilib_release(&cinfo->ncci_head);
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
capi_ctr_down(ctrl);
|
||||
}
|
||||
|
||||
static void t1isa_remove(struct pci_dev *pdev)
|
||||
{
|
||||
avmctrl_info *cinfo = pci_get_drvdata(pdev);
|
||||
avmcard *card;
|
||||
|
||||
if (!cinfo)
|
||||
return;
|
||||
|
||||
card = cinfo->card;
|
||||
|
||||
t1_disable_irq(card->port);
|
||||
b1_reset(card->port);
|
||||
b1_reset(card->port);
|
||||
t1_reset(card->port);
|
||||
|
||||
detach_capi_ctr(&cinfo->capi_ctrl);
|
||||
free_irq(card->irq, card);
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
b1_free_card(card);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
|
||||
static char *t1isa_procinfo(struct capi_ctr *ctrl);
|
||||
|
||||
static int t1isa_probe(struct pci_dev *pdev, int cardnr)
|
||||
{
|
||||
avmctrl_info *cinfo;
|
||||
avmcard *card;
|
||||
int retval;
|
||||
|
||||
card = b1_alloc_card(1);
|
||||
if (!card) {
|
||||
printk(KERN_WARNING "t1isa: no memory.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
cinfo = card->ctrlinfo;
|
||||
card->port = pci_resource_start(pdev, 0);
|
||||
card->irq = pdev->irq;
|
||||
card->cardtype = avm_t1isa;
|
||||
card->cardnr = cardnr;
|
||||
sprintf(card->name, "t1isa-%x", card->port);
|
||||
|
||||
if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) {
|
||||
printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port);
|
||||
retval = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
if (hema_irq_table[card->irq & 0xf] == 0) {
|
||||
printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq);
|
||||
retval = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
|
||||
printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n",
|
||||
card->port, card->port + AVMB1_PORTLEN);
|
||||
retval = -EBUSY;
|
||||
goto err_free;
|
||||
}
|
||||
retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card);
|
||||
if (retval) {
|
||||
printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq);
|
||||
retval = -EBUSY;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) {
|
||||
printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n",
|
||||
card->port, retval);
|
||||
retval = -ENODEV;
|
||||
goto err_free_irq;
|
||||
}
|
||||
t1_disable_irq(card->port);
|
||||
b1_reset(card->port);
|
||||
|
||||
cinfo->capi_ctrl.owner = THIS_MODULE;
|
||||
cinfo->capi_ctrl.driver_name = "t1isa";
|
||||
cinfo->capi_ctrl.driverdata = cinfo;
|
||||
cinfo->capi_ctrl.register_appl = b1_register_appl;
|
||||
cinfo->capi_ctrl.release_appl = b1_release_appl;
|
||||
cinfo->capi_ctrl.send_message = t1isa_send_message;
|
||||
cinfo->capi_ctrl.load_firmware = t1isa_load_firmware;
|
||||
cinfo->capi_ctrl.reset_ctr = t1isa_reset_ctr;
|
||||
cinfo->capi_ctrl.procinfo = t1isa_procinfo;
|
||||
cinfo->capi_ctrl.proc_show = b1_proc_show;
|
||||
strcpy(cinfo->capi_ctrl.name, card->name);
|
||||
|
||||
retval = attach_capi_ctr(&cinfo->capi_ctrl);
|
||||
if (retval) {
|
||||
printk(KERN_INFO "t1isa: attach controller failed.\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
|
||||
card->port, card->irq, card->cardnr);
|
||||
|
||||
pci_set_drvdata(pdev, cinfo);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(card->irq, card);
|
||||
err_release_region:
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
err_free:
|
||||
b1_free_card(card);
|
||||
err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
avmcard *card = cinfo->card;
|
||||
unsigned int port = card->port;
|
||||
unsigned long flags;
|
||||
u16 len = CAPIMSG_LEN(skb->data);
|
||||
u8 cmd = CAPIMSG_COMMAND(skb->data);
|
||||
u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
|
||||
u16 dlen, retval;
|
||||
|
||||
spin_lock_irqsave(&card->lock, flags);
|
||||
if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
|
||||
retval = capilib_data_b3_req(&cinfo->ncci_head,
|
||||
CAPIMSG_APPID(skb->data),
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
if (retval != CAPI_NOERROR) {
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
dlen = CAPIMSG_DATALEN(skb->data);
|
||||
|
||||
b1_put_byte(port, SEND_DATA_B3_REQ);
|
||||
t1_put_slice(port, skb->data, len);
|
||||
t1_put_slice(port, skb->data + len, dlen);
|
||||
} else {
|
||||
b1_put_byte(port, SEND_MESSAGE);
|
||||
t1_put_slice(port, skb->data, len);
|
||||
}
|
||||
spin_unlock_irqrestore(&card->lock, flags);
|
||||
dev_kfree_skb_any(skb);
|
||||
return CAPI_NOERROR;
|
||||
}
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *t1isa_procinfo(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
|
||||
if (!cinfo)
|
||||
return "";
|
||||
sprintf(cinfo->infobuf, "%s %s 0x%x %d %d",
|
||||
cinfo->cardname[0] ? cinfo->cardname : "-",
|
||||
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
|
||||
cinfo->card ? cinfo->card->port : 0x0,
|
||||
cinfo->card ? cinfo->card->irq : 0,
|
||||
cinfo->card ? cinfo->card->cardnr : 0
|
||||
);
|
||||
return cinfo->infobuf;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
#define MAX_CARDS 4
|
||||
static struct pci_dev isa_dev[MAX_CARDS];
|
||||
static int io[MAX_CARDS];
|
||||
static int irq[MAX_CARDS];
|
||||
static int cardnr[MAX_CARDS];
|
||||
|
||||
module_param_hw_array(io, int, ioport, NULL, 0);
|
||||
module_param_hw_array(irq, int, irq, NULL, 0);
|
||||
module_param_array(cardnr, int, NULL, 0);
|
||||
MODULE_PARM_DESC(io, "I/O base address(es)");
|
||||
MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
|
||||
MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)");
|
||||
|
||||
static int t1isa_add_card(struct capi_driver *driver, capicardparams *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CARDS; i++) {
|
||||
if (isa_dev[i].resource[0].start)
|
||||
continue;
|
||||
|
||||
isa_dev[i].resource[0].start = data->port;
|
||||
isa_dev[i].irq = data->irq;
|
||||
|
||||
if (t1isa_probe(&isa_dev[i], data->cardnr) == 0)
|
||||
return 0;
|
||||
}
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static struct capi_driver capi_driver_t1isa = {
|
||||
.name = "t1isa",
|
||||
.revision = "1.0",
|
||||
.add_card = t1isa_add_card,
|
||||
};
|
||||
|
||||
static int __init t1isa_init(void)
|
||||
{
|
||||
char rev[32];
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if ((p = strchr(revision, ':')) != NULL && p[1]) {
|
||||
strlcpy(rev, p + 2, 32);
|
||||
if ((p = strchr(rev, '$')) != NULL && p > rev)
|
||||
*(p - 1) = 0;
|
||||
} else
|
||||
strcpy(rev, "1.0");
|
||||
|
||||
for (i = 0; i < MAX_CARDS; i++) {
|
||||
if (!io[i])
|
||||
break;
|
||||
|
||||
isa_dev[i].resource[0].start = io[i];
|
||||
isa_dev[i].irq = irq[i];
|
||||
|
||||
if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strlcpy(capi_driver_t1isa.revision, rev, 32);
|
||||
register_capi_driver(&capi_driver_t1isa);
|
||||
printk(KERN_INFO "t1isa: revision %s\n", rev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit t1isa_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
unregister_capi_driver(&capi_driver_t1isa);
|
||||
for (i = 0; i < MAX_CARDS; i++) {
|
||||
if (!io[i])
|
||||
break;
|
||||
|
||||
t1isa_remove(&isa_dev[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(t1isa_init);
|
||||
module_exit(t1isa_exit);
|
@ -1,259 +0,0 @@
|
||||
/* $Id: t1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
|
||||
*
|
||||
* Module for AVM T1 PCI-card.
|
||||
*
|
||||
* Copyright 1999 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/capi.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
#include "avmcard.h"
|
||||
|
||||
#undef CONFIG_T1PCI_DEBUG
|
||||
#undef CONFIG_T1PCI_POLLDEBUG
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
static char *revision = "$Revision: 1.1.2.2 $";
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static struct pci_device_id t1pci_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, PCI_ANY_ID, PCI_ANY_ID },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, t1pci_pci_tbl);
|
||||
MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 PCI card");
|
||||
MODULE_AUTHOR("Carsten Paeth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *t1pci_procinfo(struct capi_ctr *ctrl);
|
||||
|
||||
static int t1pci_add_card(struct capicardparams *p, struct pci_dev *pdev)
|
||||
{
|
||||
avmcard *card;
|
||||
avmctrl_info *cinfo;
|
||||
int retval;
|
||||
|
||||
card = b1_alloc_card(1);
|
||||
if (!card) {
|
||||
printk(KERN_WARNING "t1pci: no memory.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
card->dma = avmcard_dma_alloc("t1pci", pdev, 2048 + 128, 2048 + 128);
|
||||
if (!card->dma) {
|
||||
printk(KERN_WARNING "t1pci: no memory.\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
cinfo = card->ctrlinfo;
|
||||
sprintf(card->name, "t1pci-%x", p->port);
|
||||
card->port = p->port;
|
||||
card->irq = p->irq;
|
||||
card->membase = p->membase;
|
||||
card->cardtype = avm_t1pci;
|
||||
|
||||
if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
|
||||
printk(KERN_WARNING "t1pci: ports 0x%03x-0x%03x in use.\n",
|
||||
card->port, card->port + AVMB1_PORTLEN);
|
||||
retval = -EBUSY;
|
||||
goto err_free_dma;
|
||||
}
|
||||
|
||||
card->mbase = ioremap(card->membase, 64);
|
||||
if (!card->mbase) {
|
||||
printk(KERN_NOTICE "t1pci: can't remap memory at 0x%lx\n",
|
||||
card->membase);
|
||||
retval = -EIO;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
b1dma_reset(card);
|
||||
|
||||
retval = t1pci_detect(card);
|
||||
if (retval != 0) {
|
||||
if (retval < 6)
|
||||
printk(KERN_NOTICE "t1pci: NO card at 0x%x (%d)\n",
|
||||
card->port, retval);
|
||||
else
|
||||
printk(KERN_NOTICE "t1pci: card at 0x%x, but cable not connected or T1 has no power (%d)\n",
|
||||
card->port, retval);
|
||||
retval = -EIO;
|
||||
goto err_unmap;
|
||||
}
|
||||
b1dma_reset(card);
|
||||
|
||||
retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "t1pci: unable to get IRQ %d.\n", card->irq);
|
||||
retval = -EBUSY;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
cinfo->capi_ctrl.owner = THIS_MODULE;
|
||||
cinfo->capi_ctrl.driver_name = "t1pci";
|
||||
cinfo->capi_ctrl.driverdata = cinfo;
|
||||
cinfo->capi_ctrl.register_appl = b1dma_register_appl;
|
||||
cinfo->capi_ctrl.release_appl = b1dma_release_appl;
|
||||
cinfo->capi_ctrl.send_message = b1dma_send_message;
|
||||
cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
|
||||
cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr;
|
||||
cinfo->capi_ctrl.procinfo = t1pci_procinfo;
|
||||
cinfo->capi_ctrl.proc_show = b1dma_proc_show;
|
||||
strcpy(cinfo->capi_ctrl.name, card->name);
|
||||
|
||||
retval = attach_capi_ctr(&cinfo->capi_ctrl);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "t1pci: attach controller failed.\n");
|
||||
retval = -EBUSY;
|
||||
goto err_free_irq;
|
||||
}
|
||||
card->cardnr = cinfo->capi_ctrl.cnr;
|
||||
|
||||
printk(KERN_INFO "t1pci: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n",
|
||||
card->port, card->irq, card->membase);
|
||||
|
||||
pci_set_drvdata(pdev, card);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(card->irq, card);
|
||||
err_unmap:
|
||||
iounmap(card->mbase);
|
||||
err_release_region:
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
err_free_dma:
|
||||
avmcard_dma_free(card->dma);
|
||||
err_free:
|
||||
b1_free_card(card);
|
||||
err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static void t1pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
avmcard *card = pci_get_drvdata(pdev);
|
||||
avmctrl_info *cinfo = card->ctrlinfo;
|
||||
|
||||
b1dma_reset(card);
|
||||
|
||||
detach_capi_ctr(&cinfo->capi_ctrl);
|
||||
free_irq(card->irq, card);
|
||||
iounmap(card->mbase);
|
||||
release_region(card->port, AVMB1_PORTLEN);
|
||||
avmcard_dma_free(card->dma);
|
||||
b1_free_card(card);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static char *t1pci_procinfo(struct capi_ctr *ctrl)
|
||||
{
|
||||
avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
|
||||
|
||||
if (!cinfo)
|
||||
return "";
|
||||
sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
|
||||
cinfo->cardname[0] ? cinfo->cardname : "-",
|
||||
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
|
||||
cinfo->card ? cinfo->card->port : 0x0,
|
||||
cinfo->card ? cinfo->card->irq : 0,
|
||||
cinfo->card ? cinfo->card->membase : 0
|
||||
);
|
||||
return cinfo->infobuf;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
static int t1pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
|
||||
{
|
||||
struct capicardparams param;
|
||||
int retval;
|
||||
|
||||
if (pci_enable_device(dev) < 0) {
|
||||
printk(KERN_ERR "t1pci: failed to enable AVM-T1-PCI\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
pci_set_master(dev);
|
||||
|
||||
param.port = pci_resource_start(dev, 1);
|
||||
param.irq = dev->irq;
|
||||
param.membase = pci_resource_start(dev, 0);
|
||||
|
||||
printk(KERN_INFO "t1pci: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n",
|
||||
param.port, param.irq, param.membase);
|
||||
|
||||
retval = t1pci_add_card(¶m, dev);
|
||||
if (retval != 0) {
|
||||
printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n",
|
||||
param.port, param.irq, param.membase);
|
||||
pci_disable_device(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_driver t1pci_pci_driver = {
|
||||
.name = "t1pci",
|
||||
.id_table = t1pci_pci_tbl,
|
||||
.probe = t1pci_probe,
|
||||
.remove = t1pci_remove,
|
||||
};
|
||||
|
||||
static struct capi_driver capi_driver_t1pci = {
|
||||
.name = "t1pci",
|
||||
.revision = "1.0",
|
||||
};
|
||||
|
||||
static int __init t1pci_init(void)
|
||||
{
|
||||
char *p;
|
||||
char rev[32];
|
||||
int err;
|
||||
|
||||
if ((p = strchr(revision, ':')) != NULL && p[1]) {
|
||||
strlcpy(rev, p + 2, 32);
|
||||
if ((p = strchr(rev, '$')) != NULL && p > rev)
|
||||
*(p - 1) = 0;
|
||||
} else
|
||||
strcpy(rev, "1.0");
|
||||
|
||||
err = pci_register_driver(&t1pci_pci_driver);
|
||||
if (!err) {
|
||||
strlcpy(capi_driver_t1pci.revision, rev, 32);
|
||||
register_capi_driver(&capi_driver_t1pci);
|
||||
printk(KERN_INFO "t1pci: revision %s\n", rev);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit t1pci_exit(void)
|
||||
{
|
||||
unregister_capi_driver(&capi_driver_t1pci);
|
||||
pci_unregister_driver(&t1pci_pci_driver);
|
||||
}
|
||||
|
||||
module_init(t1pci_init);
|
||||
module_exit(t1pci_exit);
|
@ -1,62 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menuconfig ISDN_DRV_GIGASET
|
||||
tristate "Siemens Gigaset support"
|
||||
depends on TTY
|
||||
select CRC_CCITT
|
||||
select BITREVERSE
|
||||
help
|
||||
This driver supports the Siemens Gigaset SX205/255 family of
|
||||
ISDN DECT bases, including the predecessors Gigaset 3070/3075
|
||||
and 4170/4175 and their T-Com versions Sinus 45isdn and Sinus
|
||||
721X.
|
||||
If you have one of these devices, say M here and for at least
|
||||
one of the connection specific parts that follow.
|
||||
This will build a module called "gigaset".
|
||||
Note: If you build your ISDN subsystem (ISDN_CAPI or ISDN_I4L)
|
||||
as a module, you have to build this driver as a module too,
|
||||
otherwise the Gigaset device won't show up as an ISDN device.
|
||||
|
||||
if ISDN_DRV_GIGASET
|
||||
|
||||
config GIGASET_CAPI
|
||||
bool "Gigaset CAPI support"
|
||||
depends on ISDN_CAPI='y'||(ISDN_CAPI='m'&&ISDN_DRV_GIGASET='m')
|
||||
default 'y'
|
||||
help
|
||||
Build the Gigaset driver as a CAPI 2.0 driver interfacing with
|
||||
the Kernel CAPI subsystem. To use it with the old ISDN4Linux
|
||||
subsystem you'll have to enable the capidrv glue driver.
|
||||
(select ISDN_CAPI_CAPIDRV.)
|
||||
Say N to build the old native ISDN4Linux variant.
|
||||
If unsure, say Y.
|
||||
|
||||
config GIGASET_BASE
|
||||
tristate "Gigaset base station support"
|
||||
depends on USB
|
||||
help
|
||||
Say M here if you want to use the USB interface of the Gigaset
|
||||
base for connection to your system.
|
||||
This will build a module called "bas_gigaset".
|
||||
|
||||
config GIGASET_M105
|
||||
tristate "Gigaset M105 support"
|
||||
depends on USB
|
||||
help
|
||||
Say M here if you want to connect to the Gigaset base via DECT
|
||||
using a Gigaset M105 (Sinus 45 Data 2) USB DECT device.
|
||||
This will build a module called "usb_gigaset".
|
||||
|
||||
config GIGASET_M101
|
||||
tristate "Gigaset M101 support"
|
||||
help
|
||||
Say M here if you want to connect to the Gigaset base via DECT
|
||||
using a Gigaset M101 (Sinus 45 Data 1) RS232 DECT device.
|
||||
This will build a module called "ser_gigaset".
|
||||
|
||||
config GIGASET_DEBUG
|
||||
bool "Gigaset debugging"
|
||||
help
|
||||
This enables debugging code in the Gigaset drivers.
|
||||
If in doubt, say yes.
|
||||
|
||||
endif # ISDN_DRV_GIGASET
|
@ -1,17 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
|
||||
|
||||
ifdef CONFIG_GIGASET_CAPI
|
||||
gigaset-y += capi.o
|
||||
else
|
||||
gigaset-y += dummyll.o
|
||||
endif
|
||||
|
||||
usb_gigaset-y := usb-gigaset.o
|
||||
ser_gigaset-y := ser-gigaset.o
|
||||
bas_gigaset-y := bas-gigaset.o isocdata.o
|
||||
|
||||
obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset.o
|
||||
obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o
|
||||
obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o
|
||||
obj-$(CONFIG_GIGASET_M101) += ser_gigaset.o
|
@ -1,606 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Common data handling layer for ser_gigaset and usb_gigaset
|
||||
*
|
||||
* Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
|
||||
* Hansjoerg Lipp <hjlipp@web.de>,
|
||||
* Stefan Eilers.
|
||||
*
|
||||
* =====================================================================
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/crc-ccitt.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
/* check if byte must be stuffed/escaped
|
||||
* I'm not sure which data should be encoded.
|
||||
* Therefore I will go the hard way and encode every value
|
||||
* less than 0x20, the flag sequence and the control escape char.
|
||||
*/
|
||||
static inline int muststuff(unsigned char c)
|
||||
{
|
||||
if (c < PPP_TRANS) return 1;
|
||||
if (c == PPP_FLAG) return 1;
|
||||
if (c == PPP_ESCAPE) return 1;
|
||||
/* other possible candidates: */
|
||||
/* 0x91: XON with parity set */
|
||||
/* 0x93: XOFF with parity set */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* == data input =========================================================== */
|
||||
|
||||
/* process a block of received bytes in command mode
|
||||
* (mstate != MS_LOCKED && (inputstate & INS_command))
|
||||
* Append received bytes to the command response buffer and forward them
|
||||
* line by line to the response handler. Exit whenever a mode/state change
|
||||
* might have occurred.
|
||||
* Note: Received lines may be terminated by CR, LF, or CR LF, which will be
|
||||
* removed before passing the line to the response handler.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
unsigned cbytes = cs->cbytes;
|
||||
unsigned procbytes = 0;
|
||||
unsigned char c;
|
||||
|
||||
while (procbytes < numbytes) {
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
if (cbytes == 0 && cs->respdata[0] == '\r') {
|
||||
/* collapse LF with preceding CR */
|
||||
cs->respdata[0] = 0;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case '\r':
|
||||
/* end of message line, pass to response handler */
|
||||
if (cbytes >= MAX_RESP_SIZE) {
|
||||
dev_warn(cs->dev, "response too large (%d)\n",
|
||||
cbytes);
|
||||
cbytes = MAX_RESP_SIZE;
|
||||
}
|
||||
cs->cbytes = cbytes;
|
||||
gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
|
||||
cbytes, cs->respdata);
|
||||
gigaset_handle_modem_response(cs);
|
||||
cbytes = 0;
|
||||
|
||||
/* store EOL byte for CRLF collapsing */
|
||||
cs->respdata[0] = c;
|
||||
|
||||
/* cs->dle may have changed */
|
||||
if (cs->dle && !(inbuf->inputstate & INS_DLE_command))
|
||||
inbuf->inputstate &= ~INS_command;
|
||||
|
||||
/* return for reevaluating state */
|
||||
goto exit;
|
||||
|
||||
case DLE_FLAG:
|
||||
if (inbuf->inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inbuf->inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle ||
|
||||
(inbuf->inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inbuf->inputstate |= INS_DLE_char;
|
||||
goto exit;
|
||||
}
|
||||
/* quoted or not in DLE mode: treat as regular data */
|
||||
/* fall through */
|
||||
default:
|
||||
/* append to line buffer if possible */
|
||||
if (cbytes < MAX_RESP_SIZE)
|
||||
cs->respdata[cbytes] = c;
|
||||
cbytes++;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
cs->cbytes = cbytes;
|
||||
return procbytes;
|
||||
}
|
||||
|
||||
/* process a block of received bytes in lock mode
|
||||
* All received bytes are passed unmodified to the tty i/f.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
|
||||
gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src);
|
||||
gigaset_if_receive(inbuf->cs, src, numbytes);
|
||||
return numbytes;
|
||||
}
|
||||
|
||||
/* process a block of received bytes in HDLC data mode
|
||||
* (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
|
||||
* Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
|
||||
* When a frame is complete, check the FCS and pass valid frames to the LL.
|
||||
* If DLE is encountered, return immediately to let the caller handle it.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
struct bc_state *bcs = cs->bcs;
|
||||
int inputstate = bcs->inputstate;
|
||||
__u16 fcs = bcs->rx_fcs;
|
||||
struct sk_buff *skb = bcs->rx_skb;
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
unsigned procbytes = 0;
|
||||
unsigned char c;
|
||||
|
||||
if (inputstate & INS_byte_stuff) {
|
||||
if (!numbytes)
|
||||
return 0;
|
||||
inputstate &= ~INS_byte_stuff;
|
||||
goto byte_stuff;
|
||||
}
|
||||
|
||||
while (procbytes < numbytes) {
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
if (c == DLE_FLAG) {
|
||||
if (inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle || (inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inputstate |= INS_DLE_char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == PPP_ESCAPE) {
|
||||
/* byte stuffing indicator: pull in next byte */
|
||||
if (procbytes >= numbytes) {
|
||||
/* end of buffer, save for later processing */
|
||||
inputstate |= INS_byte_stuff;
|
||||
break;
|
||||
}
|
||||
byte_stuff:
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
if (c == DLE_FLAG) {
|
||||
if (inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle ||
|
||||
(inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inputstate |=
|
||||
INS_DLE_char | INS_byte_stuff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c ^= PPP_TRANS;
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
if (!muststuff(c))
|
||||
gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
|
||||
#endif
|
||||
} else if (c == PPP_FLAG) {
|
||||
/* end of frame: process content if any */
|
||||
if (inputstate & INS_have_data) {
|
||||
gig_dbg(DEBUG_HDLC,
|
||||
"7e----------------------------");
|
||||
|
||||
/* check and pass received frame */
|
||||
if (!skb) {
|
||||
/* skipped frame */
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
} else if (skb->len < 2) {
|
||||
/* frame too short for FCS */
|
||||
dev_warn(cs->dev,
|
||||
"short frame (%d)\n",
|
||||
skb->len);
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
dev_kfree_skb_any(skb);
|
||||
} else if (fcs != PPP_GOODFCS) {
|
||||
/* frame check error */
|
||||
dev_err(cs->dev,
|
||||
"Checksum failed, %u bytes corrupted!\n",
|
||||
skb->len);
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
dev_kfree_skb_any(skb);
|
||||
} else {
|
||||
/* good frame */
|
||||
__skb_trim(skb, skb->len - 2);
|
||||
gigaset_skb_rcvd(bcs, skb);
|
||||
}
|
||||
|
||||
/* prepare reception of next frame */
|
||||
inputstate &= ~INS_have_data;
|
||||
skb = gigaset_new_rx_skb(bcs);
|
||||
} else {
|
||||
/* empty frame (7E 7E) */
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
++bcs->emptycount;
|
||||
#endif
|
||||
if (!skb) {
|
||||
/* skipped (?) */
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
skb = gigaset_new_rx_skb(bcs);
|
||||
}
|
||||
}
|
||||
|
||||
fcs = PPP_INITFCS;
|
||||
continue;
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
} else if (muststuff(c)) {
|
||||
/* Should not happen. Possible after ZDLE=1<CR><LF>. */
|
||||
gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* regular data byte, append to skb */
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
if (!(inputstate & INS_have_data)) {
|
||||
gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
|
||||
bcs->emptycount);
|
||||
bcs->emptycount = 0;
|
||||
}
|
||||
#endif
|
||||
inputstate |= INS_have_data;
|
||||
if (skb) {
|
||||
if (skb->len >= bcs->rx_bufsize) {
|
||||
dev_warn(cs->dev, "received packet too long\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
/* skip remainder of packet */
|
||||
bcs->rx_skb = skb = NULL;
|
||||
} else {
|
||||
__skb_put_u8(skb, c);
|
||||
fcs = crc_ccitt_byte(fcs, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bcs->inputstate = inputstate;
|
||||
bcs->rx_fcs = fcs;
|
||||
return procbytes;
|
||||
}
|
||||
|
||||
/* process a block of received bytes in transparent data mode
|
||||
* (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC)
|
||||
* Invert bytes, undoing byte stuffing and watching for DLE escapes.
|
||||
* If DLE is encountered, return immediately to let the caller handle it.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
struct bc_state *bcs = cs->bcs;
|
||||
int inputstate = bcs->inputstate;
|
||||
struct sk_buff *skb = bcs->rx_skb;
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
unsigned procbytes = 0;
|
||||
unsigned char c;
|
||||
|
||||
if (!skb) {
|
||||
/* skip this block */
|
||||
gigaset_new_rx_skb(bcs);
|
||||
return numbytes;
|
||||
}
|
||||
|
||||
while (procbytes < numbytes && skb->len < bcs->rx_bufsize) {
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
|
||||
if (c == DLE_FLAG) {
|
||||
if (inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle || (inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inputstate |= INS_DLE_char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* regular data byte: append to current skb */
|
||||
inputstate |= INS_have_data;
|
||||
__skb_put_u8(skb, bitrev8(c));
|
||||
}
|
||||
|
||||
/* pass data up */
|
||||
if (inputstate & INS_have_data) {
|
||||
gigaset_skb_rcvd(bcs, skb);
|
||||
inputstate &= ~INS_have_data;
|
||||
gigaset_new_rx_skb(bcs);
|
||||
}
|
||||
|
||||
bcs->inputstate = inputstate;
|
||||
return procbytes;
|
||||
}
|
||||
|
||||
/* process DLE escapes
|
||||
* Called whenever a DLE sequence might be encountered in the input stream.
|
||||
* Either processes the entire DLE sequence or, if that isn't possible,
|
||||
* notes the fact that an initial DLE has been received in the INS_DLE_char
|
||||
* inputstate flag and resumes processing of the sequence on the next call.
|
||||
*/
|
||||
static void handle_dle(struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
|
||||
if (cs->mstate == MS_LOCKED)
|
||||
return; /* no DLE processing in lock mode */
|
||||
|
||||
if (!(inbuf->inputstate & INS_DLE_char)) {
|
||||
/* no DLE pending */
|
||||
if (inbuf->data[inbuf->head] == DLE_FLAG &&
|
||||
(cs->dle || inbuf->inputstate & INS_DLE_command)) {
|
||||
/* start of DLE sequence */
|
||||
inbuf->head++;
|
||||
if (inbuf->head == inbuf->tail ||
|
||||
inbuf->head == RBUFSIZE) {
|
||||
/* end of buffer, save for later processing */
|
||||
inbuf->inputstate |= INS_DLE_char;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* regular data byte */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* consume pending DLE */
|
||||
inbuf->inputstate &= ~INS_DLE_char;
|
||||
|
||||
switch (inbuf->data[inbuf->head]) {
|
||||
case 'X': /* begin of event message */
|
||||
if (inbuf->inputstate & INS_command)
|
||||
dev_notice(cs->dev,
|
||||
"received <DLE>X in command mode\n");
|
||||
inbuf->inputstate |= INS_command | INS_DLE_command;
|
||||
inbuf->head++; /* byte consumed */
|
||||
break;
|
||||
case '.': /* end of event message */
|
||||
if (!(inbuf->inputstate & INS_DLE_command))
|
||||
dev_notice(cs->dev,
|
||||
"received <DLE>. without <DLE>X\n");
|
||||
inbuf->inputstate &= ~INS_DLE_command;
|
||||
/* return to data mode if in DLE mode */
|
||||
if (cs->dle)
|
||||
inbuf->inputstate &= ~INS_command;
|
||||
inbuf->head++; /* byte consumed */
|
||||
break;
|
||||
case DLE_FLAG: /* DLE in data stream */
|
||||
/* mark as quoted */
|
||||
inbuf->inputstate |= INS_DLE_char;
|
||||
if (!(cs->dle || inbuf->inputstate & INS_DLE_command))
|
||||
dev_notice(cs->dev,
|
||||
"received <DLE><DLE> not in DLE mode\n");
|
||||
break; /* quoted byte left in buffer */
|
||||
default:
|
||||
dev_notice(cs->dev, "received <DLE><%02x>\n",
|
||||
inbuf->data[inbuf->head]);
|
||||
/* quoted byte left in buffer */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_m10x_input() - process a block of data received from the device
|
||||
* @inbuf: received data and device descriptor structure.
|
||||
*
|
||||
* Called by hardware module {ser,usb}_gigaset with a block of received
|
||||
* bytes. Separates the bytes received over the serial data channel into
|
||||
* user data and command replies (locked/unlocked) according to the
|
||||
* current state of the interface.
|
||||
*/
|
||||
void gigaset_m10x_input(struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
unsigned numbytes, procbytes;
|
||||
|
||||
gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail);
|
||||
|
||||
while (inbuf->head != inbuf->tail) {
|
||||
/* check for DLE escape */
|
||||
handle_dle(inbuf);
|
||||
|
||||
/* process a contiguous block of bytes */
|
||||
numbytes = (inbuf->head > inbuf->tail ?
|
||||
RBUFSIZE : inbuf->tail) - inbuf->head;
|
||||
gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
|
||||
/*
|
||||
* numbytes may be 0 if handle_dle() ate the last byte.
|
||||
* This does no harm, *_loop() will just return 0 immediately.
|
||||
*/
|
||||
|
||||
if (cs->mstate == MS_LOCKED)
|
||||
procbytes = lock_loop(numbytes, inbuf);
|
||||
else if (inbuf->inputstate & INS_command)
|
||||
procbytes = cmd_loop(numbytes, inbuf);
|
||||
else if (cs->bcs->proto2 == L2_HDLC)
|
||||
procbytes = hdlc_loop(numbytes, inbuf);
|
||||
else
|
||||
procbytes = iraw_loop(numbytes, inbuf);
|
||||
inbuf->head += procbytes;
|
||||
|
||||
/* check for buffer wraparound */
|
||||
if (inbuf->head >= RBUFSIZE)
|
||||
inbuf->head = 0;
|
||||
|
||||
gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_m10x_input);
|
||||
|
||||
|
||||
/* == data output ========================================================== */
|
||||
|
||||
/*
|
||||
* Encode a data packet into an octet stuffed HDLC frame with FCS,
|
||||
* opening and closing flags, preserving headroom data.
|
||||
* parameters:
|
||||
* skb skb containing original packet (freed upon return)
|
||||
* Return value:
|
||||
* pointer to newly allocated skb containing the result frame
|
||||
* and the original link layer header, NULL on error
|
||||
*/
|
||||
static struct sk_buff *HDLC_Encode(struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *hdlc_skb;
|
||||
__u16 fcs;
|
||||
unsigned char c;
|
||||
unsigned char *cp;
|
||||
int len;
|
||||
unsigned int stuf_cnt;
|
||||
|
||||
stuf_cnt = 0;
|
||||
fcs = PPP_INITFCS;
|
||||
cp = skb->data;
|
||||
len = skb->len;
|
||||
while (len--) {
|
||||
if (muststuff(*cp))
|
||||
stuf_cnt++;
|
||||
fcs = crc_ccitt_byte(fcs, *cp++);
|
||||
}
|
||||
fcs ^= 0xffff; /* complement */
|
||||
|
||||
/* size of new buffer: original size + number of stuffing bytes
|
||||
* + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
|
||||
* + room for link layer header
|
||||
*/
|
||||
hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len);
|
||||
if (!hdlc_skb) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Copy link layer header into new skb */
|
||||
skb_reset_mac_header(hdlc_skb);
|
||||
skb_reserve(hdlc_skb, skb->mac_len);
|
||||
memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len);
|
||||
hdlc_skb->mac_len = skb->mac_len;
|
||||
|
||||
/* Add flag sequence in front of everything.. */
|
||||
skb_put_u8(hdlc_skb, PPP_FLAG);
|
||||
|
||||
/* Perform byte stuffing while copying data. */
|
||||
while (skb->len--) {
|
||||
if (muststuff(*skb->data)) {
|
||||
skb_put_u8(hdlc_skb, PPP_ESCAPE);
|
||||
skb_put_u8(hdlc_skb, (*skb->data++) ^ PPP_TRANS);
|
||||
} else
|
||||
skb_put_u8(hdlc_skb, *skb->data++);
|
||||
}
|
||||
|
||||
/* Finally add FCS (byte stuffed) and flag sequence */
|
||||
c = (fcs & 0x00ff); /* least significant byte first */
|
||||
if (muststuff(c)) {
|
||||
skb_put_u8(hdlc_skb, PPP_ESCAPE);
|
||||
c ^= PPP_TRANS;
|
||||
}
|
||||
skb_put_u8(hdlc_skb, c);
|
||||
|
||||
c = ((fcs >> 8) & 0x00ff);
|
||||
if (muststuff(c)) {
|
||||
skb_put_u8(hdlc_skb, PPP_ESCAPE);
|
||||
c ^= PPP_TRANS;
|
||||
}
|
||||
skb_put_u8(hdlc_skb, c);
|
||||
|
||||
skb_put_u8(hdlc_skb, PPP_FLAG);
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
return hdlc_skb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a data packet into an octet stuffed raw bit inverted frame,
|
||||
* preserving headroom data.
|
||||
* parameters:
|
||||
* skb skb containing original packet (freed upon return)
|
||||
* Return value:
|
||||
* pointer to newly allocated skb containing the result frame
|
||||
* and the original link layer header, NULL on error
|
||||
*/
|
||||
static struct sk_buff *iraw_encode(struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *iraw_skb;
|
||||
unsigned char c;
|
||||
unsigned char *cp;
|
||||
int len;
|
||||
|
||||
/* size of new buffer (worst case = every byte must be stuffed):
|
||||
* 2 * original size + room for link layer header
|
||||
*/
|
||||
iraw_skb = dev_alloc_skb(2 * skb->len + skb->mac_len);
|
||||
if (!iraw_skb) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy link layer header into new skb */
|
||||
skb_reset_mac_header(iraw_skb);
|
||||
skb_reserve(iraw_skb, skb->mac_len);
|
||||
memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len);
|
||||
iraw_skb->mac_len = skb->mac_len;
|
||||
|
||||
/* copy and stuff data */
|
||||
cp = skb->data;
|
||||
len = skb->len;
|
||||
while (len--) {
|
||||
c = bitrev8(*cp++);
|
||||
if (c == DLE_FLAG)
|
||||
skb_put_u8(iraw_skb, c);
|
||||
skb_put_u8(iraw_skb, c);
|
||||
}
|
||||
dev_kfree_skb_any(skb);
|
||||
return iraw_skb;
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_m10x_send_skb() - queue an skb for sending
|
||||
* @bcs: B channel descriptor structure.
|
||||
* @skb: data to send.
|
||||
*
|
||||
* Called by LL to encode and queue an skb for sending, and start
|
||||
* transmission if necessary.
|
||||
* Once the payload data has been transmitted completely, gigaset_skb_sent()
|
||||
* will be called with the skb's link layer header preserved.
|
||||
*
|
||||
* Return value:
|
||||
* number of bytes accepted for sending (skb->len) if ok,
|
||||
* error code < 0 (eg. -ENOMEM) on error
|
||||
*/
|
||||
int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
struct cardstate *cs = bcs->cs;
|
||||
unsigned len = skb->len;
|
||||
unsigned long flags;
|
||||
|
||||
if (bcs->proto2 == L2_HDLC)
|
||||
skb = HDLC_Encode(skb);
|
||||
else
|
||||
skb = iraw_encode(skb);
|
||||
if (!skb) {
|
||||
dev_err(cs->dev,
|
||||
"unable to allocate memory for encoding!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_queue_tail(&bcs->squeue, skb);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->connected)
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
|
||||
return len; /* ok so far */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Dummy LL interface for the Gigaset driver
|
||||
*
|
||||
* Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
|
||||
*
|
||||
* =====================================================================
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include "gigaset.h"
|
||||
|
||||
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_sent);
|
||||
|
||||
void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
|
||||
|
||||
void gigaset_isdn_rcv_err(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
|
||||
|
||||
int gigaset_isdn_icall(struct at_state_t *at_state)
|
||||
{
|
||||
return ICALL_IGNORE;
|
||||
}
|
||||
|
||||
void gigaset_isdn_connD(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_hupD(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_connB(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_hupB(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_start(struct cardstate *cs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_stop(struct cardstate *cs)
|
||||
{
|
||||
}
|
||||
|
||||
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gigaset_isdn_unregdev(struct cardstate *cs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_regdrv(void)
|
||||
{
|
||||
pr_info("no ISDN subsystem interface\n");
|
||||
}
|
||||
|
||||
void gigaset_isdn_unregdrv(void)
|
||||
{
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,827 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Siemens Gigaset 307x driver
|
||||
* Common header file for all connection variants
|
||||
*
|
||||
* Written by Stefan Eilers
|
||||
* and Hansjoerg Lipp <hjlipp@web.de>
|
||||
*
|
||||
* =====================================================================
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#ifndef GIGASET_H
|
||||
#define GIGASET_H
|
||||
|
||||
/* define global prefix for pr_ macros in linux/kernel.h */
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#define GIG_VERSION {0, 5, 0, 0}
|
||||
#define GIG_COMPAT {0, 4, 0, 0}
|
||||
|
||||
#define MAX_REC_PARAMS 10 /* Max. number of params in response string */
|
||||
#define MAX_RESP_SIZE 511 /* Max. size of a response string */
|
||||
|
||||
#define MAX_EVENTS 64 /* size of event queue */
|
||||
|
||||
#define RBUFSIZE 8192
|
||||
|
||||
#define GIG_TICK 100 /* in milliseconds */
|
||||
|
||||
/* timeout values (unit: 1 sec) */
|
||||
#define INIT_TIMEOUT 1
|
||||
|
||||
/* timeout values (unit: 0.1 sec) */
|
||||
#define RING_TIMEOUT 3 /* for additional parameters to RING */
|
||||
#define BAS_TIMEOUT 20 /* for response to Base USB ops */
|
||||
#define ATRDY_TIMEOUT 3 /* for HD_READY_SEND_ATDATA */
|
||||
|
||||
#define BAS_RETRY 3 /* max. retries for base USB ops */
|
||||
|
||||
#define MAXACT 3
|
||||
|
||||
extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */
|
||||
|
||||
/* debug flags, combine by adding/bitwise OR */
|
||||
enum debuglevel {
|
||||
DEBUG_INTR = 0x00008, /* interrupt processing */
|
||||
DEBUG_CMD = 0x00020, /* sent/received LL commands */
|
||||
DEBUG_STREAM = 0x00040, /* application data stream I/O events */
|
||||
DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */
|
||||
DEBUG_LLDATA = 0x00100, /* sent/received LL data */
|
||||
DEBUG_EVENT = 0x00200, /* event processing */
|
||||
DEBUG_HDLC = 0x00800, /* M10x HDLC processing */
|
||||
DEBUG_CHANNEL = 0x01000, /* channel allocation/deallocation */
|
||||
DEBUG_TRANSCMD = 0x02000, /* AT-COMMANDS+RESPONSES */
|
||||
DEBUG_MCMD = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */
|
||||
DEBUG_INIT = 0x08000, /* (de)allocation+initialization of data
|
||||
structures */
|
||||
DEBUG_SUSPEND = 0x10000, /* suspend/resume processing */
|
||||
DEBUG_OUTPUT = 0x20000, /* output to device */
|
||||
DEBUG_ISO = 0x40000, /* isochronous transfers */
|
||||
DEBUG_IF = 0x80000, /* character device operations */
|
||||
DEBUG_USBREQ = 0x100000, /* USB communication (except payload
|
||||
data) */
|
||||
DEBUG_LOCKCMD = 0x200000, /* AT commands and responses when
|
||||
MS_LOCKED */
|
||||
|
||||
DEBUG_ANY = 0x3fffff, /* print message if any of the others is
|
||||
activated */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
|
||||
#define gig_dbg(level, format, arg...) \
|
||||
do { \
|
||||
if (unlikely(((enum debuglevel)gigaset_debuglevel) & (level))) \
|
||||
printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \
|
||||
## arg); \
|
||||
} while (0)
|
||||
#define DEBUG_DEFAULT (DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ)
|
||||
|
||||
#else
|
||||
|
||||
#define gig_dbg(level, format, arg...) do {} while (0)
|
||||
#define DEBUG_DEFAULT 0
|
||||
|
||||
#endif
|
||||
|
||||
void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
|
||||
size_t len, const unsigned char *buf);
|
||||
|
||||
/* connection state */
|
||||
#define ZSAU_NONE 0
|
||||
#define ZSAU_PROCEEDING 1
|
||||
#define ZSAU_CALL_DELIVERED 2
|
||||
#define ZSAU_ACTIVE 3
|
||||
#define ZSAU_DISCONNECT_IND 4
|
||||
#define ZSAU_NULL 5
|
||||
#define ZSAU_DISCONNECT_REQ 6
|
||||
#define ZSAU_UNKNOWN -1
|
||||
|
||||
/* USB control transfer requests */
|
||||
#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
|
||||
#define IN_VENDOR_REQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
|
||||
|
||||
/* interrupt pipe messages */
|
||||
#define HD_B1_FLOW_CONTROL 0x80
|
||||
#define HD_B2_FLOW_CONTROL 0x81
|
||||
#define HD_RECEIVEATDATA_ACK (0x35) /* 3070 */
|
||||
#define HD_READY_SEND_ATDATA (0x36) /* 3070 */
|
||||
#define HD_OPEN_ATCHANNEL_ACK (0x37) /* 3070 */
|
||||
#define HD_CLOSE_ATCHANNEL_ACK (0x38) /* 3070 */
|
||||
#define HD_DEVICE_INIT_OK (0x11) /* ISurf USB + 3070 */
|
||||
#define HD_OPEN_B1CHANNEL_ACK (0x51) /* ISurf USB + 3070 */
|
||||
#define HD_OPEN_B2CHANNEL_ACK (0x52) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B1CHANNEL_ACK (0x53) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B2CHANNEL_ACK (0x54) /* ISurf USB + 3070 */
|
||||
#define HD_SUSPEND_END (0x61) /* ISurf USB */
|
||||
#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) /* ISurf USB + 3070 */
|
||||
|
||||
/* control requests */
|
||||
#define HD_OPEN_B1CHANNEL (0x23) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B1CHANNEL (0x24) /* ISurf USB + 3070 */
|
||||
#define HD_OPEN_B2CHANNEL (0x25) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B2CHANNEL (0x26) /* ISurf USB + 3070 */
|
||||
#define HD_RESET_INTERRUPT_PIPE (0x27) /* ISurf USB + 3070 */
|
||||
#define HD_DEVICE_INIT_ACK (0x34) /* ISurf USB + 3070 */
|
||||
#define HD_WRITE_ATMESSAGE (0x12) /* 3070 */
|
||||
#define HD_READ_ATMESSAGE (0x13) /* 3070 */
|
||||
#define HD_OPEN_ATCHANNEL (0x28) /* 3070 */
|
||||
#define HD_CLOSE_ATCHANNEL (0x29) /* 3070 */
|
||||
|
||||
/* number of B channels supported by base driver */
|
||||
#define BAS_CHANNELS 2
|
||||
|
||||
/* USB frames for isochronous transfer */
|
||||
#define BAS_FRAMETIME 1 /* number of milliseconds between frames */
|
||||
#define BAS_NUMFRAMES 8 /* number of frames per URB */
|
||||
#define BAS_MAXFRAME 16 /* allocated bytes per frame */
|
||||
#define BAS_NORMFRAME 8 /* send size without flow control */
|
||||
#define BAS_HIGHFRAME 10 /* " " with positive flow control */
|
||||
#define BAS_LOWFRAME 5 /* " " with negative flow control */
|
||||
#define BAS_CORRFRAMES 4 /* flow control multiplicator */
|
||||
|
||||
#define BAS_INBUFSIZE (BAS_MAXFRAME * BAS_NUMFRAMES) /* size of isoc in buf
|
||||
* per URB */
|
||||
#define BAS_OUTBUFSIZE 4096 /* size of common isoc out buffer */
|
||||
#define BAS_OUTBUFPAD BAS_MAXFRAME /* size of pad area for isoc out buf */
|
||||
|
||||
#define BAS_INURBS 3
|
||||
#define BAS_OUTURBS 3
|
||||
|
||||
/* variable commands in struct bc_state */
|
||||
#define AT_ISO 0
|
||||
#define AT_DIAL 1
|
||||
#define AT_MSN 2
|
||||
#define AT_BC 3
|
||||
#define AT_PROTO 4
|
||||
#define AT_TYPE 5
|
||||
#define AT_CLIP 6
|
||||
/* total number */
|
||||
#define AT_NUM 7
|
||||
|
||||
/* variables in struct at_state_t */
|
||||
/* - numeric */
|
||||
#define VAR_ZSAU 0
|
||||
#define VAR_ZDLE 1
|
||||
#define VAR_ZCTP 2
|
||||
/* total number */
|
||||
#define VAR_NUM 3
|
||||
/* - string */
|
||||
#define STR_NMBR 0
|
||||
#define STR_ZCPN 1
|
||||
#define STR_ZCON 2
|
||||
#define STR_ZBC 3
|
||||
#define STR_ZHLC 4
|
||||
/* total number */
|
||||
#define STR_NUM 5
|
||||
|
||||
/* event types */
|
||||
#define EV_TIMEOUT -105
|
||||
#define EV_IF_VER -106
|
||||
#define EV_PROC_CIDMODE -107
|
||||
#define EV_SHUTDOWN -108
|
||||
#define EV_START -110
|
||||
#define EV_STOP -111
|
||||
#define EV_IF_LOCK -112
|
||||
#define EV_ACCEPT -114
|
||||
#define EV_DIAL -115
|
||||
#define EV_HUP -116
|
||||
#define EV_BC_OPEN -117
|
||||
#define EV_BC_CLOSED -118
|
||||
|
||||
/* input state */
|
||||
#define INS_command 0x0001 /* receiving messages (not payload data) */
|
||||
#define INS_DLE_char 0x0002 /* DLE flag received (in DLE mode) */
|
||||
#define INS_byte_stuff 0x0004
|
||||
#define INS_have_data 0x0008
|
||||
#define INS_DLE_command 0x0020 /* DLE message start (<DLE> X) received */
|
||||
#define INS_flag_hunt 0x0040
|
||||
|
||||
/* channel state */
|
||||
#define CHS_D_UP 0x01
|
||||
#define CHS_B_UP 0x02
|
||||
#define CHS_NOTIFY_LL 0x04
|
||||
|
||||
#define ICALL_REJECT 0
|
||||
#define ICALL_ACCEPT 1
|
||||
#define ICALL_IGNORE 2
|
||||
|
||||
/* device state */
|
||||
#define MS_UNINITIALIZED 0
|
||||
#define MS_INIT 1
|
||||
#define MS_LOCKED 2
|
||||
#define MS_SHUTDOWN 3
|
||||
#define MS_RECOVER 4
|
||||
#define MS_READY 5
|
||||
|
||||
/* mode */
|
||||
#define M_UNKNOWN 0
|
||||
#define M_CONFIG 1
|
||||
#define M_UNIMODEM 2
|
||||
#define M_CID 3
|
||||
|
||||
/* start mode */
|
||||
#define SM_LOCKED 0
|
||||
#define SM_ISDN 1 /* default */
|
||||
|
||||
/* layer 2 protocols (AT^SBPR=...) */
|
||||
#define L2_BITSYNC 0
|
||||
#define L2_HDLC 1
|
||||
#define L2_VOICE 2
|
||||
|
||||
struct gigaset_ops;
|
||||
struct gigaset_driver;
|
||||
|
||||
struct usb_cardstate;
|
||||
struct ser_cardstate;
|
||||
struct bas_cardstate;
|
||||
|
||||
struct bc_state;
|
||||
struct usb_bc_state;
|
||||
struct ser_bc_state;
|
||||
struct bas_bc_state;
|
||||
|
||||
struct reply_t {
|
||||
int resp_code; /* RSP_XXXX */
|
||||
int min_ConState; /* <0 => ignore */
|
||||
int max_ConState; /* <0 => ignore */
|
||||
int parameter; /* e.g. ZSAU_XXXX <0: ignore*/
|
||||
int new_ConState; /* <0 => ignore */
|
||||
int timeout; /* >0 => *HZ; <=0 => TOUT_XXXX*/
|
||||
int action[MAXACT]; /* ACT_XXXX */
|
||||
char *command; /* NULL==none */
|
||||
};
|
||||
|
||||
extern struct reply_t gigaset_tab_cid[];
|
||||
extern struct reply_t gigaset_tab_nocid[];
|
||||
|
||||
struct inbuf_t {
|
||||
struct cardstate *cs;
|
||||
int inputstate;
|
||||
int head, tail;
|
||||
unsigned char data[RBUFSIZE];
|
||||
};
|
||||
|
||||
/* isochronous write buffer structure
|
||||
* circular buffer with pad area for extraction of complete USB frames
|
||||
* - data[read..nextread-1] is valid data already submitted to the USB subsystem
|
||||
* - data[nextread..write-1] is valid data yet to be sent
|
||||
* - data[write] is the next byte to write to
|
||||
* - in byte-oriented L2 procotols, it is completely free
|
||||
* - in bit-oriented L2 procotols, it may contain a partial byte of valid data
|
||||
* - data[write+1..read-1] is free
|
||||
* - wbits is the number of valid data bits in data[write], starting at the LSB
|
||||
* - writesem is the semaphore for writing to the buffer:
|
||||
* if writesem <= 0, data[write..read-1] is currently being written to
|
||||
* - idle contains the byte value to repeat when the end of valid data is
|
||||
* reached; if nextread==write (buffer contains no data to send), either the
|
||||
* BAS_OUTBUFPAD bytes immediately before data[write] (if
|
||||
* write>=BAS_OUTBUFPAD) or those of the pad area (if write<BAS_OUTBUFPAD)
|
||||
* are also filled with that value
|
||||
*/
|
||||
struct isowbuf_t {
|
||||
int read;
|
||||
int nextread;
|
||||
int write;
|
||||
atomic_t writesem;
|
||||
int wbits;
|
||||
unsigned char data[BAS_OUTBUFSIZE + BAS_OUTBUFPAD];
|
||||
unsigned char idle;
|
||||
};
|
||||
|
||||
/* isochronous write URB context structure
|
||||
* data to be stored along with the URB and retrieved when it is returned
|
||||
* as completed by the USB subsystem
|
||||
* - urb: pointer to the URB itself
|
||||
* - bcs: pointer to the B Channel control structure
|
||||
* - limit: end of write buffer area covered by this URB
|
||||
* - status: URB completion status
|
||||
*/
|
||||
struct isow_urbctx_t {
|
||||
struct urb *urb;
|
||||
struct bc_state *bcs;
|
||||
int limit;
|
||||
int status;
|
||||
};
|
||||
|
||||
/* AT state structure
|
||||
* data associated with the state of an ISDN connection, whether or not
|
||||
* it is currently assigned a B channel
|
||||
*/
|
||||
struct at_state_t {
|
||||
struct list_head list;
|
||||
int waiting;
|
||||
int getstring;
|
||||
unsigned timer_index;
|
||||
unsigned long timer_expires;
|
||||
int timer_active;
|
||||
unsigned int ConState; /* State of connection */
|
||||
struct reply_t *replystruct;
|
||||
int cid;
|
||||
int int_var[VAR_NUM]; /* see VAR_XXXX */
|
||||
char *str_var[STR_NUM]; /* see STR_XXXX */
|
||||
unsigned pending_commands; /* see PC_XXXX */
|
||||
unsigned seq_index;
|
||||
|
||||
struct cardstate *cs;
|
||||
struct bc_state *bcs;
|
||||
};
|
||||
|
||||
struct event_t {
|
||||
int type;
|
||||
void *ptr, *arg;
|
||||
int parameter;
|
||||
int cid;
|
||||
struct at_state_t *at_state;
|
||||
};
|
||||
|
||||
/* This buffer holds all information about the used B-Channel */
|
||||
struct bc_state {
|
||||
struct sk_buff *tx_skb; /* Current transfer buffer to modem */
|
||||
struct sk_buff_head squeue; /* B-Channel send Queue */
|
||||
|
||||
/* Variables for debugging .. */
|
||||
int corrupted; /* Counter for corrupted packages */
|
||||
int trans_down; /* Counter of packages (downstream) */
|
||||
int trans_up; /* Counter of packages (upstream) */
|
||||
|
||||
struct at_state_t at_state;
|
||||
|
||||
/* receive buffer */
|
||||
unsigned rx_bufsize; /* max size accepted by application */
|
||||
struct sk_buff *rx_skb;
|
||||
__u16 rx_fcs;
|
||||
int inputstate; /* see INS_XXXX */
|
||||
|
||||
int channel;
|
||||
|
||||
struct cardstate *cs;
|
||||
|
||||
unsigned chstate; /* bitmap (CHS_*) */
|
||||
int ignore;
|
||||
unsigned proto2; /* layer 2 protocol (L2_*) */
|
||||
char *commands[AT_NUM]; /* see AT_XXXX */
|
||||
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
int emptycount;
|
||||
#endif
|
||||
int busy;
|
||||
int use_count;
|
||||
|
||||
/* private data of hardware drivers */
|
||||
union {
|
||||
struct ser_bc_state *ser; /* serial hardware driver */
|
||||
struct usb_bc_state *usb; /* usb hardware driver (m105) */
|
||||
struct bas_bc_state *bas; /* usb hardware driver (base) */
|
||||
} hw;
|
||||
|
||||
void *ap; /* associated LL application */
|
||||
int apconnstate; /* LL application connection state */
|
||||
spinlock_t aplock;
|
||||
};
|
||||
|
||||
struct cardstate {
|
||||
struct gigaset_driver *driver;
|
||||
unsigned minor_index;
|
||||
struct device *dev;
|
||||
struct device *tty_dev;
|
||||
unsigned flags;
|
||||
|
||||
const struct gigaset_ops *ops;
|
||||
|
||||
/* Stuff to handle communication */
|
||||
wait_queue_head_t waitqueue;
|
||||
int waiting;
|
||||
int mode; /* see M_XXXX */
|
||||
int mstate; /* Modem state: see MS_XXXX */
|
||||
/* only changed by the event layer */
|
||||
int cmd_result;
|
||||
|
||||
int channels;
|
||||
struct bc_state *bcs; /* Array of struct bc_state */
|
||||
|
||||
int onechannel; /* data and commands transmitted in one
|
||||
stream (M10x) */
|
||||
|
||||
spinlock_t lock;
|
||||
struct at_state_t at_state; /* at_state_t for cid == 0 */
|
||||
struct list_head temp_at_states;/* list of temporary "struct
|
||||
at_state_t"s without B channel */
|
||||
|
||||
struct inbuf_t *inbuf;
|
||||
|
||||
struct cmdbuf_t *cmdbuf, *lastcmdbuf;
|
||||
spinlock_t cmdlock;
|
||||
unsigned curlen, cmdbytes;
|
||||
|
||||
struct tty_port port;
|
||||
struct tasklet_struct if_wake_tasklet;
|
||||
unsigned control_state;
|
||||
|
||||
unsigned fwver[4];
|
||||
int gotfwver;
|
||||
|
||||
unsigned running; /* !=0 if events are handled */
|
||||
unsigned connected; /* !=0 if hardware is connected */
|
||||
unsigned isdn_up; /* !=0 after gigaset_isdn_start() */
|
||||
|
||||
unsigned cidmode;
|
||||
|
||||
int myid; /* id for communication with LL */
|
||||
void *iif; /* LL interface structure */
|
||||
unsigned short hw_hdr_len; /* headroom needed in data skbs */
|
||||
|
||||
struct reply_t *tabnocid;
|
||||
struct reply_t *tabcid;
|
||||
int cs_init;
|
||||
int ignoreframes; /* frames to ignore after setting up the
|
||||
B channel */
|
||||
struct mutex mutex; /* locks this structure:
|
||||
* connected is not changed,
|
||||
* hardware_up is not changed,
|
||||
* MState is not changed to or from
|
||||
* MS_LOCKED */
|
||||
|
||||
struct timer_list timer;
|
||||
int retry_count;
|
||||
int dle; /* !=0 if DLE mode is active
|
||||
(ZDLE=1 received -- M10x only) */
|
||||
int cur_at_seq; /* sequence of AT commands being
|
||||
processed */
|
||||
int curchannel; /* channel those commands are meant
|
||||
for */
|
||||
int commands_pending; /* flag(s) in xxx.commands_pending have
|
||||
been set */
|
||||
struct tasklet_struct
|
||||
event_tasklet; /* tasklet for serializing AT commands.
|
||||
* Scheduled
|
||||
* -> for modem reponses (and
|
||||
* incoming data for M10x)
|
||||
* -> on timeout
|
||||
* -> after setting bits in
|
||||
* xxx.at_state.pending_command
|
||||
* (e.g. command from LL) */
|
||||
struct tasklet_struct
|
||||
write_tasklet; /* tasklet for serial output
|
||||
* (not used in base driver) */
|
||||
|
||||
/* event queue */
|
||||
struct event_t events[MAX_EVENTS];
|
||||
unsigned ev_tail, ev_head;
|
||||
spinlock_t ev_lock;
|
||||
|
||||
/* current modem response */
|
||||
unsigned char respdata[MAX_RESP_SIZE + 1];
|
||||
unsigned cbytes;
|
||||
|
||||
/* private data of hardware drivers */
|
||||
union {
|
||||
struct usb_cardstate *usb; /* USB hardware driver (m105) */
|
||||
struct ser_cardstate *ser; /* serial hardware driver */
|
||||
struct bas_cardstate *bas; /* USB hardware driver (base) */
|
||||
} hw;
|
||||
};
|
||||
|
||||
struct gigaset_driver {
|
||||
struct list_head list;
|
||||
spinlock_t lock; /* locks minor tables and blocked */
|
||||
struct tty_driver *tty;
|
||||
unsigned have_tty;
|
||||
unsigned minor;
|
||||
unsigned minors;
|
||||
struct cardstate *cs;
|
||||
int blocked;
|
||||
|
||||
const struct gigaset_ops *ops;
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
struct cmdbuf_t {
|
||||
struct cmdbuf_t *next, *prev;
|
||||
int len, offset;
|
||||
struct tasklet_struct *wake_tasklet;
|
||||
unsigned char buf[0];
|
||||
};
|
||||
|
||||
struct bas_bc_state {
|
||||
/* isochronous output state */
|
||||
int running;
|
||||
atomic_t corrbytes;
|
||||
spinlock_t isooutlock;
|
||||
struct isow_urbctx_t isoouturbs[BAS_OUTURBS];
|
||||
struct isow_urbctx_t *isooutdone, *isooutfree, *isooutovfl;
|
||||
struct isowbuf_t *isooutbuf;
|
||||
unsigned numsub; /* submitted URB counter
|
||||
(for diagnostic messages only) */
|
||||
struct tasklet_struct sent_tasklet;
|
||||
|
||||
/* isochronous input state */
|
||||
spinlock_t isoinlock;
|
||||
struct urb *isoinurbs[BAS_INURBS];
|
||||
unsigned char isoinbuf[BAS_INBUFSIZE * BAS_INURBS];
|
||||
struct urb *isoindone; /* completed isoc read URB */
|
||||
int isoinstatus; /* status of completed URB */
|
||||
int loststatus; /* status of dropped URB */
|
||||
unsigned isoinlost; /* number of bytes lost */
|
||||
/* state of bit unstuffing algorithm
|
||||
(in addition to BC_state.inputstate) */
|
||||
unsigned seqlen; /* number of '1' bits not yet
|
||||
unstuffed */
|
||||
unsigned inbyte, inbits; /* collected bits for next byte */
|
||||
/* statistics */
|
||||
unsigned goodbytes; /* bytes correctly received */
|
||||
unsigned alignerrs; /* frames with incomplete byte at end */
|
||||
unsigned fcserrs; /* FCS errors */
|
||||
unsigned frameerrs; /* framing errors */
|
||||
unsigned giants; /* long frames */
|
||||
unsigned runts; /* short frames */
|
||||
unsigned aborts; /* HDLC aborts */
|
||||
unsigned shared0s; /* '0' bits shared between flags */
|
||||
unsigned stolen0s; /* '0' stuff bits also serving as
|
||||
leading flag bits */
|
||||
struct tasklet_struct rcvd_tasklet;
|
||||
};
|
||||
|
||||
struct gigaset_ops {
|
||||
/* Called from ev-layer.c/interface.c for sending AT commands to the
|
||||
device */
|
||||
int (*write_cmd)(struct cardstate *cs, struct cmdbuf_t *cb);
|
||||
|
||||
/* Called from interface.c for additional device control */
|
||||
int (*write_room)(struct cardstate *cs);
|
||||
int (*chars_in_buffer)(struct cardstate *cs);
|
||||
int (*brkchars)(struct cardstate *cs, const unsigned char buf[6]);
|
||||
|
||||
/* Called from ev-layer.c after setting up connection
|
||||
* Should call gigaset_bchannel_up(), when finished. */
|
||||
int (*init_bchannel)(struct bc_state *bcs);
|
||||
|
||||
/* Called from ev-layer.c after hanging up
|
||||
* Should call gigaset_bchannel_down(), when finished. */
|
||||
int (*close_bchannel)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_initcs() for setting up bcs->hw.xxx */
|
||||
int (*initbcshw)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_freecs() for freeing bcs->hw.xxx */
|
||||
void (*freebcshw)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_bchannel_down() for resetting bcs->hw.xxx */
|
||||
void (*reinitbcshw)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_initcs() for setting up cs->hw.xxx */
|
||||
int (*initcshw)(struct cardstate *cs);
|
||||
|
||||
/* Called by gigaset_freecs() for freeing cs->hw.xxx */
|
||||
void (*freecshw)(struct cardstate *cs);
|
||||
|
||||
/* Called from common.c/interface.c for additional serial port
|
||||
control */
|
||||
int (*set_modem_ctrl)(struct cardstate *cs, unsigned old_state,
|
||||
unsigned new_state);
|
||||
int (*baud_rate)(struct cardstate *cs, unsigned cflag);
|
||||
int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag);
|
||||
|
||||
/* Called from LL interface to put an skb into the send-queue.
|
||||
* After sending is completed, gigaset_skb_sent() must be called
|
||||
* with the skb's link layer header preserved. */
|
||||
int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb);
|
||||
|
||||
/* Called from ev-layer.c to process a block of data
|
||||
* received through the common/control channel. */
|
||||
void (*handle_input)(struct inbuf_t *inbuf);
|
||||
|
||||
};
|
||||
|
||||
/* = Common structures and definitions =======================================
|
||||
*/
|
||||
|
||||
/* Parser states for DLE-Event:
|
||||
* <DLE-EVENT>: <DLE_FLAG> "X" <EVENT> <DLE_FLAG> "."
|
||||
* <DLE_FLAG>: 0x10
|
||||
* <EVENT>: ((a-z)* | (A-Z)* | (0-10)*)+
|
||||
*/
|
||||
#define DLE_FLAG 0x10
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in asyncdata.c
|
||||
*/
|
||||
|
||||
/* Called from LL interface to put an skb into the send queue. */
|
||||
int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb);
|
||||
|
||||
/* Called from ev-layer.c to process a block of data
|
||||
* received through the common/control channel. */
|
||||
void gigaset_m10x_input(struct inbuf_t *inbuf);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in isocdata.c
|
||||
*/
|
||||
|
||||
/* Called from LL interface to put an skb into the send queue. */
|
||||
int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb);
|
||||
|
||||
/* Called from ev-layer.c to process a block of data
|
||||
* received through the common/control channel. */
|
||||
void gigaset_isoc_input(struct inbuf_t *inbuf);
|
||||
|
||||
/* Called from bas-gigaset.c to process a block of data
|
||||
* received through the isochronous channel */
|
||||
void gigaset_isoc_receive(unsigned char *src, unsigned count,
|
||||
struct bc_state *bcs);
|
||||
|
||||
/* Called from bas-gigaset.c to put a block of data
|
||||
* into the isochronous output buffer */
|
||||
int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len);
|
||||
|
||||
/* Called from bas-gigaset.c to initialize the isochronous output buffer */
|
||||
void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle);
|
||||
|
||||
/* Called from bas-gigaset.c to retrieve a block of bytes for sending */
|
||||
int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in LL interface
|
||||
*/
|
||||
|
||||
/* Called from common.c for setting up/shutting down with the ISDN subsystem */
|
||||
void gigaset_isdn_regdrv(void);
|
||||
void gigaset_isdn_unregdrv(void);
|
||||
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid);
|
||||
void gigaset_isdn_unregdev(struct cardstate *cs);
|
||||
|
||||
/* Called from hardware module to indicate completion of an skb */
|
||||
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
|
||||
void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb);
|
||||
void gigaset_isdn_rcv_err(struct bc_state *bcs);
|
||||
|
||||
/* Called from common.c/ev-layer.c to indicate events relevant to the LL */
|
||||
void gigaset_isdn_start(struct cardstate *cs);
|
||||
void gigaset_isdn_stop(struct cardstate *cs);
|
||||
int gigaset_isdn_icall(struct at_state_t *at_state);
|
||||
void gigaset_isdn_connD(struct bc_state *bcs);
|
||||
void gigaset_isdn_hupD(struct bc_state *bcs);
|
||||
void gigaset_isdn_connB(struct bc_state *bcs);
|
||||
void gigaset_isdn_hupB(struct bc_state *bcs);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in ev-layer.c
|
||||
*/
|
||||
|
||||
/* tasklet called from common.c to process queued events */
|
||||
void gigaset_handle_event(unsigned long data);
|
||||
|
||||
/* called from isocdata.c / asyncdata.c
|
||||
* when a complete modem response line has been received */
|
||||
void gigaset_handle_modem_response(struct cardstate *cs);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in proc.c
|
||||
*/
|
||||
|
||||
/* initialize sysfs for device */
|
||||
void gigaset_init_dev_sysfs(struct cardstate *cs);
|
||||
void gigaset_free_dev_sysfs(struct cardstate *cs);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in common.c/gigaset.h
|
||||
*/
|
||||
|
||||
void gigaset_bcs_reinit(struct bc_state *bcs);
|
||||
void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
|
||||
struct cardstate *cs, int cid);
|
||||
int gigaset_get_channel(struct bc_state *bcs);
|
||||
struct bc_state *gigaset_get_free_channel(struct cardstate *cs);
|
||||
void gigaset_free_channel(struct bc_state *bcs);
|
||||
int gigaset_get_channels(struct cardstate *cs);
|
||||
void gigaset_free_channels(struct cardstate *cs);
|
||||
void gigaset_block_channels(struct cardstate *cs);
|
||||
|
||||
/* Allocate and initialize driver structure. */
|
||||
struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
|
||||
const char *procname,
|
||||
const char *devname,
|
||||
const struct gigaset_ops *ops,
|
||||
struct module *owner);
|
||||
|
||||
/* Deallocate driver structure. */
|
||||
void gigaset_freedriver(struct gigaset_driver *drv);
|
||||
|
||||
struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty);
|
||||
struct cardstate *gigaset_get_cs_by_id(int id);
|
||||
void gigaset_blockdriver(struct gigaset_driver *drv);
|
||||
|
||||
/* Allocate and initialize card state. Calls hardware dependent
|
||||
gigaset_init[b]cs(). */
|
||||
struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
|
||||
int onechannel, int ignoreframes,
|
||||
int cidmode, const char *modulename);
|
||||
|
||||
/* Free card state. Calls hardware dependent gigaset_free[b]cs(). */
|
||||
void gigaset_freecs(struct cardstate *cs);
|
||||
|
||||
/* Tell common.c that hardware and driver are ready. */
|
||||
int gigaset_start(struct cardstate *cs);
|
||||
|
||||
/* Tell common.c that the device is not present any more. */
|
||||
void gigaset_stop(struct cardstate *cs);
|
||||
|
||||
/* Tell common.c that the driver is being unloaded. */
|
||||
int gigaset_shutdown(struct cardstate *cs);
|
||||
|
||||
/* Append event to the queue.
|
||||
* Returns NULL on failure or a pointer to the event on success.
|
||||
* ptr must be kmalloc()ed (and not be freed by the caller).
|
||||
*/
|
||||
struct event_t *gigaset_add_event(struct cardstate *cs,
|
||||
struct at_state_t *at_state, int type,
|
||||
void *ptr, int parameter, void *arg);
|
||||
|
||||
/* Called on CONFIG1 command from frontend. */
|
||||
int gigaset_enterconfigmode(struct cardstate *cs);
|
||||
|
||||
/* cs->lock must not be locked */
|
||||
static inline void gigaset_schedule_event(struct cardstate *cs)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->running)
|
||||
tasklet_schedule(&cs->event_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
}
|
||||
|
||||
/* Tell common.c that B channel has been closed. */
|
||||
/* cs->lock must not be locked */
|
||||
static inline void gigaset_bchannel_down(struct bc_state *bcs)
|
||||
{
|
||||
gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_CLOSED, NULL, 0, NULL);
|
||||
gigaset_schedule_event(bcs->cs);
|
||||
}
|
||||
|
||||
/* Tell common.c that B channel has been opened. */
|
||||
/* cs->lock must not be locked */
|
||||
static inline void gigaset_bchannel_up(struct bc_state *bcs)
|
||||
{
|
||||
gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_OPEN, NULL, 0, NULL);
|
||||
gigaset_schedule_event(bcs->cs);
|
||||
}
|
||||
|
||||
/* set up next receive skb for data mode */
|
||||
static inline struct sk_buff *gigaset_new_rx_skb(struct bc_state *bcs)
|
||||
{
|
||||
struct cardstate *cs = bcs->cs;
|
||||
unsigned short hw_hdr_len = cs->hw_hdr_len;
|
||||
|
||||
if (bcs->ignore) {
|
||||
bcs->rx_skb = NULL;
|
||||
} else {
|
||||
bcs->rx_skb = dev_alloc_skb(bcs->rx_bufsize + hw_hdr_len);
|
||||
if (bcs->rx_skb == NULL)
|
||||
dev_warn(cs->dev, "could not allocate skb\n");
|
||||
else
|
||||
skb_reserve(bcs->rx_skb, hw_hdr_len);
|
||||
}
|
||||
return bcs->rx_skb;
|
||||
}
|
||||
|
||||
/* append received bytes to inbuf */
|
||||
int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
|
||||
unsigned numbytes);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in interface.c
|
||||
*/
|
||||
|
||||
/* initialize interface */
|
||||
void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
|
||||
const char *devname);
|
||||
/* release interface */
|
||||
void gigaset_if_freedriver(struct gigaset_driver *drv);
|
||||
/* add minor */
|
||||
void gigaset_if_init(struct cardstate *cs);
|
||||
/* remove minor */
|
||||
void gigaset_if_free(struct cardstate *cs);
|
||||
/* device received data */
|
||||
void gigaset_if_receive(struct cardstate *cs,
|
||||
unsigned char *buffer, size_t len);
|
||||
|
||||
#endif
|
@ -1,613 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* interface to user space for the gigaset driver
|
||||
*
|
||||
* Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
|
||||
*
|
||||
* =====================================================================
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/gigaset_dev.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/*** our ioctls ***/
|
||||
|
||||
static int if_lock(struct cardstate *cs, int *arg)
|
||||
{
|
||||
int cmd = *arg;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
|
||||
|
||||
if (cmd > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (cmd < 0) {
|
||||
*arg = cs->mstate == MS_LOCKED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
|
||||
cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
|
||||
cs->ops->baud_rate(cs, B115200);
|
||||
cs->ops->set_line_ctrl(cs, CS8);
|
||||
cs->control_state = TIOCM_DTR | TIOCM_RTS;
|
||||
}
|
||||
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
|
||||
NULL, cmd, NULL)) {
|
||||
cs->waiting = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
if (cs->cmd_result >= 0) {
|
||||
*arg = cs->cmd_result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cs->cmd_result;
|
||||
}
|
||||
|
||||
static int if_version(struct cardstate *cs, unsigned arg[4])
|
||||
{
|
||||
static const unsigned version[4] = GIG_VERSION;
|
||||
static const unsigned compat[4] = GIG_COMPAT;
|
||||
unsigned cmd = arg[0];
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case GIGVER_DRIVER:
|
||||
memcpy(arg, version, sizeof version);
|
||||
return 0;
|
||||
case GIGVER_COMPAT:
|
||||
memcpy(arg, compat, sizeof compat);
|
||||
return 0;
|
||||
case GIGVER_FWBASE:
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
|
||||
NULL, 0, arg)) {
|
||||
cs->waiting = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
if (cs->cmd_result >= 0)
|
||||
return 0;
|
||||
|
||||
return cs->cmd_result;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int if_config(struct cardstate *cs, int *arg)
|
||||
{
|
||||
gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
|
||||
|
||||
if (*arg != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (cs->mstate != MS_LOCKED)
|
||||
return -EBUSY;
|
||||
|
||||
if (!cs->connected) {
|
||||
pr_err("%s: not connected\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*arg = 0;
|
||||
return gigaset_enterconfigmode(cs);
|
||||
}
|
||||
|
||||
/*** the terminal driver ***/
|
||||
|
||||
static int if_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%d+%d: %s()",
|
||||
tty->driver->minor_start, tty->index, __func__);
|
||||
|
||||
cs = gigaset_get_cs_by_tty(tty);
|
||||
if (!cs || !try_module_get(cs->driver->owner))
|
||||
return -ENODEV;
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex)) {
|
||||
module_put(cs->driver->owner);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
tty->driver_data = cs;
|
||||
|
||||
++cs->port.count;
|
||||
|
||||
if (cs->port.count == 1) {
|
||||
tty_port_tty_set(&cs->port, tty);
|
||||
cs->port.low_latency = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void if_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
|
||||
if (!cs) { /* happens if we didn't find cs in open */
|
||||
gig_dbg(DEBUG_IF, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
|
||||
else if (!cs->port.count)
|
||||
dev_warn(cs->dev, "%s: device not opened\n", __func__);
|
||||
else if (!--cs->port.count)
|
||||
tty_port_tty_set(&cs->port, NULL);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
module_put(cs->driver->owner);
|
||||
}
|
||||
|
||||
static int if_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval = -ENODEV;
|
||||
int int_arg;
|
||||
unsigned char buf[6];
|
||||
unsigned version[4];
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
} else {
|
||||
retval = 0;
|
||||
switch (cmd) {
|
||||
case GIGASET_REDIR:
|
||||
retval = get_user(int_arg, (int __user *) arg);
|
||||
if (retval >= 0)
|
||||
retval = if_lock(cs, &int_arg);
|
||||
if (retval >= 0)
|
||||
retval = put_user(int_arg, (int __user *) arg);
|
||||
break;
|
||||
case GIGASET_CONFIG:
|
||||
retval = get_user(int_arg, (int __user *) arg);
|
||||
if (retval >= 0)
|
||||
retval = if_config(cs, &int_arg);
|
||||
if (retval >= 0)
|
||||
retval = put_user(int_arg, (int __user *) arg);
|
||||
break;
|
||||
case GIGASET_BRKCHARS:
|
||||
retval = copy_from_user(&buf,
|
||||
(const unsigned char __user *) arg, 6)
|
||||
? -EFAULT : 0;
|
||||
if (retval >= 0) {
|
||||
gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
|
||||
6, buf);
|
||||
retval = cs->ops->brkchars(cs, buf);
|
||||
}
|
||||
break;
|
||||
case GIGASET_VERSION:
|
||||
retval = copy_from_user(version,
|
||||
(unsigned __user *) arg, sizeof version)
|
||||
? -EFAULT : 0;
|
||||
if (retval >= 0)
|
||||
retval = if_version(cs, version);
|
||||
if (retval >= 0)
|
||||
retval = copy_to_user((unsigned __user *) arg,
|
||||
version, sizeof version)
|
||||
? -EFAULT : 0;
|
||||
break;
|
||||
default:
|
||||
gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
|
||||
__func__, cmd);
|
||||
retval = -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long if_compat_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return if_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int if_tiocmget(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
retval = cs->control_state & (TIOCM_RTS | TIOCM_DTR);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_tiocmset(struct tty_struct *tty,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval;
|
||||
unsigned mc;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
|
||||
cs->minor_index, __func__, set, clear);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
} else {
|
||||
mc = (cs->control_state | set) & ~clear & (TIOCM_RTS | TIOCM_DTR);
|
||||
retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
|
||||
cs->control_state = mc;
|
||||
}
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
struct cmdbuf_t *cb;
|
||||
int retval;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
if (cs->mstate != MS_LOCKED) {
|
||||
dev_warn(cs->dev, "can't write to unlocked device\n");
|
||||
retval = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
if (count <= 0) {
|
||||
/* nothing to do */
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
|
||||
if (!cb) {
|
||||
dev_err(cs->dev, "%s: out of memory\n", __func__);
|
||||
retval = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(cb->buf, buf, count);
|
||||
cb->len = count;
|
||||
cb->offset = 0;
|
||||
cb->next = NULL;
|
||||
cb->wake_tasklet = &cs->if_wake_tasklet;
|
||||
retval = cs->ops->write_cmd(cs, cb);
|
||||
done:
|
||||
mutex_unlock(&cs->mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
} else if (cs->mstate != MS_LOCKED) {
|
||||
dev_warn(cs->dev, "can't write to unlocked device\n");
|
||||
retval = -EBUSY;
|
||||
} else
|
||||
retval = cs->ops->write_room(cs);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval = 0;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
else if (cs->mstate != MS_LOCKED)
|
||||
dev_warn(cs->dev, "can't write to unlocked device\n");
|
||||
else
|
||||
retval = cs->ops->chars_in_buffer(cs);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void if_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
|
||||
else
|
||||
gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
static void if_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
|
||||
else
|
||||
gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
unsigned int iflag;
|
||||
unsigned int cflag;
|
||||
unsigned int old_cflag;
|
||||
unsigned int control_state, new_state;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
goto out;
|
||||
}
|
||||
|
||||
iflag = tty->termios.c_iflag;
|
||||
cflag = tty->termios.c_cflag;
|
||||
old_cflag = old ? old->c_cflag : cflag;
|
||||
gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
|
||||
cs->minor_index, iflag, cflag, old_cflag);
|
||||
|
||||
/* get a local copy of the current port settings */
|
||||
control_state = cs->control_state;
|
||||
|
||||
/*
|
||||
* Update baud rate.
|
||||
* Do not attempt to cache old rates and skip settings,
|
||||
* disconnects screw such tricks up completely.
|
||||
* Premature optimization is the root of all evil.
|
||||
*/
|
||||
|
||||
/* reassert DTR and (maybe) RTS on transition from B0 */
|
||||
if ((old_cflag & CBAUD) == B0) {
|
||||
new_state = control_state | TIOCM_DTR;
|
||||
/* don't set RTS if using hardware flow control */
|
||||
if (!(old_cflag & CRTSCTS))
|
||||
new_state |= TIOCM_RTS;
|
||||
gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
|
||||
cs->minor_index,
|
||||
(new_state & TIOCM_RTS) ? " only" : "/RTS");
|
||||
cs->ops->set_modem_ctrl(cs, control_state, new_state);
|
||||
control_state = new_state;
|
||||
}
|
||||
|
||||
cs->ops->baud_rate(cs, cflag & CBAUD);
|
||||
|
||||
if ((cflag & CBAUD) == B0) {
|
||||
/* Drop RTS and DTR */
|
||||
gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
|
||||
new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
|
||||
cs->ops->set_modem_ctrl(cs, control_state, new_state);
|
||||
control_state = new_state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update line control register (LCR)
|
||||
*/
|
||||
|
||||
cs->ops->set_line_ctrl(cs, cflag);
|
||||
|
||||
/* save off the modified port settings */
|
||||
cs->control_state = control_state;
|
||||
|
||||
out:
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
static const struct tty_operations if_ops = {
|
||||
.open = if_open,
|
||||
.close = if_close,
|
||||
.ioctl = if_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = if_compat_ioctl,
|
||||
#endif
|
||||
.write = if_write,
|
||||
.write_room = if_write_room,
|
||||
.chars_in_buffer = if_chars_in_buffer,
|
||||
.set_termios = if_set_termios,
|
||||
.throttle = if_throttle,
|
||||
.unthrottle = if_unthrottle,
|
||||
.tiocmget = if_tiocmget,
|
||||
.tiocmset = if_tiocmset,
|
||||
};
|
||||
|
||||
|
||||
/* wakeup tasklet for the write operation */
|
||||
static void if_wake(unsigned long data)
|
||||
{
|
||||
struct cardstate *cs = (struct cardstate *)data;
|
||||
|
||||
tty_port_tty_wakeup(&cs->port);
|
||||
}
|
||||
|
||||
/*** interface to common ***/
|
||||
|
||||
void gigaset_if_init(struct cardstate *cs)
|
||||
{
|
||||
struct gigaset_driver *drv;
|
||||
|
||||
drv = cs->driver;
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
cs->tty_dev = tty_port_register_device(&cs->port, drv->tty,
|
||||
cs->minor_index, NULL);
|
||||
|
||||
if (!IS_ERR(cs->tty_dev))
|
||||
dev_set_drvdata(cs->tty_dev, cs);
|
||||
else {
|
||||
pr_warn("could not register device to the tty subsystem\n");
|
||||
cs->tty_dev = NULL;
|
||||
}
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
void gigaset_if_free(struct cardstate *cs)
|
||||
{
|
||||
struct gigaset_driver *drv;
|
||||
|
||||
drv = cs->driver;
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
tasklet_disable(&cs->if_wake_tasklet);
|
||||
tasklet_kill(&cs->if_wake_tasklet);
|
||||
cs->tty_dev = NULL;
|
||||
tty_unregister_device(drv->tty, cs->minor_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_if_receive() - pass a received block of data to the tty device
|
||||
* @cs: device descriptor structure.
|
||||
* @buffer: received data.
|
||||
* @len: number of bytes received.
|
||||
*
|
||||
* Called by asyncdata/isocdata if a block of data received from the
|
||||
* device must be sent to userspace through the ttyG* device.
|
||||
*/
|
||||
void gigaset_if_receive(struct cardstate *cs,
|
||||
unsigned char *buffer, size_t len)
|
||||
{
|
||||
tty_insert_flip_string(&cs->port, buffer, len);
|
||||
tty_flip_buffer_push(&cs->port);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_if_receive);
|
||||
|
||||
/* gigaset_if_initdriver
|
||||
* Initialize tty interface.
|
||||
* parameters:
|
||||
* drv Driver
|
||||
* procname Name of the driver (e.g. for /proc/tty/drivers)
|
||||
* devname Name of the device files (prefix without minor number)
|
||||
*/
|
||||
void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
|
||||
const char *devname)
|
||||
{
|
||||
int ret;
|
||||
struct tty_driver *tty;
|
||||
|
||||
drv->have_tty = 0;
|
||||
|
||||
drv->tty = tty = alloc_tty_driver(drv->minors);
|
||||
if (tty == NULL)
|
||||
goto enomem;
|
||||
|
||||
tty->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
tty->subtype = SERIAL_TYPE_NORMAL;
|
||||
tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
|
||||
tty->driver_name = procname;
|
||||
tty->name = devname;
|
||||
tty->minor_start = drv->minor;
|
||||
|
||||
tty->init_termios = tty_std_termios;
|
||||
tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
tty_set_operations(tty, &if_ops);
|
||||
|
||||
ret = tty_register_driver(tty);
|
||||
if (ret < 0) {
|
||||
pr_err("error %d registering tty driver\n", ret);
|
||||
goto error;
|
||||
}
|
||||
gig_dbg(DEBUG_IF, "tty driver initialized");
|
||||
drv->have_tty = 1;
|
||||
return;
|
||||
|
||||
enomem:
|
||||
pr_err("out of memory\n");
|
||||
error:
|
||||
if (drv->tty)
|
||||
put_tty_driver(drv->tty);
|
||||
}
|
||||
|
||||
void gigaset_if_freedriver(struct gigaset_driver *drv)
|
||||
{
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
drv->have_tty = 0;
|
||||
tty_unregister_driver(drv->tty);
|
||||
put_tty_driver(drv->tty);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,77 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Stuff used by all variants of the driver
|
||||
*
|
||||
* Copyright (c) 2001 by Stefan Eilers,
|
||||
* Hansjoerg Lipp <hjlipp@web.de>,
|
||||
* Tilman Schmidt <tilman@imap.cc>.
|
||||
*
|
||||
* =====================================================================
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
|
||||
static ssize_t show_cidmode(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cardstate *cs = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", cs->cidmode);
|
||||
}
|
||||
|
||||
static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct cardstate *cs = dev_get_drvdata(dev);
|
||||
long int value;
|
||||
char *end;
|
||||
|
||||
value = simple_strtol(buf, &end, 0);
|
||||
while (*end)
|
||||
if (!isspace(*end++))
|
||||
return -EINVAL;
|
||||
if (value < 0 || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE,
|
||||
NULL, value, NULL)) {
|
||||
cs->waiting = 0;
|
||||
mutex_unlock(&cs->mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cidmode, S_IRUGO | S_IWUSR, show_cidmode, set_cidmode);
|
||||
|
||||
/* free sysfs for device */
|
||||
void gigaset_free_dev_sysfs(struct cardstate *cs)
|
||||
{
|
||||
if (!cs->tty_dev)
|
||||
return;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "removing sysfs entries");
|
||||
device_remove_file(cs->tty_dev, &dev_attr_cidmode);
|
||||
}
|
||||
|
||||
/* initialize sysfs for device */
|
||||
void gigaset_init_dev_sysfs(struct cardstate *cs)
|
||||
{
|
||||
if (!cs->tty_dev)
|
||||
return;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "setting up sysfs");
|
||||
if (device_create_file(cs->tty_dev, &dev_attr_cidmode))
|
||||
pr_err("could not create sysfs attribute\n");
|
||||
}
|
@ -1,796 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* This is the serial hardware link layer (HLL) for the Gigaset 307x isdn
|
||||
* DECT base (aka Sinus 45 isdn) using the RS232 DECT data module M101,
|
||||
* written as a line discipline.
|
||||
*
|
||||
* =====================================================================
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
/* Version Information */
|
||||
#define DRIVER_AUTHOR "Tilman Schmidt"
|
||||
#define DRIVER_DESC "Serial Driver for Gigaset 307x using Siemens M101"
|
||||
|
||||
#define GIGASET_MINORS 1
|
||||
#define GIGASET_MINOR 0
|
||||
#define GIGASET_MODULENAME "ser_gigaset"
|
||||
#define GIGASET_DEVNAME "ttyGS"
|
||||
|
||||
/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
|
||||
#define IF_WRITEBUF 264
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_LDISC(N_GIGASET_M101);
|
||||
|
||||
static int startmode = SM_ISDN;
|
||||
module_param(startmode, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(startmode, "initial operation mode");
|
||||
static int cidmode = 1;
|
||||
module_param(cidmode, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(cidmode, "stay in CID mode when idle");
|
||||
|
||||
static struct gigaset_driver *driver;
|
||||
|
||||
struct ser_cardstate {
|
||||
struct platform_device dev;
|
||||
struct tty_struct *tty;
|
||||
atomic_t refcnt;
|
||||
struct completion dead_cmp;
|
||||
};
|
||||
|
||||
static struct platform_driver device_driver = {
|
||||
.driver = {
|
||||
.name = GIGASET_MODULENAME,
|
||||
},
|
||||
};
|
||||
|
||||
static void flush_send_queue(struct cardstate *);
|
||||
|
||||
/* transmit data from current open skb
|
||||
* result: number of bytes sent or error code < 0
|
||||
*/
|
||||
static int write_modem(struct cardstate *cs)
|
||||
{
|
||||
struct tty_struct *tty = cs->hw.ser->tty;
|
||||
struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
|
||||
struct sk_buff *skb = bcs->tx_skb;
|
||||
int sent = -EOPNOTSUPP;
|
||||
|
||||
WARN_ON(!tty || !tty->ops || !skb);
|
||||
|
||||
if (!skb->len) {
|
||||
dev_kfree_skb_any(skb);
|
||||
bcs->tx_skb = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (tty->ops->write)
|
||||
sent = tty->ops->write(tty, skb->data, skb->len);
|
||||
gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent);
|
||||
if (sent < 0) {
|
||||
/* error */
|
||||
flush_send_queue(cs);
|
||||
return sent;
|
||||
}
|
||||
skb_pull(skb, sent);
|
||||
if (!skb->len) {
|
||||
/* skb sent completely */
|
||||
gigaset_skb_sent(bcs, skb);
|
||||
|
||||
gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
|
||||
(unsigned long) skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
bcs->tx_skb = NULL;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
/*
|
||||
* transmit first queued command buffer
|
||||
* result: number of bytes sent or error code < 0
|
||||
*/
|
||||
static int send_cb(struct cardstate *cs)
|
||||
{
|
||||
struct tty_struct *tty = cs->hw.ser->tty;
|
||||
struct cmdbuf_t *cb, *tcb;
|
||||
unsigned long flags;
|
||||
int sent = 0;
|
||||
|
||||
WARN_ON(!tty || !tty->ops);
|
||||
|
||||
cb = cs->cmdbuf;
|
||||
if (!cb)
|
||||
return 0; /* nothing to do */
|
||||
|
||||
if (cb->len) {
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
sent = tty->ops->write(tty, cb->buf + cb->offset, cb->len);
|
||||
if (sent < 0) {
|
||||
/* error */
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent);
|
||||
flush_send_queue(cs);
|
||||
return sent;
|
||||
}
|
||||
cb->offset += sent;
|
||||
cb->len -= sent;
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: sent %d, left %u, queued %u",
|
||||
sent, cb->len, cs->cmdbytes);
|
||||
}
|
||||
|
||||
while (cb && !cb->len) {
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cs->cmdbytes -= cs->curlen;
|
||||
tcb = cb;
|
||||
cs->cmdbuf = cb = cb->next;
|
||||
if (cb) {
|
||||
cb->prev = NULL;
|
||||
cs->curlen = cb->len;
|
||||
} else {
|
||||
cs->lastcmdbuf = NULL;
|
||||
cs->curlen = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
if (tcb->wake_tasklet)
|
||||
tasklet_schedule(tcb->wake_tasklet);
|
||||
kfree(tcb);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
/*
|
||||
* send queue tasklet
|
||||
* If there is already a skb opened, put data to the transfer buffer
|
||||
* by calling "write_modem".
|
||||
* Otherwise take a new skb out of the queue.
|
||||
*/
|
||||
static void gigaset_modem_fill(unsigned long data)
|
||||
{
|
||||
struct cardstate *cs = (struct cardstate *) data;
|
||||
struct bc_state *bcs;
|
||||
struct sk_buff *nextskb;
|
||||
int sent = 0;
|
||||
|
||||
if (!cs) {
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
bcs = cs->bcs;
|
||||
if (!bcs) {
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
if (!bcs->tx_skb) {
|
||||
/* no skb is being sent; send command if any */
|
||||
sent = send_cb(cs);
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: send_cb -> %d", __func__, sent);
|
||||
if (sent)
|
||||
/* something sent or error */
|
||||
return;
|
||||
|
||||
/* no command to send; get skb */
|
||||
nextskb = skb_dequeue(&bcs->squeue);
|
||||
if (!nextskb)
|
||||
/* no skb either, nothing to do */
|
||||
return;
|
||||
bcs->tx_skb = nextskb;
|
||||
|
||||
gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)",
|
||||
(unsigned long) bcs->tx_skb);
|
||||
}
|
||||
|
||||
/* send skb */
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: tx_skb", __func__);
|
||||
if (write_modem(cs) < 0)
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: write_modem failed", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* throw away all data queued for sending
|
||||
*/
|
||||
static void flush_send_queue(struct cardstate *cs)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct cmdbuf_t *cb;
|
||||
unsigned long flags;
|
||||
|
||||
/* command queue */
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
while ((cb = cs->cmdbuf) != NULL) {
|
||||
cs->cmdbuf = cb->next;
|
||||
if (cb->wake_tasklet)
|
||||
tasklet_schedule(cb->wake_tasklet);
|
||||
kfree(cb);
|
||||
}
|
||||
cs->cmdbuf = cs->lastcmdbuf = NULL;
|
||||
cs->cmdbytes = cs->curlen = 0;
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
/* data queue */
|
||||
if (cs->bcs->tx_skb)
|
||||
dev_kfree_skb_any(cs->bcs->tx_skb);
|
||||
while ((skb = skb_dequeue(&cs->bcs->squeue)) != NULL)
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
|
||||
/* Gigaset Driver Interface */
|
||||
/* ======================== */
|
||||
|
||||
/*
|
||||
* queue an AT command string for transmission to the Gigaset device
|
||||
* parameters:
|
||||
* cs controller state structure
|
||||
* buf buffer containing the string to send
|
||||
* len number of characters to send
|
||||
* wake_tasklet tasklet to run when transmission is complete, or NULL
|
||||
* return value:
|
||||
* number of bytes queued, or error code < 0
|
||||
*/
|
||||
static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
|
||||
DEBUG_TRANSCMD : DEBUG_LOCKCMD,
|
||||
"CMD Transmit", cb->len, cb->buf);
|
||||
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cb->prev = cs->lastcmdbuf;
|
||||
if (cs->lastcmdbuf)
|
||||
cs->lastcmdbuf->next = cb;
|
||||
else {
|
||||
cs->cmdbuf = cb;
|
||||
cs->curlen = cb->len;
|
||||
}
|
||||
cs->cmdbytes += cb->len;
|
||||
cs->lastcmdbuf = cb;
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->connected)
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return cb->len;
|
||||
}
|
||||
|
||||
/*
|
||||
* tty_driver.write_room interface routine
|
||||
* return number of characters the driver will accept to be written
|
||||
* parameter:
|
||||
* controller state structure
|
||||
* return value:
|
||||
* number of characters
|
||||
*/
|
||||
static int gigaset_write_room(struct cardstate *cs)
|
||||
{
|
||||
unsigned bytes;
|
||||
|
||||
bytes = cs->cmdbytes;
|
||||
return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tty_driver.chars_in_buffer interface routine
|
||||
* return number of characters waiting to be sent
|
||||
* parameter:
|
||||
* controller state structure
|
||||
* return value:
|
||||
* number of characters
|
||||
*/
|
||||
static int gigaset_chars_in_buffer(struct cardstate *cs)
|
||||
{
|
||||
return cs->cmdbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* implementation of ioctl(GIGASET_BRKCHARS)
|
||||
* parameter:
|
||||
* controller state structure
|
||||
* return value:
|
||||
* -EINVAL (unimplemented function)
|
||||
*/
|
||||
static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
|
||||
{
|
||||
/* not implemented */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open B channel
|
||||
* Called by "do_action" in ev-layer.c
|
||||
*/
|
||||
static int gigaset_init_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_up(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close B channel
|
||||
* Called by "do_action" in ev-layer.c
|
||||
*/
|
||||
static int gigaset_close_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_down(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up B channel structure
|
||||
* This is called by "gigaset_initcs" in common.c
|
||||
*/
|
||||
static int gigaset_initbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
bcs->hw.ser = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free B channel structure
|
||||
* Called by "gigaset_freebcs" in common.c
|
||||
*/
|
||||
static void gigaset_freebcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
}
|
||||
|
||||
/*
|
||||
* Reinitialize B channel structure
|
||||
* This is called by "bcs_reinit" in common.c
|
||||
*/
|
||||
static void gigaset_reinitbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
}
|
||||
|
||||
/*
|
||||
* Free hardware specific device data
|
||||
* This will be called by "gigaset_freecs" in common.c
|
||||
*/
|
||||
static void gigaset_freecshw(struct cardstate *cs)
|
||||
{
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
if (!cs->hw.ser)
|
||||
return;
|
||||
platform_device_unregister(&cs->hw.ser->dev);
|
||||
}
|
||||
|
||||
static void gigaset_device_release(struct device *dev)
|
||||
{
|
||||
kfree(container_of(dev, struct ser_cardstate, dev.dev));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up hardware specific device data
|
||||
* This is called by "gigaset_initcs" in common.c
|
||||
*/
|
||||
static int gigaset_initcshw(struct cardstate *cs)
|
||||
{
|
||||
int rc;
|
||||
struct ser_cardstate *scs;
|
||||
|
||||
scs = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL);
|
||||
if (!scs) {
|
||||
pr_err("out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
cs->hw.ser = scs;
|
||||
|
||||
cs->hw.ser->dev.name = GIGASET_MODULENAME;
|
||||
cs->hw.ser->dev.id = cs->minor_index;
|
||||
cs->hw.ser->dev.dev.release = gigaset_device_release;
|
||||
rc = platform_device_register(&cs->hw.ser->dev);
|
||||
if (rc != 0) {
|
||||
pr_err("error %d registering platform device\n", rc);
|
||||
kfree(cs->hw.ser);
|
||||
cs->hw.ser = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
tasklet_init(&cs->write_tasklet,
|
||||
gigaset_modem_fill, (unsigned long) cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* set modem control lines
|
||||
* Parameters:
|
||||
* card state structure
|
||||
* modem control line state ([TIOCM_DTR]|[TIOCM_RTS])
|
||||
* Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c
|
||||
* and by "if_lock" and "if_termios" in interface.c
|
||||
*/
|
||||
static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
|
||||
unsigned new_state)
|
||||
{
|
||||
struct tty_struct *tty = cs->hw.ser->tty;
|
||||
unsigned int set, clear;
|
||||
|
||||
WARN_ON(!tty || !tty->ops);
|
||||
/* tiocmset is an optional tty driver method */
|
||||
if (!tty->ops->tiocmset)
|
||||
return -EINVAL;
|
||||
set = new_state & ~old_state;
|
||||
clear = old_state & ~new_state;
|
||||
if (!set && !clear)
|
||||
return 0;
|
||||
gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear);
|
||||
return tty->ops->tiocmset(tty, set, clear);
|
||||
}
|
||||
|
||||
static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct gigaset_ops ops = {
|
||||
.write_cmd = gigaset_write_cmd,
|
||||
.write_room = gigaset_write_room,
|
||||
.chars_in_buffer = gigaset_chars_in_buffer,
|
||||
.brkchars = gigaset_brkchars,
|
||||
.init_bchannel = gigaset_init_bchannel,
|
||||
.close_bchannel = gigaset_close_bchannel,
|
||||
.initbcshw = gigaset_initbcshw,
|
||||
.freebcshw = gigaset_freebcshw,
|
||||
.reinitbcshw = gigaset_reinitbcshw,
|
||||
.initcshw = gigaset_initcshw,
|
||||
.freecshw = gigaset_freecshw,
|
||||
.set_modem_ctrl = gigaset_set_modem_ctrl,
|
||||
.baud_rate = gigaset_baud_rate,
|
||||
.set_line_ctrl = gigaset_set_line_ctrl,
|
||||
.send_skb = gigaset_m10x_send_skb, /* asyncdata.c */
|
||||
.handle_input = gigaset_m10x_input, /* asyncdata.c */
|
||||
};
|
||||
|
||||
|
||||
/* Line Discipline Interface */
|
||||
/* ========================= */
|
||||
|
||||
/* helper functions for cardstate refcounting */
|
||||
static struct cardstate *cs_get(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->disc_data;
|
||||
|
||||
if (!cs || !cs->hw.ser) {
|
||||
gig_dbg(DEBUG_ANY, "%s: no cardstate", __func__);
|
||||
return NULL;
|
||||
}
|
||||
atomic_inc(&cs->hw.ser->refcnt);
|
||||
return cs;
|
||||
}
|
||||
|
||||
static void cs_put(struct cardstate *cs)
|
||||
{
|
||||
if (atomic_dec_and_test(&cs->hw.ser->refcnt))
|
||||
complete(&cs->hw.ser->dead_cmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when the line discipline is pushed onto the tty.
|
||||
* Called in process context.
|
||||
*/
|
||||
static int
|
||||
gigaset_tty_open(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int rc;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101");
|
||||
|
||||
pr_info(DRIVER_DESC "\n");
|
||||
|
||||
if (!driver) {
|
||||
pr_err("%s: no driver structure\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* allocate memory for our device state and initialize it */
|
||||
cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
|
||||
if (!cs) {
|
||||
rc = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
cs->dev = &cs->hw.ser->dev.dev;
|
||||
cs->hw.ser->tty = tty;
|
||||
atomic_set(&cs->hw.ser->refcnt, 1);
|
||||
init_completion(&cs->hw.ser->dead_cmp);
|
||||
tty->disc_data = cs;
|
||||
|
||||
/* Set the amount of data we're willing to receive per call
|
||||
* from the hardware driver to half of the input buffer size
|
||||
* to leave some reserve.
|
||||
* Note: We don't do flow control towards the hardware driver.
|
||||
* If more data is received than will fit into the input buffer,
|
||||
* it will be dropped and an error will be logged. This should
|
||||
* never happen as the device is slow and the buffer size ample.
|
||||
*/
|
||||
tty->receive_room = RBUFSIZE/2;
|
||||
|
||||
/* OK.. Initialization of the datastructures and the HW is done.. Now
|
||||
* startup system and notify the LL that we are ready to run
|
||||
*/
|
||||
if (startmode == SM_LOCKED)
|
||||
cs->mstate = MS_LOCKED;
|
||||
rc = gigaset_start(cs);
|
||||
if (rc < 0) {
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Startup of HLL done");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
gig_dbg(DEBUG_INIT, "Startup of HLL failed");
|
||||
tty->disc_data = NULL;
|
||||
gigaset_freecs(cs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when the line discipline is removed.
|
||||
* Called from process context.
|
||||
*/
|
||||
static void
|
||||
gigaset_tty_close(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->disc_data;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Stopping HLL for Gigaset M101");
|
||||
|
||||
if (!cs) {
|
||||
gig_dbg(DEBUG_INIT, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prevent other callers from entering ldisc methods */
|
||||
tty->disc_data = NULL;
|
||||
|
||||
if (!cs->hw.ser)
|
||||
pr_err("%s: no hw cardstate\n", __func__);
|
||||
else {
|
||||
/* wait for running methods to finish */
|
||||
if (!atomic_dec_and_test(&cs->hw.ser->refcnt))
|
||||
wait_for_completion(&cs->hw.ser->dead_cmp);
|
||||
}
|
||||
|
||||
/* stop operations */
|
||||
gigaset_stop(cs);
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
flush_send_queue(cs);
|
||||
cs->dev = NULL;
|
||||
gigaset_freecs(cs);
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Shutdown of HLL done");
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when the tty line is hung up.
|
||||
* Wait for I/O to driver to complete and unregister ISDN device.
|
||||
* This is already done by the close routine, so just call that.
|
||||
* Called from process context.
|
||||
*/
|
||||
static int gigaset_tty_hangup(struct tty_struct *tty)
|
||||
{
|
||||
gigaset_tty_close(tty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ioctl on the tty.
|
||||
* Called in process context only.
|
||||
* May be re-entered by multiple ioctl calling threads.
|
||||
*/
|
||||
static int
|
||||
gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cardstate *cs = cs_get(tty);
|
||||
int rc, val;
|
||||
int __user *p = (int __user *)arg;
|
||||
|
||||
if (!cs)
|
||||
return -ENXIO;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case FIONREAD:
|
||||
/* unused, always return zero */
|
||||
val = 0;
|
||||
rc = put_user(val, p);
|
||||
break;
|
||||
|
||||
case TCFLSH:
|
||||
/* flush our buffers and the serial port's buffer */
|
||||
switch (arg) {
|
||||
case TCIFLUSH:
|
||||
/* no own input buffer to flush */
|
||||
break;
|
||||
case TCIOFLUSH:
|
||||
case TCOFLUSH:
|
||||
flush_send_queue(cs);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
/* pass through to underlying serial device */
|
||||
rc = n_tty_ioctl_helper(tty, file, cmd, arg);
|
||||
break;
|
||||
}
|
||||
cs_put(cs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when a block of data has been received.
|
||||
* Will not be re-entered while running but other ldisc functions
|
||||
* may be called in parallel.
|
||||
* Can be called from hard interrupt level as well as soft interrupt
|
||||
* level or mainline.
|
||||
* Parameters:
|
||||
* tty tty structure
|
||||
* buf buffer containing received characters
|
||||
* cflags buffer containing error flags for received characters (ignored)
|
||||
* count number of received characters
|
||||
*/
|
||||
static void
|
||||
gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
|
||||
char *cflags, int count)
|
||||
{
|
||||
struct cardstate *cs = cs_get(tty);
|
||||
unsigned tail, head, n;
|
||||
struct inbuf_t *inbuf;
|
||||
|
||||
if (!cs)
|
||||
return;
|
||||
inbuf = cs->inbuf;
|
||||
if (!inbuf) {
|
||||
dev_err(cs->dev, "%s: no inbuf\n", __func__);
|
||||
cs_put(cs);
|
||||
return;
|
||||
}
|
||||
|
||||
tail = inbuf->tail;
|
||||
head = inbuf->head;
|
||||
gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes",
|
||||
head, tail, count);
|
||||
|
||||
if (head <= tail) {
|
||||
/* possible buffer wraparound */
|
||||
n = min_t(unsigned, count, RBUFSIZE - tail);
|
||||
memcpy(inbuf->data + tail, buf, n);
|
||||
tail = (tail + n) % RBUFSIZE;
|
||||
buf += n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
/* tail < head and some data left */
|
||||
n = head - tail - 1;
|
||||
if (count > n) {
|
||||
dev_err(cs->dev,
|
||||
"inbuf overflow, discarding %d bytes\n",
|
||||
count - n);
|
||||
count = n;
|
||||
}
|
||||
memcpy(inbuf->data + tail, buf, count);
|
||||
tail += count;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
|
||||
inbuf->tail = tail;
|
||||
|
||||
/* Everything was received .. Push data into handler */
|
||||
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
|
||||
gigaset_schedule_event(cs);
|
||||
cs_put(cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when there's room for more data to send.
|
||||
*/
|
||||
static void
|
||||
gigaset_tty_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = cs_get(tty);
|
||||
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (!cs)
|
||||
return;
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
cs_put(cs);
|
||||
}
|
||||
|
||||
static struct tty_ldisc_ops gigaset_ldisc = {
|
||||
.owner = THIS_MODULE,
|
||||
.magic = TTY_LDISC_MAGIC,
|
||||
.name = "ser_gigaset",
|
||||
.open = gigaset_tty_open,
|
||||
.close = gigaset_tty_close,
|
||||
.hangup = gigaset_tty_hangup,
|
||||
.ioctl = gigaset_tty_ioctl,
|
||||
.receive_buf = gigaset_tty_receive,
|
||||
.write_wakeup = gigaset_tty_wakeup,
|
||||
};
|
||||
|
||||
|
||||
/* Initialization / Shutdown */
|
||||
/* ========================= */
|
||||
|
||||
static int __init ser_gigaset_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "%s", __func__);
|
||||
rc = platform_driver_register(&device_driver);
|
||||
if (rc != 0) {
|
||||
pr_err("error %d registering platform driver\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* allocate memory for our driver state and initialize it */
|
||||
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
|
||||
GIGASET_MODULENAME, GIGASET_DEVNAME,
|
||||
&ops, THIS_MODULE);
|
||||
if (!driver) {
|
||||
rc = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
|
||||
if (rc != 0) {
|
||||
pr_err("error %d registering line discipline\n", rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (driver) {
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
}
|
||||
platform_driver_unregister(&device_driver);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit ser_gigaset_exit(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "%s", __func__);
|
||||
|
||||
if (driver) {
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
}
|
||||
|
||||
rc = tty_unregister_ldisc(N_GIGASET_M101);
|
||||
if (rc != 0)
|
||||
pr_err("error %d unregistering line discipline\n", rc);
|
||||
|
||||
platform_driver_unregister(&device_driver);
|
||||
}
|
||||
|
||||
module_init(ser_gigaset_init);
|
||||
module_exit(ser_gigaset_exit);
|
@ -1,946 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* USB driver for Gigaset 307x directly or using M105 Data.
|
||||
*
|
||||
* Copyright (c) 2001 by Stefan Eilers
|
||||
* and Hansjoerg Lipp <hjlipp@web.de>.
|
||||
*
|
||||
* This driver was derived from the USB skeleton driver by
|
||||
* Greg Kroah-Hartman <greg@kroah.com>
|
||||
*
|
||||
* =====================================================================
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/usb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
/* Version Information */
|
||||
#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
|
||||
#define DRIVER_DESC "USB Driver for Gigaset 307x using M105"
|
||||
|
||||
/* Module parameters */
|
||||
|
||||
static int startmode = SM_ISDN;
|
||||
static int cidmode = 1;
|
||||
|
||||
module_param(startmode, int, S_IRUGO);
|
||||
module_param(cidmode, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
|
||||
MODULE_PARM_DESC(cidmode, "Call-ID mode");
|
||||
|
||||
#define GIGASET_MINORS 1
|
||||
#define GIGASET_MINOR 8
|
||||
#define GIGASET_MODULENAME "usb_gigaset"
|
||||
#define GIGASET_DEVNAME "ttyGU"
|
||||
|
||||
/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
|
||||
#define IF_WRITEBUF 264
|
||||
|
||||
/* Values for the Gigaset M105 Data */
|
||||
#define USB_M105_VENDOR_ID 0x0681
|
||||
#define USB_M105_PRODUCT_ID 0x0009
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
static const struct usb_device_id gigaset_table[] = {
|
||||
{ USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, gigaset_table);
|
||||
|
||||
/*
|
||||
* Control requests (empty fields: 00)
|
||||
*
|
||||
* RT|RQ|VALUE|INDEX|LEN |DATA
|
||||
* In:
|
||||
* C1 08 01
|
||||
* Get flags (1 byte). Bits: 0=dtr,1=rts,3-7:?
|
||||
* C1 0F ll ll
|
||||
* Get device information/status (llll: 0x200 and 0x40 seen).
|
||||
* Real size: I only saw MIN(llll,0x64).
|
||||
* Contents: seems to be always the same...
|
||||
* offset 0x00: Length of this structure (0x64) (len: 1,2,3 bytes)
|
||||
* offset 0x3c: String (16 bit chars): "MCCI USB Serial V2.0"
|
||||
* rest: ?
|
||||
* Out:
|
||||
* 41 11
|
||||
* Initialize/reset device ?
|
||||
* 41 00 xx 00
|
||||
* ? (xx=00 or 01; 01 on start, 00 on close)
|
||||
* 41 07 vv mm
|
||||
* Set/clear flags vv=value, mm=mask (see RQ 08)
|
||||
* 41 12 xx
|
||||
* Used before the following configuration requests are issued
|
||||
* (with xx=0x0f). I've seen other values<0xf, though.
|
||||
* 41 01 xx xx
|
||||
* Set baud rate. xxxx=ceil(0x384000/rate)=trunc(0x383fff/rate)+1.
|
||||
* 41 03 ps bb
|
||||
* Set byte size and parity. p: 0x20=even,0x10=odd,0x00=no parity
|
||||
* [ 0x30: m, 0x40: s ]
|
||||
* [s: 0: 1 stop bit; 1: 1.5; 2: 2]
|
||||
* bb: bits/byte (seen 7 and 8)
|
||||
* 41 13 -- -- -- -- 10 00 ww 00 00 00 xx 00 00 00 yy 00 00 00 zz 00 00 00
|
||||
* ??
|
||||
* Initialization: 01, 40, 00, 00
|
||||
* Open device: 00 40, 00, 00
|
||||
* yy and zz seem to be equal, either 0x00 or 0x0a
|
||||
* (ww,xx) pairs seen: (00,00), (00,40), (01,40), (09,80), (19,80)
|
||||
* 41 19 -- -- -- -- 06 00 00 00 00 xx 11 13
|
||||
* Used after every "configuration sequence" (RQ 12, RQs 01/03/13).
|
||||
* xx is usually 0x00 but was 0x7e before starting data transfer
|
||||
* in unimodem mode. So, this might be an array of characters that
|
||||
* need special treatment ("commit all bufferd data"?), 11=^Q, 13=^S.
|
||||
*
|
||||
* Unimodem mode: use "modprobe ppp_async flag_time=0" as the device _needs_ two
|
||||
* flags per packet.
|
||||
*/
|
||||
|
||||
/* functions called if a device of this driver is connected/disconnected */
|
||||
static int gigaset_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id);
|
||||
static void gigaset_disconnect(struct usb_interface *interface);
|
||||
|
||||
/* functions called before/after suspend */
|
||||
static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
|
||||
static int gigaset_resume(struct usb_interface *intf);
|
||||
static int gigaset_pre_reset(struct usb_interface *intf);
|
||||
|
||||
static struct gigaset_driver *driver;
|
||||
|
||||
/* usb specific object needed to register this driver with the usb subsystem */
|
||||
static struct usb_driver gigaset_usb_driver = {
|
||||
.name = GIGASET_MODULENAME,
|
||||
.probe = gigaset_probe,
|
||||
.disconnect = gigaset_disconnect,
|
||||
.id_table = gigaset_table,
|
||||
.suspend = gigaset_suspend,
|
||||
.resume = gigaset_resume,
|
||||
.reset_resume = gigaset_resume,
|
||||
.pre_reset = gigaset_pre_reset,
|
||||
.post_reset = gigaset_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
struct usb_cardstate {
|
||||
struct usb_device *udev; /* usb device pointer */
|
||||
struct usb_interface *interface; /* interface for this device */
|
||||
int busy; /* bulk output in progress */
|
||||
|
||||
/* Output buffer */
|
||||
unsigned char *bulk_out_buffer;
|
||||
int bulk_out_size;
|
||||
int bulk_out_epnum;
|
||||
struct urb *bulk_out_urb;
|
||||
|
||||
/* Input buffer */
|
||||
unsigned char *rcvbuf;
|
||||
int rcvbuf_size;
|
||||
struct urb *read_urb;
|
||||
|
||||
char bchars[6]; /* for request 0x19 */
|
||||
};
|
||||
|
||||
static inline unsigned tiocm_to_gigaset(unsigned state)
|
||||
{
|
||||
return ((state & TIOCM_DTR) ? 1 : 0) | ((state & TIOCM_RTS) ? 2 : 0);
|
||||
}
|
||||
|
||||
static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
|
||||
unsigned new_state)
|
||||
{
|
||||
struct usb_device *udev = cs->hw.usb->udev;
|
||||
unsigned mask, val;
|
||||
int r;
|
||||
|
||||
mask = tiocm_to_gigaset(old_state ^ new_state);
|
||||
val = tiocm_to_gigaset(new_state);
|
||||
|
||||
gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask);
|
||||
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41,
|
||||
(val & 0xff) | ((mask & 0xff) << 8), 0,
|
||||
NULL, 0, 2000 /* timeout? */);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set M105 configuration value
|
||||
* using undocumented device commands reverse engineered from USB traces
|
||||
* of the Siemens Windows driver
|
||||
*/
|
||||
static int set_value(struct cardstate *cs, u8 req, u16 val)
|
||||
{
|
||||
struct usb_device *udev = cs->hw.usb->udev;
|
||||
int r, r2;
|
||||
|
||||
gig_dbg(DEBUG_USBREQ, "request %02x (%04x)",
|
||||
(unsigned)req, (unsigned)val);
|
||||
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x12, 0x41,
|
||||
0xf /*?*/, 0, NULL, 0, 2000 /*?*/);
|
||||
/* no idea what this does */
|
||||
if (r < 0) {
|
||||
dev_err(&udev->dev, "error %d on request 0x12\n", -r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), req, 0x41,
|
||||
val, 0, NULL, 0, 2000 /*?*/);
|
||||
if (r < 0)
|
||||
dev_err(&udev->dev, "error %d on request 0x%02x\n",
|
||||
-r, (unsigned)req);
|
||||
|
||||
r2 = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
|
||||
0, 0, cs->hw.usb->bchars, 6, 2000 /*?*/);
|
||||
if (r2 < 0)
|
||||
dev_err(&udev->dev, "error %d on request 0x19\n", -r2);
|
||||
|
||||
return r < 0 ? r : (r2 < 0 ? r2 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* set the baud rate on the internal serial adapter
|
||||
* using the undocumented parameter setting command
|
||||
*/
|
||||
static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
u16 val;
|
||||
u32 rate;
|
||||
|
||||
cflag &= CBAUD;
|
||||
|
||||
switch (cflag) {
|
||||
case B300: rate = 300; break;
|
||||
case B600: rate = 600; break;
|
||||
case B1200: rate = 1200; break;
|
||||
case B2400: rate = 2400; break;
|
||||
case B4800: rate = 4800; break;
|
||||
case B9600: rate = 9600; break;
|
||||
case B19200: rate = 19200; break;
|
||||
case B38400: rate = 38400; break;
|
||||
case B57600: rate = 57600; break;
|
||||
case B115200: rate = 115200; break;
|
||||
default:
|
||||
rate = 9600;
|
||||
dev_err(cs->dev, "unsupported baudrate request 0x%x,"
|
||||
" using default of B9600\n", cflag);
|
||||
}
|
||||
|
||||
val = 0x383fff / rate + 1;
|
||||
|
||||
return set_value(cs, 1, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* set the line format on the internal serial adapter
|
||||
* using the undocumented parameter setting command
|
||||
*/
|
||||
static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
u16 val = 0;
|
||||
|
||||
/* set the parity */
|
||||
if (cflag & PARENB)
|
||||
val |= (cflag & PARODD) ? 0x10 : 0x20;
|
||||
|
||||
/* set the number of data bits */
|
||||
switch (cflag & CSIZE) {
|
||||
case CS5:
|
||||
val |= 5 << 8; break;
|
||||
case CS6:
|
||||
val |= 6 << 8; break;
|
||||
case CS7:
|
||||
val |= 7 << 8; break;
|
||||
case CS8:
|
||||
val |= 8 << 8; break;
|
||||
default:
|
||||
dev_err(cs->dev, "CSIZE was not CS5-CS8, using default of 8\n");
|
||||
val |= 8 << 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the number of stop bits */
|
||||
if (cflag & CSTOPB) {
|
||||
if ((cflag & CSIZE) == CS5)
|
||||
val |= 1; /* 1.5 stop bits */
|
||||
else
|
||||
val |= 2; /* 2 stop bits */
|
||||
}
|
||||
|
||||
return set_value(cs, 3, val);
|
||||
}
|
||||
|
||||
|
||||
/*============================================================================*/
|
||||
static int gigaset_init_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_up(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gigaset_close_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_down(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_modem(struct cardstate *cs);
|
||||
static int send_cb(struct cardstate *cs);
|
||||
|
||||
|
||||
/* Write tasklet handler: Continue sending current skb, or send command, or
|
||||
* start sending an skb from the send queue.
|
||||
*/
|
||||
static void gigaset_modem_fill(unsigned long data)
|
||||
{
|
||||
struct cardstate *cs = (struct cardstate *) data;
|
||||
struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
|
||||
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill");
|
||||
|
||||
if (cs->hw.usb->busy) {
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill: busy");
|
||||
return;
|
||||
}
|
||||
|
||||
again:
|
||||
if (!bcs->tx_skb) { /* no skb is being sent */
|
||||
if (cs->cmdbuf) { /* commands to send? */
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill: cb");
|
||||
if (send_cb(cs) < 0) {
|
||||
gig_dbg(DEBUG_OUTPUT,
|
||||
"modem_fill: send_cb failed");
|
||||
goto again; /* no callback will be called! */
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* skbs to send? */
|
||||
bcs->tx_skb = skb_dequeue(&bcs->squeue);
|
||||
if (!bcs->tx_skb)
|
||||
return;
|
||||
|
||||
gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)!",
|
||||
(unsigned long) bcs->tx_skb);
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill: tx_skb");
|
||||
if (write_modem(cs) < 0) {
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill: write_modem failed");
|
||||
goto again; /* no callback will be called! */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt Input URB completion routine
|
||||
*/
|
||||
static void gigaset_read_int_callback(struct urb *urb)
|
||||
{
|
||||
struct cardstate *cs = urb->context;
|
||||
struct inbuf_t *inbuf = cs->inbuf;
|
||||
int status = urb->status;
|
||||
int r;
|
||||
unsigned numbytes;
|
||||
unsigned char *src;
|
||||
unsigned long flags;
|
||||
|
||||
if (!status) {
|
||||
numbytes = urb->actual_length;
|
||||
|
||||
if (numbytes) {
|
||||
src = cs->hw.usb->rcvbuf;
|
||||
if (unlikely(*src))
|
||||
dev_warn(cs->dev,
|
||||
"%s: There was no leading 0, but 0x%02x!\n",
|
||||
__func__, (unsigned) *src);
|
||||
++src; /* skip leading 0x00 */
|
||||
--numbytes;
|
||||
if (gigaset_fill_inbuf(inbuf, src, numbytes)) {
|
||||
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
|
||||
gigaset_schedule_event(inbuf->cs);
|
||||
}
|
||||
} else
|
||||
gig_dbg(DEBUG_INTR, "Received zero block length");
|
||||
} else {
|
||||
/* The urb might have been killed. */
|
||||
gig_dbg(DEBUG_ANY, "%s - nonzero status received: %d",
|
||||
__func__, status);
|
||||
if (status == -ENOENT || status == -ESHUTDOWN)
|
||||
/* killed or endpoint shutdown: don't resubmit */
|
||||
return;
|
||||
}
|
||||
|
||||
/* resubmit URB */
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (!cs->connected) {
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
pr_err("%s: disconnected\n", __func__);
|
||||
return;
|
||||
}
|
||||
r = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
if (r)
|
||||
dev_err(cs->dev, "error %d resubmitting URB\n", -r);
|
||||
}
|
||||
|
||||
|
||||
/* This callback routine is called when data was transmitted to the device. */
|
||||
static void gigaset_write_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct cardstate *cs = urb->context;
|
||||
int status = urb->status;
|
||||
unsigned long flags;
|
||||
|
||||
switch (status) {
|
||||
case 0: /* normal completion */
|
||||
break;
|
||||
case -ENOENT: /* killed */
|
||||
gig_dbg(DEBUG_ANY, "%s: killed", __func__);
|
||||
cs->hw.usb->busy = 0;
|
||||
return;
|
||||
default:
|
||||
dev_err(cs->dev, "bulk transfer failed (status %d)\n",
|
||||
-status);
|
||||
/* That's all we can do. Communication problems
|
||||
are handled by timeouts or network protocols. */
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (!cs->connected) {
|
||||
pr_err("%s: disconnected\n", __func__);
|
||||
} else {
|
||||
cs->hw.usb->busy = 0;
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
}
|
||||
|
||||
static int send_cb(struct cardstate *cs)
|
||||
{
|
||||
struct cmdbuf_t *cb = cs->cmdbuf;
|
||||
unsigned long flags;
|
||||
int count;
|
||||
int status = -ENOENT;
|
||||
struct usb_cardstate *ucs = cs->hw.usb;
|
||||
|
||||
do {
|
||||
if (!cb->len) {
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cs->cmdbytes -= cs->curlen;
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: sent %u bytes, %u left",
|
||||
cs->curlen, cs->cmdbytes);
|
||||
cs->cmdbuf = cb->next;
|
||||
if (cs->cmdbuf) {
|
||||
cs->cmdbuf->prev = NULL;
|
||||
cs->curlen = cs->cmdbuf->len;
|
||||
} else {
|
||||
cs->lastcmdbuf = NULL;
|
||||
cs->curlen = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
if (cb->wake_tasklet)
|
||||
tasklet_schedule(cb->wake_tasklet);
|
||||
kfree(cb);
|
||||
|
||||
cb = cs->cmdbuf;
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
count = min(cb->len, ucs->bulk_out_size);
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: send %d bytes", count);
|
||||
|
||||
usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
|
||||
usb_sndbulkpipe(ucs->udev,
|
||||
ucs->bulk_out_epnum),
|
||||
cb->buf + cb->offset, count,
|
||||
gigaset_write_bulk_callback, cs);
|
||||
|
||||
cb->offset += count;
|
||||
cb->len -= count;
|
||||
ucs->busy = 1;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
status = cs->connected ?
|
||||
usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) :
|
||||
-ENODEV;
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
|
||||
if (status) {
|
||||
ucs->busy = 0;
|
||||
dev_err(cs->dev,
|
||||
"could not submit urb (error %d)\n",
|
||||
-status);
|
||||
cb->len = 0; /* skip urb => remove cb+wakeup
|
||||
in next loop cycle */
|
||||
}
|
||||
}
|
||||
} while (cb && status); /* next command on error */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Send command to device. */
|
||||
static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int len;
|
||||
|
||||
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
|
||||
DEBUG_TRANSCMD : DEBUG_LOCKCMD,
|
||||
"CMD Transmit", cb->len, cb->buf);
|
||||
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cb->prev = cs->lastcmdbuf;
|
||||
if (cs->lastcmdbuf)
|
||||
cs->lastcmdbuf->next = cb;
|
||||
else {
|
||||
cs->cmdbuf = cb;
|
||||
cs->curlen = cb->len;
|
||||
}
|
||||
cs->cmdbytes += cb->len;
|
||||
cs->lastcmdbuf = cb;
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
len = cb->len;
|
||||
if (cs->connected)
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gigaset_write_room(struct cardstate *cs)
|
||||
{
|
||||
unsigned bytes;
|
||||
|
||||
bytes = cs->cmdbytes;
|
||||
return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
|
||||
}
|
||||
|
||||
static int gigaset_chars_in_buffer(struct cardstate *cs)
|
||||
{
|
||||
return cs->cmdbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the break characters on the internal serial adapter
|
||||
* using undocumented device commands reverse engineered from USB traces
|
||||
* of the Siemens Windows driver
|
||||
*/
|
||||
static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
|
||||
{
|
||||
struct usb_device *udev = cs->hw.usb->udev;
|
||||
|
||||
gigaset_dbg_buffer(DEBUG_USBREQ, "brkchars", 6, buf);
|
||||
memcpy(cs->hw.usb->bchars, buf, 6);
|
||||
return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
|
||||
0, 0, &buf, 6, 2000);
|
||||
}
|
||||
|
||||
static void gigaset_freebcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
}
|
||||
|
||||
/* Initialize the b-channel structure */
|
||||
static int gigaset_initbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
bcs->hw.usb = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gigaset_reinitbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
}
|
||||
|
||||
static void gigaset_freecshw(struct cardstate *cs)
|
||||
{
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
kfree(cs->hw.usb);
|
||||
}
|
||||
|
||||
static int gigaset_initcshw(struct cardstate *cs)
|
||||
{
|
||||
struct usb_cardstate *ucs;
|
||||
|
||||
cs->hw.usb = ucs =
|
||||
kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
|
||||
if (!ucs) {
|
||||
pr_err("out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ucs->bchars[0] = 0;
|
||||
ucs->bchars[1] = 0;
|
||||
ucs->bchars[2] = 0;
|
||||
ucs->bchars[3] = 0;
|
||||
ucs->bchars[4] = 0x11;
|
||||
ucs->bchars[5] = 0x13;
|
||||
ucs->bulk_out_buffer = NULL;
|
||||
ucs->bulk_out_urb = NULL;
|
||||
ucs->read_urb = NULL;
|
||||
tasklet_init(&cs->write_tasklet,
|
||||
gigaset_modem_fill, (unsigned long) cs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send data from current skb to the device. */
|
||||
static int write_modem(struct cardstate *cs)
|
||||
{
|
||||
int ret = 0;
|
||||
int count;
|
||||
struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
|
||||
struct usb_cardstate *ucs = cs->hw.usb;
|
||||
unsigned long flags;
|
||||
|
||||
gig_dbg(DEBUG_OUTPUT, "len: %d...", bcs->tx_skb->len);
|
||||
|
||||
if (!bcs->tx_skb->len) {
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Copy data to bulk out buffer and transmit data */
|
||||
count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
|
||||
skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
|
||||
skb_pull(bcs->tx_skb, count);
|
||||
ucs->busy = 1;
|
||||
gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count);
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->connected) {
|
||||
usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
|
||||
usb_sndbulkpipe(ucs->udev,
|
||||
ucs->bulk_out_epnum),
|
||||
ucs->bulk_out_buffer, count,
|
||||
gigaset_write_bulk_callback, cs);
|
||||
ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC);
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
|
||||
if (ret) {
|
||||
dev_err(cs->dev, "could not submit urb (error %d)\n", -ret);
|
||||
ucs->busy = 0;
|
||||
}
|
||||
|
||||
if (!bcs->tx_skb->len) {
|
||||
/* skb sent completely */
|
||||
gigaset_skb_sent(bcs, bcs->tx_skb);
|
||||
|
||||
gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
|
||||
(unsigned long) bcs->tx_skb);
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gigaset_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_host_interface *hostif = interface->cur_altsetting;
|
||||
struct cardstate *cs = NULL;
|
||||
struct usb_cardstate *ucs = NULL;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
int buffer_size;
|
||||
|
||||
gig_dbg(DEBUG_ANY, "%s: Check if device matches ...", __func__);
|
||||
|
||||
/* See if the device offered us matches what we can accept */
|
||||
if ((le16_to_cpu(udev->descriptor.idVendor) != USB_M105_VENDOR_ID) ||
|
||||
(le16_to_cpu(udev->descriptor.idProduct) != USB_M105_PRODUCT_ID)) {
|
||||
gig_dbg(DEBUG_ANY, "device ID (0x%x, 0x%x) not for me - skip",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
return -ENODEV;
|
||||
}
|
||||
if (hostif->desc.bInterfaceNumber != 0) {
|
||||
gig_dbg(DEBUG_ANY, "interface %d not for me - skip",
|
||||
hostif->desc.bInterfaceNumber);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (hostif->desc.bAlternateSetting != 0) {
|
||||
dev_notice(&udev->dev, "unsupported altsetting %d - skip",
|
||||
hostif->desc.bAlternateSetting);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (hostif->desc.bInterfaceClass != 255) {
|
||||
dev_notice(&udev->dev, "unsupported interface class %d - skip",
|
||||
hostif->desc.bInterfaceClass);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);
|
||||
|
||||
/* allocate memory for our device state and initialize it */
|
||||
cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
|
||||
if (!cs)
|
||||
return -ENODEV;
|
||||
ucs = cs->hw.usb;
|
||||
|
||||
/* save off device structure ptrs for later use */
|
||||
usb_get_dev(udev);
|
||||
ucs->udev = udev;
|
||||
ucs->interface = interface;
|
||||
cs->dev = &interface->dev;
|
||||
|
||||
/* save address of controller structure */
|
||||
usb_set_intfdata(interface, cs);
|
||||
|
||||
endpoint = &hostif->endpoint[0].desc;
|
||||
|
||||
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
ucs->bulk_out_size = buffer_size;
|
||||
ucs->bulk_out_epnum = usb_endpoint_num(endpoint);
|
||||
ucs->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!ucs->bulk_out_buffer) {
|
||||
dev_err(cs->dev, "Couldn't allocate bulk_out_buffer\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ucs->bulk_out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ucs->bulk_out_urb) {
|
||||
dev_err(cs->dev, "Couldn't allocate bulk_out_urb\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
endpoint = &hostif->endpoint[1].desc;
|
||||
|
||||
ucs->busy = 0;
|
||||
|
||||
ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ucs->read_urb) {
|
||||
dev_err(cs->dev, "No free urbs available\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
ucs->rcvbuf_size = buffer_size;
|
||||
ucs->rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!ucs->rcvbuf) {
|
||||
dev_err(cs->dev, "Couldn't allocate rcvbuf\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
/* Fill the interrupt urb and send it to the core */
|
||||
usb_fill_int_urb(ucs->read_urb, udev,
|
||||
usb_rcvintpipe(udev, usb_endpoint_num(endpoint)),
|
||||
ucs->rcvbuf, buffer_size,
|
||||
gigaset_read_int_callback,
|
||||
cs, endpoint->bInterval);
|
||||
|
||||
retval = usb_submit_urb(ucs->read_urb, GFP_KERNEL);
|
||||
if (retval) {
|
||||
dev_err(cs->dev, "Could not submit URB (error %d)\n", -retval);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* tell common part that the device is ready */
|
||||
if (startmode == SM_LOCKED)
|
||||
cs->mstate = MS_LOCKED;
|
||||
|
||||
retval = gigaset_start(cs);
|
||||
if (retval < 0) {
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
usb_kill_urb(ucs->read_urb);
|
||||
kfree(ucs->bulk_out_buffer);
|
||||
usb_free_urb(ucs->bulk_out_urb);
|
||||
kfree(ucs->rcvbuf);
|
||||
usb_free_urb(ucs->read_urb);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
ucs->read_urb = ucs->bulk_out_urb = NULL;
|
||||
ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
|
||||
usb_put_dev(ucs->udev);
|
||||
ucs->udev = NULL;
|
||||
ucs->interface = NULL;
|
||||
gigaset_freecs(cs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void gigaset_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
struct usb_cardstate *ucs;
|
||||
|
||||
cs = usb_get_intfdata(interface);
|
||||
ucs = cs->hw.usb;
|
||||
|
||||
dev_info(cs->dev, "disconnecting Gigaset USB adapter\n");
|
||||
|
||||
usb_kill_urb(ucs->read_urb);
|
||||
|
||||
gigaset_stop(cs);
|
||||
|
||||
usb_set_intfdata(interface, NULL);
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
|
||||
usb_kill_urb(ucs->bulk_out_urb);
|
||||
|
||||
kfree(ucs->bulk_out_buffer);
|
||||
usb_free_urb(ucs->bulk_out_urb);
|
||||
kfree(ucs->rcvbuf);
|
||||
usb_free_urb(ucs->read_urb);
|
||||
ucs->read_urb = ucs->bulk_out_urb = NULL;
|
||||
ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
|
||||
|
||||
usb_put_dev(ucs->udev);
|
||||
ucs->interface = NULL;
|
||||
ucs->udev = NULL;
|
||||
cs->dev = NULL;
|
||||
gigaset_freecs(cs);
|
||||
}
|
||||
|
||||
/* gigaset_suspend
|
||||
* This function is called before the USB connection is suspended or reset.
|
||||
*/
|
||||
static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct cardstate *cs = usb_get_intfdata(intf);
|
||||
|
||||
/* stop activity */
|
||||
cs->connected = 0; /* prevent rescheduling */
|
||||
usb_kill_urb(cs->hw.usb->read_urb);
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
usb_kill_urb(cs->hw.usb->bulk_out_urb);
|
||||
|
||||
gig_dbg(DEBUG_SUSPEND, "suspend complete");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* gigaset_resume
|
||||
* This function is called after the USB connection has been resumed or reset.
|
||||
*/
|
||||
static int gigaset_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct cardstate *cs = usb_get_intfdata(intf);
|
||||
int rc;
|
||||
|
||||
/* resubmit interrupt URB */
|
||||
cs->connected = 1;
|
||||
rc = usb_submit_urb(cs->hw.usb->read_urb, GFP_KERNEL);
|
||||
if (rc) {
|
||||
dev_err(cs->dev, "Could not submit read URB (error %d)\n", -rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_SUSPEND, "resume complete");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* gigaset_pre_reset
|
||||
* This function is called before the USB connection is reset.
|
||||
*/
|
||||
static int gigaset_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
/* same as suspend */
|
||||
return gigaset_suspend(intf, PMSG_ON);
|
||||
}
|
||||
|
||||
static const struct gigaset_ops ops = {
|
||||
.write_cmd = gigaset_write_cmd,
|
||||
.write_room = gigaset_write_room,
|
||||
.chars_in_buffer = gigaset_chars_in_buffer,
|
||||
.brkchars = gigaset_brkchars,
|
||||
.init_bchannel = gigaset_init_bchannel,
|
||||
.close_bchannel = gigaset_close_bchannel,
|
||||
.initbcshw = gigaset_initbcshw,
|
||||
.freebcshw = gigaset_freebcshw,
|
||||
.reinitbcshw = gigaset_reinitbcshw,
|
||||
.initcshw = gigaset_initcshw,
|
||||
.freecshw = gigaset_freecshw,
|
||||
.set_modem_ctrl = gigaset_set_modem_ctrl,
|
||||
.baud_rate = gigaset_baud_rate,
|
||||
.set_line_ctrl = gigaset_set_line_ctrl,
|
||||
.send_skb = gigaset_m10x_send_skb,
|
||||
.handle_input = gigaset_m10x_input,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is called while kernel-module is loaded
|
||||
*/
|
||||
static int __init usb_gigaset_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* allocate memory for our driver state and initialize it */
|
||||
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
|
||||
GIGASET_MODULENAME, GIGASET_DEVNAME,
|
||||
&ops, THIS_MODULE);
|
||||
if (driver == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* register this driver with the USB subsystem */
|
||||
result = usb_register(&gigaset_usb_driver);
|
||||
if (result < 0) {
|
||||
pr_err("error %d registering USB driver\n", -result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
pr_info(DRIVER_DESC "\n");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (driver)
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called while unloading the kernel-module
|
||||
*/
|
||||
static void __exit usb_gigaset_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
gigaset_blockdriver(driver); /* => probe will fail
|
||||
* => no gigaset_start any more
|
||||
*/
|
||||
|
||||
/* stop all connected devices */
|
||||
for (i = 0; i < driver->minors; i++)
|
||||
gigaset_shutdown(driver->cs + i);
|
||||
|
||||
/* from now on, no isdn callback should be possible */
|
||||
|
||||
/* deregister this driver with the USB subsystem */
|
||||
usb_deregister(&gigaset_usb_driver);
|
||||
/* this will call the disconnect-callback */
|
||||
/* from now on, no disconnect/probe callback should be running */
|
||||
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
}
|
||||
|
||||
|
||||
module_init(usb_gigaset_init);
|
||||
module_exit(usb_gigaset_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
@ -1,15 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config HYSDN
|
||||
tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
|
||||
depends on m && PROC_FS && PCI
|
||||
help
|
||||
Say Y here if you have one of Hypercope's active PCI ISDN cards
|
||||
Champ, Ergo and Metro. You will then get a module called hysdn.
|
||||
Please read the file <file:Documentation/isdn/hysdn.rst> for more
|
||||
information.
|
||||
|
||||
config HYSDN_CAPI
|
||||
bool "HYSDN CAPI 2.0 support"
|
||||
depends on HYSDN && ISDN_CAPI
|
||||
help
|
||||
Say Y here if you like to use Hypercope's CAPI 2.0 interface.
|
@ -1,12 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# Makefile for the hysdn ISDN device driver
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_HYSDN) += hysdn.o
|
||||
|
||||
# Multipart objects.
|
||||
|
||||
hysdn-y := hysdn_procconf.o hysdn_proclog.o boardergo.o \
|
||||
hysdn_boot.o hysdn_sched.o hysdn_net.o hysdn_init.o
|
||||
hysdn-$(CONFIG_HYSDN_CAPI) += hycapi.o
|
@ -1,445 +0,0 @@
|
||||
/* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, specific routines for ergo type boards.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
|
||||
* DPRAM interface and layout with only minor differences all related
|
||||
* stuff is done here, not in separate modules.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/signal.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
#include "boardergo.h"
|
||||
|
||||
#define byteout(addr, val) outb(val, addr)
|
||||
#define bytein(addr) inb(addr)
|
||||
|
||||
/***************************************************/
|
||||
/* The cards interrupt handler. Called from system */
|
||||
/***************************************************/
|
||||
static irqreturn_t
|
||||
ergo_interrupt(int intno, void *dev_id)
|
||||
{
|
||||
hysdn_card *card = dev_id; /* parameter from irq */
|
||||
tErgDpram *dpr;
|
||||
unsigned long flags;
|
||||
unsigned char volatile b;
|
||||
|
||||
if (!card)
|
||||
return IRQ_NONE; /* error -> spurious interrupt */
|
||||
if (!card->irq_enabled)
|
||||
return IRQ_NONE; /* other device interrupting or irq switched off */
|
||||
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags); /* no further irqs allowed */
|
||||
|
||||
if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags); /* restore old state */
|
||||
return IRQ_NONE; /* no interrupt requested by E1 */
|
||||
}
|
||||
/* clear any pending ints on the board */
|
||||
dpr = card->dpram;
|
||||
b = dpr->ToPcInt; /* clear for ergo */
|
||||
b |= dpr->ToPcIntMetro; /* same for metro */
|
||||
b |= dpr->ToHyInt; /* and for champ */
|
||||
|
||||
/* start kernel task immediately after leaving all interrupts */
|
||||
if (!card->hw_lock)
|
||||
schedule_work(&card->irq_queue);
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
} /* ergo_interrupt */
|
||||
|
||||
/******************************************************************************/
|
||||
/* ergo_irq_bh will be called as part of the kernel clearing its shared work */
|
||||
/* queue sometime after a call to schedule_work has been made passing our */
|
||||
/* work_struct. This task is the only one handling data transfer from or to */
|
||||
/* the card after booting. The task may be queued from everywhere */
|
||||
/* (interrupts included). */
|
||||
/******************************************************************************/
|
||||
static void
|
||||
ergo_irq_bh(struct work_struct *ugli_api)
|
||||
{
|
||||
hysdn_card *card = container_of(ugli_api, hysdn_card, irq_queue);
|
||||
tErgDpram *dpr;
|
||||
int again;
|
||||
unsigned long flags;
|
||||
|
||||
if (card->state != CARD_STATE_RUN)
|
||||
return; /* invalid call */
|
||||
|
||||
dpr = card->dpram; /* point to DPRAM */
|
||||
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags);
|
||||
if (card->hw_lock) {
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags); /* hardware currently unavailable */
|
||||
return;
|
||||
}
|
||||
card->hw_lock = 1; /* we now lock the hardware */
|
||||
|
||||
do {
|
||||
again = 0; /* assume loop not to be repeated */
|
||||
|
||||
if (!dpr->ToHyFlag) {
|
||||
/* we are able to send a buffer */
|
||||
|
||||
if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
|
||||
ERG_TO_HY_BUF_SIZE)) {
|
||||
dpr->ToHyFlag = 1; /* enable tx */
|
||||
again = 1; /* restart loop */
|
||||
}
|
||||
} /* we are able to send a buffer */
|
||||
if (dpr->ToPcFlag) {
|
||||
/* a message has arrived for us, handle it */
|
||||
|
||||
if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
|
||||
dpr->ToPcFlag = 0; /* we worked the data */
|
||||
again = 1; /* restart loop */
|
||||
}
|
||||
} /* a message has arrived for us */
|
||||
if (again) {
|
||||
dpr->ToHyInt = 1;
|
||||
dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
|
||||
} else
|
||||
card->hw_lock = 0; /* free hardware again */
|
||||
} while (again); /* until nothing more to do */
|
||||
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
} /* ergo_irq_bh */
|
||||
|
||||
|
||||
/*********************************************************/
|
||||
/* stop the card (hardware reset) and disable interrupts */
|
||||
/*********************************************************/
|
||||
static void
|
||||
ergo_stopcard(hysdn_card *card)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned char val;
|
||||
|
||||
hysdn_net_release(card); /* first release the net device if existing */
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
hycapi_capi_stop(card);
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags);
|
||||
val = bytein(card->iobase + PCI9050_INTR_REG); /* get actual value */
|
||||
val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1); /* mask irq */
|
||||
byteout(card->iobase + PCI9050_INTR_REG, val);
|
||||
card->irq_enabled = 0;
|
||||
byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET); /* reset E1 processor */
|
||||
card->state = CARD_STATE_UNUSED;
|
||||
card->err_log_state = ERRLOG_STATE_OFF; /* currently no log active */
|
||||
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
} /* ergo_stopcard */
|
||||
|
||||
/**************************************************************************/
|
||||
/* enable or disable the cards error log. The event is queued if possible */
|
||||
/**************************************************************************/
|
||||
static void
|
||||
ergo_set_errlog_state(hysdn_card *card, int on)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (card->state != CARD_STATE_RUN) {
|
||||
card->err_log_state = ERRLOG_STATE_OFF; /* must be off */
|
||||
return;
|
||||
}
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags);
|
||||
|
||||
if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
|
||||
((card->err_log_state == ERRLOG_STATE_ON) && on)) {
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
return; /* nothing to do */
|
||||
}
|
||||
if (on)
|
||||
card->err_log_state = ERRLOG_STATE_START; /* request start */
|
||||
else
|
||||
card->err_log_state = ERRLOG_STATE_STOP; /* request stop */
|
||||
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
schedule_work(&card->irq_queue);
|
||||
} /* ergo_set_errlog_state */
|
||||
|
||||
/******************************************/
|
||||
/* test the cards RAM and return 0 if ok. */
|
||||
/******************************************/
|
||||
static const char TestText[36] = "This Message is filler, why read it";
|
||||
|
||||
static int
|
||||
ergo_testram(hysdn_card *card)
|
||||
{
|
||||
tErgDpram *dpr = card->dpram;
|
||||
|
||||
memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable)); /* clear all Traps */
|
||||
dpr->ToHyInt = 1; /* E1 INTR state forced */
|
||||
|
||||
memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
|
||||
sizeof(TestText));
|
||||
if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
|
||||
sizeof(TestText)))
|
||||
return (-1);
|
||||
|
||||
memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
|
||||
sizeof(TestText));
|
||||
if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
|
||||
sizeof(TestText)))
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
} /* ergo_testram */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* this function is intended to write stage 1 boot image to the cards buffer */
|
||||
/* this is done in two steps. First the 1024 hi-words are written (offs=0), */
|
||||
/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the */
|
||||
/* PCI-write-buffers flushed and the card is taken out of reset. */
|
||||
/* The function then waits for a reaction of the E1 processor or a timeout. */
|
||||
/* Negative return values are interpreted as errors. */
|
||||
/*****************************************************************************/
|
||||
static int
|
||||
ergo_writebootimg(struct HYSDN_CARD *card, unsigned char *buf,
|
||||
unsigned long offs)
|
||||
{
|
||||
unsigned char *dst;
|
||||
tErgDpram *dpram;
|
||||
int cnt = (BOOT_IMG_SIZE >> 2); /* number of words to move and swap (byte order!) */
|
||||
|
||||
if (card->debug_flags & LOG_POF_CARD)
|
||||
hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
|
||||
|
||||
dst = card->dpram; /* pointer to start of DPRAM */
|
||||
dst += (offs + ERG_DPRAM_FILL_SIZE); /* offset in the DPRAM */
|
||||
while (cnt--) {
|
||||
*dst++ = *(buf + 1); /* high byte */
|
||||
*dst++ = *buf; /* low byte */
|
||||
dst += 2; /* point to next longword */
|
||||
buf += 2; /* buffer only filled with words */
|
||||
}
|
||||
|
||||
/* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
|
||||
/* flush the PCI-write-buffer and take the E1 out of reset */
|
||||
if (offs) {
|
||||
memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE); /* fill the DPRAM still not cleared */
|
||||
dpram = card->dpram; /* get pointer to dpram structure */
|
||||
dpram->ToHyNoDpramErrLog = 0xFF; /* write a dpram register */
|
||||
while (!dpram->ToHyNoDpramErrLog); /* reread volatile register to flush PCI */
|
||||
|
||||
byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN); /* start E1 processor */
|
||||
/* the interrupts are still masked */
|
||||
|
||||
msleep_interruptible(20); /* Timeout 20ms */
|
||||
|
||||
if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
|
||||
if (card->debug_flags & LOG_POF_CARD)
|
||||
hysdn_addlog(card, "ERGO: write bootldr no answer");
|
||||
return (-ERR_BOOTIMG_FAIL);
|
||||
}
|
||||
} /* start_boot_img */
|
||||
return (0); /* successful */
|
||||
} /* ergo_writebootimg */
|
||||
|
||||
/********************************************************************************/
|
||||
/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
|
||||
/* using the boot spool mechanism. If everything works fine 0 is returned. In */
|
||||
/* case of errors a negative error value is returned. */
|
||||
/********************************************************************************/
|
||||
static int
|
||||
ergo_writebootseq(struct HYSDN_CARD *card, unsigned char *buf, int len)
|
||||
{
|
||||
tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
|
||||
unsigned char *dst;
|
||||
unsigned char buflen;
|
||||
int nr_write;
|
||||
unsigned char tmp_rdptr;
|
||||
unsigned char wr_mirror;
|
||||
int i;
|
||||
|
||||
if (card->debug_flags & LOG_POF_CARD)
|
||||
hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
|
||||
|
||||
dst = sp->Data; /* point to data in spool structure */
|
||||
buflen = sp->Len; /* maximum len of spooled data */
|
||||
wr_mirror = sp->WrPtr; /* only once read */
|
||||
|
||||
/* try until all bytes written or error */
|
||||
i = 0x1000; /* timeout value */
|
||||
while (len) {
|
||||
|
||||
/* first determine the number of bytes that may be buffered */
|
||||
do {
|
||||
tmp_rdptr = sp->RdPtr; /* first read the pointer */
|
||||
i--; /* decrement timeout */
|
||||
} while (i && (tmp_rdptr != sp->RdPtr)); /* wait for stable pointer */
|
||||
|
||||
if (!i) {
|
||||
if (card->debug_flags & LOG_POF_CARD)
|
||||
hysdn_addlog(card, "ERGO: write boot seq timeout");
|
||||
return (-ERR_BOOTSEQ_FAIL); /* value not stable -> timeout */
|
||||
}
|
||||
if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
|
||||
nr_write += buflen; /* now we got number of free bytes - 1 in buffer */
|
||||
|
||||
if (!nr_write)
|
||||
continue; /* no free bytes in buffer */
|
||||
|
||||
if (nr_write > len)
|
||||
nr_write = len; /* limit if last few bytes */
|
||||
i = 0x1000; /* reset timeout value */
|
||||
|
||||
/* now we know how much bytes we may put in the puffer */
|
||||
len -= nr_write; /* we savely could adjust len before output */
|
||||
while (nr_write--) {
|
||||
*(dst + wr_mirror) = *buf++; /* output one byte */
|
||||
if (++wr_mirror >= buflen)
|
||||
wr_mirror = 0;
|
||||
sp->WrPtr = wr_mirror; /* announce the next byte to E1 */
|
||||
} /* while (nr_write) */
|
||||
|
||||
} /* while (len) */
|
||||
return (0);
|
||||
} /* ergo_writebootseq */
|
||||
|
||||
/***********************************************************************************/
|
||||
/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
|
||||
/* boot process. If the process has been successful 0 is returned otherwise a */
|
||||
/* negative error code is returned. */
|
||||
/***********************************************************************************/
|
||||
static int
|
||||
ergo_waitpofready(struct HYSDN_CARD *card)
|
||||
{
|
||||
tErgDpram *dpr = card->dpram; /* pointer to DPRAM structure */
|
||||
int timecnt = 10000 / 50; /* timeout is 10 secs max. */
|
||||
unsigned long flags;
|
||||
int msg_size;
|
||||
int i;
|
||||
|
||||
if (card->debug_flags & LOG_POF_CARD)
|
||||
hysdn_addlog(card, "ERGO: waiting for pof ready");
|
||||
while (timecnt--) {
|
||||
/* wait until timeout */
|
||||
|
||||
if (dpr->ToPcFlag) {
|
||||
/* data has arrived */
|
||||
|
||||
if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
|
||||
(dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
|
||||
(dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
|
||||
((*(unsigned long *) dpr->ToPcBuf) != RDY_MAGIC))
|
||||
break; /* an error occurred */
|
||||
|
||||
/* Check for additional data delivered during SysReady */
|
||||
msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
|
||||
if (msg_size > 0)
|
||||
if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
|
||||
break;
|
||||
|
||||
if (card->debug_flags & LOG_POF_RECORD)
|
||||
hysdn_addlog(card, "ERGO: pof boot success");
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags);
|
||||
|
||||
card->state = CARD_STATE_RUN; /* now card is running */
|
||||
/* enable the cards interrupt */
|
||||
byteout(card->iobase + PCI9050_INTR_REG,
|
||||
bytein(card->iobase + PCI9050_INTR_REG) |
|
||||
(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
|
||||
card->irq_enabled = 1; /* we are ready to receive interrupts */
|
||||
|
||||
dpr->ToPcFlag = 0; /* reset data indicator */
|
||||
dpr->ToHyInt = 1;
|
||||
dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
|
||||
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
if ((hynet_enable & (1 << card->myid))
|
||||
&& (i = hysdn_net_create(card)))
|
||||
{
|
||||
ergo_stopcard(card);
|
||||
card->state = CARD_STATE_BOOTERR;
|
||||
return (i);
|
||||
}
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
if ((i = hycapi_capi_create(card))) {
|
||||
printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n");
|
||||
}
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
return (0); /* success */
|
||||
} /* data has arrived */
|
||||
msleep_interruptible(50); /* Timeout 50ms */
|
||||
} /* wait until timeout */
|
||||
|
||||
if (card->debug_flags & LOG_POF_CARD)
|
||||
hysdn_addlog(card, "ERGO: pof boot ready timeout");
|
||||
return (-ERR_POF_TIMEOUT);
|
||||
} /* ergo_waitpofready */
|
||||
|
||||
|
||||
|
||||
/************************************************************************************/
|
||||
/* release the cards hardware. Before releasing do a interrupt disable and hardware */
|
||||
/* reset. Also unmap dpram. */
|
||||
/* Use only during module release. */
|
||||
/************************************************************************************/
|
||||
static void
|
||||
ergo_releasehardware(hysdn_card *card)
|
||||
{
|
||||
ergo_stopcard(card); /* first stop the card if not already done */
|
||||
free_irq(card->irq, card); /* release interrupt */
|
||||
release_region(card->iobase + PCI9050_INTR_REG, 1); /* release all io ports */
|
||||
release_region(card->iobase + PCI9050_USER_IO, 1);
|
||||
iounmap(card->dpram);
|
||||
card->dpram = NULL; /* release shared mem */
|
||||
} /* ergo_releasehardware */
|
||||
|
||||
|
||||
/*********************************************************************************/
|
||||
/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
|
||||
/* value is returned. */
|
||||
/* Use only during module init. */
|
||||
/*********************************************************************************/
|
||||
int
|
||||
ergo_inithardware(hysdn_card *card)
|
||||
{
|
||||
if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN"))
|
||||
return (-1);
|
||||
if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) {
|
||||
release_region(card->iobase + PCI9050_INTR_REG, 1);
|
||||
return (-1); /* ports already in use */
|
||||
}
|
||||
card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
|
||||
if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) {
|
||||
release_region(card->iobase + PCI9050_INTR_REG, 1);
|
||||
release_region(card->iobase + PCI9050_USER_IO, 1);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ergo_stopcard(card); /* disable interrupts */
|
||||
if (request_irq(card->irq, ergo_interrupt, IRQF_SHARED, "HYSDN", card)) {
|
||||
ergo_releasehardware(card); /* return the acquired hardware */
|
||||
return (-1);
|
||||
}
|
||||
/* success, now setup the function pointers */
|
||||
card->stopcard = ergo_stopcard;
|
||||
card->releasehardware = ergo_releasehardware;
|
||||
card->testram = ergo_testram;
|
||||
card->writebootimg = ergo_writebootimg;
|
||||
card->writebootseq = ergo_writebootseq;
|
||||
card->waitpofready = ergo_waitpofready;
|
||||
card->set_errlog_state = ergo_set_errlog_state;
|
||||
INIT_WORK(&card->irq_queue, ergo_irq_bh);
|
||||
spin_lock_init(&card->hysdn_lock);
|
||||
|
||||
return (0);
|
||||
} /* ergo_inithardware */
|
@ -1,100 +0,0 @@
|
||||
/* $Id: boardergo.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/************************************************/
|
||||
/* defines for the dual port memory of the card */
|
||||
/************************************************/
|
||||
#define ERG_DPRAM_PAGE_SIZE 0x2000 /* DPRAM occupies a 8K page */
|
||||
#define BOOT_IMG_SIZE 4096
|
||||
#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
|
||||
|
||||
#define ERG_TO_HY_BUF_SIZE 0x0E00 /* 3072 bytes buffer size to card */
|
||||
#define ERG_TO_PC_BUF_SIZE 0x0E00 /* 3072 bytes to PC, too */
|
||||
|
||||
/* following DPRAM layout copied from OS2-driver boarderg.h */
|
||||
typedef struct ErgDpram_tag {
|
||||
/*0000 */ unsigned char ToHyBuf[ERG_TO_HY_BUF_SIZE];
|
||||
/*0E00 */ unsigned char ToPcBuf[ERG_TO_PC_BUF_SIZE];
|
||||
|
||||
/*1C00 */ unsigned char bSoftUart[SIZE_RSV_SOFT_UART];
|
||||
/* size 0x1B0 */
|
||||
|
||||
/*1DB0 *//* tErrLogEntry */ unsigned char volatile ErrLogMsg[64];
|
||||
/* size 64 bytes */
|
||||
/*1DB0 unsigned long ulErrType; */
|
||||
/*1DB4 unsigned long ulErrSubtype; */
|
||||
/*1DB8 unsigned long ucTextSize; */
|
||||
/*1DB9 unsigned long ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
|
||||
/*1DF0 */
|
||||
|
||||
/*1DF0 */ unsigned short volatile ToHyChannel;
|
||||
/*1DF2 */ unsigned short volatile ToHySize;
|
||||
/*1DF4 */ unsigned char volatile ToHyFlag;
|
||||
/* !=0: msg for Hy waiting */
|
||||
/*1DF5 */ unsigned char volatile ToPcFlag;
|
||||
/* !=0: msg for PC waiting */
|
||||
/*1DF6 */ unsigned short volatile ToPcChannel;
|
||||
/*1DF8 */ unsigned short volatile ToPcSize;
|
||||
/*1DFA */ unsigned char bRes1DBA[0x1E00 - 0x1DFA];
|
||||
/* 6 bytes */
|
||||
|
||||
/*1E00 */ unsigned char bRestOfEntryTbl[0x1F00 - 0x1E00];
|
||||
/*1F00 */ unsigned long TrapTable[62];
|
||||
/*1FF8 */ unsigned char bRes1FF8[0x1FFB - 0x1FF8];
|
||||
/* low part of reset vetor */
|
||||
/*1FFB */ unsigned char ToPcIntMetro;
|
||||
/* notes:
|
||||
* - metro has 32-bit boot ram - accessing
|
||||
* ToPcInt and ToHyInt would be the same;
|
||||
* so we moved ToPcInt to 1FFB.
|
||||
* Because on the PC side both vars are
|
||||
* readonly (reseting on int from E1 to PC),
|
||||
* we can read both vars on both cards
|
||||
* without destroying anything.
|
||||
* - 1FFB is the high byte of the reset vector,
|
||||
* so E1 side should NOT change this byte
|
||||
* when writing!
|
||||
*/
|
||||
/*1FFC */ unsigned char volatile ToHyNoDpramErrLog;
|
||||
/* note: ToHyNoDpramErrLog is used to inform
|
||||
* boot loader, not to use DPRAM based
|
||||
* ErrLog; when DOS driver is rewritten
|
||||
* this becomes obsolete
|
||||
*/
|
||||
/*1FFD */ unsigned char bRes1FFD;
|
||||
/*1FFE */ unsigned char ToPcInt;
|
||||
/* E1_intclear; on CHAMP2: E1_intset */
|
||||
/*1FFF */ unsigned char ToHyInt;
|
||||
/* E1_intset; on CHAMP2: E1_intclear */
|
||||
} tErgDpram;
|
||||
|
||||
/**********************************************/
|
||||
/* PCI9050 controller local register offsets: */
|
||||
/* copied from boarderg.c */
|
||||
/**********************************************/
|
||||
#define PCI9050_INTR_REG 0x4C /* Interrupt register */
|
||||
#define PCI9050_USER_IO 0x51 /* User I/O register */
|
||||
|
||||
/* bitmask for PCI9050_INTR_REG: */
|
||||
#define PCI9050_INTR_REG_EN1 0x01 /* 1= enable (def.), 0= disable */
|
||||
#define PCI9050_INTR_REG_POL1 0x02 /* 1= active high (def.), 0= active low */
|
||||
#define PCI9050_INTR_REG_STAT1 0x04 /* 1= intr. active, 0= intr. not active (def.) */
|
||||
#define PCI9050_INTR_REG_ENPCI 0x40 /* 1= PCI interrupts enable (def.) */
|
||||
|
||||
/* bitmask for PCI9050_USER_IO: */
|
||||
#define PCI9050_USER_IO_EN3 0x02 /* 1= disable , 0= enable (def.) */
|
||||
#define PCI9050_USER_IO_DIR3 0x04 /* 1= output (def.), 0= input */
|
||||
#define PCI9050_USER_IO_DAT3 0x08 /* 1= high (def.) , 0= low */
|
||||
|
||||
#define PCI9050_E1_RESET (PCI9050_USER_IO_DIR3) /* 0x04 */
|
||||
#define PCI9050_E1_RUN (PCI9050_USER_IO_DAT3 | PCI9050_USER_IO_DIR3) /* 0x0C */
|
@ -1,785 +0,0 @@
|
||||
/* $Id: hycapi.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, CAPI2.0-Interface.
|
||||
*
|
||||
* Author Ulrich Albrecht <u.albrecht@hypercope.de> for Hypercope GmbH
|
||||
* Copyright 2000 by Hypercope GmbH
|
||||
*
|
||||
* 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/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define VER_DRIVER 0
|
||||
#define VER_CARDTYPE 1
|
||||
#define VER_HWID 2
|
||||
#define VER_SERIAL 3
|
||||
#define VER_OPTION 4
|
||||
#define VER_PROTO 5
|
||||
#define VER_PROFILE 6
|
||||
#define VER_CAPI 7
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
#include <linux/kernelcapi.h>
|
||||
|
||||
static char hycapi_revision[] = "$Revision: 1.8.6.4 $";
|
||||
|
||||
unsigned int hycapi_enable = 0xffffffff;
|
||||
module_param(hycapi_enable, uint, 0);
|
||||
|
||||
typedef struct _hycapi_appl {
|
||||
unsigned int ctrl_mask;
|
||||
capi_register_params rp;
|
||||
struct sk_buff *listen_req[CAPI_MAXCONTR];
|
||||
} hycapi_appl;
|
||||
|
||||
static hycapi_appl hycapi_applications[CAPI_MAXAPPL];
|
||||
|
||||
static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
|
||||
|
||||
static inline int _hycapi_appCheck(int app_id, int ctrl_no)
|
||||
{
|
||||
if ((ctrl_no <= 0) || (ctrl_no > CAPI_MAXCONTR) || (app_id <= 0) ||
|
||||
(app_id > CAPI_MAXAPPL))
|
||||
{
|
||||
printk(KERN_ERR "HYCAPI: Invalid request app_id %d for controller %d", app_id, ctrl_no);
|
||||
return -1;
|
||||
}
|
||||
return ((hycapi_applications[app_id - 1].ctrl_mask & (1 << (ctrl_no-1))) != 0);
|
||||
}
|
||||
|
||||
/******************************
|
||||
Kernel-Capi callback reset_ctr
|
||||
******************************/
|
||||
|
||||
static void
|
||||
hycapi_reset_ctr(struct capi_ctr *ctrl)
|
||||
{
|
||||
hycapictrl_info *cinfo = ctrl->driverdata;
|
||||
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "HYCAPI hycapi_reset_ctr\n");
|
||||
#endif
|
||||
capilib_release(&cinfo->ncci_head);
|
||||
capi_ctr_down(ctrl);
|
||||
}
|
||||
|
||||
/******************************
|
||||
Kernel-Capi callback remove_ctr
|
||||
******************************/
|
||||
|
||||
static void
|
||||
hycapi_remove_ctr(struct capi_ctr *ctrl)
|
||||
{
|
||||
int i;
|
||||
hycapictrl_info *cinfo = NULL;
|
||||
hysdn_card *card = NULL;
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "HYCAPI hycapi_remove_ctr\n");
|
||||
#endif
|
||||
cinfo = (hycapictrl_info *)(ctrl->driverdata);
|
||||
if (!cinfo) {
|
||||
printk(KERN_ERR "No hycapictrl_info set!");
|
||||
return;
|
||||
}
|
||||
card = cinfo->card;
|
||||
capi_ctr_suspend_output(ctrl);
|
||||
for (i = 0; i < CAPI_MAXAPPL; i++) {
|
||||
if (hycapi_applications[i].listen_req[ctrl->cnr - 1]) {
|
||||
kfree_skb(hycapi_applications[i].listen_req[ctrl->cnr - 1]);
|
||||
hycapi_applications[i].listen_req[ctrl->cnr - 1] = NULL;
|
||||
}
|
||||
}
|
||||
detach_capi_ctr(ctrl);
|
||||
ctrl->driverdata = NULL;
|
||||
kfree(card->hyctrlinfo);
|
||||
|
||||
|
||||
card->hyctrlinfo = NULL;
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
|
||||
Queue a CAPI-message to the controller.
|
||||
|
||||
***********************************************************/
|
||||
|
||||
static void
|
||||
hycapi_sendmsg_internal(struct capi_ctr *ctrl, struct sk_buff *skb)
|
||||
{
|
||||
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
|
||||
hysdn_card *card = cinfo->card;
|
||||
|
||||
spin_lock_irq(&cinfo->lock);
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_send_message\n");
|
||||
#endif
|
||||
cinfo->skbs[cinfo->in_idx++] = skb; /* add to buffer list */
|
||||
if (cinfo->in_idx >= HYSDN_MAX_CAPI_SKB)
|
||||
cinfo->in_idx = 0; /* wrap around */
|
||||
cinfo->sk_count++; /* adjust counter */
|
||||
if (cinfo->sk_count >= HYSDN_MAX_CAPI_SKB) {
|
||||
/* inform upper layers we're full */
|
||||
printk(KERN_ERR "HYSDN Card%d: CAPI-buffer overrun!\n",
|
||||
card->myid);
|
||||
capi_ctr_suspend_output(ctrl);
|
||||
}
|
||||
cinfo->tx_skb = skb;
|
||||
spin_unlock_irq(&cinfo->lock);
|
||||
schedule_work(&card->irq_queue);
|
||||
}
|
||||
|
||||
/***********************************************************
|
||||
hycapi_register_internal
|
||||
|
||||
Send down the CAPI_REGISTER-Command to the controller.
|
||||
This functions will also be used if the adapter has been rebooted to
|
||||
re-register any applications in the private list.
|
||||
|
||||
************************************************************/
|
||||
|
||||
static void
|
||||
hycapi_register_internal(struct capi_ctr *ctrl, __u16 appl,
|
||||
capi_register_params *rp)
|
||||
{
|
||||
char ExtFeatureDefaults[] = "49 /0/0/0/0,*/1,*/2,*/3,*/4,*/5,*/6,*/7,*/8,*/9,*";
|
||||
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
|
||||
hysdn_card *card = cinfo->card;
|
||||
struct sk_buff *skb;
|
||||
__u16 len;
|
||||
__u8 _command = 0xa0, _subcommand = 0x80;
|
||||
__u16 MessageNumber = 0x0000;
|
||||
__u16 MessageBufferSize = 0;
|
||||
int slen = strlen(ExtFeatureDefaults);
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_register_appl\n");
|
||||
#endif
|
||||
MessageBufferSize = rp->level3cnt * rp->datablkcnt * rp->datablklen;
|
||||
|
||||
len = CAPI_MSG_BASELEN + 8 + slen + 1;
|
||||
if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
|
||||
card->myid);
|
||||
return;
|
||||
}
|
||||
skb_put_data(skb, &len, sizeof(__u16));
|
||||
skb_put_data(skb, &appl, sizeof(__u16));
|
||||
skb_put_data(skb, &_command, sizeof(__u8));
|
||||
skb_put_data(skb, &_subcommand, sizeof(__u8));
|
||||
skb_put_data(skb, &MessageNumber, sizeof(__u16));
|
||||
skb_put_data(skb, &MessageBufferSize, sizeof(__u16));
|
||||
skb_put_data(skb, &(rp->level3cnt), sizeof(__u16));
|
||||
skb_put_data(skb, &(rp->datablkcnt), sizeof(__u16));
|
||||
skb_put_data(skb, &(rp->datablklen), sizeof(__u16));
|
||||
skb_put_data(skb, ExtFeatureDefaults, slen);
|
||||
hycapi_applications[appl - 1].ctrl_mask |= (1 << (ctrl->cnr - 1));
|
||||
hycapi_send_message(ctrl, skb);
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
hycapi_restart_internal
|
||||
|
||||
After an adapter has been rebootet, re-register all applications and
|
||||
send a LISTEN_REQ (if there has been such a thing )
|
||||
|
||||
*************************************************************/
|
||||
|
||||
static void hycapi_restart_internal(struct capi_ctr *ctrl)
|
||||
{
|
||||
int i;
|
||||
struct sk_buff *skb;
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_WARNING "HYSDN: hycapi_restart_internal");
|
||||
#endif
|
||||
for (i = 0; i < CAPI_MAXAPPL; i++) {
|
||||
if (_hycapi_appCheck(i + 1, ctrl->cnr) == 1) {
|
||||
hycapi_register_internal(ctrl, i + 1,
|
||||
&hycapi_applications[i].rp);
|
||||
if (hycapi_applications[i].listen_req[ctrl->cnr - 1]) {
|
||||
skb = skb_copy(hycapi_applications[i].listen_req[ctrl->cnr - 1], GFP_ATOMIC);
|
||||
hycapi_sendmsg_internal(ctrl, skb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
Register an application.
|
||||
Error-checking is done for CAPI-compliance.
|
||||
|
||||
The application is recorded in the internal list.
|
||||
*************************************************************/
|
||||
|
||||
static void
|
||||
hycapi_register_appl(struct capi_ctr *ctrl, __u16 appl,
|
||||
capi_register_params *rp)
|
||||
{
|
||||
int MaxLogicalConnections = 0, MaxBDataBlocks = 0, MaxBDataLen = 0;
|
||||
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
|
||||
hysdn_card *card = cinfo->card;
|
||||
int chk = _hycapi_appCheck(appl, ctrl->cnr);
|
||||
if (chk < 0) {
|
||||
return;
|
||||
}
|
||||
if (chk == 1) {
|
||||
printk(KERN_INFO "HYSDN: apl %d already registered\n", appl);
|
||||
return;
|
||||
}
|
||||
MaxBDataBlocks = rp->datablkcnt > CAPI_MAXDATAWINDOW ? CAPI_MAXDATAWINDOW : rp->datablkcnt;
|
||||
rp->datablkcnt = MaxBDataBlocks;
|
||||
MaxBDataLen = rp->datablklen < 1024 ? 1024 : rp->datablklen;
|
||||
rp->datablklen = MaxBDataLen;
|
||||
|
||||
MaxLogicalConnections = rp->level3cnt;
|
||||
if (MaxLogicalConnections < 0) {
|
||||
MaxLogicalConnections = card->bchans * -MaxLogicalConnections;
|
||||
}
|
||||
if (MaxLogicalConnections == 0) {
|
||||
MaxLogicalConnections = card->bchans;
|
||||
}
|
||||
|
||||
rp->level3cnt = MaxLogicalConnections;
|
||||
memcpy(&hycapi_applications[appl - 1].rp,
|
||||
rp, sizeof(capi_register_params));
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
|
||||
hycapi_release_internal
|
||||
|
||||
Send down a CAPI_RELEASE to the controller.
|
||||
*********************************************************************/
|
||||
|
||||
static void hycapi_release_internal(struct capi_ctr *ctrl, __u16 appl)
|
||||
{
|
||||
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
|
||||
hysdn_card *card = cinfo->card;
|
||||
struct sk_buff *skb;
|
||||
__u16 len;
|
||||
__u8 _command = 0xa1, _subcommand = 0x80;
|
||||
__u16 MessageNumber = 0x0000;
|
||||
|
||||
capilib_release_appl(&cinfo->ncci_head, appl);
|
||||
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_release_appl\n");
|
||||
#endif
|
||||
len = CAPI_MSG_BASELEN;
|
||||
if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
|
||||
card->myid);
|
||||
return;
|
||||
}
|
||||
skb_put_data(skb, &len, sizeof(__u16));
|
||||
skb_put_data(skb, &appl, sizeof(__u16));
|
||||
skb_put_data(skb, &_command, sizeof(__u8));
|
||||
skb_put_data(skb, &_subcommand, sizeof(__u8));
|
||||
skb_put_data(skb, &MessageNumber, sizeof(__u16));
|
||||
hycapi_send_message(ctrl, skb);
|
||||
hycapi_applications[appl - 1].ctrl_mask &= ~(1 << (ctrl->cnr - 1));
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
hycapi_release_appl
|
||||
|
||||
Release the application from the internal list an remove it's
|
||||
registration at controller-level
|
||||
******************************************************************/
|
||||
|
||||
static void
|
||||
hycapi_release_appl(struct capi_ctr *ctrl, __u16 appl)
|
||||
{
|
||||
int chk;
|
||||
|
||||
chk = _hycapi_appCheck(appl, ctrl->cnr);
|
||||
if (chk < 0) {
|
||||
printk(KERN_ERR "HYCAPI: Releasing invalid appl %d on controller %d\n", appl, ctrl->cnr);
|
||||
return;
|
||||
}
|
||||
if (hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1]) {
|
||||
kfree_skb(hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1]);
|
||||
hycapi_applications[appl - 1].listen_req[ctrl->cnr - 1] = NULL;
|
||||
}
|
||||
if (chk == 1)
|
||||
{
|
||||
hycapi_release_internal(ctrl, appl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************
|
||||
Kill a single controller.
|
||||
**************************************************************/
|
||||
|
||||
int hycapi_capi_release(hysdn_card *card)
|
||||
{
|
||||
hycapictrl_info *cinfo = card->hyctrlinfo;
|
||||
struct capi_ctr *ctrl;
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_capi_release\n");
|
||||
#endif
|
||||
if (cinfo) {
|
||||
ctrl = &cinfo->capi_ctrl;
|
||||
hycapi_remove_ctr(ctrl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************************
|
||||
hycapi_capi_stop
|
||||
|
||||
Stop CAPI-Output on a card. (e.g. during reboot)
|
||||
***************************************************************/
|
||||
|
||||
int hycapi_capi_stop(hysdn_card *card)
|
||||
{
|
||||
hycapictrl_info *cinfo = card->hyctrlinfo;
|
||||
struct capi_ctr *ctrl;
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_capi_stop\n");
|
||||
#endif
|
||||
if (cinfo) {
|
||||
ctrl = &cinfo->capi_ctrl;
|
||||
/* ctrl->suspend_output(ctrl); */
|
||||
capi_ctr_down(ctrl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
hycapi_send_message
|
||||
|
||||
Send a message to the controller.
|
||||
|
||||
Messages are parsed for their Command/Subcommand-type, and appropriate
|
||||
action's are performed.
|
||||
|
||||
Note that we have to muck around with a 64Bit-DATA_REQ as there are
|
||||
firmware-releases that do not check the MsgLen-Indication!
|
||||
|
||||
***************************************************************/
|
||||
|
||||
static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
|
||||
{
|
||||
__u16 appl_id;
|
||||
int _len, _len2;
|
||||
__u8 msghead[64];
|
||||
hycapictrl_info *cinfo = ctrl->driverdata;
|
||||
u16 retval = CAPI_NOERROR;
|
||||
|
||||
appl_id = CAPIMSG_APPID(skb->data);
|
||||
switch (_hycapi_appCheck(appl_id, ctrl->cnr))
|
||||
{
|
||||
case 0:
|
||||
/* printk(KERN_INFO "Need to register\n"); */
|
||||
hycapi_register_internal(ctrl,
|
||||
appl_id,
|
||||
&(hycapi_applications[appl_id - 1].rp));
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "HYCAPI: Controller mixup!\n");
|
||||
retval = CAPI_ILLAPPNR;
|
||||
goto out;
|
||||
}
|
||||
switch (CAPIMSG_CMD(skb->data)) {
|
||||
case CAPI_DISCONNECT_B3_RESP:
|
||||
capilib_free_ncci(&cinfo->ncci_head, appl_id,
|
||||
CAPIMSG_NCCI(skb->data));
|
||||
break;
|
||||
case CAPI_DATA_B3_REQ:
|
||||
_len = CAPIMSG_LEN(skb->data);
|
||||
if (_len > 22) {
|
||||
_len2 = _len - 22;
|
||||
skb_copy_from_linear_data(skb, msghead, 22);
|
||||
skb_copy_to_linear_data_offset(skb, _len2,
|
||||
msghead, 22);
|
||||
skb_pull(skb, _len2);
|
||||
CAPIMSG_SETLEN(skb->data, 22);
|
||||
retval = capilib_data_b3_req(&cinfo->ncci_head,
|
||||
CAPIMSG_APPID(skb->data),
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
}
|
||||
break;
|
||||
case CAPI_LISTEN_REQ:
|
||||
if (hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1])
|
||||
{
|
||||
kfree_skb(hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1]);
|
||||
hycapi_applications[appl_id - 1].listen_req[ctrl->cnr - 1] = NULL;
|
||||
}
|
||||
if (!(hycapi_applications[appl_id -1].listen_req[ctrl->cnr - 1] = skb_copy(skb, GFP_ATOMIC)))
|
||||
{
|
||||
printk(KERN_ERR "HYSDN: memory squeeze in private_listen\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
out:
|
||||
if (retval == CAPI_NOERROR)
|
||||
hycapi_sendmsg_internal(ctrl, skb);
|
||||
else
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int hycapi_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct capi_ctr *ctrl = m->private;
|
||||
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
|
||||
hysdn_card *card = cinfo->card;
|
||||
char *s;
|
||||
|
||||
seq_printf(m, "%-16s %s\n", "name", cinfo->cardname);
|
||||
seq_printf(m, "%-16s 0x%x\n", "io", card->iobase);
|
||||
seq_printf(m, "%-16s %d\n", "irq", card->irq);
|
||||
|
||||
switch (card->brdtype) {
|
||||
case BD_PCCARD: s = "HYSDN Hycard"; break;
|
||||
case BD_ERGO: s = "HYSDN Ergo2"; break;
|
||||
case BD_METRO: s = "HYSDN Metro4"; break;
|
||||
case BD_CHAMP2: s = "HYSDN Champ2"; break;
|
||||
case BD_PLEXUS: s = "HYSDN Plexus30"; break;
|
||||
default: s = "???"; break;
|
||||
}
|
||||
seq_printf(m, "%-16s %s\n", "type", s);
|
||||
if ((s = cinfo->version[VER_DRIVER]) != NULL)
|
||||
seq_printf(m, "%-16s %s\n", "ver_driver", s);
|
||||
if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
|
||||
seq_printf(m, "%-16s %s\n", "ver_cardtype", s);
|
||||
if ((s = cinfo->version[VER_SERIAL]) != NULL)
|
||||
seq_printf(m, "%-16s %s\n", "ver_serial", s);
|
||||
|
||||
seq_printf(m, "%-16s %s\n", "cardname", cinfo->cardname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************************
|
||||
hycapi_load_firmware
|
||||
|
||||
This does NOT load any firmware, but the callback somehow is needed
|
||||
on capi-interface registration.
|
||||
|
||||
**************************************************************/
|
||||
|
||||
static int hycapi_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
|
||||
{
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_load_firmware\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static char *hycapi_procinfo(struct capi_ctr *ctrl)
|
||||
{
|
||||
hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "%s\n", __func__);
|
||||
#endif
|
||||
if (!cinfo)
|
||||
return "";
|
||||
sprintf(cinfo->infobuf, "%s %s 0x%x %d %s",
|
||||
cinfo->cardname[0] ? cinfo->cardname : "-",
|
||||
cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
|
||||
cinfo->card ? cinfo->card->iobase : 0x0,
|
||||
cinfo->card ? cinfo->card->irq : 0,
|
||||
hycapi_revision
|
||||
);
|
||||
return cinfo->infobuf;
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
hycapi_rx_capipkt
|
||||
|
||||
Receive a capi-message.
|
||||
|
||||
All B3_DATA_IND are converted to 64K-extension compatible format.
|
||||
New nccis are created if necessary.
|
||||
*******************************************************************/
|
||||
|
||||
void
|
||||
hycapi_rx_capipkt(hysdn_card *card, unsigned char *buf, unsigned short len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
hycapictrl_info *cinfo = card->hyctrlinfo;
|
||||
struct capi_ctr *ctrl;
|
||||
__u16 ApplId;
|
||||
__u16 MsgLen, info;
|
||||
__u16 len2, CapiCmd;
|
||||
__u32 CP64[2] = {0, 0};
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_rx_capipkt\n");
|
||||
#endif
|
||||
if (!cinfo) {
|
||||
return;
|
||||
}
|
||||
ctrl = &cinfo->capi_ctrl;
|
||||
if (len < CAPI_MSG_BASELEN) {
|
||||
printk(KERN_ERR "HYSDN Card%d: invalid CAPI-message, length %d!\n",
|
||||
card->myid, len);
|
||||
return;
|
||||
}
|
||||
MsgLen = CAPIMSG_LEN(buf);
|
||||
ApplId = CAPIMSG_APPID(buf);
|
||||
CapiCmd = CAPIMSG_CMD(buf);
|
||||
|
||||
if ((CapiCmd == CAPI_DATA_B3_IND) && (MsgLen < 30)) {
|
||||
len2 = len + (30 - MsgLen);
|
||||
if (!(skb = alloc_skb(len2, GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
|
||||
card->myid);
|
||||
return;
|
||||
}
|
||||
skb_put_data(skb, buf, MsgLen);
|
||||
skb_put_data(skb, CP64, 2 * sizeof(__u32));
|
||||
skb_put_data(skb, buf + MsgLen, len - MsgLen);
|
||||
CAPIMSG_SETLEN(skb->data, 30);
|
||||
} else {
|
||||
if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
|
||||
printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
|
||||
card->myid);
|
||||
return;
|
||||
}
|
||||
skb_put_data(skb, buf, len);
|
||||
}
|
||||
switch (CAPIMSG_CMD(skb->data))
|
||||
{
|
||||
case CAPI_CONNECT_B3_CONF:
|
||||
/* Check info-field for error-indication: */
|
||||
info = CAPIMSG_U16(skb->data, 12);
|
||||
switch (info)
|
||||
{
|
||||
case 0:
|
||||
capilib_new_ncci(&cinfo->ncci_head, ApplId, CAPIMSG_NCCI(skb->data),
|
||||
hycapi_applications[ApplId - 1].rp.datablkcnt);
|
||||
|
||||
break;
|
||||
case 0x0001:
|
||||
printk(KERN_ERR "HYSDN Card%d: NCPI not supported by current "
|
||||
"protocol. NCPI ignored.\n", card->myid);
|
||||
break;
|
||||
case 0x2001:
|
||||
printk(KERN_ERR "HYSDN Card%d: Message not supported in"
|
||||
" current state\n", card->myid);
|
||||
break;
|
||||
case 0x2002:
|
||||
printk(KERN_ERR "HYSDN Card%d: invalid PLCI\n", card->myid);
|
||||
break;
|
||||
case 0x2004:
|
||||
printk(KERN_ERR "HYSDN Card%d: out of NCCI\n", card->myid);
|
||||
break;
|
||||
case 0x3008:
|
||||
printk(KERN_ERR "HYSDN Card%d: NCPI not supported\n",
|
||||
card->myid);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "HYSDN Card%d: Info in CONNECT_B3_CONF: %d\n",
|
||||
card->myid, info);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CAPI_CONNECT_B3_IND:
|
||||
capilib_new_ncci(&cinfo->ncci_head, ApplId,
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
hycapi_applications[ApplId - 1].rp.datablkcnt);
|
||||
break;
|
||||
case CAPI_DATA_B3_CONF:
|
||||
capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
|
||||
CAPIMSG_NCCI(skb->data),
|
||||
CAPIMSG_MSGID(skb->data));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
capi_ctr_handle_message(ctrl, ApplId, skb);
|
||||
}
|
||||
|
||||
/******************************************************************
|
||||
hycapi_tx_capiack
|
||||
|
||||
Internally acknowledge a msg sent. This will remove the msg from the
|
||||
internal queue.
|
||||
|
||||
*******************************************************************/
|
||||
|
||||
void hycapi_tx_capiack(hysdn_card *card)
|
||||
{
|
||||
hycapictrl_info *cinfo = card->hyctrlinfo;
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_tx_capiack\n");
|
||||
#endif
|
||||
if (!cinfo) {
|
||||
return;
|
||||
}
|
||||
spin_lock_irq(&cinfo->lock);
|
||||
kfree_skb(cinfo->skbs[cinfo->out_idx]); /* free skb */
|
||||
cinfo->skbs[cinfo->out_idx++] = NULL;
|
||||
if (cinfo->out_idx >= HYSDN_MAX_CAPI_SKB)
|
||||
cinfo->out_idx = 0; /* wrap around */
|
||||
|
||||
if (cinfo->sk_count-- == HYSDN_MAX_CAPI_SKB) /* dec usage count */
|
||||
capi_ctr_resume_output(&cinfo->capi_ctrl);
|
||||
spin_unlock_irq(&cinfo->lock);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
hycapi_tx_capiget(hysdn_card *card)
|
||||
|
||||
This is called when polling for messages to SEND.
|
||||
|
||||
****************************************************************/
|
||||
|
||||
struct sk_buff *
|
||||
hycapi_tx_capiget(hysdn_card *card)
|
||||
{
|
||||
hycapictrl_info *cinfo = card->hyctrlinfo;
|
||||
if (!cinfo) {
|
||||
return (struct sk_buff *)NULL;
|
||||
}
|
||||
if (!cinfo->sk_count)
|
||||
return (struct sk_buff *)NULL; /* nothing available */
|
||||
|
||||
return (cinfo->skbs[cinfo->out_idx]); /* next packet to send */
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************
|
||||
int hycapi_init()
|
||||
|
||||
attach the capi-driver to the kernel-capi.
|
||||
|
||||
***********************************************************/
|
||||
|
||||
int hycapi_init(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < CAPI_MAXAPPL; i++) {
|
||||
memset(&(hycapi_applications[i]), 0, sizeof(hycapi_appl));
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**************************************************************
|
||||
hycapi_cleanup(void)
|
||||
|
||||
detach the capi-driver to the kernel-capi. Actually this should
|
||||
free some more ressources. Do that later.
|
||||
**************************************************************/
|
||||
|
||||
void
|
||||
hycapi_cleanup(void)
|
||||
{
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
hycapi_capi_create(hysdn_card *card)
|
||||
|
||||
Attach the card with its capi-ctrl.
|
||||
*********************************************************************/
|
||||
|
||||
static void hycapi_fill_profile(hysdn_card *card)
|
||||
{
|
||||
hycapictrl_info *cinfo = NULL;
|
||||
struct capi_ctr *ctrl = NULL;
|
||||
cinfo = card->hyctrlinfo;
|
||||
if (!cinfo) return;
|
||||
ctrl = &cinfo->capi_ctrl;
|
||||
strcpy(ctrl->manu, "Hypercope");
|
||||
ctrl->version.majorversion = 2;
|
||||
ctrl->version.minorversion = 0;
|
||||
ctrl->version.majormanuversion = 3;
|
||||
ctrl->version.minormanuversion = 2;
|
||||
ctrl->profile.ncontroller = card->myid;
|
||||
ctrl->profile.nbchannel = card->bchans;
|
||||
ctrl->profile.goptions = GLOBAL_OPTION_INTERNAL_CONTROLLER |
|
||||
GLOBAL_OPTION_B_CHANNEL_OPERATION;
|
||||
ctrl->profile.support1 = B1_PROT_64KBIT_HDLC |
|
||||
(card->faxchans ? B1_PROT_T30 : 0) |
|
||||
B1_PROT_64KBIT_TRANSPARENT;
|
||||
ctrl->profile.support2 = B2_PROT_ISO7776 |
|
||||
(card->faxchans ? B2_PROT_T30 : 0) |
|
||||
B2_PROT_TRANSPARENT;
|
||||
ctrl->profile.support3 = B3_PROT_TRANSPARENT |
|
||||
B3_PROT_T90NL |
|
||||
(card->faxchans ? B3_PROT_T30 : 0) |
|
||||
(card->faxchans ? B3_PROT_T30EXT : 0) |
|
||||
B3_PROT_ISO8208;
|
||||
}
|
||||
|
||||
int
|
||||
hycapi_capi_create(hysdn_card *card)
|
||||
{
|
||||
hycapictrl_info *cinfo = NULL;
|
||||
struct capi_ctr *ctrl = NULL;
|
||||
int retval;
|
||||
#ifdef HYCAPI_PRINTFNAMES
|
||||
printk(KERN_NOTICE "hycapi_capi_create\n");
|
||||
#endif
|
||||
if ((hycapi_enable & (1 << card->myid)) == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (!card->hyctrlinfo) {
|
||||
cinfo = kzalloc(sizeof(hycapictrl_info), GFP_ATOMIC);
|
||||
if (!cinfo) {
|
||||
printk(KERN_WARNING "HYSDN: no memory for capi-ctrl.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
card->hyctrlinfo = cinfo;
|
||||
cinfo->card = card;
|
||||
spin_lock_init(&cinfo->lock);
|
||||
INIT_LIST_HEAD(&cinfo->ncci_head);
|
||||
|
||||
switch (card->brdtype) {
|
||||
case BD_PCCARD: strcpy(cinfo->cardname, "HYSDN Hycard"); break;
|
||||
case BD_ERGO: strcpy(cinfo->cardname, "HYSDN Ergo2"); break;
|
||||
case BD_METRO: strcpy(cinfo->cardname, "HYSDN Metro4"); break;
|
||||
case BD_CHAMP2: strcpy(cinfo->cardname, "HYSDN Champ2"); break;
|
||||
case BD_PLEXUS: strcpy(cinfo->cardname, "HYSDN Plexus30"); break;
|
||||
default: strcpy(cinfo->cardname, "HYSDN ???"); break;
|
||||
}
|
||||
|
||||
ctrl = &cinfo->capi_ctrl;
|
||||
ctrl->driver_name = "hycapi";
|
||||
ctrl->driverdata = cinfo;
|
||||
ctrl->register_appl = hycapi_register_appl;
|
||||
ctrl->release_appl = hycapi_release_appl;
|
||||
ctrl->send_message = hycapi_send_message;
|
||||
ctrl->load_firmware = hycapi_load_firmware;
|
||||
ctrl->reset_ctr = hycapi_reset_ctr;
|
||||
ctrl->procinfo = hycapi_procinfo;
|
||||
ctrl->proc_show = hycapi_proc_show;
|
||||
strcpy(ctrl->name, cinfo->cardname);
|
||||
ctrl->owner = THIS_MODULE;
|
||||
|
||||
retval = attach_capi_ctr(ctrl);
|
||||
if (retval) {
|
||||
printk(KERN_ERR "hycapi: attach controller failed.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
/* fill in the blanks: */
|
||||
hycapi_fill_profile(card);
|
||||
capi_ctr_ready(ctrl);
|
||||
} else {
|
||||
/* resume output on stopped ctrl */
|
||||
ctrl = &card->hyctrlinfo->capi_ctrl;
|
||||
hycapi_fill_profile(card);
|
||||
capi_ctr_ready(ctrl);
|
||||
hycapi_restart_internal(ctrl);
|
||||
/* ctrl->resume_output(ctrl); */
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,400 +0,0 @@
|
||||
/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards
|
||||
* specific routines for booting and pof handling
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
#include "hysdn_pof.h"
|
||||
|
||||
/********************************/
|
||||
/* defines for pof read handler */
|
||||
/********************************/
|
||||
#define POF_READ_FILE_HEAD 0
|
||||
#define POF_READ_TAG_HEAD 1
|
||||
#define POF_READ_TAG_DATA 2
|
||||
|
||||
/************************************************************/
|
||||
/* definition of boot specific data area. This data is only */
|
||||
/* needed during boot and so allocated dynamically. */
|
||||
/************************************************************/
|
||||
struct boot_data {
|
||||
unsigned short Cryptor; /* for use with Decrypt function */
|
||||
unsigned short Nrecs; /* records remaining in file */
|
||||
unsigned char pof_state;/* actual state of read handler */
|
||||
unsigned char is_crypted;/* card data is crypted */
|
||||
int BufSize; /* actual number of bytes bufferd */
|
||||
int last_error; /* last occurred error */
|
||||
unsigned short pof_recid;/* actual pof recid */
|
||||
unsigned long pof_reclen;/* total length of pof record data */
|
||||
unsigned long pof_recoffset;/* actual offset inside pof record */
|
||||
union {
|
||||
unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
|
||||
tPofRecHdr PofRecHdr; /* header for actual record/chunk */
|
||||
tPofFileHdr PofFileHdr; /* header from POF file */
|
||||
tPofTimeStamp PofTime; /* time information */
|
||||
} buf;
|
||||
};
|
||||
|
||||
/*****************************************************/
|
||||
/* start decryption of successive POF file chuncks. */
|
||||
/* */
|
||||
/* to be called at start of POF file reading, */
|
||||
/* before starting any decryption on any POF record. */
|
||||
/*****************************************************/
|
||||
static void
|
||||
StartDecryption(struct boot_data *boot)
|
||||
{
|
||||
boot->Cryptor = CRYPT_STARTTERM;
|
||||
} /* StartDecryption */
|
||||
|
||||
|
||||
/***************************************************************/
|
||||
/* decrypt complete BootBuf */
|
||||
/* NOTE: decryption must be applied to all or none boot tags - */
|
||||
/* to HI and LO boot loader and (all) seq tags, because */
|
||||
/* global Cryptor is started for whole POF. */
|
||||
/***************************************************************/
|
||||
static void
|
||||
DecryptBuf(struct boot_data *boot, int cnt)
|
||||
{
|
||||
unsigned char *bufp = boot->buf.BootBuf;
|
||||
|
||||
while (cnt--) {
|
||||
boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
|
||||
*bufp++ ^= (unsigned char)boot->Cryptor;
|
||||
}
|
||||
} /* DecryptBuf */
|
||||
|
||||
/********************************************************************************/
|
||||
/* pof_handle_data executes the required actions dependent on the active record */
|
||||
/* id. If successful 0 is returned, a negative value shows an error. */
|
||||
/********************************************************************************/
|
||||
static int
|
||||
pof_handle_data(hysdn_card *card, int datlen)
|
||||
{
|
||||
struct boot_data *boot = card->boot; /* pointer to boot specific data */
|
||||
long l;
|
||||
unsigned char *imgp;
|
||||
int img_len;
|
||||
|
||||
/* handle the different record types */
|
||||
switch (boot->pof_recid) {
|
||||
|
||||
case TAG_TIMESTMP:
|
||||
if (card->debug_flags & LOG_POF_RECORD)
|
||||
hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
|
||||
break;
|
||||
|
||||
case TAG_CBOOTDTA:
|
||||
DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
|
||||
/* fall through */
|
||||
case TAG_BOOTDTA:
|
||||
if (card->debug_flags & LOG_POF_RECORD)
|
||||
hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
|
||||
(boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
|
||||
datlen, boot->pof_recoffset);
|
||||
|
||||
if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
|
||||
boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */
|
||||
return (boot->last_error);
|
||||
}
|
||||
imgp = boot->buf.BootBuf; /* start of buffer */
|
||||
img_len = datlen; /* maximum length to transfer */
|
||||
|
||||
l = POF_BOOT_LOADER_OFF_IN_PAGE -
|
||||
(boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
|
||||
if (l > 0) {
|
||||
/* buffer needs to be truncated */
|
||||
imgp += l; /* advance pointer */
|
||||
img_len -= l; /* adjust len */
|
||||
}
|
||||
/* at this point no special handling for data wrapping over buffer */
|
||||
/* is necessary, because the boot image always will be adjusted to */
|
||||
/* match a page boundary inside the buffer. */
|
||||
/* The buffer for the boot image on the card is filled in 2 cycles */
|
||||
/* first the 1024 hi-words are put in the buffer, then the low 1024 */
|
||||
/* word are handled in the same way with different offset. */
|
||||
|
||||
if (img_len > 0) {
|
||||
/* data available for copy */
|
||||
if ((boot->last_error =
|
||||
card->writebootimg(card, imgp,
|
||||
(boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
|
||||
return (boot->last_error);
|
||||
}
|
||||
break; /* end of case boot image hi/lo */
|
||||
|
||||
case TAG_CABSDATA:
|
||||
DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
|
||||
/* fall through */
|
||||
case TAG_ABSDATA:
|
||||
if (card->debug_flags & LOG_POF_RECORD)
|
||||
hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
|
||||
(boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
|
||||
datlen, boot->pof_recoffset);
|
||||
|
||||
if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0)
|
||||
return (boot->last_error); /* error writing data */
|
||||
|
||||
if (boot->pof_recoffset + datlen >= boot->pof_reclen)
|
||||
return (card->waitpofready(card)); /* data completely spooled, wait for ready */
|
||||
|
||||
break; /* end of case boot seq data */
|
||||
|
||||
default:
|
||||
if (card->debug_flags & LOG_POF_RECORD)
|
||||
hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
|
||||
datlen, boot->pof_recoffset);
|
||||
|
||||
break; /* simply skip record */
|
||||
} /* switch boot->pof_recid */
|
||||
|
||||
return (0);
|
||||
} /* pof_handle_data */
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* pof_write_buffer is called when the buffer has been filled with the needed */
|
||||
/* number of data bytes. The number delivered is additionally supplied for */
|
||||
/* verification. The functions handles the data and returns the needed number */
|
||||
/* of bytes for the next action. If the returned value is 0 or less an error */
|
||||
/* occurred and booting must be aborted. */
|
||||
/******************************************************************************/
|
||||
int
|
||||
pof_write_buffer(hysdn_card *card, int datlen)
|
||||
{
|
||||
struct boot_data *boot = card->boot; /* pointer to boot specific data */
|
||||
|
||||
if (!boot)
|
||||
return (-EFAULT); /* invalid call */
|
||||
if (boot->last_error < 0)
|
||||
return (boot->last_error); /* repeated error */
|
||||
|
||||
if (card->debug_flags & LOG_POF_WRITE)
|
||||
hysdn_addlog(card, "POF write: got %d bytes ", datlen);
|
||||
|
||||
switch (boot->pof_state) {
|
||||
case POF_READ_FILE_HEAD:
|
||||
if (card->debug_flags & LOG_POF_WRITE)
|
||||
hysdn_addlog(card, "POF write: checking file header");
|
||||
|
||||
if (datlen != sizeof(tPofFileHdr)) {
|
||||
boot->last_error = -EPOF_INTERNAL;
|
||||
break;
|
||||
}
|
||||
if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
|
||||
boot->last_error = -EPOF_BAD_MAGIC;
|
||||
break;
|
||||
}
|
||||
/* Setup the new state and vars */
|
||||
boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
|
||||
boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
|
||||
boot->last_error = sizeof(tPofRecHdr); /* new length */
|
||||
break;
|
||||
|
||||
case POF_READ_TAG_HEAD:
|
||||
if (card->debug_flags & LOG_POF_WRITE)
|
||||
hysdn_addlog(card, "POF write: checking tag header");
|
||||
|
||||
if (datlen != sizeof(tPofRecHdr)) {
|
||||
boot->last_error = -EPOF_INTERNAL;
|
||||
break;
|
||||
}
|
||||
boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */
|
||||
boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */
|
||||
boot->pof_recoffset = 0; /* no starting offset */
|
||||
|
||||
if (card->debug_flags & LOG_POF_RECORD)
|
||||
hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
|
||||
boot->pof_recid, boot->pof_reclen);
|
||||
|
||||
boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */
|
||||
if (boot->pof_reclen < BOOT_BUF_SIZE)
|
||||
boot->last_error = boot->pof_reclen; /* limit size */
|
||||
else
|
||||
boot->last_error = BOOT_BUF_SIZE; /* maximum */
|
||||
|
||||
if (!boot->last_error) { /* no data inside record */
|
||||
boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
|
||||
boot->last_error = sizeof(tPofRecHdr); /* new length */
|
||||
}
|
||||
break;
|
||||
|
||||
case POF_READ_TAG_DATA:
|
||||
if (card->debug_flags & LOG_POF_WRITE)
|
||||
hysdn_addlog(card, "POF write: getting tag data");
|
||||
|
||||
if (datlen != boot->last_error) {
|
||||
boot->last_error = -EPOF_INTERNAL;
|
||||
break;
|
||||
}
|
||||
if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
|
||||
return (boot->last_error); /* an error occurred */
|
||||
boot->pof_recoffset += datlen;
|
||||
if (boot->pof_recoffset >= boot->pof_reclen) {
|
||||
boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
|
||||
boot->last_error = sizeof(tPofRecHdr); /* new length */
|
||||
} else {
|
||||
if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
|
||||
boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */
|
||||
else
|
||||
boot->last_error = BOOT_BUF_SIZE; /* maximum */
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
boot->last_error = -EPOF_INTERNAL; /* unknown state */
|
||||
break;
|
||||
} /* switch (boot->pof_state) */
|
||||
|
||||
return (boot->last_error);
|
||||
} /* pof_write_buffer */
|
||||
|
||||
|
||||
/*******************************************************************************/
|
||||
/* pof_write_open is called when an open for boot on the cardlog device occurs. */
|
||||
/* The function returns the needed number of bytes for the next operation. If */
|
||||
/* the returned number is less or equal 0 an error specified by this code */
|
||||
/* occurred. Additionally the pointer to the buffer data area is set on success */
|
||||
/*******************************************************************************/
|
||||
int
|
||||
pof_write_open(hysdn_card *card, unsigned char **bufp)
|
||||
{
|
||||
struct boot_data *boot; /* pointer to boot specific data */
|
||||
|
||||
if (card->boot) {
|
||||
if (card->debug_flags & LOG_POF_OPEN)
|
||||
hysdn_addlog(card, "POF open: already opened for boot");
|
||||
return (-ERR_ALREADY_BOOT); /* boot already active */
|
||||
}
|
||||
/* error no mem available */
|
||||
if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
|
||||
if (card->debug_flags & LOG_MEM_ERR)
|
||||
hysdn_addlog(card, "POF open: unable to allocate mem");
|
||||
return (-EFAULT);
|
||||
}
|
||||
card->boot = boot;
|
||||
card->state = CARD_STATE_BOOTING;
|
||||
|
||||
card->stopcard(card); /* first stop the card */
|
||||
if (card->testram(card)) {
|
||||
if (card->debug_flags & LOG_POF_OPEN)
|
||||
hysdn_addlog(card, "POF open: DPRAM test failure");
|
||||
boot->last_error = -ERR_BOARD_DPRAM;
|
||||
card->state = CARD_STATE_BOOTERR; /* show boot error */
|
||||
return (boot->last_error);
|
||||
}
|
||||
boot->BufSize = 0; /* Buffer is empty */
|
||||
boot->pof_state = POF_READ_FILE_HEAD; /* read file header */
|
||||
StartDecryption(boot); /* if POF File should be encrypted */
|
||||
|
||||
if (card->debug_flags & LOG_POF_OPEN)
|
||||
hysdn_addlog(card, "POF open: success");
|
||||
|
||||
*bufp = boot->buf.BootBuf; /* point to buffer */
|
||||
return (sizeof(tPofFileHdr));
|
||||
} /* pof_write_open */
|
||||
|
||||
/********************************************************************************/
|
||||
/* pof_write_close is called when an close of boot on the cardlog device occurs. */
|
||||
/* The return value must be 0 if everything has happened as desired. */
|
||||
/********************************************************************************/
|
||||
int
|
||||
pof_write_close(hysdn_card *card)
|
||||
{
|
||||
struct boot_data *boot = card->boot; /* pointer to boot specific data */
|
||||
|
||||
if (!boot)
|
||||
return (-EFAULT); /* invalid call */
|
||||
|
||||
card->boot = NULL; /* no boot active */
|
||||
kfree(boot);
|
||||
|
||||
if (card->state == CARD_STATE_RUN)
|
||||
card->set_errlog_state(card, 1); /* activate error log */
|
||||
|
||||
if (card->debug_flags & LOG_POF_OPEN)
|
||||
hysdn_addlog(card, "POF close: success");
|
||||
|
||||
return (0);
|
||||
} /* pof_write_close */
|
||||
|
||||
/*********************************************************************************/
|
||||
/* EvalSysrTokData checks additional records delivered with the Sysready Message */
|
||||
/* when POF has been booted. A return value of 0 is used if no error occurred. */
|
||||
/*********************************************************************************/
|
||||
int
|
||||
EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
|
||||
{
|
||||
u_char *p;
|
||||
u_char crc;
|
||||
|
||||
if (card->debug_flags & LOG_POF_RECORD)
|
||||
hysdn_addlog(card, "SysReady Token data length %d", len);
|
||||
|
||||
if (len < 2) {
|
||||
hysdn_addlog(card, "SysReady Token Data to short");
|
||||
return (1);
|
||||
}
|
||||
for (p = cp, crc = 0; p < (cp + len - 2); p++)
|
||||
if ((crc & 0x80))
|
||||
crc = (((u_char) (crc << 1)) + 1) + *p;
|
||||
else
|
||||
crc = ((u_char) (crc << 1)) + *p;
|
||||
crc = ~crc;
|
||||
if (crc != *(cp + len - 1)) {
|
||||
hysdn_addlog(card, "SysReady Token Data invalid CRC");
|
||||
return (1);
|
||||
}
|
||||
len--; /* don't check CRC byte */
|
||||
while (len > 0) {
|
||||
|
||||
if (*cp == SYSR_TOK_END)
|
||||
return (0); /* End of Token stream */
|
||||
|
||||
if (len < (*(cp + 1) + 2)) {
|
||||
hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
|
||||
return (1);
|
||||
}
|
||||
switch (*cp) {
|
||||
case SYSR_TOK_B_CHAN: /* 1 */
|
||||
if (*(cp + 1) != 1)
|
||||
return (1); /* length invalid */
|
||||
card->bchans = *(cp + 2);
|
||||
break;
|
||||
|
||||
case SYSR_TOK_FAX_CHAN: /* 2 */
|
||||
if (*(cp + 1) != 1)
|
||||
return (1); /* length invalid */
|
||||
card->faxchans = *(cp + 2);
|
||||
break;
|
||||
|
||||
case SYSR_TOK_MAC_ADDR: /* 3 */
|
||||
if (*(cp + 1) != 6)
|
||||
return (1); /* length invalid */
|
||||
memcpy(card->mac_addr, cp + 2, 6);
|
||||
break;
|
||||
|
||||
default:
|
||||
hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
|
||||
break;
|
||||
}
|
||||
len -= (*(cp + 1) + 2); /* adjust len */
|
||||
cp += (*(cp + 1) + 2); /* and pointer */
|
||||
}
|
||||
|
||||
hysdn_addlog(card, "no end token found");
|
||||
return (1);
|
||||
} /* EvalSysrTokData */
|
@ -1,282 +0,0 @@
|
||||
/* $Id: hysdn_defs.h,v 1.5.6.3 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards
|
||||
* global definitions and exported vars and functions.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HYSDN_DEFS_H
|
||||
#define HYSDN_DEFS_H
|
||||
|
||||
#include <linux/hysdn_if.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include "ince1pc.h"
|
||||
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
#include <linux/capi.h>
|
||||
#include <linux/isdn/capicmd.h>
|
||||
#include <linux/isdn/capiutil.h>
|
||||
#include <linux/isdn/capilli.h>
|
||||
|
||||
/***************************/
|
||||
/* CAPI-Profile values. */
|
||||
/***************************/
|
||||
|
||||
#define GLOBAL_OPTION_INTERNAL_CONTROLLER 0x0001
|
||||
#define GLOBAL_OPTION_EXTERNAL_CONTROLLER 0x0002
|
||||
#define GLOBAL_OPTION_HANDSET 0x0004
|
||||
#define GLOBAL_OPTION_DTMF 0x0008
|
||||
#define GLOBAL_OPTION_SUPPL_SERVICES 0x0010
|
||||
#define GLOBAL_OPTION_CHANNEL_ALLOCATION 0x0020
|
||||
#define GLOBAL_OPTION_B_CHANNEL_OPERATION 0x0040
|
||||
|
||||
#define B1_PROT_64KBIT_HDLC 0x0001
|
||||
#define B1_PROT_64KBIT_TRANSPARENT 0x0002
|
||||
#define B1_PROT_V110_ASYNCH 0x0004
|
||||
#define B1_PROT_V110_SYNCH 0x0008
|
||||
#define B1_PROT_T30 0x0010
|
||||
#define B1_PROT_64KBIT_INV_HDLC 0x0020
|
||||
#define B1_PROT_56KBIT_TRANSPARENT 0x0040
|
||||
|
||||
#define B2_PROT_ISO7776 0x0001
|
||||
#define B2_PROT_TRANSPARENT 0x0002
|
||||
#define B2_PROT_SDLC 0x0004
|
||||
#define B2_PROT_LAPD 0x0008
|
||||
#define B2_PROT_T30 0x0010
|
||||
#define B2_PROT_PPP 0x0020
|
||||
#define B2_PROT_TRANSPARENT_IGNORE_B1_FRAMING_ERRORS 0x0040
|
||||
|
||||
#define B3_PROT_TRANSPARENT 0x0001
|
||||
#define B3_PROT_T90NL 0x0002
|
||||
#define B3_PROT_ISO8208 0x0004
|
||||
#define B3_PROT_X25_DCE 0x0008
|
||||
#define B3_PROT_T30 0x0010
|
||||
#define B3_PROT_T30EXT 0x0020
|
||||
|
||||
#define HYSDN_MAXVERSION 8
|
||||
|
||||
/* Number of sendbuffers in CAPI-queue */
|
||||
#define HYSDN_MAX_CAPI_SKB 20
|
||||
|
||||
#endif /* CONFIG_HYSDN_CAPI*/
|
||||
|
||||
/************************************************/
|
||||
/* constants and bits for debugging/log outputs */
|
||||
/************************************************/
|
||||
#define LOG_MAX_LINELEN 120
|
||||
#define DEB_OUT_SYSLOG 0x80000000 /* output to syslog instead of proc fs */
|
||||
#define LOG_MEM_ERR 0x00000001 /* log memory errors like kmalloc failure */
|
||||
#define LOG_POF_OPEN 0x00000010 /* log pof open and close activities */
|
||||
#define LOG_POF_RECORD 0x00000020 /* log pof record parser */
|
||||
#define LOG_POF_WRITE 0x00000040 /* log detailed pof write operation */
|
||||
#define LOG_POF_CARD 0x00000080 /* log pof related card functions */
|
||||
#define LOG_CNF_LINE 0x00000100 /* all conf lines are put to procfs */
|
||||
#define LOG_CNF_DATA 0x00000200 /* non comment conf lines are shown with channel */
|
||||
#define LOG_CNF_MISC 0x00000400 /* additional conf line debug outputs */
|
||||
#define LOG_SCHED_ASYN 0x00001000 /* debug schedulers async tx routines */
|
||||
#define LOG_PROC_OPEN 0x00100000 /* open and close from procfs are logged */
|
||||
#define LOG_PROC_ALL 0x00200000 /* all actions from procfs are logged */
|
||||
#define LOG_NET_INIT 0x00010000 /* network init and deinit logging */
|
||||
|
||||
#define DEF_DEB_FLAGS 0x7fff000f /* everything is logged to procfs */
|
||||
|
||||
/**********************************/
|
||||
/* proc filesystem name constants */
|
||||
/**********************************/
|
||||
#define PROC_SUBDIR_NAME "hysdn"
|
||||
#define PROC_CONF_BASENAME "cardconf"
|
||||
#define PROC_LOG_BASENAME "cardlog"
|
||||
|
||||
/***********************************/
|
||||
/* PCI 32 bit parms for IO and MEM */
|
||||
/***********************************/
|
||||
#define PCI_REG_PLX_MEM_BASE 0
|
||||
#define PCI_REG_PLX_IO_BASE 1
|
||||
#define PCI_REG_MEMORY_BASE 3
|
||||
|
||||
/**************/
|
||||
/* card types */
|
||||
/**************/
|
||||
#define BD_NONE 0U
|
||||
#define BD_PERFORMANCE 1U
|
||||
#define BD_VALUE 2U
|
||||
#define BD_PCCARD 3U
|
||||
#define BD_ERGO 4U
|
||||
#define BD_METRO 5U
|
||||
#define BD_CHAMP2 6U
|
||||
#define BD_PLEXUS 7U
|
||||
|
||||
/******************************************************/
|
||||
/* defined states for cards shown by reading cardconf */
|
||||
/******************************************************/
|
||||
#define CARD_STATE_UNUSED 0 /* never been used or booted */
|
||||
#define CARD_STATE_BOOTING 1 /* booting is in progress */
|
||||
#define CARD_STATE_BOOTERR 2 /* a previous boot was aborted */
|
||||
#define CARD_STATE_RUN 3 /* card is active */
|
||||
|
||||
/*******************************/
|
||||
/* defines for error_log_state */
|
||||
/*******************************/
|
||||
#define ERRLOG_STATE_OFF 0 /* error log is switched off, nothing to do */
|
||||
#define ERRLOG_STATE_ON 1 /* error log is switched on, wait for data */
|
||||
#define ERRLOG_STATE_START 2 /* start error logging */
|
||||
#define ERRLOG_STATE_STOP 3 /* stop error logging */
|
||||
|
||||
/*******************************/
|
||||
/* data structure for one card */
|
||||
/*******************************/
|
||||
typedef struct HYSDN_CARD {
|
||||
|
||||
/* general variables for the cards */
|
||||
int myid; /* own driver card id */
|
||||
unsigned char bus; /* pci bus the card is connected to */
|
||||
unsigned char devfn; /* slot+function bit encoded */
|
||||
unsigned short subsysid;/* PCI subsystem id */
|
||||
unsigned char brdtype; /* type of card */
|
||||
unsigned int bchans; /* number of available B-channels */
|
||||
unsigned int faxchans; /* number of available fax-channels */
|
||||
unsigned char mac_addr[6];/* MAC Address read from card */
|
||||
unsigned int irq; /* interrupt number */
|
||||
unsigned int iobase; /* IO-port base address */
|
||||
unsigned long plxbase; /* PLX memory base */
|
||||
unsigned long membase; /* DPRAM memory base */
|
||||
unsigned long memend; /* DPRAM memory end */
|
||||
void *dpram; /* mapped dpram */
|
||||
int state; /* actual state of card -> CARD_STATE_** */
|
||||
struct HYSDN_CARD *next; /* pointer to next card */
|
||||
|
||||
/* data areas for the /proc file system */
|
||||
void *proclog; /* pointer to proclog filesystem specific data */
|
||||
void *procconf; /* pointer to procconf filesystem specific data */
|
||||
|
||||
/* debugging and logging */
|
||||
unsigned char err_log_state;/* actual error log state of the card */
|
||||
unsigned long debug_flags;/* tells what should be debugged and where */
|
||||
void (*set_errlog_state) (struct HYSDN_CARD *, int);
|
||||
|
||||
/* interrupt handler + interrupt synchronisation */
|
||||
struct work_struct irq_queue; /* interrupt task queue */
|
||||
unsigned char volatile irq_enabled;/* interrupt enabled if != 0 */
|
||||
unsigned char volatile hw_lock;/* hardware is currently locked -> no access */
|
||||
|
||||
/* boot process */
|
||||
void *boot; /* pointer to boot private data */
|
||||
int (*writebootimg) (struct HYSDN_CARD *, unsigned char *, unsigned long);
|
||||
int (*writebootseq) (struct HYSDN_CARD *, unsigned char *, int);
|
||||
int (*waitpofready) (struct HYSDN_CARD *);
|
||||
int (*testram) (struct HYSDN_CARD *);
|
||||
|
||||
/* scheduler for data transfer (only async parts) */
|
||||
unsigned char async_data[256];/* async data to be sent (normally for config) */
|
||||
unsigned short volatile async_len;/* length of data to sent */
|
||||
unsigned short volatile async_channel;/* channel number for async transfer */
|
||||
int volatile async_busy; /* flag != 0 sending in progress */
|
||||
int volatile net_tx_busy; /* a network packet tx is in progress */
|
||||
|
||||
/* network interface */
|
||||
void *netif; /* pointer to network structure */
|
||||
|
||||
/* init and deinit stopcard for booting, too */
|
||||
void (*stopcard) (struct HYSDN_CARD *);
|
||||
void (*releasehardware) (struct HYSDN_CARD *);
|
||||
|
||||
spinlock_t hysdn_lock;
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
struct hycapictrl_info {
|
||||
char cardname[32];
|
||||
spinlock_t lock;
|
||||
int versionlen;
|
||||
char versionbuf[1024];
|
||||
char *version[HYSDN_MAXVERSION];
|
||||
|
||||
char infobuf[128]; /* for function procinfo */
|
||||
|
||||
struct HYSDN_CARD *card;
|
||||
struct capi_ctr capi_ctrl;
|
||||
struct sk_buff *skbs[HYSDN_MAX_CAPI_SKB];
|
||||
int in_idx, out_idx; /* indexes to buffer ring */
|
||||
int sk_count; /* number of buffers currently in ring */
|
||||
struct sk_buff *tx_skb; /* buffer for tx operation */
|
||||
|
||||
struct list_head ncci_head;
|
||||
} *hyctrlinfo;
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
} hysdn_card;
|
||||
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
typedef struct hycapictrl_info hycapictrl_info;
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
|
||||
|
||||
/*****************/
|
||||
/* exported vars */
|
||||
/*****************/
|
||||
extern hysdn_card *card_root; /* pointer to first card */
|
||||
|
||||
|
||||
|
||||
/*************************/
|
||||
/* im/exported functions */
|
||||
/*************************/
|
||||
|
||||
/* hysdn_procconf.c */
|
||||
extern int hysdn_procconf_init(void); /* init proc config filesys */
|
||||
extern void hysdn_procconf_release(void); /* deinit proc config filesys */
|
||||
|
||||
/* hysdn_proclog.c */
|
||||
extern int hysdn_proclog_init(hysdn_card *); /* init proc log entry */
|
||||
extern void hysdn_proclog_release(hysdn_card *); /* deinit proc log entry */
|
||||
extern void hysdn_addlog(hysdn_card *, char *, ...); /* output data to log */
|
||||
extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int); /* output card log */
|
||||
|
||||
/* boardergo.c */
|
||||
extern int ergo_inithardware(hysdn_card *card); /* get hardware -> module init */
|
||||
|
||||
/* hysdn_boot.c */
|
||||
extern int pof_write_close(hysdn_card *); /* close proc file after writing pof */
|
||||
extern int pof_write_open(hysdn_card *, unsigned char **); /* open proc file for writing pof */
|
||||
extern int pof_write_buffer(hysdn_card *, int); /* write boot data to card */
|
||||
extern int EvalSysrTokData(hysdn_card *, unsigned char *, int); /* Check Sysready Token Data */
|
||||
|
||||
/* hysdn_sched.c */
|
||||
extern int hysdn_sched_tx(hysdn_card *, unsigned char *,
|
||||
unsigned short volatile *, unsigned short volatile *,
|
||||
unsigned short);
|
||||
extern int hysdn_sched_rx(hysdn_card *, unsigned char *, unsigned short,
|
||||
unsigned short);
|
||||
extern int hysdn_tx_cfgline(hysdn_card *, unsigned char *,
|
||||
unsigned short); /* send one cfg line */
|
||||
|
||||
/* hysdn_net.c */
|
||||
extern unsigned int hynet_enable;
|
||||
extern int hysdn_net_create(hysdn_card *); /* create a new net device */
|
||||
extern int hysdn_net_release(hysdn_card *); /* delete the device */
|
||||
extern char *hysdn_net_getname(hysdn_card *); /* get name of net interface */
|
||||
extern void hysdn_tx_netack(hysdn_card *); /* acknowledge a packet tx */
|
||||
extern struct sk_buff *hysdn_tx_netget(hysdn_card *); /* get next network packet */
|
||||
extern void hysdn_rx_netpkt(hysdn_card *, unsigned char *,
|
||||
unsigned short); /* rxed packet from network */
|
||||
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
extern unsigned int hycapi_enable;
|
||||
extern int hycapi_capi_create(hysdn_card *); /* create a new capi device */
|
||||
extern int hycapi_capi_release(hysdn_card *); /* delete the device */
|
||||
extern int hycapi_capi_stop(hysdn_card *card); /* suspend */
|
||||
extern void hycapi_rx_capipkt(hysdn_card *card, unsigned char *buf,
|
||||
unsigned short len);
|
||||
extern void hycapi_tx_capiack(hysdn_card *card);
|
||||
extern struct sk_buff *hycapi_tx_capiget(hysdn_card *card);
|
||||
extern int hycapi_init(void);
|
||||
extern void hycapi_cleanup(void);
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
|
||||
#endif /* HYSDN_DEFS_H */
|
@ -1,213 +0,0 @@
|
||||
/* $Id: hysdn_init.c,v 1.6.6.6 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, init functions.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.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/poll.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
static struct pci_device_id hysdn_pci_tbl[] = {
|
||||
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
|
||||
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO, 0, 0, BD_METRO },
|
||||
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
|
||||
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, 0, 0, BD_CHAMP2 },
|
||||
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
|
||||
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, 0, 0, BD_ERGO },
|
||||
{ PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
|
||||
PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, 0, 0, BD_ERGO },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, hysdn_pci_tbl);
|
||||
MODULE_DESCRIPTION("ISDN4Linux: Driver for HYSDN cards");
|
||||
MODULE_AUTHOR("Werner Cornelius");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int cardmax; /* number of found cards */
|
||||
hysdn_card *card_root = NULL; /* pointer to first card */
|
||||
static hysdn_card *card_last = NULL; /* pointer to first card */
|
||||
|
||||
|
||||
/****************************************************************************/
|
||||
/* The module startup and shutdown code. Only compiled when used as module. */
|
||||
/* Using the driver as module is always advisable, because the booting */
|
||||
/* image becomes smaller and the driver code is only loaded when needed. */
|
||||
/* Additionally newer versions may be activated without rebooting. */
|
||||
/****************************************************************************/
|
||||
|
||||
/****************************************************************************/
|
||||
/* init_module is called once when the module is loaded to do all necessary */
|
||||
/* things like autodetect... */
|
||||
/* If the return value of this function is 0 the init has been successful */
|
||||
/* and the module is added to the list in /proc/modules, otherwise an error */
|
||||
/* is assumed and the module will not be kept in memory. */
|
||||
/****************************************************************************/
|
||||
|
||||
static int hysdn_pci_init_one(struct pci_dev *akt_pcidev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
hysdn_card *card;
|
||||
int rc;
|
||||
|
||||
rc = pci_enable_device(akt_pcidev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!(card = kzalloc(sizeof(hysdn_card), GFP_KERNEL))) {
|
||||
printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
|
||||
rc = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
card->myid = cardmax; /* set own id */
|
||||
card->bus = akt_pcidev->bus->number;
|
||||
card->devfn = akt_pcidev->devfn; /* slot + function */
|
||||
card->subsysid = akt_pcidev->subsystem_device;
|
||||
card->irq = akt_pcidev->irq;
|
||||
card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE);
|
||||
card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE);
|
||||
card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE);
|
||||
card->brdtype = BD_NONE; /* unknown */
|
||||
card->debug_flags = DEF_DEB_FLAGS; /* set default debug */
|
||||
card->faxchans = 0; /* default no fax channels */
|
||||
card->bchans = 2; /* and 2 b-channels */
|
||||
card->brdtype = ent->driver_data;
|
||||
|
||||
if (ergo_inithardware(card)) {
|
||||
printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
|
||||
rc = -EBUSY;
|
||||
goto err_out_card;
|
||||
}
|
||||
|
||||
cardmax++;
|
||||
card->next = NULL; /*end of chain */
|
||||
if (card_last)
|
||||
card_last->next = card; /* pointer to next card */
|
||||
else
|
||||
card_root = card;
|
||||
card_last = card; /* new chain end */
|
||||
|
||||
pci_set_drvdata(akt_pcidev, card);
|
||||
return 0;
|
||||
|
||||
err_out_card:
|
||||
kfree(card);
|
||||
err_out:
|
||||
pci_disable_device(akt_pcidev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void hysdn_pci_remove_one(struct pci_dev *akt_pcidev)
|
||||
{
|
||||
hysdn_card *card = pci_get_drvdata(akt_pcidev);
|
||||
|
||||
pci_set_drvdata(akt_pcidev, NULL);
|
||||
|
||||
if (card->stopcard)
|
||||
card->stopcard(card);
|
||||
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
hycapi_capi_release(card);
|
||||
#endif
|
||||
|
||||
if (card->releasehardware)
|
||||
card->releasehardware(card); /* free all hardware resources */
|
||||
|
||||
if (card == card_root) {
|
||||
card_root = card_root->next;
|
||||
if (!card_root)
|
||||
card_last = NULL;
|
||||
} else {
|
||||
hysdn_card *tmp = card_root;
|
||||
while (tmp) {
|
||||
if (tmp->next == card)
|
||||
tmp->next = card->next;
|
||||
card_last = tmp;
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(card);
|
||||
pci_disable_device(akt_pcidev);
|
||||
}
|
||||
|
||||
static struct pci_driver hysdn_pci_driver = {
|
||||
.name = "hysdn",
|
||||
.id_table = hysdn_pci_tbl,
|
||||
.probe = hysdn_pci_init_one,
|
||||
.remove = hysdn_pci_remove_one,
|
||||
};
|
||||
|
||||
static int hysdn_have_procfs;
|
||||
|
||||
static int __init
|
||||
hysdn_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
printk(KERN_NOTICE "HYSDN: module loaded\n");
|
||||
|
||||
rc = pci_register_driver(&hysdn_pci_driver);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
|
||||
|
||||
if (!hysdn_procconf_init())
|
||||
hysdn_have_procfs = 1;
|
||||
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
if (cardmax > 0) {
|
||||
if (hycapi_init()) {
|
||||
printk(KERN_ERR "HYCAPI: init failed\n");
|
||||
|
||||
if (hysdn_have_procfs)
|
||||
hysdn_procconf_release();
|
||||
|
||||
pci_unregister_driver(&hysdn_pci_driver);
|
||||
return -ESPIPE;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
|
||||
return 0; /* no error */
|
||||
} /* init_module */
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* cleanup_module is called when the module is released by the kernel. */
|
||||
/* The routine is only called if init_module has been successful and */
|
||||
/* the module counter has a value of 0. Otherwise this function will */
|
||||
/* not be called. This function must release all resources still allo- */
|
||||
/* cated as after the return from this function the module code will */
|
||||
/* be removed from memory. */
|
||||
/***********************************************************************/
|
||||
static void __exit
|
||||
hysdn_exit(void)
|
||||
{
|
||||
if (hysdn_have_procfs)
|
||||
hysdn_procconf_release();
|
||||
|
||||
pci_unregister_driver(&hysdn_pci_driver);
|
||||
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
hycapi_cleanup();
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
|
||||
printk(KERN_NOTICE "HYSDN: module unloaded\n");
|
||||
} /* cleanup_module */
|
||||
|
||||
module_init(hysdn_init);
|
||||
module_exit(hysdn_exit);
|
@ -1,330 +0,0 @@
|
||||
/* $Id: hysdn_net.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, net (ethernet type) handling routines.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
* This net module has been inspired by the skeleton driver from
|
||||
* Donald Becker (becker@CESDIS.gsfc.nasa.gov)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/inetdevice.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
unsigned int hynet_enable = 0xffffffff;
|
||||
module_param(hynet_enable, uint, 0);
|
||||
|
||||
#define MAX_SKB_BUFFERS 20 /* number of buffers for keeping TX-data */
|
||||
|
||||
/****************************************************************************/
|
||||
/* structure containing the complete network data. The structure is aligned */
|
||||
/* in a way that both, the device and statistics are kept inside it. */
|
||||
/* for proper access, the device structure MUST be the first var/struct */
|
||||
/* inside the definition. */
|
||||
/****************************************************************************/
|
||||
struct net_local {
|
||||
/* Tx control lock. This protects the transmit buffer ring
|
||||
* state along with the "tx full" state of the driver. This
|
||||
* means all netif_queue flow control actions are protected
|
||||
* by this lock as well.
|
||||
*/
|
||||
struct net_device *dev;
|
||||
spinlock_t lock;
|
||||
struct sk_buff *skbs[MAX_SKB_BUFFERS]; /* pointers to tx-skbs */
|
||||
int in_idx, out_idx; /* indexes to buffer ring */
|
||||
int sk_count; /* number of buffers currently in ring */
|
||||
}; /* net_local */
|
||||
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/* Open/initialize the board. This is called (in the current kernel) */
|
||||
/* sometime after booting when the 'ifconfig' program is run. */
|
||||
/* This routine should set everything up anew at each open, even */
|
||||
/* registers that "should" only need to be set once at boot, so that */
|
||||
/* there is non-reboot way to recover if something goes wrong. */
|
||||
/*********************************************************************/
|
||||
static int
|
||||
net_open(struct net_device *dev)
|
||||
{
|
||||
struct in_device *in_dev;
|
||||
hysdn_card *card = dev->ml_priv;
|
||||
int i;
|
||||
|
||||
netif_start_queue(dev); /* start tx-queueing */
|
||||
|
||||
/* Fill in the MAC-level header (if not already set) */
|
||||
if (!card->mac_addr[0]) {
|
||||
for (i = 0; i < ETH_ALEN; i++)
|
||||
dev->dev_addr[i] = 0xfc;
|
||||
if ((in_dev = dev->ip_ptr) != NULL) {
|
||||
const struct in_ifaddr *ifa;
|
||||
|
||||
rcu_read_lock();
|
||||
ifa = rcu_dereference(in_dev->ifa_list);
|
||||
if (ifa != NULL)
|
||||
memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ifa->ifa_local)), &ifa->ifa_local, sizeof(ifa->ifa_local));
|
||||
rcu_read_unlock();
|
||||
}
|
||||
} else
|
||||
memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
|
||||
|
||||
return (0);
|
||||
} /* net_open */
|
||||
|
||||
/*******************************************/
|
||||
/* flush the currently occupied tx-buffers */
|
||||
/* must only be called when device closed */
|
||||
/*******************************************/
|
||||
static void
|
||||
flush_tx_buffers(struct net_local *nl)
|
||||
{
|
||||
|
||||
while (nl->sk_count) {
|
||||
dev_kfree_skb(nl->skbs[nl->out_idx++]); /* free skb */
|
||||
if (nl->out_idx >= MAX_SKB_BUFFERS)
|
||||
nl->out_idx = 0; /* wrap around */
|
||||
nl->sk_count--;
|
||||
}
|
||||
} /* flush_tx_buffers */
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/* close/decativate the device. The device is not removed, but only */
|
||||
/* deactivated. */
|
||||
/*********************************************************************/
|
||||
static int
|
||||
net_close(struct net_device *dev)
|
||||
{
|
||||
|
||||
netif_stop_queue(dev); /* disable queueing */
|
||||
|
||||
flush_tx_buffers((struct net_local *) dev);
|
||||
|
||||
return (0); /* success */
|
||||
} /* net_close */
|
||||
|
||||
/************************************/
|
||||
/* send a packet on this interface. */
|
||||
/* new style for kernel >= 2.3.33 */
|
||||
/************************************/
|
||||
static netdev_tx_t
|
||||
net_send_packet(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct net_local *lp = (struct net_local *) dev;
|
||||
|
||||
spin_lock_irq(&lp->lock);
|
||||
|
||||
lp->skbs[lp->in_idx++] = skb; /* add to buffer list */
|
||||
if (lp->in_idx >= MAX_SKB_BUFFERS)
|
||||
lp->in_idx = 0; /* wrap around */
|
||||
lp->sk_count++; /* adjust counter */
|
||||
netif_trans_update(dev);
|
||||
|
||||
/* If we just used up the very last entry in the
|
||||
* TX ring on this device, tell the queueing
|
||||
* layer to send no more.
|
||||
*/
|
||||
if (lp->sk_count >= MAX_SKB_BUFFERS)
|
||||
netif_stop_queue(dev);
|
||||
|
||||
/* When the TX completion hw interrupt arrives, this
|
||||
* is when the transmit statistics are updated.
|
||||
*/
|
||||
|
||||
spin_unlock_irq(&lp->lock);
|
||||
|
||||
if (lp->sk_count <= 3) {
|
||||
schedule_work(&((hysdn_card *) dev->ml_priv)->irq_queue);
|
||||
}
|
||||
return NETDEV_TX_OK; /* success */
|
||||
} /* net_send_packet */
|
||||
|
||||
|
||||
|
||||
/***********************************************************************/
|
||||
/* acknowlegde a packet send. The network layer will be informed about */
|
||||
/* completion */
|
||||
/***********************************************************************/
|
||||
void
|
||||
hysdn_tx_netack(hysdn_card *card)
|
||||
{
|
||||
struct net_local *lp = card->netif;
|
||||
|
||||
if (!lp)
|
||||
return; /* non existing device */
|
||||
|
||||
|
||||
if (!lp->sk_count)
|
||||
return; /* error condition */
|
||||
|
||||
lp->dev->stats.tx_packets++;
|
||||
lp->dev->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
|
||||
|
||||
dev_kfree_skb(lp->skbs[lp->out_idx++]); /* free skb */
|
||||
if (lp->out_idx >= MAX_SKB_BUFFERS)
|
||||
lp->out_idx = 0; /* wrap around */
|
||||
|
||||
if (lp->sk_count-- == MAX_SKB_BUFFERS) /* dec usage count */
|
||||
netif_start_queue((struct net_device *) lp);
|
||||
} /* hysdn_tx_netack */
|
||||
|
||||
/*****************************************************/
|
||||
/* we got a packet from the network, go and queue it */
|
||||
/*****************************************************/
|
||||
void
|
||||
hysdn_rx_netpkt(hysdn_card *card, unsigned char *buf, unsigned short len)
|
||||
{
|
||||
struct net_local *lp = card->netif;
|
||||
struct net_device *dev;
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (!lp)
|
||||
return; /* non existing device */
|
||||
|
||||
dev = lp->dev;
|
||||
dev->stats.rx_bytes += len;
|
||||
|
||||
skb = dev_alloc_skb(len);
|
||||
if (skb == NULL) {
|
||||
printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
|
||||
dev->name);
|
||||
dev->stats.rx_dropped++;
|
||||
return;
|
||||
}
|
||||
/* copy the data */
|
||||
skb_put_data(skb, buf, len);
|
||||
|
||||
/* determine the used protocol */
|
||||
skb->protocol = eth_type_trans(skb, dev);
|
||||
|
||||
dev->stats.rx_packets++; /* adjust packet count */
|
||||
|
||||
netif_rx(skb);
|
||||
} /* hysdn_rx_netpkt */
|
||||
|
||||
/*****************************************************/
|
||||
/* return the pointer to a network packet to be send */
|
||||
/*****************************************************/
|
||||
struct sk_buff *
|
||||
hysdn_tx_netget(hysdn_card *card)
|
||||
{
|
||||
struct net_local *lp = card->netif;
|
||||
|
||||
if (!lp)
|
||||
return (NULL); /* non existing device */
|
||||
|
||||
if (!lp->sk_count)
|
||||
return (NULL); /* nothing available */
|
||||
|
||||
return (lp->skbs[lp->out_idx]); /* next packet to send */
|
||||
} /* hysdn_tx_netget */
|
||||
|
||||
static const struct net_device_ops hysdn_netdev_ops = {
|
||||
.ndo_open = net_open,
|
||||
.ndo_stop = net_close,
|
||||
.ndo_start_xmit = net_send_packet,
|
||||
.ndo_set_mac_address = eth_mac_addr,
|
||||
.ndo_validate_addr = eth_validate_addr,
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hysdn_net_create creates a new net device for the given card. If a device */
|
||||
/* already exists, it will be deleted and created a new one. The return value */
|
||||
/* 0 announces success, else a negative error code will be returned. */
|
||||
/*****************************************************************************/
|
||||
int
|
||||
hysdn_net_create(hysdn_card *card)
|
||||
{
|
||||
struct net_device *dev;
|
||||
int i;
|
||||
struct net_local *lp;
|
||||
|
||||
if (!card) {
|
||||
printk(KERN_WARNING "No card-pt in hysdn_net_create!\n");
|
||||
return (-ENOMEM);
|
||||
}
|
||||
hysdn_net_release(card); /* release an existing net device */
|
||||
|
||||
dev = alloc_etherdev(sizeof(struct net_local));
|
||||
if (!dev) {
|
||||
printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
|
||||
return (-ENOMEM);
|
||||
}
|
||||
|
||||
lp = netdev_priv(dev);
|
||||
lp->dev = dev;
|
||||
|
||||
dev->netdev_ops = &hysdn_netdev_ops;
|
||||
spin_lock_init(&((struct net_local *) dev)->lock);
|
||||
|
||||
/* initialise necessary or informing fields */
|
||||
dev->base_addr = card->iobase; /* IO address */
|
||||
dev->irq = card->irq; /* irq */
|
||||
|
||||
dev->netdev_ops = &hysdn_netdev_ops;
|
||||
if ((i = register_netdev(dev))) {
|
||||
printk(KERN_WARNING "HYSDN: unable to create network device\n");
|
||||
free_netdev(dev);
|
||||
return (i);
|
||||
}
|
||||
dev->ml_priv = card; /* remember pointer to own data structure */
|
||||
card->netif = dev; /* setup the local pointer */
|
||||
|
||||
if (card->debug_flags & LOG_NET_INIT)
|
||||
hysdn_addlog(card, "network device created");
|
||||
return 0; /* and return success */
|
||||
} /* hysdn_net_create */
|
||||
|
||||
/***************************************************************************/
|
||||
/* hysdn_net_release deletes the net device for the given card. The return */
|
||||
/* value 0 announces success, else a negative error code will be returned. */
|
||||
/***************************************************************************/
|
||||
int
|
||||
hysdn_net_release(hysdn_card *card)
|
||||
{
|
||||
struct net_device *dev = card->netif;
|
||||
|
||||
if (!dev)
|
||||
return (0); /* non existing */
|
||||
|
||||
card->netif = NULL; /* clear out pointer */
|
||||
net_close(dev);
|
||||
|
||||
flush_tx_buffers((struct net_local *) dev); /* empty buffers */
|
||||
|
||||
unregister_netdev(dev); /* release the device */
|
||||
free_netdev(dev); /* release the memory allocated */
|
||||
if (card->debug_flags & LOG_NET_INIT)
|
||||
hysdn_addlog(card, "network device deleted");
|
||||
|
||||
return (0); /* always successful */
|
||||
} /* hysdn_net_release */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hysdn_net_getname returns a pointer to the name of the network interface. */
|
||||
/* if the interface is not existing, a "-" is returned. */
|
||||
/*****************************************************************************/
|
||||
char *
|
||||
hysdn_net_getname(hysdn_card *card)
|
||||
{
|
||||
struct net_device *dev = card->netif;
|
||||
|
||||
if (!dev)
|
||||
return ("-"); /* non existing */
|
||||
|
||||
return (dev->name);
|
||||
} /* hysdn_net_getname */
|
@ -1,78 +0,0 @@
|
||||
/* $Id: hysdn_pof.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, definitions used for handling pof-files.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/************************/
|
||||
/* POF specific defines */
|
||||
/************************/
|
||||
#define BOOT_BUF_SIZE 0x1000 /* =4096, maybe moved to other h file */
|
||||
#define CRYPT_FEEDTERM 0x8142
|
||||
#define CRYPT_STARTTERM 0x81a5
|
||||
/* max. timeout time in seconds
|
||||
* from end of booting to POF is ready
|
||||
*/
|
||||
#define POF_READY_TIME_OUT_SEC 10
|
||||
|
||||
/**********************************/
|
||||
/* defines for 1.stage boot image */
|
||||
/**********************************/
|
||||
|
||||
/* the POF file record containing the boot loader image
|
||||
* has 2 pages a 16KB:
|
||||
* 1. page contains the high 16-bit part of the 32-bit E1 words
|
||||
* 2. page contains the low 16-bit part of the 32-bit E1 words
|
||||
*
|
||||
* In each 16KB page we assume the start of the boot loader code
|
||||
* in the highest 2KB part (at offset 0x3800);
|
||||
* the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
|
||||
*/
|
||||
|
||||
#define POF_BOOT_LOADER_PAGE_SIZE 0x4000 /* =16384U */
|
||||
#define POF_BOOT_LOADER_TOTAL_SIZE (2U * POF_BOOT_LOADER_PAGE_SIZE)
|
||||
|
||||
#define POF_BOOT_LOADER_CODE_SIZE 0x0800 /* =2KB =2048U */
|
||||
|
||||
/* offset in boot page, where loader code may start */
|
||||
/* =0x3800= 14336U */
|
||||
#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
|
||||
|
||||
|
||||
/*--------------------------------------POF file record structs------------*/
|
||||
typedef struct PofFileHdr_tag { /* Pof file header */
|
||||
/*00 */ unsigned long Magic __attribute__((packed));
|
||||
/*04 */ unsigned long N_PofRecs __attribute__((packed));
|
||||
/*08 */
|
||||
} tPofFileHdr;
|
||||
|
||||
typedef struct PofRecHdr_tag { /* Pof record header */
|
||||
/*00 */ unsigned short PofRecId __attribute__((packed));
|
||||
/*02 */ unsigned long PofRecDataLen __attribute__((packed));
|
||||
/*06 */
|
||||
} tPofRecHdr;
|
||||
|
||||
typedef struct PofTimeStamp_tag {
|
||||
/*00 */ unsigned long UnixTime __attribute__((packed));
|
||||
/*04 */ unsigned char DateTimeText[0x28];
|
||||
/* =40 */
|
||||
/*2C */
|
||||
} tPofTimeStamp;
|
||||
|
||||
/* tPofFileHdr.Magic value: */
|
||||
#define TAGFILEMAGIC 0x464F501AUL
|
||||
/* tPofRecHdr.PofRecId values: */
|
||||
#define TAG_ABSDATA 0x1000 /* abs. data */
|
||||
#define TAG_BOOTDTA 0x1001 /* boot data */
|
||||
#define TAG_COMMENT 0x0020
|
||||
#define TAG_SYSCALL 0x0021
|
||||
#define TAG_FLOWCTRL 0x0022
|
||||
#define TAG_TIMESTMP 0x0010 /* date/time stamp of version */
|
||||
#define TAG_CABSDATA 0x1100 /* crypted abs. data */
|
||||
#define TAG_CBOOTDTA 0x1101 /* crypted boot data */
|
@ -1,411 +0,0 @@
|
||||
/* $Id: hysdn_procconf.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
|
||||
*
|
||||
* written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/cred.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <net/net_namespace.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
static DEFINE_MUTEX(hysdn_conf_mutex);
|
||||
|
||||
#define INFO_OUT_LEN 80 /* length of info line including lf */
|
||||
|
||||
/********************************************************/
|
||||
/* defines and data structure for conf write operations */
|
||||
/********************************************************/
|
||||
#define CONF_STATE_DETECT 0 /* waiting for detect */
|
||||
#define CONF_STATE_CONF 1 /* writing config data */
|
||||
#define CONF_STATE_POF 2 /* writing pof data */
|
||||
#define CONF_LINE_LEN 255 /* 255 chars max */
|
||||
|
||||
struct conf_writedata {
|
||||
hysdn_card *card; /* card the device is connected to */
|
||||
int buf_size; /* actual number of bytes in the buffer */
|
||||
int needed_size; /* needed size when reading pof */
|
||||
int state; /* actual interface states from above constants */
|
||||
unsigned char conf_line[CONF_LINE_LEN]; /* buffered conf line */
|
||||
unsigned short channel; /* active channel number */
|
||||
unsigned char *pof_buffer; /* buffer when writing pof */
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
/* process_line parses one config line and transfers it to the card if */
|
||||
/* necessary. */
|
||||
/* if the return value is negative an error occurred. */
|
||||
/***********************************************************************/
|
||||
static int
|
||||
process_line(struct conf_writedata *cnf)
|
||||
{
|
||||
unsigned char *cp = cnf->conf_line;
|
||||
int i;
|
||||
|
||||
if (cnf->card->debug_flags & LOG_CNF_LINE)
|
||||
hysdn_addlog(cnf->card, "conf line: %s", cp);
|
||||
|
||||
if (*cp == '-') { /* option */
|
||||
cp++; /* point to option char */
|
||||
|
||||
if (*cp++ != 'c')
|
||||
return (0); /* option unknown or used */
|
||||
i = 0; /* start value for channel */
|
||||
while ((*cp <= '9') && (*cp >= '0'))
|
||||
i = i * 10 + *cp++ - '0'; /* get decimal number */
|
||||
if (i > 65535) {
|
||||
if (cnf->card->debug_flags & LOG_CNF_MISC)
|
||||
hysdn_addlog(cnf->card, "conf channel invalid %d", i);
|
||||
return (-ERR_INV_CHAN); /* invalid channel */
|
||||
}
|
||||
cnf->channel = i & 0xFFFF; /* set new channel number */
|
||||
return (0); /* success */
|
||||
} /* option */
|
||||
if (*cp == '*') { /* line to send */
|
||||
if (cnf->card->debug_flags & LOG_CNF_DATA)
|
||||
hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
|
||||
return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
|
||||
cnf->channel)); /* send the line without * */
|
||||
} /* line to send */
|
||||
return (0);
|
||||
} /* process_line */
|
||||
|
||||
/***********************************/
|
||||
/* conf file operations and tables */
|
||||
/***********************************/
|
||||
|
||||
/****************************************************/
|
||||
/* write conf file -> boot or send cfg line to card */
|
||||
/****************************************************/
|
||||
static ssize_t
|
||||
hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
|
||||
{
|
||||
struct conf_writedata *cnf;
|
||||
int i;
|
||||
unsigned char ch, *cp;
|
||||
|
||||
if (!count)
|
||||
return (0); /* nothing to handle */
|
||||
|
||||
if (!(cnf = file->private_data))
|
||||
return (-EFAULT); /* should never happen */
|
||||
|
||||
if (cnf->state == CONF_STATE_DETECT) { /* auto detect cnf or pof data */
|
||||
if (copy_from_user(&ch, buf, 1)) /* get first char for detect */
|
||||
return (-EFAULT);
|
||||
|
||||
if (ch == 0x1A) {
|
||||
/* we detected a pof file */
|
||||
if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
|
||||
return (cnf->needed_size); /* an error occurred -> exit */
|
||||
cnf->buf_size = 0; /* buffer is empty */
|
||||
cnf->state = CONF_STATE_POF; /* new state */
|
||||
} else {
|
||||
/* conf data has been detected */
|
||||
cnf->buf_size = 0; /* buffer is empty */
|
||||
cnf->state = CONF_STATE_CONF; /* requested conf data write */
|
||||
if (cnf->card->state != CARD_STATE_RUN)
|
||||
return (-ERR_NOT_BOOTED);
|
||||
cnf->conf_line[CONF_LINE_LEN - 1] = 0; /* limit string length */
|
||||
cnf->channel = 4098; /* default channel for output */
|
||||
}
|
||||
} /* state was auto detect */
|
||||
if (cnf->state == CONF_STATE_POF) { /* pof write active */
|
||||
i = cnf->needed_size - cnf->buf_size; /* bytes still missing for write */
|
||||
if (i <= 0)
|
||||
return (-EINVAL); /* size error handling pof */
|
||||
|
||||
if (i < count)
|
||||
count = i; /* limit requested number of bytes */
|
||||
if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
|
||||
return (-EFAULT); /* error while copying */
|
||||
cnf->buf_size += count;
|
||||
|
||||
if (cnf->needed_size == cnf->buf_size) {
|
||||
cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size); /* write data */
|
||||
if (cnf->needed_size <= 0) {
|
||||
cnf->card->state = CARD_STATE_BOOTERR; /* show boot error */
|
||||
return (cnf->needed_size); /* an error occurred */
|
||||
}
|
||||
cnf->buf_size = 0; /* buffer is empty again */
|
||||
}
|
||||
}
|
||||
/* pof write active */
|
||||
else { /* conf write active */
|
||||
|
||||
if (cnf->card->state != CARD_STATE_RUN) {
|
||||
if (cnf->card->debug_flags & LOG_CNF_MISC)
|
||||
hysdn_addlog(cnf->card, "cnf write denied -> not booted");
|
||||
return (-ERR_NOT_BOOTED);
|
||||
}
|
||||
i = (CONF_LINE_LEN - 1) - cnf->buf_size; /* bytes available in buffer */
|
||||
if (i > 0) {
|
||||
/* copy remaining bytes into buffer */
|
||||
|
||||
if (count > i)
|
||||
count = i; /* limit transfer */
|
||||
if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
|
||||
return (-EFAULT); /* error while copying */
|
||||
|
||||
i = count; /* number of chars in buffer */
|
||||
cp = cnf->conf_line + cnf->buf_size;
|
||||
while (i) {
|
||||
/* search for end of line */
|
||||
if ((*cp < ' ') && (*cp != 9))
|
||||
break; /* end of line found */
|
||||
cp++;
|
||||
i--;
|
||||
} /* search for end of line */
|
||||
|
||||
if (i) {
|
||||
/* delimiter found */
|
||||
*cp++ = 0; /* string termination */
|
||||
count -= (i - 1); /* subtract remaining bytes from count */
|
||||
while ((i) && (*cp < ' ') && (*cp != 9)) {
|
||||
i--; /* discard next char */
|
||||
count++; /* mark as read */
|
||||
cp++; /* next char */
|
||||
}
|
||||
cnf->buf_size = 0; /* buffer is empty after transfer */
|
||||
if ((i = process_line(cnf)) < 0) /* handle the line */
|
||||
count = i; /* return the error */
|
||||
}
|
||||
/* delimiter found */
|
||||
else {
|
||||
cnf->buf_size += count; /* add chars to string */
|
||||
if (cnf->buf_size >= CONF_LINE_LEN - 1) {
|
||||
if (cnf->card->debug_flags & LOG_CNF_MISC)
|
||||
hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
|
||||
return (-ERR_CONF_LONG);
|
||||
}
|
||||
} /* not delimited */
|
||||
|
||||
}
|
||||
/* copy remaining bytes into buffer */
|
||||
else {
|
||||
if (cnf->card->debug_flags & LOG_CNF_MISC)
|
||||
hysdn_addlog(cnf->card, "cnf line too long");
|
||||
return (-ERR_CONF_LONG);
|
||||
}
|
||||
} /* conf write active */
|
||||
|
||||
return (count);
|
||||
} /* hysdn_conf_write */
|
||||
|
||||
/*******************************************/
|
||||
/* read conf file -> output card info data */
|
||||
/*******************************************/
|
||||
static ssize_t
|
||||
hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t *off)
|
||||
{
|
||||
char *cp;
|
||||
|
||||
if (!(file->f_mode & FMODE_READ))
|
||||
return -EPERM; /* no permission to read */
|
||||
|
||||
if (!(cp = file->private_data))
|
||||
return -EFAULT; /* should never happen */
|
||||
|
||||
return simple_read_from_buffer(buf, count, off, cp, strlen(cp));
|
||||
} /* hysdn_conf_read */
|
||||
|
||||
/******************/
|
||||
/* open conf file */
|
||||
/******************/
|
||||
static int
|
||||
hysdn_conf_open(struct inode *ino, struct file *filep)
|
||||
{
|
||||
hysdn_card *card;
|
||||
struct conf_writedata *cnf;
|
||||
char *cp, *tmp;
|
||||
|
||||
/* now search the addressed card */
|
||||
mutex_lock(&hysdn_conf_mutex);
|
||||
card = PDE_DATA(ino);
|
||||
if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
|
||||
hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
|
||||
filep->f_cred->fsuid, filep->f_cred->fsgid,
|
||||
filep->f_mode);
|
||||
|
||||
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
|
||||
/* write only access -> write boot file or conf line */
|
||||
|
||||
if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
|
||||
mutex_unlock(&hysdn_conf_mutex);
|
||||
return (-EFAULT);
|
||||
}
|
||||
cnf->card = card;
|
||||
cnf->buf_size = 0; /* nothing buffered */
|
||||
cnf->state = CONF_STATE_DETECT; /* start auto detect */
|
||||
filep->private_data = cnf;
|
||||
|
||||
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
|
||||
/* read access -> output card info data */
|
||||
|
||||
if (!(tmp = kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
|
||||
mutex_unlock(&hysdn_conf_mutex);
|
||||
return (-EFAULT); /* out of memory */
|
||||
}
|
||||
filep->private_data = tmp; /* start of string */
|
||||
|
||||
/* first output a headline */
|
||||
sprintf(tmp, "id bus slot type irq iobase dp-mem b-chans fax-chans state device");
|
||||
cp = tmp; /* start of string */
|
||||
while (*cp)
|
||||
cp++;
|
||||
while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
|
||||
*cp++ = ' ';
|
||||
*cp++ = '\n';
|
||||
|
||||
/* and now the data */
|
||||
sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08lx %7d %9d %3d %s",
|
||||
card->myid,
|
||||
card->bus,
|
||||
PCI_SLOT(card->devfn),
|
||||
card->brdtype,
|
||||
card->irq,
|
||||
card->iobase,
|
||||
card->membase,
|
||||
card->bchans,
|
||||
card->faxchans,
|
||||
card->state,
|
||||
hysdn_net_getname(card));
|
||||
while (*cp)
|
||||
cp++;
|
||||
while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
|
||||
*cp++ = ' ';
|
||||
*cp++ = '\n';
|
||||
*cp = 0; /* end of string */
|
||||
} else { /* simultaneous read/write access forbidden ! */
|
||||
mutex_unlock(&hysdn_conf_mutex);
|
||||
return (-EPERM); /* no permission this time */
|
||||
}
|
||||
mutex_unlock(&hysdn_conf_mutex);
|
||||
return nonseekable_open(ino, filep);
|
||||
} /* hysdn_conf_open */
|
||||
|
||||
/***************************/
|
||||
/* close a config file. */
|
||||
/***************************/
|
||||
static int
|
||||
hysdn_conf_close(struct inode *ino, struct file *filep)
|
||||
{
|
||||
hysdn_card *card;
|
||||
struct conf_writedata *cnf;
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock(&hysdn_conf_mutex);
|
||||
card = PDE_DATA(ino);
|
||||
if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
|
||||
hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
|
||||
filep->f_cred->fsuid, filep->f_cred->fsgid,
|
||||
filep->f_mode);
|
||||
|
||||
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
|
||||
/* write only access -> write boot file or conf line */
|
||||
if (filep->private_data) {
|
||||
cnf = filep->private_data;
|
||||
|
||||
if (cnf->state == CONF_STATE_POF)
|
||||
retval = pof_write_close(cnf->card); /* close the pof write */
|
||||
kfree(filep->private_data); /* free allocated memory for buffer */
|
||||
|
||||
} /* handle write private data */
|
||||
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
|
||||
/* read access -> output card info data */
|
||||
|
||||
kfree(filep->private_data); /* release memory */
|
||||
}
|
||||
mutex_unlock(&hysdn_conf_mutex);
|
||||
return (retval);
|
||||
} /* hysdn_conf_close */
|
||||
|
||||
/******************************************************/
|
||||
/* table for conf filesystem functions defined above. */
|
||||
/******************************************************/
|
||||
static const struct file_operations conf_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = hysdn_conf_read,
|
||||
.write = hysdn_conf_write,
|
||||
.open = hysdn_conf_open,
|
||||
.release = hysdn_conf_close,
|
||||
};
|
||||
|
||||
/*****************************/
|
||||
/* hysdn subdir in /proc/net */
|
||||
/*****************************/
|
||||
struct proc_dir_entry *hysdn_proc_entry = NULL;
|
||||
|
||||
/*******************************************************************************/
|
||||
/* hysdn_procconf_init is called when the module is loaded and after the cards */
|
||||
/* have been detected. The needed proc dir and card config files are created. */
|
||||
/* The log init is called at last. */
|
||||
/*******************************************************************************/
|
||||
int
|
||||
hysdn_procconf_init(void)
|
||||
{
|
||||
hysdn_card *card;
|
||||
unsigned char conf_name[20];
|
||||
|
||||
hysdn_proc_entry = proc_mkdir(PROC_SUBDIR_NAME, init_net.proc_net);
|
||||
if (!hysdn_proc_entry) {
|
||||
printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
|
||||
return (-1);
|
||||
}
|
||||
card = card_root; /* point to first card */
|
||||
while (card) {
|
||||
|
||||
sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
|
||||
if ((card->procconf = (void *) proc_create_data(conf_name,
|
||||
S_IFREG | S_IRUGO | S_IWUSR,
|
||||
hysdn_proc_entry,
|
||||
&conf_fops,
|
||||
card)) != NULL) {
|
||||
hysdn_proclog_init(card); /* init the log file entry */
|
||||
}
|
||||
card = card->next; /* next entry */
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE "HYSDN: procfs initialised\n");
|
||||
return 0;
|
||||
} /* hysdn_procconf_init */
|
||||
|
||||
/*************************************************************************************/
|
||||
/* hysdn_procconf_release is called when the module is unloaded and before the cards */
|
||||
/* resources are released. The module counter is assumed to be 0 ! */
|
||||
/*************************************************************************************/
|
||||
void
|
||||
hysdn_procconf_release(void)
|
||||
{
|
||||
hysdn_card *card;
|
||||
unsigned char conf_name[20];
|
||||
|
||||
card = card_root; /* start with first card */
|
||||
while (card) {
|
||||
|
||||
sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
|
||||
if (card->procconf)
|
||||
remove_proc_entry(conf_name, hysdn_proc_entry);
|
||||
|
||||
hysdn_proclog_release(card); /* init the log file entry */
|
||||
|
||||
card = card->next; /* point to next card */
|
||||
}
|
||||
|
||||
remove_proc_entry(PROC_SUBDIR_NAME, init_net.proc_net);
|
||||
}
|
@ -1,357 +0,0 @@
|
||||
/* $Id: hysdn_proclog.c,v 1.9.6.3 2001/09/23 22:24:54 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards, /proc/net filesystem log functions.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.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/proc_fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
/* the proc subdir for the interface is defined in the procconf module */
|
||||
extern struct proc_dir_entry *hysdn_proc_entry;
|
||||
|
||||
static DEFINE_MUTEX(hysdn_log_mutex);
|
||||
static void put_log_buffer(hysdn_card *card, char *cp);
|
||||
|
||||
/*************************************************/
|
||||
/* structure keeping ascii log for device output */
|
||||
/*************************************************/
|
||||
struct log_data {
|
||||
struct log_data *next;
|
||||
unsigned long usage_cnt;/* number of files still to work */
|
||||
void *proc_ctrl; /* pointer to own control procdata structure */
|
||||
char log_start[2]; /* log string start (final len aligned by size) */
|
||||
};
|
||||
|
||||
/**********************************************/
|
||||
/* structure holding proc entrys for one card */
|
||||
/**********************************************/
|
||||
struct procdata {
|
||||
struct proc_dir_entry *log; /* log entry */
|
||||
char log_name[15]; /* log filename */
|
||||
struct log_data *log_head, *log_tail; /* head and tail for queue */
|
||||
int if_used; /* open count for interface */
|
||||
unsigned char logtmp[LOG_MAX_LINELEN];
|
||||
wait_queue_head_t rd_queue;
|
||||
};
|
||||
|
||||
|
||||
/**********************************************/
|
||||
/* log function for cards error log interface */
|
||||
/**********************************************/
|
||||
void
|
||||
hysdn_card_errlog(hysdn_card *card, tErrLogEntry *logp, int maxsize)
|
||||
{
|
||||
char buf[ERRLOG_TEXT_SIZE + 40];
|
||||
|
||||
sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
|
||||
put_log_buffer(card, buf); /* output the string */
|
||||
} /* hysdn_card_errlog */
|
||||
|
||||
/***************************************************/
|
||||
/* Log function using format specifiers for output */
|
||||
/***************************************************/
|
||||
void
|
||||
hysdn_addlog(hysdn_card *card, char *fmt, ...)
|
||||
{
|
||||
struct procdata *pd = card->proclog;
|
||||
char *cp;
|
||||
va_list args;
|
||||
|
||||
if (!pd)
|
||||
return; /* log structure non existent */
|
||||
|
||||
cp = pd->logtmp;
|
||||
cp += sprintf(cp, "HYSDN: card %d ", card->myid);
|
||||
|
||||
va_start(args, fmt);
|
||||
cp += vsprintf(cp, fmt, args);
|
||||
va_end(args);
|
||||
*cp++ = '\n';
|
||||
*cp = 0;
|
||||
|
||||
if (card->debug_flags & DEB_OUT_SYSLOG)
|
||||
printk(KERN_INFO "%s", pd->logtmp);
|
||||
else
|
||||
put_log_buffer(card, pd->logtmp);
|
||||
|
||||
} /* hysdn_addlog */
|
||||
|
||||
/********************************************/
|
||||
/* put an log buffer into the log queue. */
|
||||
/* This buffer will be kept until all files */
|
||||
/* opened for read got the contents. */
|
||||
/* Flushes buffers not longer in use. */
|
||||
/********************************************/
|
||||
static void
|
||||
put_log_buffer(hysdn_card *card, char *cp)
|
||||
{
|
||||
struct log_data *ib;
|
||||
struct procdata *pd = card->proclog;
|
||||
unsigned long flags;
|
||||
|
||||
if (!pd)
|
||||
return;
|
||||
if (!cp)
|
||||
return;
|
||||
if (!*cp)
|
||||
return;
|
||||
if (pd->if_used <= 0)
|
||||
return; /* no open file for read */
|
||||
|
||||
if (!(ib = kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
|
||||
return; /* no memory */
|
||||
strcpy(ib->log_start, cp); /* set output string */
|
||||
ib->next = NULL;
|
||||
ib->proc_ctrl = pd; /* point to own control structure */
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags);
|
||||
ib->usage_cnt = pd->if_used;
|
||||
if (!pd->log_head)
|
||||
pd->log_head = ib; /* new head */
|
||||
else
|
||||
pd->log_tail->next = ib; /* follows existing messages */
|
||||
pd->log_tail = ib; /* new tail */
|
||||
|
||||
/* delete old entrys */
|
||||
while (pd->log_head->next) {
|
||||
if ((pd->log_head->usage_cnt <= 0) &&
|
||||
(pd->log_head->next->usage_cnt <= 0)) {
|
||||
ib = pd->log_head;
|
||||
pd->log_head = pd->log_head->next;
|
||||
kfree(ib);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} /* pd->log_head->next */
|
||||
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
|
||||
wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */
|
||||
} /* put_log_buffer */
|
||||
|
||||
|
||||
/******************************/
|
||||
/* file operations and tables */
|
||||
/******************************/
|
||||
|
||||
/****************************************/
|
||||
/* write log file -> set log level bits */
|
||||
/****************************************/
|
||||
static ssize_t
|
||||
hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t *off)
|
||||
{
|
||||
int rc;
|
||||
hysdn_card *card = file->private_data;
|
||||
|
||||
rc = kstrtoul_from_user(buf, count, 0, &card->debug_flags);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
|
||||
return (count);
|
||||
} /* hysdn_log_write */
|
||||
|
||||
/******************/
|
||||
/* read log file */
|
||||
/******************/
|
||||
static ssize_t
|
||||
hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t *off)
|
||||
{
|
||||
struct log_data *inf;
|
||||
int len;
|
||||
hysdn_card *card = PDE_DATA(file_inode(file));
|
||||
|
||||
if (!(inf = *((struct log_data **) file->private_data))) {
|
||||
struct procdata *pd = card->proclog;
|
||||
if (file->f_flags & O_NONBLOCK)
|
||||
return (-EAGAIN);
|
||||
|
||||
wait_event_interruptible(pd->rd_queue, (inf =
|
||||
*((struct log_data **) file->private_data)));
|
||||
}
|
||||
if (!inf)
|
||||
return (0);
|
||||
|
||||
inf->usage_cnt--; /* new usage count */
|
||||
file->private_data = &inf->next; /* next structure */
|
||||
if ((len = strlen(inf->log_start)) <= count) {
|
||||
if (copy_to_user(buf, inf->log_start, len))
|
||||
return -EFAULT;
|
||||
*off += len;
|
||||
return (len);
|
||||
}
|
||||
return (0);
|
||||
} /* hysdn_log_read */
|
||||
|
||||
/******************/
|
||||
/* open log file */
|
||||
/******************/
|
||||
static int
|
||||
hysdn_log_open(struct inode *ino, struct file *filep)
|
||||
{
|
||||
hysdn_card *card = PDE_DATA(ino);
|
||||
|
||||
mutex_lock(&hysdn_log_mutex);
|
||||
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
|
||||
/* write only access -> write log level only */
|
||||
filep->private_data = card; /* remember our own card */
|
||||
} else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
|
||||
struct procdata *pd = card->proclog;
|
||||
unsigned long flags;
|
||||
|
||||
/* read access -> log/debug read */
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags);
|
||||
pd->if_used++;
|
||||
if (pd->log_head)
|
||||
filep->private_data = &pd->log_tail->next;
|
||||
else
|
||||
filep->private_data = &pd->log_head;
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
} else { /* simultaneous read/write access forbidden ! */
|
||||
mutex_unlock(&hysdn_log_mutex);
|
||||
return (-EPERM); /* no permission this time */
|
||||
}
|
||||
mutex_unlock(&hysdn_log_mutex);
|
||||
return nonseekable_open(ino, filep);
|
||||
} /* hysdn_log_open */
|
||||
|
||||
/*******************************************************************************/
|
||||
/* close a cardlog file. If the file has been opened for exclusive write it is */
|
||||
/* assumed as pof data input and the pof loader is noticed about. */
|
||||
/* Otherwise file is handled as log output. In this case the interface usage */
|
||||
/* count is decremented and all buffers are noticed of closing. If this file */
|
||||
/* was the last one to be closed, all buffers are freed. */
|
||||
/*******************************************************************************/
|
||||
static int
|
||||
hysdn_log_close(struct inode *ino, struct file *filep)
|
||||
{
|
||||
struct log_data *inf;
|
||||
struct procdata *pd;
|
||||
hysdn_card *card;
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock(&hysdn_log_mutex);
|
||||
if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
|
||||
/* write only access -> write debug level written */
|
||||
retval = 0; /* success */
|
||||
} else {
|
||||
/* read access -> log/debug read, mark one further file as closed */
|
||||
|
||||
inf = *((struct log_data **) filep->private_data); /* get first log entry */
|
||||
if (inf)
|
||||
pd = (struct procdata *) inf->proc_ctrl; /* still entries there */
|
||||
else {
|
||||
/* no info available -> search card */
|
||||
card = PDE_DATA(file_inode(filep));
|
||||
pd = card->proclog; /* pointer to procfs log */
|
||||
}
|
||||
if (pd)
|
||||
pd->if_used--; /* decrement interface usage count by one */
|
||||
|
||||
while (inf) {
|
||||
inf->usage_cnt--; /* decrement usage count for buffers */
|
||||
inf = inf->next;
|
||||
}
|
||||
|
||||
if (pd)
|
||||
if (pd->if_used <= 0) /* delete buffers if last file closed */
|
||||
while (pd->log_head) {
|
||||
inf = pd->log_head;
|
||||
pd->log_head = pd->log_head->next;
|
||||
kfree(inf);
|
||||
}
|
||||
} /* read access */
|
||||
mutex_unlock(&hysdn_log_mutex);
|
||||
|
||||
return (retval);
|
||||
} /* hysdn_log_close */
|
||||
|
||||
/*************************************************/
|
||||
/* select/poll routine to be able using select() */
|
||||
/*************************************************/
|
||||
static __poll_t
|
||||
hysdn_log_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
__poll_t mask = 0;
|
||||
hysdn_card *card = PDE_DATA(file_inode(file));
|
||||
struct procdata *pd = card->proclog;
|
||||
|
||||
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
|
||||
return (mask); /* no polling for write supported */
|
||||
|
||||
poll_wait(file, &(pd->rd_queue), wait);
|
||||
|
||||
if (*((struct log_data **) file->private_data))
|
||||
mask |= EPOLLIN | EPOLLRDNORM;
|
||||
|
||||
return mask;
|
||||
} /* hysdn_log_poll */
|
||||
|
||||
/**************************************************/
|
||||
/* table for log filesystem functions defined above. */
|
||||
/**************************************************/
|
||||
static const struct file_operations log_fops =
|
||||
{
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = hysdn_log_read,
|
||||
.write = hysdn_log_write,
|
||||
.poll = hysdn_log_poll,
|
||||
.open = hysdn_log_open,
|
||||
.release = hysdn_log_close,
|
||||
};
|
||||
|
||||
|
||||
/***********************************************************************************/
|
||||
/* hysdn_proclog_init is called when the module is loaded after creating the cards */
|
||||
/* conf files. */
|
||||
/***********************************************************************************/
|
||||
int
|
||||
hysdn_proclog_init(hysdn_card *card)
|
||||
{
|
||||
struct procdata *pd;
|
||||
|
||||
/* create a cardlog proc entry */
|
||||
|
||||
if ((pd = kzalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
|
||||
sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
|
||||
pd->log = proc_create_data(pd->log_name,
|
||||
S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry,
|
||||
&log_fops, card);
|
||||
|
||||
init_waitqueue_head(&(pd->rd_queue));
|
||||
|
||||
card->proclog = (void *) pd; /* remember procfs structure */
|
||||
}
|
||||
return (0);
|
||||
} /* hysdn_proclog_init */
|
||||
|
||||
/************************************************************************************/
|
||||
/* hysdn_proclog_release is called when the module is unloaded and before the cards */
|
||||
/* conf file is released */
|
||||
/* The module counter is assumed to be 0 ! */
|
||||
/************************************************************************************/
|
||||
void
|
||||
hysdn_proclog_release(hysdn_card *card)
|
||||
{
|
||||
struct procdata *pd;
|
||||
|
||||
if ((pd = (struct procdata *) card->proclog) != NULL) {
|
||||
if (pd->log)
|
||||
remove_proc_entry(pd->log_name, hysdn_proc_entry);
|
||||
kfree(pd); /* release memory */
|
||||
card->proclog = NULL;
|
||||
}
|
||||
} /* hysdn_proclog_release */
|
@ -1,197 +0,0 @@
|
||||
/* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards
|
||||
* scheduler routines for handling exchange card <-> pc.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/signal.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "hysdn_defs.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hysdn_sched_rx is called from the cards handler to announce new data is */
|
||||
/* available from the card. The routine has to handle the data and return */
|
||||
/* with a nonzero code if the data could be worked (or even thrown away), if */
|
||||
/* no room to buffer the data is available a zero return tells the card */
|
||||
/* to keep the data until later. */
|
||||
/*****************************************************************************/
|
||||
int
|
||||
hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len,
|
||||
unsigned short chan)
|
||||
{
|
||||
|
||||
switch (chan) {
|
||||
case CHAN_NDIS_DATA:
|
||||
if (hynet_enable & (1 << card->myid)) {
|
||||
/* give packet to network handler */
|
||||
hysdn_rx_netpkt(card, buf, len);
|
||||
}
|
||||
break;
|
||||
|
||||
case CHAN_ERRLOG:
|
||||
hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
|
||||
if (card->err_log_state == ERRLOG_STATE_ON)
|
||||
card->err_log_state = ERRLOG_STATE_START; /* start new fetch */
|
||||
break;
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
case CHAN_CAPI:
|
||||
/* give packet to CAPI handler */
|
||||
if (hycapi_enable & (1 << card->myid)) {
|
||||
hycapi_rx_capipkt(card, buf, len);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
default:
|
||||
printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
|
||||
break;
|
||||
|
||||
} /* switch rx channel */
|
||||
|
||||
return (1); /* always handled */
|
||||
} /* hysdn_sched_rx */
|
||||
|
||||
/*****************************************************************************/
|
||||
/* hysdn_sched_tx is called from the cards handler to announce that there is */
|
||||
/* room in the tx-buffer to the card and data may be sent if needed. */
|
||||
/* If the routine wants to send data it must fill buf, len and chan with the */
|
||||
/* appropriate data and return a nonzero value. With a zero return no new */
|
||||
/* data to send is assumed. maxlen specifies the buffer size available for */
|
||||
/* sending. */
|
||||
/*****************************************************************************/
|
||||
int
|
||||
hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
|
||||
unsigned short volatile *len, unsigned short volatile *chan,
|
||||
unsigned short maxlen)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (card->net_tx_busy) {
|
||||
card->net_tx_busy = 0; /* reset flag */
|
||||
hysdn_tx_netack(card); /* acknowledge packet send */
|
||||
} /* a network packet has completely been transferred */
|
||||
/* first of all async requests are handled */
|
||||
if (card->async_busy) {
|
||||
if (card->async_len <= maxlen) {
|
||||
memcpy(buf, card->async_data, card->async_len);
|
||||
*len = card->async_len;
|
||||
*chan = card->async_channel;
|
||||
card->async_busy = 0; /* reset request */
|
||||
return (1);
|
||||
}
|
||||
card->async_busy = 0; /* in case of length error */
|
||||
} /* async request */
|
||||
if ((card->err_log_state == ERRLOG_STATE_START) &&
|
||||
(maxlen >= ERRLOG_CMD_REQ_SIZE)) {
|
||||
strcpy(buf, ERRLOG_CMD_REQ); /* copy the command */
|
||||
*len = ERRLOG_CMD_REQ_SIZE; /* buffer length */
|
||||
*chan = CHAN_ERRLOG; /* and channel */
|
||||
card->err_log_state = ERRLOG_STATE_ON; /* new state is on */
|
||||
return (1); /* tell that data should be send */
|
||||
} /* error log start and able to send */
|
||||
if ((card->err_log_state == ERRLOG_STATE_STOP) &&
|
||||
(maxlen >= ERRLOG_CMD_STOP_SIZE)) {
|
||||
strcpy(buf, ERRLOG_CMD_STOP); /* copy the command */
|
||||
*len = ERRLOG_CMD_STOP_SIZE; /* buffer length */
|
||||
*chan = CHAN_ERRLOG; /* and channel */
|
||||
card->err_log_state = ERRLOG_STATE_OFF; /* new state is off */
|
||||
return (1); /* tell that data should be send */
|
||||
} /* error log start and able to send */
|
||||
/* now handle network interface packets */
|
||||
if ((hynet_enable & (1 << card->myid)) &&
|
||||
(skb = hysdn_tx_netget(card)) != NULL)
|
||||
{
|
||||
if (skb->len <= maxlen) {
|
||||
/* copy the packet to the buffer */
|
||||
skb_copy_from_linear_data(skb, buf, skb->len);
|
||||
*len = skb->len;
|
||||
*chan = CHAN_NDIS_DATA;
|
||||
card->net_tx_busy = 1; /* we are busy sending network data */
|
||||
return (1); /* go and send the data */
|
||||
} else
|
||||
hysdn_tx_netack(card); /* aknowledge packet -> throw away */
|
||||
} /* send a network packet if available */
|
||||
#ifdef CONFIG_HYSDN_CAPI
|
||||
if (((hycapi_enable & (1 << card->myid))) &&
|
||||
((skb = hycapi_tx_capiget(card)) != NULL))
|
||||
{
|
||||
if (skb->len <= maxlen) {
|
||||
skb_copy_from_linear_data(skb, buf, skb->len);
|
||||
*len = skb->len;
|
||||
*chan = CHAN_CAPI;
|
||||
hycapi_tx_capiack(card);
|
||||
return (1); /* go and send the data */
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_HYSDN_CAPI */
|
||||
return (0); /* nothing to send */
|
||||
} /* hysdn_sched_tx */
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* send one config line to the card and return 0 if successful, otherwise a */
|
||||
/* negative error code. */
|
||||
/* The function works with timeouts perhaps not giving the greatest speed */
|
||||
/* sending the line, but this should be meaningless because only some lines */
|
||||
/* are to be sent and this happens very seldom. */
|
||||
/*****************************************************************************/
|
||||
int
|
||||
hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan)
|
||||
{
|
||||
int cnt = 50; /* timeout intervalls */
|
||||
unsigned long flags;
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
|
||||
|
||||
while (card->async_busy) {
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg delayed");
|
||||
|
||||
msleep_interruptible(20); /* Timeout 20ms */
|
||||
if (!--cnt)
|
||||
return (-ERR_ASYNC_TIME); /* timed out */
|
||||
} /* wait for buffer to become free */
|
||||
|
||||
spin_lock_irqsave(&card->hysdn_lock, flags);
|
||||
strcpy(card->async_data, line);
|
||||
card->async_len = strlen(line) + 1;
|
||||
card->async_channel = chan;
|
||||
card->async_busy = 1; /* request transfer */
|
||||
|
||||
/* now queue the task */
|
||||
schedule_work(&card->irq_queue);
|
||||
spin_unlock_irqrestore(&card->hysdn_lock, flags);
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg data queued");
|
||||
|
||||
cnt++; /* short delay */
|
||||
|
||||
while (card->async_busy) {
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
|
||||
|
||||
msleep_interruptible(20); /* Timeout 20ms */
|
||||
if (!--cnt)
|
||||
return (-ERR_ASYNC_TIME); /* timed out */
|
||||
} /* wait for buffer to become free again */
|
||||
|
||||
if (card->debug_flags & LOG_SCHED_ASYN)
|
||||
hysdn_addlog(card, "async tx-cfg data send");
|
||||
|
||||
return (0); /* line send correctly */
|
||||
} /* hysdn_tx_cfgline */
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Linux driver for HYSDN cards
|
||||
* common definitions for both sides of the bus:
|
||||
* - conventions both spoolers must know
|
||||
* - channel numbers agreed upon
|
||||
*
|
||||
* Author M. Steinkopf
|
||||
* Copyright 1999 by M. Steinkopf
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __INCE1PC_H__
|
||||
#define __INCE1PC_H__
|
||||
|
||||
/* basic scalar definitions have same meanning,
|
||||
* but their declaration location depends on environment
|
||||
*/
|
||||
|
||||
/*--------------------------------------channel numbers---------------------*/
|
||||
#define CHAN_SYSTEM 0x0001 /* system channel (spooler to spooler) */
|
||||
#define CHAN_ERRLOG 0x0005 /* error logger */
|
||||
#define CHAN_CAPI 0x0064 /* CAPI interface */
|
||||
#define CHAN_NDIS_DATA 0x1001 /* NDIS data transfer */
|
||||
|
||||
/*--------------------------------------POF ready msg-----------------------*/
|
||||
/* NOTE: after booting POF sends system ready message to PC: */
|
||||
#define RDY_MAGIC 0x52535953UL /* 'SYSR' reversed */
|
||||
#define RDY_MAGIC_SIZE 4 /* size in bytes */
|
||||
|
||||
#define MAX_N_TOK_BYTES 255
|
||||
|
||||
#define MIN_RDY_MSG_SIZE RDY_MAGIC_SIZE
|
||||
#define MAX_RDY_MSG_SIZE (RDY_MAGIC_SIZE + MAX_N_TOK_BYTES)
|
||||
|
||||
#define SYSR_TOK_END 0
|
||||
#define SYSR_TOK_B_CHAN 1 /* nr. of B-Channels; DataLen=1; def: 2 */
|
||||
#define SYSR_TOK_FAX_CHAN 2 /* nr. of FAX Channels; DataLen=1; def: 0 */
|
||||
#define SYSR_TOK_MAC_ADDR 3 /* MAC-Address; DataLen=6; def: auto */
|
||||
#define SYSR_TOK_ESC 255 /* undefined data size yet */
|
||||
/* default values, if not corrected by token: */
|
||||
#define SYSR_TOK_B_CHAN_DEF 2 /* assume 2 B-Channels */
|
||||
#define SYSR_TOK_FAX_CHAN_DEF 1 /* assume 1 FAX Channel */
|
||||
|
||||
/* syntax of new SYSR token stream:
|
||||
* channel: CHAN_SYSTEM
|
||||
* msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
|
||||
* RDY_MAGIC_SIZE <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
|
||||
* msg : 0 1 2 3 {4 5 6 ..}
|
||||
* S Y S R MAX_N_TOK_BYTES bytes of TokenStream
|
||||
*
|
||||
* TokenStream := empty
|
||||
* | {NonEndTokenChunk} EndToken RotlCRC
|
||||
* NonEndTokenChunk:= NonEndTokenId DataLen [Data]
|
||||
* NonEndTokenId := 0x01 .. 0xFE 1 BYTE
|
||||
* DataLen := 0x00 .. 0xFF 1 BYTE
|
||||
* Data := DataLen bytes
|
||||
* EndToken := 0x00
|
||||
* RotlCRC := special 1 byte CRC over all NonEndTokenChunk bytes
|
||||
* s. RotlCRC algorithm
|
||||
*
|
||||
* RotlCRC algorithm:
|
||||
* ucSum= 0 1 unsigned char
|
||||
* for all NonEndTokenChunk bytes:
|
||||
* ROTL(ucSum,1) rotate left by 1
|
||||
* ucSum += Char; add current byte with swap around
|
||||
* RotlCRC= ~ucSum; invert all bits for result
|
||||
*
|
||||
* note:
|
||||
* - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
|
||||
*/
|
||||
|
||||
/*--------------------------------------error logger------------------------*/
|
||||
/* note: pof needs final 0 ! */
|
||||
#define ERRLOG_CMD_REQ "ERRLOG ON"
|
||||
#define ERRLOG_CMD_REQ_SIZE 10 /* with final 0 byte ! */
|
||||
#define ERRLOG_CMD_STOP "ERRLOG OFF"
|
||||
#define ERRLOG_CMD_STOP_SIZE 11 /* with final 0 byte ! */
|
||||
|
||||
#define ERRLOG_ENTRY_SIZE 64 /* sizeof(tErrLogEntry) */
|
||||
/* remaining text size = 55 */
|
||||
#define ERRLOG_TEXT_SIZE (ERRLOG_ENTRY_SIZE - 2 * 4 - 1)
|
||||
|
||||
typedef struct ErrLogEntry_tag {
|
||||
|
||||
/*00 */ unsigned long ulErrType;
|
||||
|
||||
/*04 */ unsigned long ulErrSubtype;
|
||||
|
||||
/*08 */ unsigned char ucTextSize;
|
||||
|
||||
/*09 */ unsigned char ucText[ERRLOG_TEXT_SIZE];
|
||||
/* ASCIIZ of len ucTextSize-1 */
|
||||
|
||||
/*40 */
|
||||
} tErrLogEntry;
|
||||
|
||||
|
||||
#if defined(__TURBOC__)
|
||||
#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
|
||||
#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
|
||||
#endif /* */
|
||||
#endif /* */
|
||||
|
||||
/*--------------------------------------DPRAM boot spooler------------------*/
|
||||
/* this is the struture used between pc and
|
||||
* hyperstone to exchange boot data
|
||||
*/
|
||||
#define DPRAM_SPOOLER_DATA_SIZE 0x20
|
||||
typedef struct DpramBootSpooler_tag {
|
||||
|
||||
/*00 */ unsigned char Len;
|
||||
|
||||
/*01 */ volatile unsigned char RdPtr;
|
||||
|
||||
/*02 */ unsigned char WrPtr;
|
||||
|
||||
/*03 */ unsigned char Data[DPRAM_SPOOLER_DATA_SIZE];
|
||||
|
||||
/*23 */
|
||||
} tDpramBootSpooler;
|
||||
|
||||
|
||||
#define DPRAM_SPOOLER_MIN_SIZE 5 /* Len+RdPtr+Wrptr+2*data */
|
||||
#define DPRAM_SPOOLER_DEF_SIZE 0x23 /* current default size */
|
||||
|
||||
/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/
|
||||
/* at DPRAM offset 0x1C00: */
|
||||
#define SIZE_RSV_SOFT_UART 0x1B0 /* 432 bytes reserved for SoftUart */
|
||||
|
||||
|
||||
#endif /* __INCE1PC_H__ */
|
@ -1,21 +0,0 @@
|
||||
/* $Id: b1pcmcia.h,v 1.1.8.2 2001/09/23 22:25:05 kai Exp $
|
||||
*
|
||||
* Exported functions of module b1pcmcia to be called by
|
||||
* avm_cs card services module.
|
||||
*
|
||||
* Copyright 1999 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _B1PCMCIA_H_
|
||||
#define _B1PCMCIA_H_
|
||||
|
||||
int b1pcmcia_addcard_b1(unsigned int port, unsigned irq);
|
||||
int b1pcmcia_addcard_m1(unsigned int port, unsigned irq);
|
||||
int b1pcmcia_addcard_m2(unsigned int port, unsigned irq);
|
||||
int b1pcmcia_delcard(unsigned int port, unsigned irq);
|
||||
|
||||
#endif /* _B1PCMCIA_H_ */
|
@ -1,39 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* interface to user space for the gigaset driver
|
||||
*
|
||||
* Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#ifndef GIGASET_INTERFACE_H
|
||||
#define GIGASET_INTERFACE_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
/* The magic IOCTL value for this interface. */
|
||||
#define GIGASET_IOCTL 0x47
|
||||
|
||||
/* enable/disable device control via character device (lock out ISDN subsys) */
|
||||
#define GIGASET_REDIR _IOWR(GIGASET_IOCTL, 0, int)
|
||||
|
||||
/* enable adapter configuration mode (M10x only) */
|
||||
#define GIGASET_CONFIG _IOWR(GIGASET_IOCTL, 1, int)
|
||||
|
||||
/* set break characters (M105 only) */
|
||||
#define GIGASET_BRKCHARS _IOW(GIGASET_IOCTL, 2, unsigned char[6])
|
||||
|
||||
/* get version information selected by arg[0] */
|
||||
#define GIGASET_VERSION _IOWR(GIGASET_IOCTL, 3, unsigned[4])
|
||||
/* values for GIGASET_VERSION arg[0] */
|
||||
#define GIGVER_DRIVER 0 /* get driver version */
|
||||
#define GIGVER_COMPAT 1 /* get interface compatibility version */
|
||||
#define GIGVER_FWBASE 2 /* get base station firmware version */
|
||||
|
||||
#endif
|
@ -1,34 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* $Id: hysdn_if.h,v 1.1.8.3 2001/09/23 22:25:05 kai Exp $
|
||||
*
|
||||
* Linux driver for HYSDN cards
|
||||
* ioctl definitions shared by hynetmgr and driver.
|
||||
*
|
||||
* Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
|
||||
* Copyright 1999 by Werner Cornelius (werner@titro.de)
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU General Public License, incorporated herein by reference.
|
||||
*
|
||||
*/
|
||||
|
||||
/****************/
|
||||
/* error values */
|
||||
/****************/
|
||||
#define ERR_NONE 0 /* no error occurred */
|
||||
#define ERR_ALREADY_BOOT 1000 /* we are already booting */
|
||||
#define EPOF_BAD_MAGIC 1001 /* bad magic in POF header */
|
||||
#define ERR_BOARD_DPRAM 1002 /* board DPRAM failed */
|
||||
#define EPOF_INTERNAL 1003 /* internal POF handler error */
|
||||
#define EPOF_BAD_IMG_SIZE 1004 /* POF boot image size invalid */
|
||||
#define ERR_BOOTIMG_FAIL 1005 /* 1. stage boot image did not start */
|
||||
#define ERR_BOOTSEQ_FAIL 1006 /* 2. stage boot seq handshake timeout */
|
||||
#define ERR_POF_TIMEOUT 1007 /* timeout waiting for card pof ready */
|
||||
#define ERR_NOT_BOOTED 1008 /* operation only allowed when booted */
|
||||
#define ERR_CONF_LONG 1009 /* conf line is too long */
|
||||
#define ERR_INV_CHAN 1010 /* invalid channel number */
|
||||
#define ERR_ASYNC_TIME 1011 /* timeout sending async data */
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user