net: remove ISDN subsystem and Bluetooth CMTP

Remove the ISDN (mISDN, CAPI) subsystem and Bluetooth CMTP protocol
from the kernel tree.

ISDN is a pretty old technology and it's unclear whether anyone still
uses it. I went over the last few years of git history and all the
commits are either tree-wide conversions or syzbot/static analyzer
fixes.

When we discussed removal in the past IIRC there were some concerns
about ISDN still being used in parts of Germany. Unfortunately, the
code base is quite old, none of the current maintainers are familiar
with it and AI tools will have a field day finding bugs here.

Delete this code and preserve it in an out-of-tree repository
for any remaining users:
https://github.com/linux-netdev/mod-orphan

UAPI constants AF_ISDN/PF_ISDN and the SELinux isdn_socket class
are preserved for ABI stability, but the rest of uAPI is removed.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Acked-by: Stephen Hemminger <stephen@networkplumber.org>
Acked-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260421022108.1299678-1-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Jakub Kicinski
2026-04-20 19:21:07 -07:00
parent 6d5431555d
commit 4f10f1dfb2
90 changed files with 5 additions and 44903 deletions

View File

@@ -3649,6 +3649,11 @@ S: Dag Hammerskjolds v. 3E
S: S-226 64 LUND
S: Sweden
N: Tilman Schmidt
E: tilman@imap.cc
D: Siemens Gigaset ISDN driver author and maintainer
D: ISDN CAPI subsystem contributions
N: Henning P. Schmiedehausen
E: hps@tanstaafl.de
D: added PCI support to the serial driver

View File

@@ -1,73 +0,0 @@
=======
Credits
=======
I want to thank all who contributed to this project and especially to:
(in alphabetical order)
Thomas Bogendörfer (tsbogend@bigbug.franken.de)
Tester, lots of bugfixes and hints.
Alan Cox (alan@lxorguk.ukuu.org.uk)
For help getting into standard-kernel.
Henner Eisen (eis@baty.hanse.de)
For X.25 implementation.
Volker Götz (volker@oops.franken.de)
For contribution of man-pages, the imontty-tool and a perfect
maintaining of the mailing-list at hub-wue.
Matthias Hessler (hessler@isdn4linux.de)
For creating and maintaining the FAQ.
Bernhard Hailer (Bernhard.Hailer@lrz.uni-muenchen.de)
For creating the FAQ, and the leafsite HOWTO.
Michael 'Ghandi' Herold (michael@abadonna.franken.de)
For contribution of the vbox answering machine.
Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
For his Sync-PPP-code.
Karsten Keil (keil@isdn4linux.de)
For adding 1TR6-support to the Teles-driver.
For the HiSax-driver.
Michael Knigge (knick@cove.han.de)
For contributing the imon-tool
Andreas Kool (akool@Kool.f.EUnet.de)
For contribution of the isdnlog/isdnrep-tool
Pedro Roque Marques (roque@di.fc.ul.pt)
For lot of new ideas and the pcbit driver.
Eberhard Mönkeberg (emoenke@gwdg.de)
For testing and help to get into kernel.
Thomas Neumann (tn@ruhr.de)
For help with Cisco-SLARP and keepalive
Jan den Ouden (denouden@groovin.xs4all.nl)
For contribution of the original teles-driver
Carsten Paeth (calle@calle.in-berlin.de)
For the AVM-B1-CAPI2.0 driver
Thomas Pfeiffer (pfeiffer@pds.de)
For V.110, extended T.70 and Hylafax extensions in isdn_tty.c
Max Riegel (riegel@max.franken.de)
For making the ICN hardware-documentation and test-equipment available.
Armin Schindler (mac@melware.de)
For the eicon active card driver.
Gerhard 'Fido' Schneider (fido@wuff.mayn.de)
For heavy-duty-beta-testing with his BBS ;)
Thomas Uhl (uhl@think.de)
For distributing the cards.
For pushing me to work ;-)

View File

@@ -1,14 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0
====
ISDN
====
.. toctree::
:maxdepth: 2
interface_capi
m_isdn
credits

View File

@@ -1,336 +0,0 @@
=========================================
Kernel CAPI Interface to Hardware Drivers
=========================================
1. Overview
===========
From the CAPI 2.0 specification:
COMMON-ISDN-API (CAPI) is an application programming interface standard used
to access ISDN equipment connected to basic rate interfaces (BRI) and primary
rate interfaces (PRI).
Kernel CAPI operates as a dispatching layer between CAPI applications and CAPI
hardware drivers. Hardware drivers register ISDN devices (controllers, in CAPI
lingo) with Kernel CAPI to indicate their readiness to provide their service
to CAPI applications. CAPI applications also register with Kernel CAPI,
requesting association with a CAPI device. Kernel CAPI then dispatches the
application registration to an available device, forwarding it to the
corresponding hardware driver. Kernel CAPI then forwards CAPI messages in both
directions between the application and the hardware driver.
Format and semantics of CAPI messages are specified in the CAPI 2.0 standard.
This standard is freely available from https://www.capi.org.
2. Driver and Device Registration
=================================
CAPI drivers must register each of the ISDN devices they control with Kernel
CAPI by calling the Kernel CAPI function attach_capi_ctr() with a pointer to a
struct capi_ctr before they can be used. This structure must be filled with
the names of the driver and controller, and a number of callback function
pointers which are subsequently used by Kernel CAPI for communicating with the
driver. The registration can be revoked by calling the function
detach_capi_ctr() with a pointer to the same struct capi_ctr.
Before the device can be actually used, the driver must fill in the device
information fields 'manu', 'version', 'profile' and 'serial' in the capi_ctr
structure of the device, and signal its readiness by calling capi_ctr_ready().
From then on, Kernel CAPI may call the registered callback functions for the
device.
If the device becomes unusable for any reason (shutdown, disconnect ...), the
driver has to call capi_ctr_down(). This will prevent further calls to the
callback functions by Kernel CAPI.
3. Application Registration and Communication
=============================================
Kernel CAPI forwards registration requests from applications (calls to CAPI
operation CAPI_REGISTER) to an appropriate hardware driver by calling its
register_appl() callback function. A unique Application ID (ApplID, u16) is
allocated by Kernel CAPI and passed to register_appl() along with the
parameter structure provided by the application. This is analogous to the
open() operation on regular files or character devices.
After a successful return from register_appl(), CAPI messages from the
application may be passed to the driver for the device via calls to the
send_message() callback function. Conversely, the driver may call Kernel
CAPI's capi_ctr_handle_message() function to pass a received CAPI message to
Kernel CAPI for forwarding to an application, specifying its ApplID.
Deregistration requests (CAPI operation CAPI_RELEASE) from applications are
forwarded as calls to the release_appl() callback function, passing the same
ApplID as with register_appl(). After return from release_appl(), no CAPI
messages for that application may be passed to or from the device anymore.
4. Data Structures
==================
4.1 struct capi_driver
----------------------
This structure describes a Kernel CAPI driver itself. It is used in the
register_capi_driver() and unregister_capi_driver() functions, and contains
the following non-private fields, all to be set by the driver before calling
register_capi_driver():
``char name[32]``
the name of the driver, as a zero-terminated ASCII string
``char revision[32]``
the revision number of the driver, as a zero-terminated ASCII string
4.2 struct capi_ctr
-------------------
This structure describes an ISDN device (controller) handled by a Kernel CAPI
driver. After registration via the attach_capi_ctr() function it is passed to
all controller specific lower layer interface and callback functions to
identify the controller to operate on.
It contains the following non-private fields:
to be set by the driver before calling attach_capi_ctr():
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``struct module *owner``
pointer to the driver module owning the device
``void *driverdata``
an opaque pointer to driver specific data, not touched by Kernel CAPI
``char name[32]``
the name of the controller, as a zero-terminated ASCII string
``char *driver_name``
the name of the driver, as a zero-terminated ASCII string
``int (*load_firmware)(struct capi_ctr *ctrlr, capiloaddata *ldata)``
(optional) pointer to a callback function for sending firmware and
configuration data to the device
The function may return before the operation has completed.
Completion must be signalled by a call to capi_ctr_ready().
Return value: 0 on success, error code on error
Called in process context.
``void (*reset_ctr)(struct capi_ctr *ctrlr)``
(optional) pointer to a callback function for stopping the device,
releasing all registered applications
The function may return before the operation has completed.
Completion must be signalled by a call to capi_ctr_down().
Called in process context.
``void (*register_appl)(struct capi_ctr *ctrlr, u16 applid, capi_register_params *rparam)``
pointers to callback function for registration of
applications with the device
Calls to these functions are serialized by Kernel CAPI so that only
one call to any of them is active at any time.
``void (*release_appl)(struct capi_ctr *ctrlr, u16 applid)``
pointers to callback functions deregistration of
applications with the device
Calls to these functions are serialized by Kernel CAPI so that only
one call to any of them is active at any time.
``u16 (*send_message)(struct capi_ctr *ctrlr, struct sk_buff *skb)``
pointer to a callback function for sending a CAPI message to the
device
Return value: CAPI error code
If the method returns 0 (CAPI_NOERROR) the driver has taken ownership
of the skb and the caller may no longer access it. If it returns a
non-zero (error) value then ownership of the skb returns to the caller
who may reuse or free it.
The return value should only be used to signal problems with respect
to accepting or queueing the message. Errors occurring during the
actual processing of the message should be signaled with an
appropriate reply message.
May be called in process or interrupt context.
Calls to this function are not serialized by Kernel CAPI, ie. it must
be prepared to be re-entered.
``char *(*procinfo)(struct capi_ctr *ctrlr)``
pointer to a callback function returning the entry for the device in
the CAPI controller info table, /proc/capi/controller
Note:
Callback functions except send_message() are never called in interrupt
context.
to be filled in before calling capi_ctr_ready():
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``u8 manu[CAPI_MANUFACTURER_LEN]``
value to return for CAPI_GET_MANUFACTURER
``capi_version version``
value to return for CAPI_GET_VERSION
``capi_profile profile``
value to return for CAPI_GET_PROFILE
``u8 serial[CAPI_SERIAL_LEN]``
value to return for CAPI_GET_SERIAL
4.3 SKBs
--------
CAPI messages are passed between Kernel CAPI and the driver via send_message()
and capi_ctr_handle_message(), stored in the data portion of a socket buffer
(skb). Each skb contains a single CAPI message coded according to the CAPI 2.0
standard.
For the data transfer messages, DATA_B3_REQ and DATA_B3_IND, the actual
payload data immediately follows the CAPI message itself within the same skb.
The Data and Data64 parameters are not used for processing. The Data64
parameter may be omitted by setting the length field of the CAPI message to 22
instead of 30.
4.4 The _cmsg Structure
-----------------------
(declared in <linux/isdn/capiutil.h>)
The _cmsg structure stores the contents of a CAPI 2.0 message in an easily
accessible form. It contains members for all possible CAPI 2.0 parameters,
including subparameters of the Additional Info and B Protocol structured
parameters, with the following exceptions:
* second Calling party number (CONNECT_IND)
* Data64 (DATA_B3_REQ and DATA_B3_IND)
* Sending complete (subparameter of Additional Info, CONNECT_REQ and INFO_REQ)
* Global Configuration (subparameter of B Protocol, CONNECT_REQ, CONNECT_RESP
and SELECT_B_PROTOCOL_REQ)
Only those parameters appearing in the message type currently being processed
are actually used. Unused members should be set to zero.
Members are named after the CAPI 2.0 standard names of the parameters they
represent. See <linux/isdn/capiutil.h> for the exact spelling. Member data
types are:
=========== =================================================================
u8 for CAPI parameters of type 'byte'
u16 for CAPI parameters of type 'word'
u32 for CAPI parameters of type 'dword'
_cstruct for CAPI parameters of type 'struct'
The member is a pointer to a buffer containing the parameter in
CAPI encoding (length + content). It may also be NULL, which will
be taken to represent an empty (zero length) parameter.
Subparameters are stored in encoded form within the content part.
_cmstruct alternative representation for CAPI parameters of type 'struct'
(used only for the 'Additional Info' and 'B Protocol' parameters)
The representation is a single byte containing one of the values:
CAPI_DEFAULT: The parameter is empty/absent.
CAPI_COMPOSE: The parameter is present.
Subparameter values are stored individually in the corresponding
_cmsg structure members.
=========== =================================================================
5. Lower Layer Interface Functions
==================================
::
int attach_capi_ctr(struct capi_ctr *ctrlr)
int detach_capi_ctr(struct capi_ctr *ctrlr)
register/unregister a device (controller) with Kernel CAPI
::
void capi_ctr_ready(struct capi_ctr *ctrlr)
void capi_ctr_down(struct capi_ctr *ctrlr)
signal controller ready/not ready
::
void capi_ctr_handle_message(struct capi_ctr * ctrlr, u16 applid,
struct sk_buff *skb)
pass a received CAPI message to Kernel CAPI
for forwarding to the specified application
6. Helper Functions and Macros
==============================
Macros to extract/set element values from/in a CAPI message header
(from <linux/isdn/capiutil.h>):
====================== ============================= ====================
Get Macro Set Macro Element (Type)
====================== ============================= ====================
CAPIMSG_LEN(m) CAPIMSG_SETLEN(m, len) Total Length (u16)
CAPIMSG_APPID(m) CAPIMSG_SETAPPID(m, applid) ApplID (u16)
CAPIMSG_COMMAND(m) CAPIMSG_SETCOMMAND(m,cmd) Command (u8)
CAPIMSG_SUBCOMMAND(m) CAPIMSG_SETSUBCOMMAND(m, cmd) Subcommand (u8)
CAPIMSG_CMD(m) - Command*256
+ Subcommand (u16)
CAPIMSG_MSGID(m) CAPIMSG_SETMSGID(m, msgid) Message Number (u16)
CAPIMSG_CONTROL(m) CAPIMSG_SETCONTROL(m, contr) Controller/PLCI/NCCI
(u32)
CAPIMSG_DATALEN(m) CAPIMSG_SETDATALEN(m, len) Data Length (u16)
====================== ============================= ====================
Library functions for working with _cmsg structures
(from <linux/isdn/capiutil.h>):
``char *capi_cmd2str(u8 Command, u8 Subcommand)``
Returns the CAPI 2.0 message name corresponding to the given command
and subcommand values, as a static ASCII string. The return value may
be NULL if the command/subcommand is not one of those defined in the
CAPI 2.0 standard.
7. Debugging
============
The module kernelcapi has a module parameter showcapimsgs controlling some
debugging output produced by the module. It can only be set when the module is
loaded, via a parameter "showcapimsgs=<n>" to the modprobe command, either on
the command line or in the configuration file.
If the lowest bit of showcapimsgs is set, kernelcapi logs controller and
application up and down events.
In addition, every registered CAPI controller has an associated traceflag
parameter controlling how CAPI messages sent from and to the controller are
logged. The traceflag parameter is initialized with the value of the
showcapimsgs parameter when the controller is registered, but can later be
changed via the MANUFACTURER_REQ command KCAPI_CMD_TRACE.
If the value of traceflag is non-zero, CAPI messages are logged.
DATA_B3 messages are only logged if the value of traceflag is > 2.
If the lowest bit of traceflag is set, only the command/subcommand and message
length are logged. Otherwise, kernelcapi logs a readable representation of
the entire message.

View File

@@ -1,9 +0,0 @@
============
mISDN Driver
============
mISDN is a new modular ISDN driver, in the long term it should replace
the old I4L driver architecture for passive ISDN cards.
It was designed to allow a broad range of applications and interfaces
but only have the basic function in kernel, the interface to the user
space is based on sockets with a own address family AF_ISDN.

View File

@@ -46,7 +46,6 @@ Networking interfaces
networking/index
netlabel/index
infiniband/index
isdn/index
mhi/index
Storage interfaces

View File

@@ -13599,25 +13599,6 @@ S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git master
F: drivers/infiniband/ulp/isert
ISDN/CMTP OVER BLUETOOTH
L: netdev@vger.kernel.org
S: Orphan
W: http://www.isdn4linux.de
F: Documentation/isdn/
F: drivers/isdn/capi/
F: include/linux/isdn/
F: include/uapi/linux/isdn/
F: net/bluetooth/cmtp/
ISDN/mISDN SUBSYSTEM
L: netdev@vger.kernel.org
S: Orphan
W: http://www.isdn4linux.de
F: drivers/isdn/Kconfig
F: drivers/isdn/Makefile
F: drivers/isdn/hardware/
F: drivers/isdn/mISDN/
ISL28022 HARDWARE MONITORING DRIVER
M: Carsten Spieß <mail@carsten-spiess.de>
L: linux-hwmon@vger.kernel.org

View File

@@ -61,8 +61,6 @@ source "drivers/macintosh/Kconfig"
source "drivers/net/Kconfig"
source "drivers/isdn/Kconfig"
# input before char - char/joystick depends on it. As does USB.
source "drivers/input/Kconfig"

View File

@@ -124,7 +124,6 @@ obj-$(CONFIG_WATCHDOG) += watchdog/
obj-$(CONFIG_MD) += md/
obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_ACCESSIBILITY) += accessibility/
obj-$(CONFIG_ISDN) += isdn/
obj-$(CONFIG_EDAC) += edac/
obj-$(CONFIG_EISA) += eisa/
obj-$(CONFIG_PM_OPP) += opp/

View File

@@ -1,27 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# ISDN device configuration
#
menuconfig ISDN
bool "ISDN support"
depends on NET && NETDEVICES
help
ISDN ("Integrated Services Digital Network", called RNIS in France)
is a fully digital telephone service that can be used for voice and
data connections. If your computer is equipped with an ISDN
adapter you can use it to connect to your Internet service provider
(with SLIP or PPP) faster than via a conventional telephone modem
(though still much slower than with DSL) or to make and accept
voice calls (eg. turning your PC into a software answering machine
or PABX).
Select this option if you want your kernel to support ISDN.
if ISDN
source "drivers/isdn/capi/Kconfig"
source "drivers/isdn/mISDN/Kconfig"
endif # ISDN

View File

@@ -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_BT_CMTP) += capi/
obj-$(CONFIG_MISDN) += mISDN/
obj-$(CONFIG_ISDN) += hardware/

View File

@@ -1,32 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
config ISDN_CAPI
def_bool ISDN && BT
help
This provides CAPI (the Common ISDN Application Programming
Interface) Version 2.0, a standard making it easy for programs to
access ISDN hardware in a device independent way. (For details see
<https://www.capi.org/>.) CAPI supports making and accepting voice
and data connections, controlling call options and protocols,
as well as ISDN supplementary services like call forwarding or
three-party conferences (if supported by the specific hardware
driver).
This subsystem requires a hardware specific driver.
See CONFIG_BT_CMTP for the last remaining regular driver
in the kernel that uses the CAPI subsystem.
config CAPI_TRACE
def_bool BT_CMTP
help
If you say Y here, the kernelcapi driver can make verbose traces
of CAPI messages. This feature can be enabled/disabled via IOCTL for
every controller (default disabled).
config ISDN_CAPI_MIDDLEWARE
def_bool BT_CMTP && TTY
help
This option will enhance the capabilities of the /dev/capi20
interface. It will provide a means of moving a data connection,
established via the usual /dev/capi20 interface to a special tty
device. If you want to use pppd with pppdcapiplugin to dial up to
your ISP, say Y here.

View File

@@ -1,6 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
# Makefile for the CAPI subsystem used by BT_CMTP
obj-$(CONFIG_BT_CMTP) += kernelcapi.o
kernelcapi-y := kcapi.o capiutil.o capi.o
kernelcapi-$(CONFIG_PROC_FS) += kcapi_proc.o

File diff suppressed because it is too large Load Diff

View File

@@ -1,677 +0,0 @@
/* $Id: capiutil.c,v 1.13.6.4 2001/09/23 22:24:33 kai Exp $
*
* CAPI 2.0 convert capi message to capi message struct
*
* From CAPI 2.0 Development Kit AVM 1995 (msg.c)
* Rewritten for Linux 1996 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/string.h>
#include <linux/ctype.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/isdn/capiutil.h>
#include <linux/slab.h>
#include "kcapi.h"
/* from CAPI2.0 DDK AVM Berlin GmbH */
typedef struct {
int typ;
size_t off;
} _cdef;
#define _CBYTE 1
#define _CWORD 2
#define _CDWORD 3
#define _CSTRUCT 4
#define _CMSTRUCT 5
#define _CEND 6
static _cdef cdef[] =
{
/*00 */
{_CEND},
/*01 */
{_CEND},
/*02 */
{_CEND},
/*03 */
{_CDWORD, offsetof(_cmsg, adr.adrController)},
/*04 */
{_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)},
/*05 */
{_CSTRUCT, offsetof(_cmsg, B1configuration)},
/*06 */
{_CWORD, offsetof(_cmsg, B1protocol)},
/*07 */
{_CSTRUCT, offsetof(_cmsg, B2configuration)},
/*08 */
{_CWORD, offsetof(_cmsg, B2protocol)},
/*09 */
{_CSTRUCT, offsetof(_cmsg, B3configuration)},
/*0a */
{_CWORD, offsetof(_cmsg, B3protocol)},
/*0b */
{_CSTRUCT, offsetof(_cmsg, BC)},
/*0c */
{_CSTRUCT, offsetof(_cmsg, BChannelinformation)},
/*0d */
{_CMSTRUCT, offsetof(_cmsg, BProtocol)},
/*0e */
{_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)},
/*0f */
{_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)},
/*10 */
{_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)},
/*11 */
{_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)},
/*12 */
{_CDWORD, offsetof(_cmsg, CIPmask)},
/*13 */
{_CDWORD, offsetof(_cmsg, CIPmask2)},
/*14 */
{_CWORD, offsetof(_cmsg, CIPValue)},
/*15 */
{_CDWORD, offsetof(_cmsg, Class)},
/*16 */
{_CSTRUCT, offsetof(_cmsg, ConnectedNumber)},
/*17 */
{_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)},
/*18 */
{_CDWORD, offsetof(_cmsg, Data)},
/*19 */
{_CWORD, offsetof(_cmsg, DataHandle)},
/*1a */
{_CWORD, offsetof(_cmsg, DataLength)},
/*1b */
{_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)},
/*1c */
{_CSTRUCT, offsetof(_cmsg, Facilitydataarray)},
/*1d */
{_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)},
/*1e */
{_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)},
/*1f */
{_CWORD, offsetof(_cmsg, FacilitySelector)},
/*20 */
{_CWORD, offsetof(_cmsg, Flags)},
/*21 */
{_CDWORD, offsetof(_cmsg, Function)},
/*22 */
{_CSTRUCT, offsetof(_cmsg, HLC)},
/*23 */
{_CWORD, offsetof(_cmsg, Info)},
/*24 */
{_CSTRUCT, offsetof(_cmsg, InfoElement)},
/*25 */
{_CDWORD, offsetof(_cmsg, InfoMask)},
/*26 */
{_CWORD, offsetof(_cmsg, InfoNumber)},
/*27 */
{_CSTRUCT, offsetof(_cmsg, Keypadfacility)},
/*28 */
{_CSTRUCT, offsetof(_cmsg, LLC)},
/*29 */
{_CSTRUCT, offsetof(_cmsg, ManuData)},
/*2a */
{_CDWORD, offsetof(_cmsg, ManuID)},
/*2b */
{_CSTRUCT, offsetof(_cmsg, NCPI)},
/*2c */
{_CWORD, offsetof(_cmsg, Reason)},
/*2d */
{_CWORD, offsetof(_cmsg, Reason_B3)},
/*2e */
{_CWORD, offsetof(_cmsg, Reject)},
/*2f */
{_CSTRUCT, offsetof(_cmsg, Useruserdata)}
};
static unsigned char *cpars[] =
{
/* ALERT_REQ */ [0x01] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
/* CONNECT_REQ */ [0x02] = "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
/* DISCONNECT_REQ */ [0x04] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
/* LISTEN_REQ */ [0x05] = "\x03\x25\x12\x13\x10\x11\x01",
/* INFO_REQ */ [0x08] = "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01",
/* FACILITY_REQ */ [0x09] = "\x03\x1f\x1e\x01",
/* SELECT_B_PROTOCOL_REQ */ [0x0a] = "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01",
/* CONNECT_B3_REQ */ [0x0b] = "\x03\x2b\x01",
/* DISCONNECT_B3_REQ */ [0x0d] = "\x03\x2b\x01",
/* DATA_B3_REQ */ [0x0f] = "\x03\x18\x1a\x19\x20\x01",
/* RESET_B3_REQ */ [0x10] = "\x03\x2b\x01",
/* ALERT_CONF */ [0x13] = "\x03\x23\x01",
/* CONNECT_CONF */ [0x14] = "\x03\x23\x01",
/* DISCONNECT_CONF */ [0x16] = "\x03\x23\x01",
/* LISTEN_CONF */ [0x17] = "\x03\x23\x01",
/* MANUFACTURER_REQ */ [0x18] = "\x03\x2a\x15\x21\x29\x01",
/* INFO_CONF */ [0x1a] = "\x03\x23\x01",
/* FACILITY_CONF */ [0x1b] = "\x03\x23\x1f\x1b\x01",
/* SELECT_B_PROTOCOL_CONF */ [0x1c] = "\x03\x23\x01",
/* CONNECT_B3_CONF */ [0x1d] = "\x03\x23\x01",
/* DISCONNECT_B3_CONF */ [0x1f] = "\x03\x23\x01",
/* DATA_B3_CONF */ [0x21] = "\x03\x19\x23\x01",
/* RESET_B3_CONF */ [0x22] = "\x03\x23\x01",
/* CONNECT_IND */ [0x26] = "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
/* CONNECT_ACTIVE_IND */ [0x27] = "\x03\x16\x17\x28\x01",
/* DISCONNECT_IND */ [0x28] = "\x03\x2c\x01",
/* MANUFACTURER_CONF */ [0x2a] = "\x03\x2a\x15\x21\x29\x01",
/* INFO_IND */ [0x2c] = "\x03\x26\x24\x01",
/* FACILITY_IND */ [0x2d] = "\x03\x1f\x1d\x01",
/* CONNECT_B3_IND */ [0x2f] = "\x03\x2b\x01",
/* CONNECT_B3_ACTIVE_IND */ [0x30] = "\x03\x2b\x01",
/* DISCONNECT_B3_IND */ [0x31] = "\x03\x2d\x2b\x01",
/* DATA_B3_IND */ [0x33] = "\x03\x18\x1a\x19\x20\x01",
/* RESET_B3_IND */ [0x34] = "\x03\x2b\x01",
/* CONNECT_B3_T90_ACTIVE_IND */ [0x35] = "\x03\x2b\x01",
/* CONNECT_RESP */ [0x38] = "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01",
/* CONNECT_ACTIVE_RESP */ [0x39] = "\x03\x01",
/* DISCONNECT_RESP */ [0x3a] = "\x03\x01",
/* MANUFACTURER_IND */ [0x3c] = "\x03\x2a\x15\x21\x29\x01",
/* INFO_RESP */ [0x3e] = "\x03\x01",
/* FACILITY_RESP */ [0x3f] = "\x03\x1f\x01",
/* CONNECT_B3_RESP */ [0x41] = "\x03\x2e\x2b\x01",
/* CONNECT_B3_ACTIVE_RESP */ [0x42] = "\x03\x01",
/* DISCONNECT_B3_RESP */ [0x43] = "\x03\x01",
/* DATA_B3_RESP */ [0x45] = "\x03\x19\x01",
/* RESET_B3_RESP */ [0x46] = "\x03\x01",
/* CONNECT_B3_T90_ACTIVE_RESP */ [0x47] = "\x03\x01",
/* MANUFACTURER_RESP */ [0x4e] = "\x03\x2a\x15\x21\x29\x01",
};
/*-------------------------------------------------------*/
#define byteTLcpy(x, y) *(u8 *)(x) = *(u8 *)(y);
#define wordTLcpy(x, y) *(u16 *)(x) = *(u16 *)(y);
#define dwordTLcpy(x, y) memcpy(x, y, 4);
#define structTLcpy(x, y, l) memcpy(x, y, l)
#define structTLcpyovl(x, y, l) memmove(x, y, l)
#define byteTRcpy(x, y) *(u8 *)(y) = *(u8 *)(x);
#define wordTRcpy(x, y) *(u16 *)(y) = *(u16 *)(x);
#define dwordTRcpy(x, y) memcpy(y, x, 4);
#define structTRcpy(x, y, l) memcpy(y, x, l)
#define structTRcpyovl(x, y, l) memmove(y, x, l)
/*-------------------------------------------------------*/
static unsigned command_2_index(u8 c, u8 sc)
{
if (c & 0x80)
c = 0x9 + (c & 0x0f);
else if (c == 0x41)
c = 0x9 + 0x1;
if (c > 0x18)
c = 0x00;
return (sc & 3) * (0x9 + 0x9) + c;
}
/**
* capi_cmd2par() - find parameter string for CAPI 2.0 command/subcommand
* @cmd: command number
* @subcmd: subcommand number
*
* Return value: static string, NULL if command/subcommand unknown
*/
static unsigned char *capi_cmd2par(u8 cmd, u8 subcmd)
{
return cpars[command_2_index(cmd, subcmd)];
}
/*-------------------------------------------------------*/
#define TYP (cdef[cmsg->par[cmsg->p]].typ)
#define OFF (((u8 *)cmsg) + cdef[cmsg->par[cmsg->p]].off)
static void jumpcstruct(_cmsg *cmsg)
{
unsigned layer;
for (cmsg->p++, layer = 1; layer;) {
/* $$$$$ assert (cmsg->p); */
cmsg->p++;
switch (TYP) {
case _CMSTRUCT:
layer++;
break;
case _CEND:
layer--;
break;
}
}
}
/*-------------------------------------------------------*/
static char *mnames[] =
{
[0x01] = "ALERT_REQ",
[0x02] = "CONNECT_REQ",
[0x04] = "DISCONNECT_REQ",
[0x05] = "LISTEN_REQ",
[0x08] = "INFO_REQ",
[0x09] = "FACILITY_REQ",
[0x0a] = "SELECT_B_PROTOCOL_REQ",
[0x0b] = "CONNECT_B3_REQ",
[0x0d] = "DISCONNECT_B3_REQ",
[0x0f] = "DATA_B3_REQ",
[0x10] = "RESET_B3_REQ",
[0x13] = "ALERT_CONF",
[0x14] = "CONNECT_CONF",
[0x16] = "DISCONNECT_CONF",
[0x17] = "LISTEN_CONF",
[0x18] = "MANUFACTURER_REQ",
[0x1a] = "INFO_CONF",
[0x1b] = "FACILITY_CONF",
[0x1c] = "SELECT_B_PROTOCOL_CONF",
[0x1d] = "CONNECT_B3_CONF",
[0x1f] = "DISCONNECT_B3_CONF",
[0x21] = "DATA_B3_CONF",
[0x22] = "RESET_B3_CONF",
[0x26] = "CONNECT_IND",
[0x27] = "CONNECT_ACTIVE_IND",
[0x28] = "DISCONNECT_IND",
[0x2a] = "MANUFACTURER_CONF",
[0x2c] = "INFO_IND",
[0x2d] = "FACILITY_IND",
[0x2f] = "CONNECT_B3_IND",
[0x30] = "CONNECT_B3_ACTIVE_IND",
[0x31] = "DISCONNECT_B3_IND",
[0x33] = "DATA_B3_IND",
[0x34] = "RESET_B3_IND",
[0x35] = "CONNECT_B3_T90_ACTIVE_IND",
[0x38] = "CONNECT_RESP",
[0x39] = "CONNECT_ACTIVE_RESP",
[0x3a] = "DISCONNECT_RESP",
[0x3c] = "MANUFACTURER_IND",
[0x3e] = "INFO_RESP",
[0x3f] = "FACILITY_RESP",
[0x41] = "CONNECT_B3_RESP",
[0x42] = "CONNECT_B3_ACTIVE_RESP",
[0x43] = "DISCONNECT_B3_RESP",
[0x45] = "DATA_B3_RESP",
[0x46] = "RESET_B3_RESP",
[0x47] = "CONNECT_B3_T90_ACTIVE_RESP",
[0x4e] = "MANUFACTURER_RESP"
};
/**
* capi_cmd2str() - convert CAPI 2.0 command/subcommand number to name
* @cmd: command number
* @subcmd: subcommand number
*
* Return value: static string
*/
char *capi_cmd2str(u8 cmd, u8 subcmd)
{
char *result;
result = mnames[command_2_index(cmd, subcmd)];
if (result == NULL)
result = "INVALID_COMMAND";
return result;
}
/*-------------------------------------------------------*/
#ifdef CONFIG_CAPI_TRACE
/*-------------------------------------------------------*/
static char *pnames[] =
{
/*00 */ NULL,
/*01 */ NULL,
/*02 */ NULL,
/*03 */ "Controller/PLCI/NCCI",
/*04 */ "AdditionalInfo",
/*05 */ "B1configuration",
/*06 */ "B1protocol",
/*07 */ "B2configuration",
/*08 */ "B2protocol",
/*09 */ "B3configuration",
/*0a */ "B3protocol",
/*0b */ "BC",
/*0c */ "BChannelinformation",
/*0d */ "BProtocol",
/*0e */ "CalledPartyNumber",
/*0f */ "CalledPartySubaddress",
/*10 */ "CallingPartyNumber",
/*11 */ "CallingPartySubaddress",
/*12 */ "CIPmask",
/*13 */ "CIPmask2",
/*14 */ "CIPValue",
/*15 */ "Class",
/*16 */ "ConnectedNumber",
/*17 */ "ConnectedSubaddress",
/*18 */ "Data32",
/*19 */ "DataHandle",
/*1a */ "DataLength",
/*1b */ "FacilityConfirmationParameter",
/*1c */ "Facilitydataarray",
/*1d */ "FacilityIndicationParameter",
/*1e */ "FacilityRequestParameter",
/*1f */ "FacilitySelector",
/*20 */ "Flags",
/*21 */ "Function",
/*22 */ "HLC",
/*23 */ "Info",
/*24 */ "InfoElement",
/*25 */ "InfoMask",
/*26 */ "InfoNumber",
/*27 */ "Keypadfacility",
/*28 */ "LLC",
/*29 */ "ManuData",
/*2a */ "ManuID",
/*2b */ "NCPI",
/*2c */ "Reason",
/*2d */ "Reason_B3",
/*2e */ "Reject",
/*2f */ "Useruserdata"
};
#include <linux/stdarg.h>
/*-------------------------------------------------------*/
static _cdebbuf *bufprint(_cdebbuf *cdb, char *fmt, ...)
{
va_list f;
size_t n, r;
if (!cdb)
return NULL;
va_start(f, fmt);
r = cdb->size - cdb->pos;
n = vsnprintf(cdb->p, r, fmt, f);
va_end(f);
if (n >= r) {
/* truncated, need bigger buffer */
size_t ns = 2 * cdb->size;
u_char *nb;
while ((ns - cdb->pos) <= n)
ns *= 2;
nb = kmalloc(ns, GFP_ATOMIC);
if (!nb) {
cdebbuf_free(cdb);
return NULL;
}
memcpy(nb, cdb->buf, cdb->pos);
kfree(cdb->buf);
nb[cdb->pos] = 0;
cdb->buf = nb;
cdb->p = cdb->buf + cdb->pos;
cdb->size = ns;
va_start(f, fmt);
r = cdb->size - cdb->pos;
n = vsnprintf(cdb->p, r, fmt, f);
va_end(f);
}
cdb->p += n;
cdb->pos += n;
return cdb;
}
static _cdebbuf *printstructlen(_cdebbuf *cdb, u8 *m, unsigned len)
{
unsigned hex = 0;
if (!cdb)
return NULL;
for (; len; len--, m++)
if (isalnum(*m) || *m == ' ') {
if (hex)
cdb = bufprint(cdb, ">");
cdb = bufprint(cdb, "%c", *m);
hex = 0;
} else {
if (!hex)
cdb = bufprint(cdb, "<%02x", *m);
else
cdb = bufprint(cdb, " %02x", *m);
hex = 1;
}
if (hex)
cdb = bufprint(cdb, ">");
return cdb;
}
static _cdebbuf *printstruct(_cdebbuf *cdb, u8 *m)
{
unsigned len;
if (m[0] != 0xff) {
len = m[0];
m += 1;
} else {
len = ((u16 *) (m + 1))[0];
m += 3;
}
cdb = printstructlen(cdb, m, len);
return cdb;
}
/*-------------------------------------------------------*/
#define NAME (pnames[cmsg->par[cmsg->p]])
static _cdebbuf *protocol_message_2_pars(_cdebbuf *cdb, _cmsg *cmsg, int level)
{
if (!cmsg->par)
return NULL; /* invalid command/subcommand */
for (; TYP != _CEND; cmsg->p++) {
int slen = 29 + 3 - level;
int i;
if (!cdb)
return NULL;
cdb = bufprint(cdb, " ");
for (i = 0; i < level - 1; i++)
cdb = bufprint(cdb, " ");
switch (TYP) {
case _CBYTE:
cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l));
cmsg->l++;
break;
case _CWORD:
cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l));
cmsg->l += 2;
break;
case _CDWORD:
cdb = bufprint(cdb, "%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l));
cmsg->l += 4;
break;
case _CSTRUCT:
cdb = bufprint(cdb, "%-*s = ", slen, NAME);
if (cmsg->m[cmsg->l] == '\0')
cdb = bufprint(cdb, "default");
else
cdb = printstruct(cdb, cmsg->m + cmsg->l);
cdb = bufprint(cdb, "\n");
if (cmsg->m[cmsg->l] != 0xff)
cmsg->l += 1 + cmsg->m[cmsg->l];
else
cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1);
break;
case _CMSTRUCT:
/*----- Metastruktur 0 -----*/
if (cmsg->m[cmsg->l] == '\0') {
cdb = bufprint(cdb, "%-*s = default\n", slen, NAME);
cmsg->l++;
jumpcstruct(cmsg);
} else {
char *name = NAME;
unsigned _l = cmsg->l;
cdb = bufprint(cdb, "%-*s\n", slen, name);
cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
cmsg->p++;
cdb = protocol_message_2_pars(cdb, cmsg, level + 1);
}
break;
}
}
return cdb;
}
/*-------------------------------------------------------*/
static _cdebbuf *g_debbuf;
static u_long g_debbuf_lock;
static _cmsg *g_cmsg;
static _cdebbuf *cdebbuf_alloc(void)
{
_cdebbuf *cdb;
if (likely(!test_and_set_bit(1, &g_debbuf_lock))) {
cdb = g_debbuf;
goto init;
} else
cdb = kmalloc_obj(_cdebbuf, GFP_ATOMIC);
if (!cdb)
return NULL;
cdb->buf = kmalloc(CDEBUG_SIZE, GFP_ATOMIC);
if (!cdb->buf) {
kfree(cdb);
return NULL;
}
cdb->size = CDEBUG_SIZE;
init:
cdb->buf[0] = 0;
cdb->p = cdb->buf;
cdb->pos = 0;
return cdb;
}
/**
* cdebbuf_free() - free CAPI debug buffer
* @cdb: buffer to free
*/
void cdebbuf_free(_cdebbuf *cdb)
{
if (likely(cdb == g_debbuf)) {
test_and_clear_bit(1, &g_debbuf_lock);
return;
}
if (likely(cdb))
kfree(cdb->buf);
kfree(cdb);
}
/**
* capi_message2str() - format CAPI 2.0 message for printing
* @msg: CAPI 2.0 message
*
* Allocates a CAPI debug buffer and fills it with a printable representation
* of the CAPI 2.0 message in @msg.
* Return value: allocated debug buffer, NULL on error
* The returned buffer should be freed by a call to cdebbuf_free() after use.
*/
_cdebbuf *capi_message2str(u8 *msg)
{
_cdebbuf *cdb;
_cmsg *cmsg;
cdb = cdebbuf_alloc();
if (unlikely(!cdb))
return NULL;
if (likely(cdb == g_debbuf))
cmsg = g_cmsg;
else
cmsg = kmalloc_obj(_cmsg, GFP_ATOMIC);
if (unlikely(!cmsg)) {
cdebbuf_free(cdb);
return NULL;
}
cmsg->m = msg;
cmsg->l = 8;
cmsg->p = 0;
byteTRcpy(cmsg->m + 4, &cmsg->Command);
byteTRcpy(cmsg->m + 5, &cmsg->Subcommand);
cmsg->par = capi_cmd2par(cmsg->Command, cmsg->Subcommand);
cdb = bufprint(cdb, "%-26s ID=%03d #0x%04x LEN=%04d\n",
capi_cmd2str(cmsg->Command, cmsg->Subcommand),
((unsigned short *) msg)[1],
((unsigned short *) msg)[3],
((unsigned short *) msg)[0]);
cdb = protocol_message_2_pars(cdb, cmsg, 1);
if (unlikely(cmsg != g_cmsg))
kfree(cmsg);
return cdb;
}
int __init cdebug_init(void)
{
g_cmsg = kmalloc_obj(_cmsg);
if (!g_cmsg)
return -ENOMEM;
g_debbuf = kmalloc_obj(_cdebbuf);
if (!g_debbuf) {
kfree(g_cmsg);
return -ENOMEM;
}
g_debbuf->buf = kmalloc(CDEBUG_GSIZE, GFP_KERNEL);
if (!g_debbuf->buf) {
kfree(g_cmsg);
kfree(g_debbuf);
return -ENOMEM;
}
g_debbuf->size = CDEBUG_GSIZE;
g_debbuf->buf[0] = 0;
g_debbuf->p = g_debbuf->buf;
g_debbuf->pos = 0;
return 0;
}
void cdebug_exit(void)
{
if (g_debbuf)
kfree(g_debbuf->buf);
kfree(g_debbuf);
kfree(g_cmsg);
}
#else /* !CONFIG_CAPI_TRACE */
static _cdebbuf g_debbuf = {"CONFIG_CAPI_TRACE not enabled", NULL, 0, 0};
_cdebbuf *capi_message2str(u8 *msg)
{
return &g_debbuf;
}
_cdebbuf *capi_cmsg2str(_cmsg *cmsg)
{
return &g_debbuf;
}
void cdebbuf_free(_cdebbuf *cdb)
{
}
int __init cdebug_init(void)
{
return 0;
}
void cdebug_exit(void)
{
}
#endif

View File

@@ -1,933 +0,0 @@
/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $
*
* Kernel CAPI 2.0 Module
*
* Copyright 1999 by Carsten Paeth <calle@calle.de>
* Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "kcapi.h"
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/sched/signal.h>
#include <linux/seq_file.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/capi.h>
#include <linux/kernelcapi.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/isdn/capicmd.h>
#include <linux/isdn/capiutil.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
static int showcapimsgs;
static struct workqueue_struct *kcapi_wq;
module_param(showcapimsgs, uint, 0);
/* ------------------------------------------------------------- */
struct capictr_event {
struct work_struct work;
unsigned int type;
u32 controller;
};
/* ------------------------------------------------------------- */
static const struct capi_version driver_version = {2, 0, 1, 1 << 4};
static char driver_serial[CAPI_SERIAL_LEN] = "0004711";
static char capi_manufakturer[64] = "AVM Berlin";
#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f)
struct capi_ctr *capi_controller[CAPI_MAXCONTR];
DEFINE_MUTEX(capi_controller_lock);
struct capi20_appl *capi_applications[CAPI_MAXAPPL];
static int ncontrollers;
/* -------- controller ref counting -------------------------------------- */
static inline struct capi_ctr *
capi_ctr_get(struct capi_ctr *ctr)
{
if (!try_module_get(ctr->owner))
return NULL;
return ctr;
}
static inline void
capi_ctr_put(struct capi_ctr *ctr)
{
module_put(ctr->owner);
}
/* ------------------------------------------------------------- */
static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr)
{
if (contr < 1 || contr - 1 >= CAPI_MAXCONTR)
return NULL;
return capi_controller[contr - 1];
}
static inline struct capi20_appl *__get_capi_appl_by_nr(u16 applid)
{
lockdep_assert_held(&capi_controller_lock);
if (applid < 1 || applid - 1 >= CAPI_MAXAPPL)
return NULL;
return capi_applications[applid - 1];
}
static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid)
{
if (applid < 1 || applid - 1 >= CAPI_MAXAPPL)
return NULL;
return rcu_dereference(capi_applications[applid - 1]);
}
/* -------- util functions ------------------------------------ */
static inline int capi_cmd_valid(u8 cmd)
{
switch (cmd) {
case CAPI_ALERT:
case CAPI_CONNECT:
case CAPI_CONNECT_ACTIVE:
case CAPI_CONNECT_B3_ACTIVE:
case CAPI_CONNECT_B3:
case CAPI_CONNECT_B3_T90_ACTIVE:
case CAPI_DATA_B3:
case CAPI_DISCONNECT_B3:
case CAPI_DISCONNECT:
case CAPI_FACILITY:
case CAPI_INFO:
case CAPI_LISTEN:
case CAPI_MANUFACTURER:
case CAPI_RESET_B3:
case CAPI_SELECT_B_PROTOCOL:
return 1;
}
return 0;
}
static inline int capi_subcmd_valid(u8 subcmd)
{
switch (subcmd) {
case CAPI_REQ:
case CAPI_CONF:
case CAPI_IND:
case CAPI_RESP:
return 1;
}
return 0;
}
/* ------------------------------------------------------------ */
static void
register_appl(struct capi_ctr *ctr, u16 applid, capi_register_params *rparam)
{
ctr = capi_ctr_get(ctr);
if (ctr)
ctr->register_appl(ctr, applid, rparam);
else
printk(KERN_WARNING "%s: cannot get controller resources\n",
__func__);
}
static void release_appl(struct capi_ctr *ctr, u16 applid)
{
DBG("applid %#x", applid);
ctr->release_appl(ctr, applid);
capi_ctr_put(ctr);
}
static void notify_up(u32 contr)
{
struct capi20_appl *ap;
struct capi_ctr *ctr;
u16 applid;
mutex_lock(&capi_controller_lock);
if (showcapimsgs & 1)
printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr);
ctr = get_capi_ctr_by_nr(contr);
if (ctr) {
if (ctr->state == CAPI_CTR_RUNNING)
goto unlock_out;
ctr->state = CAPI_CTR_RUNNING;
for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
ap = __get_capi_appl_by_nr(applid);
if (ap)
register_appl(ctr, applid, &ap->rparam);
}
} else
printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr);
unlock_out:
mutex_unlock(&capi_controller_lock);
}
static void ctr_down(struct capi_ctr *ctr, int new_state)
{
struct capi20_appl *ap;
u16 applid;
if (ctr->state == CAPI_CTR_DETECTED || ctr->state == CAPI_CTR_DETACHED)
return;
ctr->state = new_state;
memset(ctr->manu, 0, sizeof(ctr->manu));
memset(&ctr->version, 0, sizeof(ctr->version));
memset(&ctr->profile, 0, sizeof(ctr->profile));
memset(ctr->serial, 0, sizeof(ctr->serial));
for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
ap = __get_capi_appl_by_nr(applid);
if (ap)
capi_ctr_put(ctr);
}
}
static void notify_down(u32 contr)
{
struct capi_ctr *ctr;
mutex_lock(&capi_controller_lock);
if (showcapimsgs & 1)
printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr);
ctr = get_capi_ctr_by_nr(contr);
if (ctr)
ctr_down(ctr, CAPI_CTR_DETECTED);
else
printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr);
mutex_unlock(&capi_controller_lock);
}
static void do_notify_work(struct work_struct *work)
{
struct capictr_event *event =
container_of(work, struct capictr_event, work);
switch (event->type) {
case CAPICTR_UP:
notify_up(event->controller);
break;
case CAPICTR_DOWN:
notify_down(event->controller);
break;
}
kfree(event);
}
static int notify_push(unsigned int event_type, u32 controller)
{
struct capictr_event *event = kmalloc_obj(*event, GFP_ATOMIC);
if (!event)
return -ENOMEM;
INIT_WORK(&event->work, do_notify_work);
event->type = event_type;
event->controller = controller;
queue_work(kcapi_wq, &event->work);
return 0;
}
/* -------- Receiver ------------------------------------------ */
static void recv_handler(struct work_struct *work)
{
struct sk_buff *skb;
struct capi20_appl *ap =
container_of(work, struct capi20_appl, recv_work);
if ((!ap) || (ap->release_in_progress))
return;
mutex_lock(&ap->recv_mtx);
while ((skb = skb_dequeue(&ap->recv_queue))) {
if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND)
ap->nrecvdatapkt++;
else
ap->nrecvctlpkt++;
ap->recv_message(ap, skb);
}
mutex_unlock(&ap->recv_mtx);
}
/**
* capi_ctr_handle_message() - handle incoming CAPI message
* @ctr: controller descriptor structure.
* @appl: application ID.
* @skb: message.
*
* Called by hardware driver to pass a CAPI message to the application.
*/
void capi_ctr_handle_message(struct capi_ctr *ctr, u16 appl,
struct sk_buff *skb)
{
struct capi20_appl *ap;
int showctl = 0;
u8 cmd, subcmd;
_cdebbuf *cdb;
if (ctr->state != CAPI_CTR_RUNNING) {
cdb = capi_message2str(skb->data);
if (cdb) {
printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s",
ctr->cnr, cdb->buf);
cdebbuf_free(cdb);
} else
printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n",
ctr->cnr);
goto error;
}
cmd = CAPIMSG_COMMAND(skb->data);
subcmd = CAPIMSG_SUBCOMMAND(skb->data);
if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
ctr->nrecvdatapkt++;
if (ctr->traceflag > 2)
showctl |= 2;
} else {
ctr->nrecvctlpkt++;
if (ctr->traceflag)
showctl |= 2;
}
showctl |= (ctr->traceflag & 1);
if (showctl & 2) {
if (showctl & 1) {
printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n",
ctr->cnr, CAPIMSG_APPID(skb->data),
capi_cmd2str(cmd, subcmd),
CAPIMSG_LEN(skb->data));
} else {
cdb = capi_message2str(skb->data);
if (cdb) {
printk(KERN_DEBUG "kcapi: got [%03d] %s\n",
ctr->cnr, cdb->buf);
cdebbuf_free(cdb);
} else
printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n",
ctr->cnr, CAPIMSG_APPID(skb->data),
capi_cmd2str(cmd, subcmd),
CAPIMSG_LEN(skb->data));
}
}
rcu_read_lock();
ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
if (!ap) {
rcu_read_unlock();
cdb = capi_message2str(skb->data);
if (cdb) {
printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n",
CAPIMSG_APPID(skb->data), cdb->buf);
cdebbuf_free(cdb);
} else
printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n",
CAPIMSG_APPID(skb->data),
capi_cmd2str(cmd, subcmd));
goto error;
}
skb_queue_tail(&ap->recv_queue, skb);
queue_work(kcapi_wq, &ap->recv_work);
rcu_read_unlock();
return;
error:
kfree_skb(skb);
}
EXPORT_SYMBOL(capi_ctr_handle_message);
/**
* capi_ctr_ready() - signal CAPI controller ready
* @ctr: controller descriptor structure.
*
* Called by hardware driver to signal that the controller is up and running.
*/
void capi_ctr_ready(struct capi_ctr *ctr)
{
printk(KERN_NOTICE "kcapi: controller [%03d] \"%s\" ready.\n",
ctr->cnr, ctr->name);
notify_push(CAPICTR_UP, ctr->cnr);
}
EXPORT_SYMBOL(capi_ctr_ready);
/**
* capi_ctr_down() - signal CAPI controller not ready
* @ctr: controller descriptor structure.
*
* Called by hardware driver to signal that the controller is down and
* unavailable for use.
*/
void capi_ctr_down(struct capi_ctr *ctr)
{
printk(KERN_NOTICE "kcapi: controller [%03d] down.\n", ctr->cnr);
notify_push(CAPICTR_DOWN, ctr->cnr);
}
EXPORT_SYMBOL(capi_ctr_down);
/* ------------------------------------------------------------- */
/**
* attach_capi_ctr() - register CAPI controller
* @ctr: controller descriptor structure.
*
* Called by hardware driver to register a controller with the CAPI subsystem.
* Return value: 0 on success, error code < 0 on error
*/
int attach_capi_ctr(struct capi_ctr *ctr)
{
int i;
mutex_lock(&capi_controller_lock);
for (i = 0; i < CAPI_MAXCONTR; i++) {
if (!capi_controller[i])
break;
}
if (i == CAPI_MAXCONTR) {
mutex_unlock(&capi_controller_lock);
printk(KERN_ERR "kcapi: out of controller slots\n");
return -EBUSY;
}
capi_controller[i] = ctr;
ctr->nrecvctlpkt = 0;
ctr->nrecvdatapkt = 0;
ctr->nsentctlpkt = 0;
ctr->nsentdatapkt = 0;
ctr->cnr = i + 1;
ctr->state = CAPI_CTR_DETECTED;
ctr->blocked = 0;
ctr->traceflag = showcapimsgs;
sprintf(ctr->procfn, "capi/controllers/%d", ctr->cnr);
ctr->procent = proc_create_single_data(ctr->procfn, 0, NULL,
ctr->proc_show, ctr);
ncontrollers++;
mutex_unlock(&capi_controller_lock);
printk(KERN_NOTICE "kcapi: controller [%03d]: %s attached\n",
ctr->cnr, ctr->name);
return 0;
}
EXPORT_SYMBOL(attach_capi_ctr);
/**
* detach_capi_ctr() - unregister CAPI controller
* @ctr: controller descriptor structure.
*
* Called by hardware driver to remove the registration of a controller
* with the CAPI subsystem.
* Return value: 0 on success, error code < 0 on error
*/
int detach_capi_ctr(struct capi_ctr *ctr)
{
int err = 0;
mutex_lock(&capi_controller_lock);
ctr_down(ctr, CAPI_CTR_DETACHED);
if (ctr->cnr < 1 || ctr->cnr - 1 >= CAPI_MAXCONTR) {
err = -EINVAL;
goto unlock_out;
}
if (capi_controller[ctr->cnr - 1] != ctr) {
err = -EINVAL;
goto unlock_out;
}
capi_controller[ctr->cnr - 1] = NULL;
ncontrollers--;
if (ctr->procent)
remove_proc_entry(ctr->procfn, NULL);
printk(KERN_NOTICE "kcapi: controller [%03d]: %s unregistered\n",
ctr->cnr, ctr->name);
unlock_out:
mutex_unlock(&capi_controller_lock);
return err;
}
EXPORT_SYMBOL(detach_capi_ctr);
/* ------------------------------------------------------------- */
/* -------- CAPI2.0 Interface ---------------------------------- */
/* ------------------------------------------------------------- */
/**
* capi20_isinstalled() - CAPI 2.0 operation CAPI_INSTALLED
*
* Return value: CAPI result code (CAPI_NOERROR if at least one ISDN controller
* is ready for use, CAPI_REGNOTINSTALLED otherwise)
*/
u16 capi20_isinstalled(void)
{
u16 ret = CAPI_REGNOTINSTALLED;
int i;
mutex_lock(&capi_controller_lock);
for (i = 0; i < CAPI_MAXCONTR; i++)
if (capi_controller[i] &&
capi_controller[i]->state == CAPI_CTR_RUNNING) {
ret = CAPI_NOERROR;
break;
}
mutex_unlock(&capi_controller_lock);
return ret;
}
/**
* capi20_register() - CAPI 2.0 operation CAPI_REGISTER
* @ap: CAPI application descriptor structure.
*
* Register an application's presence with CAPI.
* A unique application ID is assigned and stored in @ap->applid.
* After this function returns successfully, the message receive
* callback function @ap->recv_message() may be called at any time
* until capi20_release() has been called for the same @ap.
* Return value: CAPI result code
*/
u16 capi20_register(struct capi20_appl *ap)
{
int i;
u16 applid;
DBG("");
if (ap->rparam.datablklen < 128)
return CAPI_LOGBLKSIZETOSMALL;
ap->nrecvctlpkt = 0;
ap->nrecvdatapkt = 0;
ap->nsentctlpkt = 0;
ap->nsentdatapkt = 0;
mutex_init(&ap->recv_mtx);
skb_queue_head_init(&ap->recv_queue);
INIT_WORK(&ap->recv_work, recv_handler);
ap->release_in_progress = 0;
mutex_lock(&capi_controller_lock);
for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
if (capi_applications[applid - 1] == NULL)
break;
}
if (applid > CAPI_MAXAPPL) {
mutex_unlock(&capi_controller_lock);
return CAPI_TOOMANYAPPLS;
}
ap->applid = applid;
capi_applications[applid - 1] = ap;
for (i = 0; i < CAPI_MAXCONTR; i++) {
if (!capi_controller[i] ||
capi_controller[i]->state != CAPI_CTR_RUNNING)
continue;
register_appl(capi_controller[i], applid, &ap->rparam);
}
mutex_unlock(&capi_controller_lock);
if (showcapimsgs & 1) {
printk(KERN_DEBUG "kcapi: appl %d up\n", applid);
}
return CAPI_NOERROR;
}
/**
* capi20_release() - CAPI 2.0 operation CAPI_RELEASE
* @ap: CAPI application descriptor structure.
*
* Terminate an application's registration with CAPI.
* After this function returns successfully, the message receive
* callback function @ap->recv_message() will no longer be called.
* Return value: CAPI result code
*/
u16 capi20_release(struct capi20_appl *ap)
{
int i;
DBG("applid %#x", ap->applid);
mutex_lock(&capi_controller_lock);
ap->release_in_progress = 1;
capi_applications[ap->applid - 1] = NULL;
synchronize_rcu();
for (i = 0; i < CAPI_MAXCONTR; i++) {
if (!capi_controller[i] ||
capi_controller[i]->state != CAPI_CTR_RUNNING)
continue;
release_appl(capi_controller[i], ap->applid);
}
mutex_unlock(&capi_controller_lock);
flush_workqueue(kcapi_wq);
skb_queue_purge(&ap->recv_queue);
if (showcapimsgs & 1) {
printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid);
}
return CAPI_NOERROR;
}
/**
* capi20_put_message() - CAPI 2.0 operation CAPI_PUT_MESSAGE
* @ap: CAPI application descriptor structure.
* @skb: CAPI message.
*
* Transfer a single message to CAPI.
* Return value: CAPI result code
*/
u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
{
struct capi_ctr *ctr;
int showctl = 0;
u8 cmd, subcmd;
DBG("applid %#x", ap->applid);
if (ncontrollers == 0)
return CAPI_REGNOTINSTALLED;
if ((ap->applid == 0) || ap->release_in_progress)
return CAPI_ILLAPPNR;
if (skb->len < 12
|| !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
|| !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
/*
* The controller reference is protected by the existence of the
* application passed to us. We assume that the caller properly
* synchronizes this service with capi20_release.
*/
ctr = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data));
if (!ctr || ctr->state != CAPI_CTR_RUNNING)
return CAPI_REGNOTINSTALLED;
if (ctr->blocked)
return CAPI_SENDQUEUEFULL;
cmd = CAPIMSG_COMMAND(skb->data);
subcmd = CAPIMSG_SUBCOMMAND(skb->data);
if (cmd == CAPI_DATA_B3 && subcmd == CAPI_REQ) {
ctr->nsentdatapkt++;
ap->nsentdatapkt++;
if (ctr->traceflag > 2)
showctl |= 2;
} else {
ctr->nsentctlpkt++;
ap->nsentctlpkt++;
if (ctr->traceflag)
showctl |= 2;
}
showctl |= (ctr->traceflag & 1);
if (showctl & 2) {
if (showctl & 1) {
printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n",
CAPIMSG_CONTROLLER(skb->data),
CAPIMSG_APPID(skb->data),
capi_cmd2str(cmd, subcmd),
CAPIMSG_LEN(skb->data));
} else {
_cdebbuf *cdb = capi_message2str(skb->data);
if (cdb) {
printk(KERN_DEBUG "kcapi: put [%03d] %s\n",
CAPIMSG_CONTROLLER(skb->data),
cdb->buf);
cdebbuf_free(cdb);
} else
printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n",
CAPIMSG_CONTROLLER(skb->data),
CAPIMSG_APPID(skb->data),
capi_cmd2str(cmd, subcmd),
CAPIMSG_LEN(skb->data));
}
}
return ctr->send_message(ctr, skb);
}
/**
* capi20_get_manufacturer() - CAPI 2.0 operation CAPI_GET_MANUFACTURER
* @contr: controller number.
* @buf: result buffer (64 bytes).
*
* Retrieve information about the manufacturer of the specified ISDN controller
* or (for @contr == 0) the driver itself.
* Return value: CAPI result code
*/
u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN])
{
struct capi_ctr *ctr;
u16 ret;
if (contr == 0) {
strscpy_pad(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
return CAPI_NOERROR;
}
mutex_lock(&capi_controller_lock);
ctr = get_capi_ctr_by_nr(contr);
if (ctr && ctr->state == CAPI_CTR_RUNNING) {
strscpy_pad(buf, ctr->manu, CAPI_MANUFACTURER_LEN);
ret = CAPI_NOERROR;
} else
ret = CAPI_REGNOTINSTALLED;
mutex_unlock(&capi_controller_lock);
return ret;
}
/**
* capi20_get_version() - CAPI 2.0 operation CAPI_GET_VERSION
* @contr: controller number.
* @verp: result structure.
*
* Retrieve version information for the specified ISDN controller
* or (for @contr == 0) the driver itself.
* Return value: CAPI result code
*/
u16 capi20_get_version(u32 contr, struct capi_version *verp)
{
struct capi_ctr *ctr;
u16 ret;
if (contr == 0) {
*verp = driver_version;
return CAPI_NOERROR;
}
mutex_lock(&capi_controller_lock);
ctr = get_capi_ctr_by_nr(contr);
if (ctr && ctr->state == CAPI_CTR_RUNNING) {
memcpy(verp, &ctr->version, sizeof(capi_version));
ret = CAPI_NOERROR;
} else
ret = CAPI_REGNOTINSTALLED;
mutex_unlock(&capi_controller_lock);
return ret;
}
/**
* capi20_get_serial() - CAPI 2.0 operation CAPI_GET_SERIAL_NUMBER
* @contr: controller number.
* @serial: result buffer (8 bytes).
*
* Retrieve the serial number of the specified ISDN controller
* or (for @contr == 0) the driver itself.
* Return value: CAPI result code
*/
u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN])
{
struct capi_ctr *ctr;
u16 ret;
if (contr == 0) {
strscpy(serial, driver_serial, CAPI_SERIAL_LEN);
return CAPI_NOERROR;
}
mutex_lock(&capi_controller_lock);
ctr = get_capi_ctr_by_nr(contr);
if (ctr && ctr->state == CAPI_CTR_RUNNING) {
strscpy(serial, ctr->serial, CAPI_SERIAL_LEN);
ret = CAPI_NOERROR;
} else
ret = CAPI_REGNOTINSTALLED;
mutex_unlock(&capi_controller_lock);
return ret;
}
/**
* capi20_get_profile() - CAPI 2.0 operation CAPI_GET_PROFILE
* @contr: controller number.
* @profp: result structure.
*
* Retrieve capability information for the specified ISDN controller
* or (for @contr == 0) the number of installed controllers.
* Return value: CAPI result code
*/
u16 capi20_get_profile(u32 contr, struct capi_profile *profp)
{
struct capi_ctr *ctr;
u16 ret;
if (contr == 0) {
profp->ncontroller = ncontrollers;
return CAPI_NOERROR;
}
mutex_lock(&capi_controller_lock);
ctr = get_capi_ctr_by_nr(contr);
if (ctr && ctr->state == CAPI_CTR_RUNNING) {
memcpy(profp, &ctr->profile, sizeof(struct capi_profile));
ret = CAPI_NOERROR;
} else
ret = CAPI_REGNOTINSTALLED;
mutex_unlock(&capi_controller_lock);
return ret;
}
/**
* capi20_manufacturer() - CAPI 2.0 operation CAPI_MANUFACTURER
* @cmd: command.
* @data: parameter.
*
* Perform manufacturer specific command.
* Return value: CAPI result code
*/
int capi20_manufacturer(unsigned long cmd, void __user *data)
{
struct capi_ctr *ctr;
int retval;
switch (cmd) {
case KCAPI_CMD_TRACE:
{
kcapi_flagdef fdef;
if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef)))
return -EFAULT;
mutex_lock(&capi_controller_lock);
ctr = get_capi_ctr_by_nr(fdef.contr);
if (ctr) {
ctr->traceflag = fdef.flag;
printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n",
ctr->cnr, ctr->traceflag);
retval = 0;
} else
retval = -ESRCH;
mutex_unlock(&capi_controller_lock);
return retval;
}
default:
printk(KERN_ERR "kcapi: manufacturer command %lu unknown.\n",
cmd);
break;
}
return -EINVAL;
}
/* ------------------------------------------------------------- */
/* -------- Init & Cleanup ------------------------------------- */
/* ------------------------------------------------------------- */
/*
* init / exit functions
*/
int __init kcapi_init(void)
{
int err;
kcapi_wq = alloc_workqueue("kcapi", WQ_PERCPU, 0);
if (!kcapi_wq)
return -ENOMEM;
err = cdebug_init();
if (err) {
destroy_workqueue(kcapi_wq);
return err;
}
if (IS_ENABLED(CONFIG_PROC_FS))
kcapi_proc_init();
return 0;
}
void kcapi_exit(void)
{
if (IS_ENABLED(CONFIG_PROC_FS))
kcapi_proc_exit();
cdebug_exit();
destroy_workqueue(kcapi_wq);
}

View File

@@ -1,182 +0,0 @@
/*
* Kernel CAPI 2.0 Module
*
* Copyright 1999 by Carsten Paeth <calle@calle.de>
* Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/isdn/capilli.h>
#ifdef KCAPI_DEBUG
#define DBG(format, arg...) do { \
printk(KERN_DEBUG "%s: " format "\n" , __func__ , ## arg); \
} while (0)
#else
#define DBG(format, arg...) /* */
#endif
enum {
CAPI_CTR_DETACHED = 0,
CAPI_CTR_DETECTED = 1,
CAPI_CTR_LOADING = 2,
CAPI_CTR_RUNNING = 3,
};
extern struct capi_ctr *capi_controller[CAPI_MAXCONTR];
extern struct mutex capi_controller_lock;
extern struct capi20_appl *capi_applications[CAPI_MAXAPPL];
void kcapi_proc_init(void);
void kcapi_proc_exit(void);
struct capi20_appl {
u16 applid;
capi_register_params rparam;
void (*recv_message)(struct capi20_appl *ap, struct sk_buff *skb);
void *private;
/* internal to kernelcapi.o */
unsigned long nrecvctlpkt;
unsigned long nrecvdatapkt;
unsigned long nsentctlpkt;
unsigned long nsentdatapkt;
struct mutex recv_mtx;
struct sk_buff_head recv_queue;
struct work_struct recv_work;
int release_in_progress;
};
u16 capi20_isinstalled(void);
u16 capi20_register(struct capi20_appl *ap);
u16 capi20_release(struct capi20_appl *ap);
u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb);
u16 capi20_get_manufacturer(u32 contr, u8 buf[CAPI_MANUFACTURER_LEN]);
u16 capi20_get_version(u32 contr, struct capi_version *verp);
u16 capi20_get_serial(u32 contr, u8 serial[CAPI_SERIAL_LEN]);
u16 capi20_get_profile(u32 contr, struct capi_profile *profp);
int capi20_manufacturer(unsigned long cmd, void __user *data);
#define CAPICTR_UP 0
#define CAPICTR_DOWN 1
int kcapi_init(void);
void kcapi_exit(void);
/*----- basic-type definitions -----*/
typedef __u8 *_cstruct;
typedef enum {
CAPI_COMPOSE,
CAPI_DEFAULT
} _cmstruct;
/*
The _cmsg structure contains all possible CAPI 2.0 parameter.
All parameters are stored here first. The function CAPI_CMSG_2_MESSAGE
assembles the parameter and builds CAPI2.0 conform messages.
CAPI_MESSAGE_2_CMSG disassembles CAPI 2.0 messages and stores the
parameter in the _cmsg structure
*/
typedef struct {
/* Header */
__u16 ApplId;
__u8 Command;
__u8 Subcommand;
__u16 Messagenumber;
/* Parameter */
union {
__u32 adrController;
__u32 adrPLCI;
__u32 adrNCCI;
} adr;
_cmstruct AdditionalInfo;
_cstruct B1configuration;
__u16 B1protocol;
_cstruct B2configuration;
__u16 B2protocol;
_cstruct B3configuration;
__u16 B3protocol;
_cstruct BC;
_cstruct BChannelinformation;
_cmstruct BProtocol;
_cstruct CalledPartyNumber;
_cstruct CalledPartySubaddress;
_cstruct CallingPartyNumber;
_cstruct CallingPartySubaddress;
__u32 CIPmask;
__u32 CIPmask2;
__u16 CIPValue;
__u32 Class;
_cstruct ConnectedNumber;
_cstruct ConnectedSubaddress;
__u32 Data;
__u16 DataHandle;
__u16 DataLength;
_cstruct FacilityConfirmationParameter;
_cstruct Facilitydataarray;
_cstruct FacilityIndicationParameter;
_cstruct FacilityRequestParameter;
__u16 FacilitySelector;
__u16 Flags;
__u32 Function;
_cstruct HLC;
__u16 Info;
_cstruct InfoElement;
__u32 InfoMask;
__u16 InfoNumber;
_cstruct Keypadfacility;
_cstruct LLC;
_cstruct ManuData;
__u32 ManuID;
_cstruct NCPI;
__u16 Reason;
__u16 Reason_B3;
__u16 Reject;
_cstruct Useruserdata;
/* intern */
unsigned l, p;
unsigned char *par;
__u8 *m;
/* buffer to construct message */
__u8 buf[180];
} _cmsg;
/*-----------------------------------------------------------------------*/
/*
* Debugging / Tracing functions
*/
char *capi_cmd2str(__u8 cmd, __u8 subcmd);
typedef struct {
u_char *buf;
u_char *p;
size_t size;
size_t pos;
} _cdebbuf;
#define CDEBUG_SIZE 1024
#define CDEBUG_GSIZE 4096
void cdebbuf_free(_cdebbuf *cdb);
int cdebug_init(void);
void cdebug_exit(void);
_cdebbuf *capi_message2str(__u8 *msg);

View File

@@ -1,231 +0,0 @@
/*
* Kernel CAPI 2.0 Module - /proc/capi handling
*
* Copyright 1999 by Carsten Paeth <calle@calle.de>
* Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include "kcapi.h"
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/export.h>
static char *state2str(unsigned short state)
{
switch (state) {
case CAPI_CTR_DETECTED: return "detected";
case CAPI_CTR_LOADING: return "loading";
case CAPI_CTR_RUNNING: return "running";
default: return "???";
}
}
// /proc/capi
// ===========================================================================
// /proc/capi/controller:
// cnr driver cardstate name driverinfo
// /proc/capi/contrstats:
// cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
// ---------------------------------------------------------------------------
static void *controller_start(struct seq_file *seq, loff_t *pos)
__acquires(capi_controller_lock)
{
mutex_lock(&capi_controller_lock);
if (*pos < CAPI_MAXCONTR)
return &capi_controller[*pos];
return NULL;
}
static void *controller_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
if (*pos < CAPI_MAXCONTR)
return &capi_controller[*pos];
return NULL;
}
static void controller_stop(struct seq_file *seq, void *v)
__releases(capi_controller_lock)
{
mutex_unlock(&capi_controller_lock);
}
static int controller_show(struct seq_file *seq, void *v)
{
struct capi_ctr *ctr = *(struct capi_ctr **) v;
if (!ctr)
return 0;
seq_printf(seq, "%d %-10s %-8s %-16s %s\n",
ctr->cnr, ctr->driver_name,
state2str(ctr->state),
ctr->name,
ctr->procinfo ? ctr->procinfo(ctr) : "");
return 0;
}
static int contrstats_show(struct seq_file *seq, void *v)
{
struct capi_ctr *ctr = *(struct capi_ctr **) v;
if (!ctr)
return 0;
seq_printf(seq, "%d %lu %lu %lu %lu\n",
ctr->cnr,
ctr->nrecvctlpkt,
ctr->nrecvdatapkt,
ctr->nsentctlpkt,
ctr->nsentdatapkt);
return 0;
}
static const struct seq_operations seq_controller_ops = {
.start = controller_start,
.next = controller_next,
.stop = controller_stop,
.show = controller_show,
};
static const struct seq_operations seq_contrstats_ops = {
.start = controller_start,
.next = controller_next,
.stop = controller_stop,
.show = contrstats_show,
};
// /proc/capi/applications:
// applid l3cnt dblkcnt dblklen #ncci recvqueuelen
// /proc/capi/applstats:
// applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
// ---------------------------------------------------------------------------
static void *applications_start(struct seq_file *seq, loff_t *pos)
__acquires(capi_controller_lock)
{
mutex_lock(&capi_controller_lock);
if (*pos < CAPI_MAXAPPL)
return &capi_applications[*pos];
return NULL;
}
static void *
applications_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
if (*pos < CAPI_MAXAPPL)
return &capi_applications[*pos];
return NULL;
}
static void applications_stop(struct seq_file *seq, void *v)
__releases(capi_controller_lock)
{
mutex_unlock(&capi_controller_lock);
}
static int
applications_show(struct seq_file *seq, void *v)
{
struct capi20_appl *ap = *(struct capi20_appl **) v;
if (!ap)
return 0;
seq_printf(seq, "%u %d %d %d\n",
ap->applid,
ap->rparam.level3cnt,
ap->rparam.datablkcnt,
ap->rparam.datablklen);
return 0;
}
static int
applstats_show(struct seq_file *seq, void *v)
{
struct capi20_appl *ap = *(struct capi20_appl **) v;
if (!ap)
return 0;
seq_printf(seq, "%u %lu %lu %lu %lu\n",
ap->applid,
ap->nrecvctlpkt,
ap->nrecvdatapkt,
ap->nsentctlpkt,
ap->nsentdatapkt);
return 0;
}
static const struct seq_operations seq_applications_ops = {
.start = applications_start,
.next = applications_next,
.stop = applications_stop,
.show = applications_show,
};
static const struct seq_operations seq_applstats_ops = {
.start = applications_start,
.next = applications_next,
.stop = applications_stop,
.show = applstats_show,
};
// ---------------------------------------------------------------------------
/* /proc/capi/drivers is always empty */
static ssize_t empty_read(struct file *file, char __user *buf,
size_t size, loff_t *off)
{
return 0;
}
static const struct proc_ops empty_proc_ops = {
.proc_read = empty_read,
.proc_lseek = default_llseek,
};
// ---------------------------------------------------------------------------
void __init
kcapi_proc_init(void)
{
proc_mkdir("capi", NULL);
proc_mkdir("capi/controllers", NULL);
proc_create_seq("capi/controller", 0, NULL, &seq_controller_ops);
proc_create_seq("capi/contrstats", 0, NULL, &seq_contrstats_ops);
proc_create_seq("capi/applications", 0, NULL, &seq_applications_ops);
proc_create_seq("capi/applstats", 0, NULL, &seq_applstats_ops);
proc_create("capi/driver", 0, NULL, &empty_proc_ops);
}
void
kcapi_proc_exit(void)
{
remove_proc_entry("capi/driver", NULL);
remove_proc_entry("capi/controller", NULL);
remove_proc_entry("capi/contrstats", NULL);
remove_proc_entry("capi/applications", NULL);
remove_proc_entry("capi/applstats", NULL);
remove_proc_entry("capi/controllers", NULL);
remove_proc_entry("capi", NULL);
}

View File

@@ -1,6 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
# Makefile for the CAPI hardware drivers
# Object files in subdirectories
obj-$(CONFIG_MISDN) += mISDN/

View File

@@ -1,98 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Hardware for mISDN
#
comment "mISDN hardware drivers"
config MISDN_HFCPCI
tristate "Support for HFC PCI cards"
depends on MISDN
depends on PCI
help
Enable support for cards with Cologne Chip AG's
HFC PCI chip.
config MISDN_HFCMULTI
tristate "Support for HFC multiport cards (HFC-4S/8S/E1)"
depends on (PCI || CPM1) && HAS_IOPORT
depends on MISDN
help
Enable support for cards with Cologne Chip AG's HFC multiport
chip. There are three types of chips that are quite similar,
but the interface is different:
* HFC-4S (4 S/T interfaces on one chip)
* HFC-8S (8 S/T interfaces on one chip)
* HFC-E1 (E1 interface for 2Mbit ISDN)
config MISDN_HFCMULTI_8xx
bool "Support for XHFC embedded board in HFC multiport driver"
depends on MISDN
depends on MISDN_HFCMULTI
depends on CPM1
default CPM1
help
Enable support for the XHFC embedded solution from Speech Design.
config MISDN_HFCUSB
tristate "Support for HFC-S USB based TAs"
depends on USB
help
Enable support for USB ISDN TAs with Cologne Chip AG's
HFC-S USB ISDN Controller
config MISDN_AVMFRITZ
tristate "Support for AVM FRITZ!CARD PCI"
depends on MISDN
depends on PCI && HAS_IOPORT
select MISDN_IPAC
help
Enable support for AVMs FRITZ!CARD PCI cards
config MISDN_SPEEDFAX
tristate "Support for Sedlbauer Speedfax+"
depends on MISDN
depends on PCI && HAS_IOPORT
select MISDN_IPAC
select MISDN_ISAR
help
Enable support for Sedlbauer Speedfax+.
config MISDN_INFINEON
tristate "Support for cards with Infineon chipset"
depends on MISDN
depends on PCI && HAS_IOPORT
select MISDN_IPAC
help
Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX
chip from Infineon (former manufacturer Siemens).
config MISDN_W6692
tristate "Support for cards with Winbond 6692"
depends on MISDN
depends on PCI && HAS_IOPORT
help
Enable support for Winbond 6692 PCI chip based cards.
config MISDN_NETJET
tristate "Support for NETJet cards"
depends on MISDN
depends on PCI && HAS_IOPORT
depends on TTY
select MISDN_IPAC
select MISDN_HDLC
help
Enable support for Traverse Technologies NETJet PCI cards.
config MISDN_HDLC
tristate
select CRC_CCITT
select BITREVERSE
config MISDN_IPAC
tristate
depends on MISDN
config MISDN_ISAR
tristate
depends on MISDN

View File

@@ -1,19 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the modular ISDN hardware drivers
#
#
obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o
obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o
obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o
obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o
obj-$(CONFIG_MISDN_W6692) += w6692.o
obj-$(CONFIG_MISDN_NETJET) += netjet.o
# chip modules
obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o
obj-$(CONFIG_MISDN_HDLC) += isdnhdlc.o

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,167 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* For License see notice in hfc_multi.c
*
* special IO and init functions for the embedded XHFC board
* from Speech Design
*
*/
#include <asm/cpm1.h>
/* Change this to the value used by your board */
#ifndef IMAP_ADDR
#define IMAP_ADDR 0xFFF00000
#endif
static void
#ifdef HFC_REGISTER_DEBUG
HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val,
const char *function, int line)
#else
HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val)
#endif
{
hc->immap->im_ioport.iop_padat |= PA_XHFC_A0;
writeb(reg, hc->xhfc_memaddr);
hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0);
writeb(val, hc->xhfc_memdata);
}
static u_char
#ifdef HFC_REGISTER_DEBUG
HFC_inb_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line)
#else
HFC_inb_embsd(struct hfc_multi *hc, u_char reg)
#endif
{
hc->immap->im_ioport.iop_padat |= PA_XHFC_A0;
writeb(reg, hc->xhfc_memaddr);
hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0);
return readb(hc->xhfc_memdata);
}
static u_short
#ifdef HFC_REGISTER_DEBUG
HFC_inw_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line)
#else
HFC_inw_embsd(struct hfc_multi *hc, u_char reg)
#endif
{
hc->immap->im_ioport.iop_padat |= PA_XHFC_A0;
writeb(reg, hc->xhfc_memaddr);
hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0);
return readb(hc->xhfc_memdata);
}
static void
#ifdef HFC_REGISTER_DEBUG
HFC_wait_embsd(struct hfc_multi *hc, const char *function, int line)
#else
HFC_wait_embsd(struct hfc_multi *hc)
#endif
{
hc->immap->im_ioport.iop_padat |= PA_XHFC_A0;
writeb(R_STATUS, hc->xhfc_memaddr);
hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0);
while (readb(hc->xhfc_memdata) & V_BUSY)
cpu_relax();
}
/* write fifo data (EMBSD) */
void
write_fifo_embsd(struct hfc_multi *hc, u_char *data, int len)
{
hc->immap->im_ioport.iop_padat |= PA_XHFC_A0;
*hc->xhfc_memaddr = A_FIFO_DATA0;
hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0);
while (len) {
*hc->xhfc_memdata = *data;
data++;
len--;
}
}
/* read fifo data (EMBSD) */
void
read_fifo_embsd(struct hfc_multi *hc, u_char *data, int len)
{
hc->immap->im_ioport.iop_padat |= PA_XHFC_A0;
*hc->xhfc_memaddr = A_FIFO_DATA0;
hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0);
while (len) {
*data = (u_char)(*hc->xhfc_memdata);
data++;
len--;
}
}
static int
setup_embedded(struct hfc_multi *hc, struct hm_map *m)
{
printk(KERN_INFO
"HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n",
m->vendor_name, m->card_name, m->clock2 ? "double" : "normal");
hc->pci_dev = NULL;
if (m->clock2)
test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
hc->leds = m->leds;
hc->ledstate = 0xAFFEAFFE;
hc->opticalsupport = m->opticalsupport;
hc->pci_iobase = 0;
hc->pci_membase = 0;
hc->xhfc_membase = NULL;
hc->xhfc_memaddr = NULL;
hc->xhfc_memdata = NULL;
/* set memory access methods */
if (m->io_mode) /* use mode from card config */
hc->io_mode = m->io_mode;
switch (hc->io_mode) {
case HFC_IO_MODE_EMBSD:
test_and_set_bit(HFC_CHIP_EMBSD, &hc->chip);
hc->slots = 128; /* required */
hc->HFC_outb = HFC_outb_embsd;
hc->HFC_inb = HFC_inb_embsd;
hc->HFC_inw = HFC_inw_embsd;
hc->HFC_wait = HFC_wait_embsd;
hc->read_fifo = read_fifo_embsd;
hc->write_fifo = write_fifo_embsd;
hc->xhfc_origmembase = XHFC_MEMBASE + XHFC_OFFSET * hc->id;
hc->xhfc_membase = (u_char *)ioremap(hc->xhfc_origmembase,
XHFC_MEMSIZE);
if (!hc->xhfc_membase) {
printk(KERN_WARNING
"HFC-multi: failed to remap xhfc address space. "
"(internal error)\n");
return -EIO;
}
hc->xhfc_memaddr = (u_long *)(hc->xhfc_membase + 4);
hc->xhfc_memdata = (u_long *)(hc->xhfc_membase);
printk(KERN_INFO
"HFC-multi: xhfc_membase:%#lx xhfc_origmembase:%#lx "
"xhfc_memaddr:%#lx xhfc_memdata:%#lx\n",
(u_long)hc->xhfc_membase, hc->xhfc_origmembase,
(u_long)hc->xhfc_memaddr, (u_long)hc->xhfc_memdata);
break;
default:
printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
return -EIO;
}
/* Prepare the MPC8XX PortA 10 as output (address/data selector) */
hc->immap = (struct immap *)(IMAP_ADDR);
hc->immap->im_ioport.iop_papar &= ~(PA_XHFC_A0);
hc->immap->im_ioport.iop_paodr &= ~(PA_XHFC_A0);
hc->immap->im_ioport.iop_padir |= PA_XHFC_A0;
/* Prepare the MPC8xx PortB __X__ as input (ISDN__X__IRQ) */
hc->pb_irqmsk = (PB_XHFC_IRQ1 << hc->id);
hc->immap->im_cpm.cp_pbpar &= ~(hc->pb_irqmsk);
hc->immap->im_cpm.cp_pbodr &= ~(hc->pb_irqmsk);
hc->immap->im_cpm.cp_pbdir &= ~(hc->pb_irqmsk);
/* At this point the needed config is done */
/* fifos are still not enabled */
return 0;
}

View File

@@ -1,214 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* specific defines for CCD's HFC 2BDS0 PCI chips
*
* Author Werner Cornelius (werner@isdn4linux.de)
*
* Copyright 1999 by Werner Cornelius (werner@isdn4linux.de)
*/
/*
* thresholds for transparent B-channel mode
* change mask and threshold simultaneously
*/
#define HFCPCI_BTRANS_THRESHOLD 128
#define HFCPCI_FILLEMPTY 64
#define HFCPCI_BTRANS_THRESMASK 0x00
/* defines for PCI config */
#define PCI_ENA_MEMIO 0x02
#define PCI_ENA_MASTER 0x04
/* GCI/IOM bus monitor registers */
#define HCFPCI_C_I 0x08
#define HFCPCI_TRxR 0x0C
#define HFCPCI_MON1_D 0x28
#define HFCPCI_MON2_D 0x2C
/* GCI/IOM bus timeslot registers */
#define HFCPCI_B1_SSL 0x80
#define HFCPCI_B2_SSL 0x84
#define HFCPCI_AUX1_SSL 0x88
#define HFCPCI_AUX2_SSL 0x8C
#define HFCPCI_B1_RSL 0x90
#define HFCPCI_B2_RSL 0x94
#define HFCPCI_AUX1_RSL 0x98
#define HFCPCI_AUX2_RSL 0x9C
/* GCI/IOM bus data registers */
#define HFCPCI_B1_D 0xA0
#define HFCPCI_B2_D 0xA4
#define HFCPCI_AUX1_D 0xA8
#define HFCPCI_AUX2_D 0xAC
/* GCI/IOM bus configuration registers */
#define HFCPCI_MST_EMOD 0xB4
#define HFCPCI_MST_MODE 0xB8
#define HFCPCI_CONNECT 0xBC
/* Interrupt and status registers */
#define HFCPCI_FIFO_EN 0x44
#define HFCPCI_TRM 0x48
#define HFCPCI_B_MODE 0x4C
#define HFCPCI_CHIP_ID 0x58
#define HFCPCI_CIRM 0x60
#define HFCPCI_CTMT 0x64
#define HFCPCI_INT_M1 0x68
#define HFCPCI_INT_M2 0x6C
#define HFCPCI_INT_S1 0x78
#define HFCPCI_INT_S2 0x7C
#define HFCPCI_STATUS 0x70
/* S/T section registers */
#define HFCPCI_STATES 0xC0
#define HFCPCI_SCTRL 0xC4
#define HFCPCI_SCTRL_E 0xC8
#define HFCPCI_SCTRL_R 0xCC
#define HFCPCI_SQ 0xD0
#define HFCPCI_CLKDEL 0xDC
#define HFCPCI_B1_REC 0xF0
#define HFCPCI_B1_SEND 0xF0
#define HFCPCI_B2_REC 0xF4
#define HFCPCI_B2_SEND 0xF4
#define HFCPCI_D_REC 0xF8
#define HFCPCI_D_SEND 0xF8
#define HFCPCI_E_REC 0xFC
/* bits in status register (READ) */
#define HFCPCI_PCI_PROC 0x02
#define HFCPCI_NBUSY 0x04
#define HFCPCI_TIMER_ELAP 0x10
#define HFCPCI_STATINT 0x20
#define HFCPCI_FRAMEINT 0x40
#define HFCPCI_ANYINT 0x80
/* bits in CTMT (Write) */
#define HFCPCI_CLTIMER 0x80
#define HFCPCI_TIM3_125 0x04
#define HFCPCI_TIM25 0x10
#define HFCPCI_TIM50 0x14
#define HFCPCI_TIM400 0x18
#define HFCPCI_TIM800 0x1C
#define HFCPCI_AUTO_TIMER 0x20
#define HFCPCI_TRANSB2 0x02
#define HFCPCI_TRANSB1 0x01
/* bits in CIRM (Write) */
#define HFCPCI_AUX_MSK 0x07
#define HFCPCI_RESET 0x08
#define HFCPCI_B1_REV 0x40
#define HFCPCI_B2_REV 0x80
/* bits in INT_M1 and INT_S1 */
#define HFCPCI_INTS_B1TRANS 0x01
#define HFCPCI_INTS_B2TRANS 0x02
#define HFCPCI_INTS_DTRANS 0x04
#define HFCPCI_INTS_B1REC 0x08
#define HFCPCI_INTS_B2REC 0x10
#define HFCPCI_INTS_DREC 0x20
#define HFCPCI_INTS_L1STATE 0x40
#define HFCPCI_INTS_TIMER 0x80
/* bits in INT_M2 */
#define HFCPCI_PROC_TRANS 0x01
#define HFCPCI_GCI_I_CHG 0x02
#define HFCPCI_GCI_MON_REC 0x04
#define HFCPCI_IRQ_ENABLE 0x08
#define HFCPCI_PMESEL 0x80
/* bits in STATES */
#define HFCPCI_STATE_MSK 0x0F
#define HFCPCI_LOAD_STATE 0x10
#define HFCPCI_ACTIVATE 0x20
#define HFCPCI_DO_ACTION 0x40
#define HFCPCI_NT_G2_G3 0x80
/* bits in HFCD_MST_MODE */
#define HFCPCI_MASTER 0x01
#define HFCPCI_SLAVE 0x00
#define HFCPCI_F0IO_POSITIV 0x02
#define HFCPCI_F0_NEGATIV 0x04
#define HFCPCI_F0_2C4 0x08
/* remaining bits are for codecs control */
/* bits in HFCD_SCTRL */
#define SCTRL_B1_ENA 0x01
#define SCTRL_B2_ENA 0x02
#define SCTRL_MODE_TE 0x00
#define SCTRL_MODE_NT 0x04
#define SCTRL_LOW_PRIO 0x08
#define SCTRL_SQ_ENA 0x10
#define SCTRL_TEST 0x20
#define SCTRL_NONE_CAP 0x40
#define SCTRL_PWR_DOWN 0x80
/* bits in SCTRL_E */
#define HFCPCI_AUTO_AWAKE 0x01
#define HFCPCI_DBIT_1 0x04
#define HFCPCI_IGNORE_COL 0x08
#define HFCPCI_CHG_B1_B2 0x80
/* bits in FIFO_EN register */
#define HFCPCI_FIFOEN_B1 0x03
#define HFCPCI_FIFOEN_B2 0x0C
#define HFCPCI_FIFOEN_DTX 0x10
#define HFCPCI_FIFOEN_B1TX 0x01
#define HFCPCI_FIFOEN_B1RX 0x02
#define HFCPCI_FIFOEN_B2TX 0x04
#define HFCPCI_FIFOEN_B2RX 0x08
/* definitions of fifo memory area */
#define MAX_D_FRAMES 15
#define MAX_B_FRAMES 31
#define B_SUB_VAL 0x200
#define B_FIFO_SIZE (0x2000 - B_SUB_VAL)
#define D_FIFO_SIZE 512
#define D_FREG_MASK 0xF
struct zt {
__le16 z1; /* Z1 pointer 16 Bit */
__le16 z2; /* Z2 pointer 16 Bit */
};
struct dfifo {
u_char data[D_FIFO_SIZE]; /* FIFO data space */
u_char fill1[0x20A0 - D_FIFO_SIZE]; /* reserved, do not use */
u_char f1, f2; /* f pointers */
u_char fill2[0x20C0 - 0x20A2]; /* reserved, do not use */
/* mask index with D_FREG_MASK for access */
struct zt za[MAX_D_FRAMES + 1];
u_char fill3[0x4000 - 0x2100]; /* align 16K */
};
struct bzfifo {
struct zt za[MAX_B_FRAMES + 1]; /* only range 0x0..0x1F allowed */
u_char f1, f2; /* f pointers */
u_char fill[0x2100 - 0x2082]; /* alignment */
};
union fifo_area {
struct {
struct dfifo d_tx; /* D-send channel */
struct dfifo d_rx; /* D-receive channel */
} d_chan;
struct {
u_char fill1[0x200];
u_char txdat_b1[B_FIFO_SIZE];
struct bzfifo txbz_b1;
struct bzfifo txbz_b2;
u_char txdat_b2[B_FIFO_SIZE];
u_char fill2[D_FIFO_SIZE];
u_char rxdat_b1[B_FIFO_SIZE];
struct bzfifo rxbz_b1;
struct bzfifo rxbz_b2;
u_char rxdat_b2[B_FIFO_SIZE];
} b_chans;
u_char fill[32768];
};
#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io) + b))
#define Read_hfc(a, b) (readb((a->hw.pci_io) + b))

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

View File

@@ -1,425 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* hfcsusb.h, HFC-S USB mISDN driver
*/
#ifndef __HFCSUSB_H__
#define __HFCSUSB_H__
#define DRIVER_NAME "HFC-S_USB"
#define DBG_HFC_CALL_TRACE 0x00010000
#define DBG_HFC_FIFO_VERBOSE 0x00020000
#define DBG_HFC_USB_VERBOSE 0x00100000
#define DBG_HFC_URB_INFO 0x00200000
#define DBG_HFC_URB_ERROR 0x00400000
#define DEFAULT_TRANSP_BURST_SZ 128
#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */
#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */
#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */
/* hfcsusb Layer1 commands */
#define HFC_L1_ACTIVATE_TE 1
#define HFC_L1_ACTIVATE_NT 2
#define HFC_L1_DEACTIVATE_NT 3
#define HFC_L1_FORCE_DEACTIVATE_TE 4
/* cmd FLAGS in HFCUSB_STATES register */
#define HFCUSB_LOAD_STATE 0x10
#define HFCUSB_ACTIVATE 0x20
#define HFCUSB_DO_ACTION 0x40
#define HFCUSB_NT_G2_G3 0x80
/* timers */
#define NT_ACTIVATION_TIMER 0x01 /* enables NT mode activation Timer */
#define NT_T1_COUNT 10
#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */
#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */
#define HFCUSB_TX_THRESHOLD 96 /* threshold for fifo report bit tx */
#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */
#define HFCUSB_CIRM 0x00 /* cirm register index */
#define HFCUSB_USB_SIZE 0x07 /* int length register */
#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */
#define HFCUSB_F_CROSS 0x0b /* bit order register */
#define HFCUSB_CLKDEL 0x37 /* bit delay register */
#define HFCUSB_CON_HDLC 0xfa /* channel connect register */
#define HFCUSB_HDLC_PAR 0xfb
#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */
#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */
#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */
#define HFCUSB_F_THRES 0x0c /* threshold register */
#define HFCUSB_FIFO 0x0f /* fifo select register */
#define HFCUSB_F_USAGE 0x1a /* fifo usage register */
#define HFCUSB_MST_MODE0 0x14
#define HFCUSB_MST_MODE1 0x15
#define HFCUSB_P_DATA 0x1f
#define HFCUSB_INC_RES_F 0x0e
#define HFCUSB_B1_SSL 0x20
#define HFCUSB_B2_SSL 0x21
#define HFCUSB_B1_RSL 0x24
#define HFCUSB_B2_RSL 0x25
#define HFCUSB_STATES 0x30
#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */
/* fifo registers */
#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */
#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */
#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */
#define HFCUSB_B2_TX 2
#define HFCUSB_B2_RX 3
#define HFCUSB_D_TX 4
#define HFCUSB_D_RX 5
#define HFCUSB_PCM_TX 6
#define HFCUSB_PCM_RX 7
#define USB_INT 0
#define USB_BULK 1
#define USB_ISOC 2
#define ISOC_PACKETS_D 8
#define ISOC_PACKETS_B 8
#define ISO_BUFFER_SIZE 128
/* defines how much ISO packets are handled in one URB */
static int iso_packets[8] =
{ ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B,
ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D
};
/* Fifo flow Control for TX ISO */
#define SINK_MAX 68
#define SINK_MIN 48
#define SINK_DMIN 12
#define SINK_DMAX 18
#define BITLINE_INF (-96 * 8)
/* HFC-S USB register access by Control-URSs */
#define write_reg_atomic(a, b, c) \
usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \
0, 0, HFC_CTRL_TIMEOUT)
#define read_reg_atomic(a, b, c) \
usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \
1, HFC_CTRL_TIMEOUT)
#define HFC_CTRL_BUFSIZE 64
struct ctrl_buf {
__u8 hfcs_reg; /* register number */
__u8 reg_val; /* value to be written (or read) */
};
/*
* URB error codes
* Used to represent a list of values and their respective symbolic names
*/
struct hfcusb_symbolic_list {
const int num;
const char *name;
};
static struct hfcusb_symbolic_list urb_errlist[] = {
{-ENOMEM, "No memory for allocation of internal structures"},
{-ENOSPC, "The host controller's bandwidth is already consumed"},
{-ENOENT, "URB was canceled by unlink_urb"},
{-EXDEV, "ISO transfer only partially completed"},
{-EAGAIN, "Too match scheduled for the future"},
{-ENXIO, "URB already queued"},
{-EFBIG, "Too much ISO frames requested"},
{-ENOSR, "Buffer error (overrun)"},
{-EPIPE, "Specified endpoint is stalled (device not responding)"},
{-EOVERFLOW, "Babble (bad cable?)"},
{-EPROTO, "Bit-stuff error (bad cable?)"},
{-EILSEQ, "CRC/Timeout"},
{-ETIMEDOUT, "NAK (device does not respond)"},
{-ESHUTDOWN, "Device unplugged"},
{-1, NULL}
};
static inline const char *
symbolic(struct hfcusb_symbolic_list list[], const int num)
{
int i;
for (i = 0; list[i].name != NULL; i++)
if (list[i].num == num)
return list[i].name;
return "<unknown USB Error>";
}
/* USB descriptor need to contain one of the following EndPoint combination: */
#define CNF_4INT3ISO 1 /* 4 INT IN, 3 ISO OUT */
#define CNF_3INT3ISO 2 /* 3 INT IN, 3 ISO OUT */
#define CNF_4ISO3ISO 3 /* 4 ISO IN, 3 ISO OUT */
#define CNF_3ISO3ISO 4 /* 3 ISO IN, 3 ISO OUT */
#define EP_NUL 1 /* Endpoint at this position not allowed */
#define EP_NOP 2 /* all type of endpoints allowed at this position */
#define EP_ISO 3 /* Isochron endpoint mandatory at this position */
#define EP_BLK 4 /* Bulk endpoint mandatory at this position */
#define EP_INT 5 /* Interrupt endpoint mandatory at this position */
#define HFC_CHAN_B1 0
#define HFC_CHAN_B2 1
#define HFC_CHAN_D 2
#define HFC_CHAN_E 3
/*
* List of all supported endpoint configuration sets, used to find the
* best matching endpoint configuration within a device's USB descriptor.
* We need at least 3 RX endpoints, and 3 TX endpoints, either
* INT-in and ISO-out, or ISO-in and ISO-out)
* with 4 RX endpoints even E-Channel logging is possible
*/
static int
validconf[][19] = {
/* INT in, ISO out config */
{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT,
EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
CNF_4INT3ISO, 2, 1},
{EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL,
EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
CNF_3INT3ISO, 2, 0},
/* ISO in, ISO out config */
{EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP,
EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO,
CNF_4ISO3ISO, 2, 1},
{EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL,
CNF_3ISO3ISO, 2, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */
};
/* string description of chosen config */
static char *conf_str[] = {
"4 Interrupt IN + 3 Isochron OUT",
"3 Interrupt IN + 3 Isochron OUT",
"4 Isochron IN + 3 Isochron OUT",
"3 Isochron IN + 3 Isochron OUT"
};
#define LED_OFF 0 /* no LED support */
#define LED_SCHEME1 1 /* LED standard scheme */
#define LED_SCHEME2 2 /* not used yet... */
#define LED_POWER_ON 1
#define LED_POWER_OFF 2
#define LED_S0_ON 3
#define LED_S0_OFF 4
#define LED_B1_ON 5
#define LED_B1_OFF 6
#define LED_B1_DATA 7
#define LED_B2_ON 8
#define LED_B2_OFF 9
#define LED_B2_DATA 10
#define LED_NORMAL 0 /* LEDs are normal */
#define LED_INVERTED 1 /* LEDs are inverted */
/* time in ms to perform a Flashing LED when B-Channel has traffic */
#define LED_TIME 250
struct hfcsusb;
struct usb_fifo;
/* structure defining input+output fifos (interrupt/bulk mode) */
struct iso_urb {
struct urb *urb;
__u8 buffer[ISO_BUFFER_SIZE]; /* buffer rx/tx USB URB data */
struct usb_fifo *owner_fifo; /* pointer to owner fifo */
__u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */
#ifdef ISO_FRAME_START_DEBUG
int start_frames[ISO_FRAME_START_RING_COUNT];
__u8 iso_frm_strt_pos; /* index in start_frame[] */
#endif
};
struct usb_fifo {
int fifonum; /* fifo index attached to this structure */
int active; /* fifo is currently active */
struct hfcsusb *hw; /* pointer to main structure */
int pipe; /* address of endpoint */
__u8 usb_packet_maxlen; /* maximum length for usb transfer */
unsigned int max_size; /* maximum size of receive/send packet */
__u8 intervall; /* interrupt interval */
struct urb *urb; /* transfer structure for usb routines */
__u8 buffer[128]; /* buffer USB INT OUT URB data */
int bit_line; /* how much bits are in the fifo? */
__u8 usb_transfer_mode; /* switched between ISO and INT */
struct iso_urb iso[2]; /* two urbs to have one always
one pending */
struct dchannel *dch; /* link to hfcsusb_t->dch */
struct bchannel *bch; /* link to hfcsusb_t->bch */
struct dchannel *ech; /* link to hfcsusb_t->ech, TODO: E-CHANNEL */
int last_urblen; /* remember length of last packet */
__u8 stop_gracefull; /* stops URB retransmission */
};
struct hfcsusb {
struct list_head list;
struct dchannel dch;
struct bchannel bch[2];
struct dchannel ech; /* TODO : wait for struct echannel ;) */
struct usb_device *dev; /* our device */
struct usb_interface *intf; /* used interface */
int if_used; /* used interface number */
int alt_used; /* used alternate config */
int cfg_used; /* configuration index used */
int vend_idx; /* index in hfcsusb_idtab */
int packet_size;
int iso_packet_size;
struct usb_fifo fifos[HFCUSB_NUM_FIFOS];
/* control pipe background handling */
struct ctrl_buf ctrl_buff[HFC_CTRL_BUFSIZE];
int ctrl_in_idx, ctrl_out_idx, ctrl_cnt;
struct urb *ctrl_urb;
struct usb_ctrlrequest ctrl_write;
struct usb_ctrlrequest ctrl_read;
int ctrl_paksize;
int ctrl_in_pipe, ctrl_out_pipe;
spinlock_t ctrl_lock; /* lock for ctrl */
spinlock_t lock;
__u8 threshold_mask;
__u8 led_state;
__u8 protocol;
int nt_timer;
int open;
__u8 timers;
__u8 initdone;
char name[MISDN_MAX_IDLEN];
};
/* private vendor specific data */
struct hfcsusb_vdata {
__u8 led_scheme; /* led display scheme */
signed short led_bits[8]; /* array of 8 possible LED bitmask */
char *vend_name; /* device name */
};
#define HFC_MAX_TE_LAYER1_STATE 8
#define HFC_MAX_NT_LAYER1_STATE 4
static const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = {
"TE F0 - Reset",
"TE F1 - Reset",
"TE F2 - Sensing",
"TE F3 - Deactivated",
"TE F4 - Awaiting signal",
"TE F5 - Identifying input",
"TE F6 - Synchronized",
"TE F7 - Activated",
"TE F8 - Lost framing",
};
static const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = {
"NT G0 - Reset",
"NT G1 - Deactive",
"NT G2 - Pending activation",
"NT G3 - Active",
"NT G4 - Pending deactivation",
};
/* supported devices */
static const struct usb_device_id hfcsusb_idtab[] = {
{
USB_DEVICE(0x0959, 0x2bd0),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_OFF, {4, 0, 2, 1},
"ISDN USB TA (Cologne Chip HFC-S USB based)"}),
},
{
USB_DEVICE(0x0675, 0x1688),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {1, 2, 0, 0},
"DrayTek miniVigor 128 USB ISDN TA"}),
},
{
USB_DEVICE(0x07b0, 0x0007),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Billion tiny USB ISDN TA 128"}),
},
{
USB_DEVICE(0x0742, 0x2008),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {4, 0, 2, 1},
"Stollmann USB TA"}),
},
{
USB_DEVICE(0x0742, 0x2009),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {4, 0, 2, 1},
"Aceex USB ISDN TA"}),
},
{
USB_DEVICE(0x0742, 0x200A),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {4, 0, 2, 1},
"OEM USB ISDN TA"}),
},
{
USB_DEVICE(0x08e3, 0x0301),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {2, 0, 1, 4},
"Olitec USB RNIS"}),
},
{
USB_DEVICE(0x07fa, 0x0846),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Bewan Modem RNIS USB"}),
},
{
USB_DEVICE(0x07fa, 0x0847),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Djinn Numeris USB"}),
},
{
USB_DEVICE(0x07b0, 0x0006),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {0x80, -64, -32, -16},
"Twister ISDN TA"}),
},
{
USB_DEVICE(0x071d, 0x1005),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {0x02, 0, 0x01, 0x04},
"Eicon DIVA USB 4.0"}),
},
{
USB_DEVICE(0x0586, 0x0102),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {0x88, -64, -32, -16},
"ZyXEL OMNI.NET USB II"}),
},
{
USB_DEVICE(0x1ae7, 0x0525),
.driver_info = (unsigned long) &((struct hfcsusb_vdata)
{LED_SCHEME1, {0x88, -64, -32, -16},
"X-Tensions USB ISDN TA XC-525"}),
},
{ }
};
MODULE_DEVICE_TABLE(usb, hfcsusb_idtab);
#endif /* __HFCSUSB_H__ */

View File

@@ -1,96 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* iohelper.h
* helper for define functions to access ISDN hardware
* supported are memory mapped IO
* indirect port IO (one port for address, one for data)
*
* Author Karsten Keil <keil@isdn4linux.de>
*
* Copyright 2009 by Karsten Keil <keil@isdn4linux.de>
*/
#ifndef _IOHELPER_H
#define _IOHELPER_H
typedef u8 (read_reg_func)(void *hwp, u8 offset);
typedef void (write_reg_func)(void *hwp, u8 offset, u8 value);
typedef void (fifo_func)(void *hwp, u8 offset, u8 *datap, int size);
struct _ioport {
u32 port;
u32 ale;
};
#define IOFUNC_IO(name, hws, ap) \
static u8 Read##name##_IO(void *p, u8 off) { \
struct hws *hw = p; \
return inb(hw->ap.port + off); \
} \
static void Write##name##_IO(void *p, u8 off, u8 val) { \
struct hws *hw = p; \
outb(val, hw->ap.port + off); \
} \
static void ReadFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \
struct hws *hw = p; \
insb(hw->ap.port + off, dp, size); \
} \
static void WriteFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) { \
struct hws *hw = p; \
outsb(hw->ap.port + off, dp, size); \
}
#define IOFUNC_IND(name, hws, ap) \
static u8 Read##name##_IND(void *p, u8 off) { \
struct hws *hw = p; \
outb(off, hw->ap.ale); \
return inb(hw->ap.port); \
} \
static void Write##name##_IND(void *p, u8 off, u8 val) { \
struct hws *hw = p; \
outb(off, hw->ap.ale); \
outb(val, hw->ap.port); \
} \
static void ReadFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \
struct hws *hw = p; \
outb(off, hw->ap.ale); \
insb(hw->ap.port, dp, size); \
} \
static void WriteFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) { \
struct hws *hw = p; \
outb(off, hw->ap.ale); \
outsb(hw->ap.port, dp, size); \
}
#define IOFUNC_MEMIO(name, hws, typ, adr) \
static u8 Read##name##_MIO(void *p, u8 off) { \
struct hws *hw = p; \
return readb(((typ *)hw->adr) + off); \
} \
static void Write##name##_MIO(void *p, u8 off, u8 val) { \
struct hws *hw = p; \
writeb(val, ((typ *)hw->adr) + off); \
} \
static void ReadFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \
struct hws *hw = p; \
while (size--) \
*dp++ = readb(((typ *)hw->adr) + off); \
} \
static void WriteFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) { \
struct hws *hw = p; \
while (size--) \
writeb(*dp++, ((typ *)hw->adr) + off); \
}
#define ASSIGN_FUNC(typ, name, dest) do { \
dest.read_reg = &Read##name##_##typ; \
dest.write_reg = &Write##name##_##typ; \
dest.read_fifo = &ReadFiFo##name##_##typ; \
dest.write_fifo = &WriteFiFo##name##_##typ; \
} while (0)
#define ASSIGN_FUNC_IPAC(typ, target) do { \
ASSIGN_FUNC(typ, ISAC, target.isac); \
ASSIGN_FUNC(typ, IPAC, target); \
} while (0)
#endif

View File

@@ -1,393 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* ipac.h Defines for the Infineon (former Siemens) ISDN
* chip series
*
* Author Karsten Keil <keil@isdn4linux.de>
*
* Copyright 2009 by Karsten Keil <keil@isdn4linux.de>
*/
#include "iohelper.h"
struct isac_hw {
struct dchannel dch;
u32 type;
u32 off; /* offset to isac regs */
char *name;
spinlock_t *hwlock; /* lock HW access */
read_reg_func *read_reg;
write_reg_func *write_reg;
fifo_func *read_fifo;
fifo_func *write_fifo;
int (*monitor)(void *, u32, u8 *, int);
void (*release)(struct isac_hw *);
int (*init)(struct isac_hw *);
int (*ctrl)(struct isac_hw *, u32, u_long);
int (*open)(struct isac_hw *, struct channel_req *);
u8 *mon_tx;
u8 *mon_rx;
int mon_txp;
int mon_txc;
int mon_rxp;
struct arcofi_msg *arcofi_list;
struct timer_list arcofitimer;
wait_queue_head_t arcofi_wait;
u8 arcofi_bc;
u8 arcofi_state;
u8 mocr;
u8 adf2;
u8 state;
};
struct ipac_hw;
struct hscx_hw {
struct bchannel bch;
struct ipac_hw *ip;
u8 fifo_size;
u8 off; /* offset to ICA or ICB */
u8 slot;
char log[64];
};
struct ipac_hw {
struct isac_hw isac;
struct hscx_hw hscx[2];
char *name;
void *hw;
spinlock_t *hwlock; /* lock HW access */
struct module *owner;
u32 type;
read_reg_func *read_reg;
write_reg_func *write_reg;
fifo_func *read_fifo;
fifo_func *write_fifo;
void (*release)(struct ipac_hw *);
int (*init)(struct ipac_hw *);
int (*ctrl)(struct ipac_hw *, u32, u_long);
u8 conf;
};
#define IPAC_TYPE_ISAC 0x0010
#define IPAC_TYPE_IPAC 0x0020
#define IPAC_TYPE_ISACX 0x0040
#define IPAC_TYPE_IPACX 0x0080
#define IPAC_TYPE_HSCX 0x0100
#define ISAC_USE_ARCOFI 0x1000
/* Monitor functions */
#define MONITOR_RX_0 0x1000
#define MONITOR_RX_1 0x1001
#define MONITOR_TX_0 0x2000
#define MONITOR_TX_1 0x2001
/* All registers original Siemens Spec */
/* IPAC/ISAC registers */
#define ISAC_ISTA 0x20
#define ISAC_MASK 0x20
#define ISAC_CMDR 0x21
#define ISAC_STAR 0x21
#define ISAC_MODE 0x22
#define ISAC_TIMR 0x23
#define ISAC_EXIR 0x24
#define ISAC_RBCL 0x25
#define ISAC_RSTA 0x27
#define ISAC_RBCH 0x2A
#define ISAC_SPCR 0x30
#define ISAC_CIR0 0x31
#define ISAC_CIX0 0x31
#define ISAC_MOR0 0x32
#define ISAC_MOX0 0x32
#define ISAC_CIR1 0x33
#define ISAC_CIX1 0x33
#define ISAC_MOR1 0x34
#define ISAC_MOX1 0x34
#define ISAC_STCR 0x37
#define ISAC_ADF1 0x38
#define ISAC_ADF2 0x39
#define ISAC_MOCR 0x3a
#define ISAC_MOSR 0x3a
#define ISAC_SQRR 0x3b
#define ISAC_SQXR 0x3b
#define ISAC_RBCH_XAC 0x80
#define IPAC_D_TIN2 0x01
/* IPAC/HSCX */
#define IPAC_ISTAB 0x20 /* RD */
#define IPAC_MASKB 0x20 /* WR */
#define IPAC_STARB 0x21 /* RD */
#define IPAC_CMDRB 0x21 /* WR */
#define IPAC_MODEB 0x22 /* R/W */
#define IPAC_EXIRB 0x24 /* RD */
#define IPAC_RBCLB 0x25 /* RD */
#define IPAC_RAH1 0x26 /* WR */
#define IPAC_RAH2 0x27 /* WR */
#define IPAC_RSTAB 0x27 /* RD */
#define IPAC_RAL1 0x28 /* R/W */
#define IPAC_RAL2 0x29 /* WR */
#define IPAC_RHCRB 0x29 /* RD */
#define IPAC_XBCL 0x2A /* WR */
#define IPAC_CCR2 0x2C /* R/W */
#define IPAC_RBCHB 0x2D /* RD */
#define IPAC_XBCH 0x2D /* WR */
#define HSCX_VSTR 0x2E /* RD */
#define IPAC_RLCR 0x2E /* WR */
#define IPAC_CCR1 0x2F /* R/W */
#define IPAC_TSAX 0x30 /* WR */
#define IPAC_TSAR 0x31 /* WR */
#define IPAC_XCCR 0x32 /* WR */
#define IPAC_RCCR 0x33 /* WR */
/* IPAC_ISTAB/IPAC_MASKB bits */
#define IPAC_B_XPR 0x10
#define IPAC_B_RPF 0x40
#define IPAC_B_RME 0x80
#define IPAC_B_ON 0x2F
/* IPAC_EXIRB bits */
#define IPAC_B_RFS 0x04
#define IPAC_B_RFO 0x10
#define IPAC_B_XDU 0x40
#define IPAC_B_XMR 0x80
/* IPAC special registers */
#define IPAC_CONF 0xC0 /* R/W */
#define IPAC_ISTA 0xC1 /* RD */
#define IPAC_MASK 0xC1 /* WR */
#define IPAC_ID 0xC2 /* RD */
#define IPAC_ACFG 0xC3 /* R/W */
#define IPAC_AOE 0xC4 /* R/W */
#define IPAC_ARX 0xC5 /* RD */
#define IPAC_ATX 0xC5 /* WR */
#define IPAC_PITA1 0xC6 /* R/W */
#define IPAC_PITA2 0xC7 /* R/W */
#define IPAC_POTA1 0xC8 /* R/W */
#define IPAC_POTA2 0xC9 /* R/W */
#define IPAC_PCFG 0xCA /* R/W */
#define IPAC_SCFG 0xCB /* R/W */
#define IPAC_TIMR2 0xCC /* R/W */
/* IPAC_ISTA/_MASK bits */
#define IPAC__EXB 0x01
#define IPAC__ICB 0x02
#define IPAC__EXA 0x04
#define IPAC__ICA 0x08
#define IPAC__EXD 0x10
#define IPAC__ICD 0x20
#define IPAC__INT0 0x40
#define IPAC__INT1 0x80
#define IPAC__ON 0xC0
/* HSCX ISTA/MASK bits */
#define HSCX__EXB 0x01
#define HSCX__EXA 0x02
#define HSCX__ICA 0x04
/* ISAC/ISACX/IPAC/IPACX L1 commands */
#define ISAC_CMD_TIM 0x0
#define ISAC_CMD_RS 0x1
#define ISAC_CMD_SCZ 0x4
#define ISAC_CMD_SSZ 0x2
#define ISAC_CMD_AR8 0x8
#define ISAC_CMD_AR10 0x9
#define ISAC_CMD_ARL 0xA
#define ISAC_CMD_DUI 0xF
/* ISAC/ISACX/IPAC/IPACX L1 indications */
#define ISAC_IND_DR 0x0
#define ISAC_IND_RS 0x1
#define ISAC_IND_SD 0x2
#define ISAC_IND_DIS 0x3
#define ISAC_IND_RSY 0x4
#define ISAC_IND_DR6 0x5
#define ISAC_IND_EI 0x6
#define ISAC_IND_PU 0x7
#define ISAC_IND_ARD 0x8
#define ISAC_IND_TI 0xA
#define ISAC_IND_ATI 0xB
#define ISAC_IND_AI8 0xC
#define ISAC_IND_AI10 0xD
#define ISAC_IND_DID 0xF
/* the new ISACX / IPACX */
/* D-channel registers */
#define ISACX_RFIFOD 0x00 /* RD */
#define ISACX_XFIFOD 0x00 /* WR */
#define ISACX_ISTAD 0x20 /* RD */
#define ISACX_MASKD 0x20 /* WR */
#define ISACX_STARD 0x21 /* RD */
#define ISACX_CMDRD 0x21 /* WR */
#define ISACX_MODED 0x22 /* R/W */
#define ISACX_EXMD1 0x23 /* R/W */
#define ISACX_TIMR1 0x24 /* R/W */
#define ISACX_SAP1 0x25 /* WR */
#define ISACX_SAP2 0x26 /* WR */
#define ISACX_RBCLD 0x26 /* RD */
#define ISACX_RBCHD 0x27 /* RD */
#define ISACX_TEI1 0x27 /* WR */
#define ISACX_TEI2 0x28 /* WR */
#define ISACX_RSTAD 0x28 /* RD */
#define ISACX_TMD 0x29 /* R/W */
#define ISACX_CIR0 0x2E /* RD */
#define ISACX_CIX0 0x2E /* WR */
#define ISACX_CIR1 0x2F /* RD */
#define ISACX_CIX1 0x2F /* WR */
/* Transceiver registers */
#define ISACX_TR_CONF0 0x30 /* R/W */
#define ISACX_TR_CONF1 0x31 /* R/W */
#define ISACX_TR_CONF2 0x32 /* R/W */
#define ISACX_TR_STA 0x33 /* RD */
#define ISACX_TR_CMD 0x34 /* R/W */
#define ISACX_SQRR1 0x35 /* RD */
#define ISACX_SQXR1 0x35 /* WR */
#define ISACX_SQRR2 0x36 /* RD */
#define ISACX_SQXR2 0x36 /* WR */
#define ISACX_SQRR3 0x37 /* RD */
#define ISACX_SQXR3 0x37 /* WR */
#define ISACX_ISTATR 0x38 /* RD */
#define ISACX_MASKTR 0x39 /* R/W */
#define ISACX_TR_MODE 0x3A /* R/W */
#define ISACX_ACFG1 0x3C /* R/W */
#define ISACX_ACFG2 0x3D /* R/W */
#define ISACX_AOE 0x3E /* R/W */
#define ISACX_ARX 0x3F /* RD */
#define ISACX_ATX 0x3F /* WR */
/* IOM: Timeslot, DPS, CDA */
#define ISACX_CDA10 0x40 /* R/W */
#define ISACX_CDA11 0x41 /* R/W */
#define ISACX_CDA20 0x42 /* R/W */
#define ISACX_CDA21 0x43 /* R/W */
#define ISACX_CDA_TSDP10 0x44 /* R/W */
#define ISACX_CDA_TSDP11 0x45 /* R/W */
#define ISACX_CDA_TSDP20 0x46 /* R/W */
#define ISACX_CDA_TSDP21 0x47 /* R/W */
#define ISACX_BCHA_TSDP_BC1 0x48 /* R/W */
#define ISACX_BCHA_TSDP_BC2 0x49 /* R/W */
#define ISACX_BCHB_TSDP_BC1 0x4A /* R/W */
#define ISACX_BCHB_TSDP_BC2 0x4B /* R/W */
#define ISACX_TR_TSDP_BC1 0x4C /* R/W */
#define ISACX_TR_TSDP_BC2 0x4D /* R/W */
#define ISACX_CDA1_CR 0x4E /* R/W */
#define ISACX_CDA2_CR 0x4F /* R/W */
/* IOM: Contol, Sync transfer, Monitor */
#define ISACX_TR_CR 0x50 /* R/W */
#define ISACX_TRC_CR 0x50 /* R/W */
#define ISACX_BCHA_CR 0x51 /* R/W */
#define ISACX_BCHB_CR 0x52 /* R/W */
#define ISACX_DCI_CR 0x53 /* R/W */
#define ISACX_DCIC_CR 0x53 /* R/W */
#define ISACX_MON_CR 0x54 /* R/W */
#define ISACX_SDS1_CR 0x55 /* R/W */
#define ISACX_SDS2_CR 0x56 /* R/W */
#define ISACX_IOM_CR 0x57 /* R/W */
#define ISACX_STI 0x58 /* RD */
#define ISACX_ASTI 0x58 /* WR */
#define ISACX_MSTI 0x59 /* R/W */
#define ISACX_SDS_CONF 0x5A /* R/W */
#define ISACX_MCDA 0x5B /* RD */
#define ISACX_MOR 0x5C /* RD */
#define ISACX_MOX 0x5C /* WR */
#define ISACX_MOSR 0x5D /* RD */
#define ISACX_MOCR 0x5E /* R/W */
#define ISACX_MSTA 0x5F /* RD */
#define ISACX_MCONF 0x5F /* WR */
/* Interrupt and general registers */
#define ISACX_ISTA 0x60 /* RD */
#define ISACX_MASK 0x60 /* WR */
#define ISACX_AUXI 0x61 /* RD */
#define ISACX_AUXM 0x61 /* WR */
#define ISACX_MODE1 0x62 /* R/W */
#define ISACX_MODE2 0x63 /* R/W */
#define ISACX_ID 0x64 /* RD */
#define ISACX_SRES 0x64 /* WR */
#define ISACX_TIMR2 0x65 /* R/W */
/* Register Bits */
/* ISACX/IPACX _ISTAD (R) and _MASKD (W) */
#define ISACX_D_XDU 0x04
#define ISACX_D_XMR 0x08
#define ISACX_D_XPR 0x10
#define ISACX_D_RFO 0x20
#define ISACX_D_RPF 0x40
#define ISACX_D_RME 0x80
/* ISACX/IPACX _ISTA (R) and _MASK (W) */
#define ISACX__ICD 0x01
#define ISACX__MOS 0x02
#define ISACX__TRAN 0x04
#define ISACX__AUX 0x08
#define ISACX__CIC 0x10
#define ISACX__ST 0x20
#define IPACX__ON 0x2C
#define IPACX__ICB 0x40
#define IPACX__ICA 0x80
/* ISACX/IPACX _CMDRD (W) */
#define ISACX_CMDRD_XRES 0x01
#define ISACX_CMDRD_XME 0x02
#define ISACX_CMDRD_XTF 0x08
#define ISACX_CMDRD_STI 0x10
#define ISACX_CMDRD_RRES 0x40
#define ISACX_CMDRD_RMC 0x80
/* ISACX/IPACX _RSTAD (R) */
#define ISACX_RSTAD_TA 0x01
#define ISACX_RSTAD_CR 0x02
#define ISACX_RSTAD_SA0 0x04
#define ISACX_RSTAD_SA1 0x08
#define ISACX_RSTAD_RAB 0x10
#define ISACX_RSTAD_CRC 0x20
#define ISACX_RSTAD_RDO 0x40
#define ISACX_RSTAD_VFR 0x80
/* ISACX/IPACX _CIR0 (R) */
#define ISACX_CIR0_BAS 0x01
#define ISACX_CIR0_SG 0x08
#define ISACX_CIR0_CIC1 0x08
#define ISACX_CIR0_CIC0 0x08
/* B-channel registers */
#define IPACX_OFF_ICA 0x70
#define IPACX_OFF_ICB 0x80
/* ICA: IPACX_OFF_ICA + Reg ICB: IPACX_OFF_ICB + Reg */
#define IPACX_ISTAB 0x00 /* RD */
#define IPACX_MASKB 0x00 /* WR */
#define IPACX_STARB 0x01 /* RD */
#define IPACX_CMDRB 0x01 /* WR */
#define IPACX_MODEB 0x02 /* R/W */
#define IPACX_EXMB 0x03 /* R/W */
#define IPACX_RAH1 0x05 /* WR */
#define IPACX_RAH2 0x06 /* WR */
#define IPACX_RBCLB 0x06 /* RD */
#define IPACX_RBCHB 0x07 /* RD */
#define IPACX_RAL1 0x07 /* WR */
#define IPACX_RAL2 0x08 /* WR */
#define IPACX_RSTAB 0x08 /* RD */
#define IPACX_TMB 0x09 /* R/W */
#define IPACX_RFIFOB 0x0A /* RD */
#define IPACX_XFIFOB 0x0A /* WR */
/* IPACX_ISTAB / IPACX_MASKB bits */
#define IPACX_B_XDU 0x04
#define IPACX_B_XPR 0x10
#define IPACX_B_RFO 0x20
#define IPACX_B_RPF 0x40
#define IPACX_B_RME 0x80
#define IPACX_B_ON 0x0B
extern int mISDNisac_init(struct isac_hw *, void *);
extern irqreturn_t mISDNisac_irq(struct isac_hw *, u8);
extern u32 mISDNipac_init(struct ipac_hw *, void *);
extern irqreturn_t mISDNipac_irq(struct ipac_hw *, int);

View File

@@ -1,256 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* isar.h ISAR (Siemens PSB 7110) specific defines
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* Copyright 2009 by Karsten Keil <keil@isdn4linux.de>
*/
#include "iohelper.h"
struct isar_hw;
struct isar_ch {
struct bchannel bch;
struct isar_hw *is;
struct timer_list ftimer;
u8 nr;
u8 dpath;
u8 mml;
u8 state;
u8 cmd;
u8 mod;
u8 newcmd;
u8 newmod;
u8 try_mod;
u8 conmsg[16];
};
struct isar_hw {
struct isar_ch ch[2];
void *hw;
spinlock_t *hwlock; /* lock HW access */
char *name;
struct module *owner;
read_reg_func *read_reg;
write_reg_func *write_reg;
fifo_func *read_fifo;
fifo_func *write_fifo;
int (*ctrl)(void *, u32, u_long);
void (*release)(struct isar_hw *);
int (*init)(struct isar_hw *);
int (*open)(struct isar_hw *, struct channel_req *);
int (*firmware)(struct isar_hw *, const u8 *, int);
unsigned long Flags;
int version;
u8 bstat;
u8 iis;
u8 cmsb;
u8 clsb;
u8 buf[256];
u8 log[256];
};
#define ISAR_IRQMSK 0x04
#define ISAR_IRQSTA 0x04
#define ISAR_IRQBIT 0x75
#define ISAR_CTRL_H 0x61
#define ISAR_CTRL_L 0x60
#define ISAR_IIS 0x58
#define ISAR_IIA 0x58
#define ISAR_HIS 0x50
#define ISAR_HIA 0x50
#define ISAR_MBOX 0x4c
#define ISAR_WADR 0x4a
#define ISAR_RADR 0x48
#define ISAR_HIS_VNR 0x14
#define ISAR_HIS_DKEY 0x02
#define ISAR_HIS_FIRM 0x1e
#define ISAR_HIS_STDSP 0x08
#define ISAR_HIS_DIAG 0x05
#define ISAR_HIS_P0CFG 0x3c
#define ISAR_HIS_P12CFG 0x24
#define ISAR_HIS_SARTCFG 0x25
#define ISAR_HIS_PUMPCFG 0x26
#define ISAR_HIS_PUMPCTRL 0x2a
#define ISAR_HIS_IOM2CFG 0x27
#define ISAR_HIS_IOM2REQ 0x07
#define ISAR_HIS_IOM2CTRL 0x2b
#define ISAR_HIS_BSTREQ 0x0c
#define ISAR_HIS_PSTREQ 0x0e
#define ISAR_HIS_SDATA 0x20
#define ISAR_HIS_DPS1 0x40
#define ISAR_HIS_DPS2 0x80
#define SET_DPS(x) ((x << 6) & 0xc0)
#define ISAR_IIS_MSCMSD 0x3f
#define ISAR_IIS_VNR 0x15
#define ISAR_IIS_DKEY 0x03
#define ISAR_IIS_FIRM 0x1f
#define ISAR_IIS_STDSP 0x09
#define ISAR_IIS_DIAG 0x25
#define ISAR_IIS_GSTEV 0x00
#define ISAR_IIS_BSTEV 0x28
#define ISAR_IIS_BSTRSP 0x2c
#define ISAR_IIS_PSTRSP 0x2e
#define ISAR_IIS_PSTEV 0x2a
#define ISAR_IIS_IOM2RSP 0x27
#define ISAR_IIS_RDATA 0x20
#define ISAR_IIS_INVMSG 0x3f
#define ISAR_CTRL_SWVER 0x10
#define ISAR_CTRL_STST 0x40
#define ISAR_MSG_HWVER 0x20
#define ISAR_DP1_USE 1
#define ISAR_DP2_USE 2
#define ISAR_RATE_REQ 3
#define PMOD_DISABLE 0
#define PMOD_FAX 1
#define PMOD_DATAMODEM 2
#define PMOD_HALFDUPLEX 3
#define PMOD_V110 4
#define PMOD_DTMF 5
#define PMOD_DTMF_TRANS 6
#define PMOD_BYPASS 7
#define PCTRL_ORIG 0x80
#define PV32P2_V23R 0x40
#define PV32P2_V22A 0x20
#define PV32P2_V22B 0x10
#define PV32P2_V22C 0x08
#define PV32P2_V21 0x02
#define PV32P2_BEL 0x01
/* LSB MSB in ISAR doc wrong !!! Arghhh */
#define PV32P3_AMOD 0x80
#define PV32P3_V32B 0x02
#define PV32P3_V23B 0x01
#define PV32P4_48 0x11
#define PV32P5_48 0x05
#define PV32P4_UT48 0x11
#define PV32P5_UT48 0x0d
#define PV32P4_96 0x11
#define PV32P5_96 0x03
#define PV32P4_UT96 0x11
#define PV32P5_UT96 0x0f
#define PV32P4_B96 0x91
#define PV32P5_B96 0x0b
#define PV32P4_UTB96 0xd1
#define PV32P5_UTB96 0x0f
#define PV32P4_120 0xb1
#define PV32P5_120 0x09
#define PV32P4_UT120 0xf1
#define PV32P5_UT120 0x0f
#define PV32P4_144 0x99
#define PV32P5_144 0x09
#define PV32P4_UT144 0xf9
#define PV32P5_UT144 0x0f
#define PV32P6_CTN 0x01
#define PV32P6_ATN 0x02
#define PFAXP2_CTN 0x01
#define PFAXP2_ATN 0x04
#define PSEV_10MS_TIMER 0x02
#define PSEV_CON_ON 0x18
#define PSEV_CON_OFF 0x19
#define PSEV_V24_OFF 0x20
#define PSEV_CTS_ON 0x21
#define PSEV_CTS_OFF 0x22
#define PSEV_DCD_ON 0x23
#define PSEV_DCD_OFF 0x24
#define PSEV_DSR_ON 0x25
#define PSEV_DSR_OFF 0x26
#define PSEV_REM_RET 0xcc
#define PSEV_REM_REN 0xcd
#define PSEV_GSTN_CLR 0xd4
#define PSEV_RSP_READY 0xbc
#define PSEV_LINE_TX_H 0xb3
#define PSEV_LINE_TX_B 0xb2
#define PSEV_LINE_RX_H 0xb1
#define PSEV_LINE_RX_B 0xb0
#define PSEV_RSP_CONN 0xb5
#define PSEV_RSP_DISC 0xb7
#define PSEV_RSP_FCERR 0xb9
#define PSEV_RSP_SILDET 0xbe
#define PSEV_RSP_SILOFF 0xab
#define PSEV_FLAGS_DET 0xba
#define PCTRL_CMD_TDTMF 0x5a
#define PCTRL_CMD_FTH 0xa7
#define PCTRL_CMD_FRH 0xa5
#define PCTRL_CMD_FTM 0xa8
#define PCTRL_CMD_FRM 0xa6
#define PCTRL_CMD_SILON 0xac
#define PCTRL_CMD_CONT 0xa2
#define PCTRL_CMD_ESC 0xa4
#define PCTRL_CMD_SILOFF 0xab
#define PCTRL_CMD_HALT 0xa9
#define PCTRL_LOC_RET 0xcf
#define PCTRL_LOC_REN 0xce
#define SMODE_DISABLE 0
#define SMODE_V14 2
#define SMODE_HDLC 3
#define SMODE_BINARY 4
#define SMODE_FSK_V14 5
#define SCTRL_HDMC_BOTH 0x00
#define SCTRL_HDMC_DTX 0x80
#define SCTRL_HDMC_DRX 0x40
#define S_P1_OVSP 0x40
#define S_P1_SNP 0x20
#define S_P1_EOP 0x10
#define S_P1_EDP 0x08
#define S_P1_NSB 0x04
#define S_P1_CHS_8 0x03
#define S_P1_CHS_7 0x02
#define S_P1_CHS_6 0x01
#define S_P1_CHS_5 0x00
#define S_P2_BFT_DEF 0x10
#define IOM_CTRL_ENA 0x80
#define IOM_CTRL_NOPCM 0x00
#define IOM_CTRL_ALAW 0x02
#define IOM_CTRL_ULAW 0x04
#define IOM_CTRL_RCV 0x01
#define IOM_P1_TXD 0x10
#define HDLC_FED 0x40
#define HDLC_FSD 0x20
#define HDLC_FST 0x20
#define HDLC_ERROR 0x1c
#define HDLC_ERR_FAD 0x10
#define HDLC_ERR_RER 0x08
#define HDLC_ERR_CER 0x04
#define SART_NMD 0x01
#define BSTAT_RDM0 0x1
#define BSTAT_RDM1 0x2
#define BSTAT_RDM2 0x4
#define BSTAT_RDM3 0x8
#define BSTEV_TBO 0x1f
#define BSTEV_RBO 0x2f
/* FAX State Machine */
#define STFAX_NULL 0
#define STFAX_READY 1
#define STFAX_LINE 2
#define STFAX_CONT 3
#define STFAX_ACTIV 4
#define STFAX_ESCAPE 5
#define STFAX_SILDET 6
extern u32 mISDNisar_init(struct isar_hw *, void *);
extern void mISDNisar_irq(struct isar_hw *);

View File

@@ -1,617 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* isdnhdlc.c -- General purpose ISDN HDLC decoder.
*
* Copyright (C)
* 2009 Karsten Keil <keil@b1-systems.de>
* 2002 Wolfgang Mües <wolfgang@iksw-muees.de>
* 2001 Frode Isaksen <fisaksen@bewan.com>
* 2001 Kai Germaschewski <kai.germaschewski@gmx.de>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/crc-ccitt.h>
#include <linux/bitrev.h>
#include "isdnhdlc.h"
/*-------------------------------------------------------------------*/
MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
"Frode Isaksen <fisaksen@bewan.com>, "
"Kai Germaschewski <kai.germaschewski@gmx.de>");
MODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
MODULE_LICENSE("GPL");
/*-------------------------------------------------------------------*/
enum {
HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7,
HDLC_GET_DATA, HDLC_FAST_FLAG
};
enum {
HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG,
HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG,
HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0,
HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE
};
void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features)
{
memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
hdlc->state = HDLC_GET_DATA;
if (features & HDLC_56KBIT)
hdlc->do_adapt56 = 1;
if (features & HDLC_BITREVERSE)
hdlc->do_bitreverse = 1;
}
EXPORT_SYMBOL(isdnhdlc_out_init);
void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features)
{
memset(hdlc, 0, sizeof(struct isdnhdlc_vars));
if (features & HDLC_DCHANNEL) {
hdlc->dchannel = 1;
hdlc->state = HDLC_SEND_FIRST_FLAG;
} else {
hdlc->dchannel = 0;
hdlc->state = HDLC_SEND_FAST_FLAG;
hdlc->ffvalue = 0x7e;
}
hdlc->cbin = 0x7e;
if (features & HDLC_56KBIT) {
hdlc->do_adapt56 = 1;
hdlc->state = HDLC_SENDFLAG_B0;
} else
hdlc->data_bits = 8;
if (features & HDLC_BITREVERSE)
hdlc->do_bitreverse = 1;
}
EXPORT_SYMBOL(isdnhdlc_rcv_init);
static int
check_frame(struct isdnhdlc_vars *hdlc)
{
int status;
if (hdlc->dstpos < 2) /* too small - framing error */
status = -HDLC_FRAMING_ERROR;
else if (hdlc->crc != 0xf0b8) /* crc error */
status = -HDLC_CRC_ERROR;
else {
/* remove CRC */
hdlc->dstpos -= 2;
/* good frame */
status = hdlc->dstpos;
}
return status;
}
/*
isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
The source buffer is scanned for valid HDLC frames looking for
flags (01111110) to indicate the start of a frame. If the start of
the frame is found, the bit stuffing is removed (0 after 5 1's).
When a new flag is found, the complete frame has been received
and the CRC is checked.
If a valid frame is found, the function returns the frame length
excluding the CRC with the bit HDLC_END_OF_FRAME set.
If the beginning of a valid frame is found, the function returns
the length.
If a framing error is found (too many 1s and not a flag) the function
returns the length with the bit HDLC_FRAMING_ERROR set.
If a CRC error is found the function returns the length with the
bit HDLC_CRC_ERROR set.
If the frame length exceeds the destination buffer size, the function
returns the length with the bit HDLC_LENGTH_ERROR set.
src - source buffer
slen - source buffer length
count - number of bytes removed (decoded) from the source buffer
dst _ destination buffer
dsize - destination buffer size
returns - number of decoded bytes in the destination buffer and status
flag.
*/
int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen,
int *count, u8 *dst, int dsize)
{
int status = 0;
static const unsigned char fast_flag[] = {
0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f
};
static const unsigned char fast_flag_value[] = {
0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f
};
static const unsigned char fast_abort[] = {
0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
};
#define handle_fast_flag(h) \
do { \
if (h->cbin == fast_flag[h->bit_shift]) { \
h->ffvalue = fast_flag_value[h->bit_shift]; \
h->state = HDLC_FAST_FLAG; \
h->ffbit_shift = h->bit_shift; \
h->bit_shift = 1; \
} else { \
h->state = HDLC_GET_DATA; \
h->data_received = 0; \
} \
} while (0)
#define handle_abort(h) \
do { \
h->shift_reg = fast_abort[h->ffbit_shift - 1]; \
h->hdlc_bits1 = h->ffbit_shift - 2; \
if (h->hdlc_bits1 < 0) \
h->hdlc_bits1 = 0; \
h->data_bits = h->ffbit_shift - 1; \
h->state = HDLC_GET_DATA; \
h->data_received = 0; \
} while (0)
*count = slen;
while (slen > 0) {
if (hdlc->bit_shift == 0) {
/* the code is for bitreverse streams */
if (hdlc->do_bitreverse == 0)
hdlc->cbin = bitrev8(*src++);
else
hdlc->cbin = *src++;
slen--;
hdlc->bit_shift = 8;
if (hdlc->do_adapt56)
hdlc->bit_shift--;
}
switch (hdlc->state) {
case STOPPED:
return 0;
case HDLC_FAST_IDLE:
if (hdlc->cbin == 0xff) {
hdlc->bit_shift = 0;
break;
}
hdlc->state = HDLC_GET_FLAG_B0;
hdlc->hdlc_bits1 = 0;
hdlc->bit_shift = 8;
break;
case HDLC_GET_FLAG_B0:
if (!(hdlc->cbin & 0x80)) {
hdlc->state = HDLC_GETFLAG_B1A6;
hdlc->hdlc_bits1 = 0;
} else {
if ((!hdlc->do_adapt56) &&
(++hdlc->hdlc_bits1 >= 8) &&
(hdlc->bit_shift == 1))
hdlc->state = HDLC_FAST_IDLE;
}
hdlc->cbin <<= 1;
hdlc->bit_shift--;
break;
case HDLC_GETFLAG_B1A6:
if (hdlc->cbin & 0x80) {
hdlc->hdlc_bits1++;
if (hdlc->hdlc_bits1 == 6)
hdlc->state = HDLC_GETFLAG_B7;
} else
hdlc->hdlc_bits1 = 0;
hdlc->cbin <<= 1;
hdlc->bit_shift--;
break;
case HDLC_GETFLAG_B7:
if (hdlc->cbin & 0x80) {
hdlc->state = HDLC_GET_FLAG_B0;
} else {
hdlc->state = HDLC_GET_DATA;
hdlc->crc = 0xffff;
hdlc->shift_reg = 0;
hdlc->hdlc_bits1 = 0;
hdlc->data_bits = 0;
hdlc->data_received = 0;
}
hdlc->cbin <<= 1;
hdlc->bit_shift--;
break;
case HDLC_GET_DATA:
if (hdlc->cbin & 0x80) {
hdlc->hdlc_bits1++;
switch (hdlc->hdlc_bits1) {
case 6:
break;
case 7:
if (hdlc->data_received)
/* bad frame */
status = -HDLC_FRAMING_ERROR;
if (!hdlc->do_adapt56) {
if (hdlc->cbin == fast_abort
[hdlc->bit_shift + 1]) {
hdlc->state =
HDLC_FAST_IDLE;
hdlc->bit_shift = 1;
break;
}
} else
hdlc->state = HDLC_GET_FLAG_B0;
break;
default:
hdlc->shift_reg >>= 1;
hdlc->shift_reg |= 0x80;
hdlc->data_bits++;
break;
}
} else {
switch (hdlc->hdlc_bits1) {
case 5:
break;
case 6:
if (hdlc->data_received)
status = check_frame(hdlc);
hdlc->crc = 0xffff;
hdlc->shift_reg = 0;
hdlc->data_bits = 0;
if (!hdlc->do_adapt56)
handle_fast_flag(hdlc);
else {
hdlc->state = HDLC_GET_DATA;
hdlc->data_received = 0;
}
break;
default:
hdlc->shift_reg >>= 1;
hdlc->data_bits++;
break;
}
hdlc->hdlc_bits1 = 0;
}
if (status) {
hdlc->dstpos = 0;
*count -= slen;
hdlc->cbin <<= 1;
hdlc->bit_shift--;
return status;
}
if (hdlc->data_bits == 8) {
hdlc->data_bits = 0;
hdlc->data_received = 1;
hdlc->crc = crc_ccitt_byte(hdlc->crc,
hdlc->shift_reg);
/* good byte received */
if (hdlc->dstpos < dsize)
dst[hdlc->dstpos++] = hdlc->shift_reg;
else {
/* frame too long */
status = -HDLC_LENGTH_ERROR;
hdlc->dstpos = 0;
}
}
hdlc->cbin <<= 1;
hdlc->bit_shift--;
break;
case HDLC_FAST_FLAG:
if (hdlc->cbin == hdlc->ffvalue) {
hdlc->bit_shift = 0;
break;
} else {
if (hdlc->cbin == 0xff) {
hdlc->state = HDLC_FAST_IDLE;
hdlc->bit_shift = 0;
} else if (hdlc->ffbit_shift == 8) {
hdlc->state = HDLC_GETFLAG_B7;
break;
} else
handle_abort(hdlc);
}
break;
default:
break;
}
}
*count -= slen;
return 0;
}
EXPORT_SYMBOL(isdnhdlc_decode);
/*
isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
The bit stream starts with a beginning flag (01111110). After
that each byte is added to the bit stream with bit stuffing added
(0 after 5 1's).
When the last byte has been removed from the source buffer, the
CRC (2 bytes is added) and the frame terminates with the ending flag.
For the dchannel, the idle character (all 1's) is also added at the end.
If this function is called with empty source buffer (slen=0), flags or
idle character will be generated.
src - source buffer
slen - source buffer length
count - number of bytes removed (encoded) from source buffer
dst _ destination buffer
dsize - destination buffer size
returns - number of encoded bytes in the destination buffer
*/
int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen,
int *count, u8 *dst, int dsize)
{
static const unsigned char xfast_flag_value[] = {
0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e
};
int len = 0;
*count = slen;
/* special handling for one byte frames */
if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG))
hdlc->state = HDLC_SENDFLAG_ONE;
while (dsize > 0) {
if (hdlc->bit_shift == 0) {
if (slen && !hdlc->do_closing) {
hdlc->shift_reg = *src++;
slen--;
if (slen == 0)
/* closing sequence, CRC + flag(s) */
hdlc->do_closing = 1;
hdlc->bit_shift = 8;
} else {
if (hdlc->state == HDLC_SEND_DATA) {
if (hdlc->data_received) {
hdlc->state = HDLC_SEND_CRC1;
hdlc->crc ^= 0xffff;
hdlc->bit_shift = 8;
hdlc->shift_reg =
hdlc->crc & 0xff;
} else if (!hdlc->do_adapt56)
hdlc->state =
HDLC_SEND_FAST_FLAG;
else
hdlc->state =
HDLC_SENDFLAG_B0;
}
}
}
switch (hdlc->state) {
case STOPPED:
while (dsize--)
*dst++ = 0xff;
return dsize;
case HDLC_SEND_FAST_FLAG:
hdlc->do_closing = 0;
if (slen == 0) {
/* the code is for bitreverse streams */
if (hdlc->do_bitreverse == 0)
*dst++ = bitrev8(hdlc->ffvalue);
else
*dst++ = hdlc->ffvalue;
len++;
dsize--;
break;
}
fallthrough;
case HDLC_SENDFLAG_ONE:
if (hdlc->bit_shift == 8) {
hdlc->cbin = hdlc->ffvalue >>
(8 - hdlc->data_bits);
hdlc->state = HDLC_SEND_DATA;
hdlc->crc = 0xffff;
hdlc->hdlc_bits1 = 0;
hdlc->data_received = 1;
}
break;
case HDLC_SENDFLAG_B0:
hdlc->do_closing = 0;
hdlc->cbin <<= 1;
hdlc->data_bits++;
hdlc->hdlc_bits1 = 0;
hdlc->state = HDLC_SENDFLAG_B1A6;
break;
case HDLC_SENDFLAG_B1A6:
hdlc->cbin <<= 1;
hdlc->data_bits++;
hdlc->cbin++;
if (++hdlc->hdlc_bits1 == 6)
hdlc->state = HDLC_SENDFLAG_B7;
break;
case HDLC_SENDFLAG_B7:
hdlc->cbin <<= 1;
hdlc->data_bits++;
if (slen == 0) {
hdlc->state = HDLC_SENDFLAG_B0;
break;
}
if (hdlc->bit_shift == 8) {
hdlc->state = HDLC_SEND_DATA;
hdlc->crc = 0xffff;
hdlc->hdlc_bits1 = 0;
hdlc->data_received = 1;
}
break;
case HDLC_SEND_FIRST_FLAG:
hdlc->data_received = 1;
if (hdlc->data_bits == 8) {
hdlc->state = HDLC_SEND_DATA;
hdlc->crc = 0xffff;
hdlc->hdlc_bits1 = 0;
break;
}
hdlc->cbin <<= 1;
hdlc->data_bits++;
if (hdlc->shift_reg & 0x01)
hdlc->cbin++;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
if (hdlc->bit_shift == 0) {
hdlc->state = HDLC_SEND_DATA;
hdlc->crc = 0xffff;
hdlc->hdlc_bits1 = 0;
}
break;
case HDLC_SEND_DATA:
hdlc->cbin <<= 1;
hdlc->data_bits++;
if (hdlc->hdlc_bits1 == 5) {
hdlc->hdlc_bits1 = 0;
break;
}
if (hdlc->bit_shift == 8)
hdlc->crc = crc_ccitt_byte(hdlc->crc,
hdlc->shift_reg);
if (hdlc->shift_reg & 0x01) {
hdlc->hdlc_bits1++;
hdlc->cbin++;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
} else {
hdlc->hdlc_bits1 = 0;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
}
break;
case HDLC_SEND_CRC1:
hdlc->cbin <<= 1;
hdlc->data_bits++;
if (hdlc->hdlc_bits1 == 5) {
hdlc->hdlc_bits1 = 0;
break;
}
if (hdlc->shift_reg & 0x01) {
hdlc->hdlc_bits1++;
hdlc->cbin++;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
} else {
hdlc->hdlc_bits1 = 0;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
}
if (hdlc->bit_shift == 0) {
hdlc->shift_reg = (hdlc->crc >> 8);
hdlc->state = HDLC_SEND_CRC2;
hdlc->bit_shift = 8;
}
break;
case HDLC_SEND_CRC2:
hdlc->cbin <<= 1;
hdlc->data_bits++;
if (hdlc->hdlc_bits1 == 5) {
hdlc->hdlc_bits1 = 0;
break;
}
if (hdlc->shift_reg & 0x01) {
hdlc->hdlc_bits1++;
hdlc->cbin++;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
} else {
hdlc->hdlc_bits1 = 0;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
}
if (hdlc->bit_shift == 0) {
hdlc->shift_reg = 0x7e;
hdlc->state = HDLC_SEND_CLOSING_FLAG;
hdlc->bit_shift = 8;
}
break;
case HDLC_SEND_CLOSING_FLAG:
hdlc->cbin <<= 1;
hdlc->data_bits++;
if (hdlc->hdlc_bits1 == 5) {
hdlc->hdlc_bits1 = 0;
break;
}
if (hdlc->shift_reg & 0x01)
hdlc->cbin++;
hdlc->shift_reg >>= 1;
hdlc->bit_shift--;
if (hdlc->bit_shift == 0) {
hdlc->ffvalue =
xfast_flag_value[hdlc->data_bits];
if (hdlc->dchannel) {
hdlc->ffvalue = 0x7e;
hdlc->state = HDLC_SEND_IDLE1;
hdlc->bit_shift = 8-hdlc->data_bits;
if (hdlc->bit_shift == 0)
hdlc->state =
HDLC_SEND_FAST_IDLE;
} else {
if (!hdlc->do_adapt56) {
hdlc->state =
HDLC_SEND_FAST_FLAG;
hdlc->data_received = 0;
} else {
hdlc->state = HDLC_SENDFLAG_B0;
hdlc->data_received = 0;
}
/* Finished this frame, send flags */
if (dsize > 1)
dsize = 1;
}
}
break;
case HDLC_SEND_IDLE1:
hdlc->do_closing = 0;
hdlc->cbin <<= 1;
hdlc->cbin++;
hdlc->data_bits++;
hdlc->bit_shift--;
if (hdlc->bit_shift == 0) {
hdlc->state = HDLC_SEND_FAST_IDLE;
hdlc->bit_shift = 0;
}
break;
case HDLC_SEND_FAST_IDLE:
hdlc->do_closing = 0;
hdlc->cbin = 0xff;
hdlc->data_bits = 8;
if (hdlc->bit_shift == 8) {
hdlc->cbin = 0x7e;
hdlc->state = HDLC_SEND_FIRST_FLAG;
} else {
/* the code is for bitreverse streams */
if (hdlc->do_bitreverse == 0)
*dst++ = bitrev8(hdlc->cbin);
else
*dst++ = hdlc->cbin;
hdlc->bit_shift = 0;
hdlc->data_bits = 0;
len++;
dsize = 0;
}
break;
default:
break;
}
if (hdlc->do_adapt56) {
if (hdlc->data_bits == 7) {
hdlc->cbin <<= 1;
hdlc->cbin++;
hdlc->data_bits++;
}
}
if (hdlc->data_bits == 8) {
/* the code is for bitreverse streams */
if (hdlc->do_bitreverse == 0)
*dst++ = bitrev8(hdlc->cbin);
else
*dst++ = hdlc->cbin;
hdlc->data_bits = 0;
len++;
dsize--;
}
}
*count -= slen;
return len;
}
EXPORT_SYMBOL(isdnhdlc_encode);

View File

@@ -1,69 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* hdlc.h -- General purpose ISDN HDLC decoder.
*
* Implementation of a HDLC decoder/encoder in software.
* Necessary because some ISDN devices don't have HDLC
* controllers.
*
* Copyright (C)
* 2009 Karsten Keil <keil@b1-systems.de>
* 2002 Wolfgang Mües <wolfgang@iksw-muees.de>
* 2001 Frode Isaksen <fisaksen@bewan.com>
* 2001 Kai Germaschewski <kai.germaschewski@gmx.de>
*/
#ifndef __ISDNHDLC_H__
#define __ISDNHDLC_H__
struct isdnhdlc_vars {
int bit_shift;
int hdlc_bits1;
int data_bits;
int ffbit_shift; /* encoding only */
int state;
int dstpos;
u16 crc;
u8 cbin;
u8 shift_reg;
u8 ffvalue;
/* set if transferring data */
u32 data_received:1;
/* set if D channel (send idle instead of flags) */
u32 dchannel:1;
/* set if 56K adaptation */
u32 do_adapt56:1;
/* set if in closing phase (need to send CRC + flag) */
u32 do_closing:1;
/* set if data is bitreverse */
u32 do_bitreverse:1;
};
/* Feature Flags */
#define HDLC_56KBIT 0x01
#define HDLC_DCHANNEL 0x02
#define HDLC_BITREVERSE 0x04
/*
The return value from isdnhdlc_decode is
the frame length, 0 if no complete frame was decoded,
or a negative error number
*/
#define HDLC_FRAMING_ERROR 1
#define HDLC_CRC_ERROR 2
#define HDLC_LENGTH_ERROR 3
extern void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features);
extern int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src,
int slen, int *count, u8 *dst, int dsize);
extern void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features);
extern int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src,
u16 slen, int *count, u8 *dst, int dsize);
#endif /* __ISDNHDLC_H__ */

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* NETjet common header file
*
* Author Karsten Keil
* based on work of Matt Henderson and Daniel Potts,
* Traverse Technologies P/L www.traverse.com.au
*
* Copyright 2009 by Karsten Keil <keil@isdn4linux.de>
*/
#define NJ_CTRL 0x00
#define NJ_DMACTRL 0x01
#define NJ_AUXCTRL 0x02
#define NJ_AUXDATA 0x03
#define NJ_IRQMASK0 0x04
#define NJ_IRQMASK1 0x05
#define NJ_IRQSTAT0 0x06
#define NJ_IRQSTAT1 0x07
#define NJ_DMA_READ_START 0x08
#define NJ_DMA_READ_IRQ 0x0c
#define NJ_DMA_READ_END 0x10
#define NJ_DMA_READ_ADR 0x14
#define NJ_DMA_WRITE_START 0x18
#define NJ_DMA_WRITE_IRQ 0x1c
#define NJ_DMA_WRITE_END 0x20
#define NJ_DMA_WRITE_ADR 0x24
#define NJ_PULSE_CNT 0x28
#define NJ_ISAC_OFF 0xc0
#define NJ_ISACIRQ 0x10
#define NJ_IRQM0_RD_MASK 0x03
#define NJ_IRQM0_RD_IRQ 0x01
#define NJ_IRQM0_RD_END 0x02
#define NJ_IRQM0_WR_MASK 0x0c
#define NJ_IRQM0_WR_IRQ 0x04
#define NJ_IRQM0_WR_END 0x08
/* one page here is no need to be smaller */
#define NJ_DMA_SIZE 4096
/* 2 * 64 byte is a compromise between IRQ count and latency */
#define NJ_DMA_RXSIZE 128 /* 2 * 64 */
#define NJ_DMA_TXSIZE 128 /* 2 * 64 */

View File

@@ -1,520 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* speedfax.c low level stuff for Sedlbauer Speedfax+ cards
* based on the ISAR DSP
* Thanks to Sedlbauer AG for informations and HW
*
* Author Karsten Keil <keil@isdn4linux.de>
*
* Copyright 2009 by Karsten Keil <keil@isdn4linux.de>
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/mISDNhw.h>
#include <linux/firmware.h>
#include "ipac.h"
#include "isar.h"
#define SPEEDFAX_REV "2.0"
#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51
#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54
#define PCI_SUB_ID_SEDLBAUER 0x01
#define SFAX_PCI_ADDR 0xc8
#define SFAX_PCI_ISAC 0xd0
#define SFAX_PCI_ISAR 0xe0
/* TIGER 100 Registers */
#define TIGER_RESET_ADDR 0x00
#define TIGER_EXTERN_RESET_ON 0x01
#define TIGER_EXTERN_RESET_OFF 0x00
#define TIGER_AUX_CTRL 0x02
#define TIGER_AUX_DATA 0x03
#define TIGER_AUX_IRQMASK 0x05
#define TIGER_AUX_STATUS 0x07
/* Tiger AUX BITs */
#define SFAX_AUX_IOMASK 0xdd /* 1 and 5 are inputs */
#define SFAX_ISAR_RESET_BIT_OFF 0x00
#define SFAX_ISAR_RESET_BIT_ON 0x01
#define SFAX_TIGER_IRQ_BIT 0x02
#define SFAX_LED1_BIT 0x08
#define SFAX_LED2_BIT 0x10
#define SFAX_PCI_RESET_ON (SFAX_ISAR_RESET_BIT_ON)
#define SFAX_PCI_RESET_OFF (SFAX_LED1_BIT | SFAX_LED2_BIT)
static int sfax_cnt;
static u32 debug;
static u32 irqloops = 4;
struct sfax_hw {
struct list_head list;
struct pci_dev *pdev;
char name[MISDN_MAX_IDLEN];
u32 irq;
u32 irqcnt;
u32 cfg;
struct _ioport p_isac;
struct _ioport p_isar;
u8 aux_data;
spinlock_t lock; /* HW access lock */
struct isac_hw isac;
struct isar_hw isar;
};
static LIST_HEAD(Cards);
static DEFINE_RWLOCK(card_lock); /* protect Cards */
static void
_set_debug(struct sfax_hw *card)
{
card->isac.dch.debug = debug;
card->isar.ch[0].bch.debug = debug;
card->isar.ch[1].bch.debug = debug;
}
static int
set_debug(const char *val, const struct kernel_param *kp)
{
int ret;
struct sfax_hw *card;
ret = param_set_uint(val, kp);
if (!ret) {
read_lock(&card_lock);
list_for_each_entry(card, &Cards, list)
_set_debug(card);
read_unlock(&card_lock);
}
return ret;
}
MODULE_AUTHOR("Karsten Keil");
MODULE_DESCRIPTION("mISDN driver for Sedlbauer Speedfax+ cards");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(SPEEDFAX_REV);
MODULE_FIRMWARE("isdn/ISAR.BIN");
module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Speedfax debug mask");
module_param(irqloops, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)");
IOFUNC_IND(ISAC, sfax_hw, p_isac)
IOFUNC_IND(ISAR, sfax_hw, p_isar)
static irqreturn_t
speedfax_irq(int intno, void *dev_id)
{
struct sfax_hw *sf = dev_id;
u8 val;
int cnt = irqloops;
spin_lock(&sf->lock);
val = inb(sf->cfg + TIGER_AUX_STATUS);
if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */
spin_unlock(&sf->lock);
return IRQ_NONE; /* shared */
}
sf->irqcnt++;
val = ReadISAR_IND(sf, ISAR_IRQBIT);
Start_ISAR:
if (val & ISAR_IRQSTA)
mISDNisar_irq(&sf->isar);
val = ReadISAC_IND(sf, ISAC_ISTA);
if (val)
mISDNisac_irq(&sf->isac, val);
val = ReadISAR_IND(sf, ISAR_IRQBIT);
if ((val & ISAR_IRQSTA) && cnt--)
goto Start_ISAR;
if (cnt < irqloops)
pr_debug("%s: %d irqloops cpu%d\n", sf->name,
irqloops - cnt, smp_processor_id());
if (irqloops && !cnt)
pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name,
irqloops, smp_processor_id());
spin_unlock(&sf->lock);
return IRQ_HANDLED;
}
static void
enable_hwirq(struct sfax_hw *sf)
{
WriteISAC_IND(sf, ISAC_MASK, 0);
WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK);
outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK);
}
static void
disable_hwirq(struct sfax_hw *sf)
{
WriteISAC_IND(sf, ISAC_MASK, 0xFF);
WriteISAR_IND(sf, ISAR_IRQBIT, 0);
outb(0, sf->cfg + TIGER_AUX_IRQMASK);
}
static void
reset_speedfax(struct sfax_hw *sf)
{
pr_debug("%s: resetting card\n", sf->name);
outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR);
outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA);
mdelay(1);
outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR);
sf->aux_data = SFAX_PCI_RESET_OFF;
outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
mdelay(1);
}
static int
sfax_ctrl(struct sfax_hw *sf, u32 cmd, u_long arg)
{
int ret = 0;
switch (cmd) {
case HW_RESET_REQ:
reset_speedfax(sf);
break;
case HW_ACTIVATE_IND:
if (arg & 1)
sf->aux_data &= ~SFAX_LED1_BIT;
if (arg & 2)
sf->aux_data &= ~SFAX_LED2_BIT;
outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
break;
case HW_DEACT_IND:
if (arg & 1)
sf->aux_data |= SFAX_LED1_BIT;
if (arg & 2)
sf->aux_data |= SFAX_LED2_BIT;
outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
break;
default:
pr_info("%s: %s unknown command %x %lx\n",
sf->name, __func__, cmd, arg);
ret = -EINVAL;
break;
}
return ret;
}
static int
channel_ctrl(struct sfax_hw *sf, struct mISDN_ctrl_req *cq)
{
int ret = 0;
switch (cq->op) {
case MISDN_CTRL_GETOP:
cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
break;
case MISDN_CTRL_LOOP:
/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
if (cq->channel < 0 || cq->channel > 3) {
ret = -EINVAL;
break;
}
ret = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel);
break;
case MISDN_CTRL_L1_TIMER3:
ret = sf->isac.ctrl(&sf->isac, HW_TIMER3_VALUE, cq->p1);
break;
default:
pr_info("%s: unknown Op %x\n", sf->name, cq->op);
ret = -EINVAL;
break;
}
return ret;
}
static int
sfax_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
{
struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
struct dchannel *dch = container_of(dev, struct dchannel, dev);
struct sfax_hw *sf = dch->hw;
struct channel_req *rq;
int err = 0;
pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg);
switch (cmd) {
case OPEN_CHANNEL:
rq = arg;
if (rq->protocol == ISDN_P_TE_S0)
err = sf->isac.open(&sf->isac, rq);
else
err = sf->isar.open(&sf->isar, rq);
if (err)
break;
if (!try_module_get(THIS_MODULE))
pr_info("%s: cannot get module\n", sf->name);
break;
case CLOSE_CHANNEL:
pr_debug("%s: dev(%d) close from %p\n", sf->name,
dch->dev.id, __builtin_return_address(0));
module_put(THIS_MODULE);
break;
case CONTROL_CHANNEL:
err = channel_ctrl(sf, arg);
break;
default:
pr_debug("%s: unknown command %x\n", sf->name, cmd);
return -EINVAL;
}
return err;
}
static int
init_card(struct sfax_hw *sf)
{
int ret, cnt = 3;
u_long flags;
ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf);
if (ret) {
pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq);
return ret;
}
while (cnt--) {
spin_lock_irqsave(&sf->lock, flags);
ret = sf->isac.init(&sf->isac);
if (ret) {
spin_unlock_irqrestore(&sf->lock, flags);
pr_info("%s: ISAC init failed with %d\n",
sf->name, ret);
break;
}
enable_hwirq(sf);
/* RESET Receiver and Transmitter */
WriteISAC_IND(sf, ISAC_CMDR, 0x41);
spin_unlock_irqrestore(&sf->lock, flags);
msleep_interruptible(10);
if (debug & DEBUG_HW)
pr_notice("%s: IRQ %d count %d\n", sf->name,
sf->irq, sf->irqcnt);
if (!sf->irqcnt) {
pr_info("%s: IRQ(%d) got no requests during init %d\n",
sf->name, sf->irq, 3 - cnt);
} else
return 0;
}
free_irq(sf->irq, sf);
return -EIO;
}
static int
setup_speedfax(struct sfax_hw *sf)
{
u_long flags;
if (!request_region(sf->cfg, 256, sf->name)) {
pr_info("mISDN: %s config port %x-%x already in use\n",
sf->name, sf->cfg, sf->cfg + 255);
return -EIO;
}
outb(0xff, sf->cfg);
outb(0, sf->cfg);
outb(0xdd, sf->cfg + TIGER_AUX_CTRL);
outb(0, sf->cfg + TIGER_AUX_IRQMASK);
sf->isac.type = IPAC_TYPE_ISAC;
sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR;
sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC;
sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR;
sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR;
ASSIGN_FUNC(IND, ISAC, sf->isac);
ASSIGN_FUNC(IND, ISAR, sf->isar);
spin_lock_irqsave(&sf->lock, flags);
reset_speedfax(sf);
disable_hwirq(sf);
spin_unlock_irqrestore(&sf->lock, flags);
return 0;
}
static void
release_card(struct sfax_hw *card) {
u_long flags;
spin_lock_irqsave(&card->lock, flags);
disable_hwirq(card);
spin_unlock_irqrestore(&card->lock, flags);
card->isac.release(&card->isac);
free_irq(card->irq, card);
card->isar.release(&card->isar);
mISDN_unregister_device(&card->isac.dch.dev);
release_region(card->cfg, 256);
pci_disable_device(card->pdev);
pci_set_drvdata(card->pdev, NULL);
write_lock_irqsave(&card_lock, flags);
list_del(&card->list);
write_unlock_irqrestore(&card_lock, flags);
kfree(card);
sfax_cnt--;
}
static int
setup_instance(struct sfax_hw *card)
{
const struct firmware *firmware;
int i, err;
u_long flags;
snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1);
write_lock_irqsave(&card_lock, flags);
list_add_tail(&card->list, &Cards);
write_unlock_irqrestore(&card_lock, flags);
_set_debug(card);
spin_lock_init(&card->lock);
card->isac.hwlock = &card->lock;
card->isar.hwlock = &card->lock;
card->isar.ctrl = (void *)&sfax_ctrl;
card->isac.name = card->name;
card->isar.name = card->name;
card->isar.owner = THIS_MODULE;
err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev);
if (err < 0) {
pr_info("%s: firmware request failed %d\n",
card->name, err);
goto error_fw;
}
if (debug & DEBUG_HW)
pr_notice("%s: got firmware %zu bytes\n",
card->name, firmware->size);
mISDNisac_init(&card->isac, card);
card->isac.dch.dev.D.ctrl = sfax_dctrl;
card->isac.dch.dev.Bprotocols =
mISDNisar_init(&card->isar, card);
for (i = 0; i < 2; i++) {
set_channelmap(i + 1, card->isac.dch.dev.channelmap);
list_add(&card->isar.ch[i].bch.ch.list,
&card->isac.dch.dev.bchannels);
}
err = setup_speedfax(card);
if (err)
goto error_setup;
err = card->isar.init(&card->isar);
if (err)
goto error;
err = mISDN_register_device(&card->isac.dch.dev,
&card->pdev->dev, card->name);
if (err)
goto error;
err = init_card(card);
if (err)
goto error_init;
err = card->isar.firmware(&card->isar, firmware->data, firmware->size);
if (!err) {
release_firmware(firmware);
sfax_cnt++;
pr_notice("SpeedFax %d cards installed\n", sfax_cnt);
return 0;
}
disable_hwirq(card);
free_irq(card->irq, card);
error_init:
mISDN_unregister_device(&card->isac.dch.dev);
error:
release_region(card->cfg, 256);
error_setup:
card->isac.release(&card->isac);
card->isar.release(&card->isar);
release_firmware(firmware);
error_fw:
pci_disable_device(card->pdev);
write_lock_irqsave(&card_lock, flags);
list_del(&card->list);
write_unlock_irqrestore(&card_lock, flags);
kfree(card);
return err;
}
static int
sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int err = -ENOMEM;
struct sfax_hw *card = kzalloc_obj(struct sfax_hw);
if (!card) {
pr_info("No memory for Speedfax+ PCI\n");
return err;
}
card->pdev = pdev;
err = pci_enable_device(pdev);
if (err) {
kfree(card);
return err;
}
pr_notice("mISDN: Speedfax found adapter %s at %s\n",
(char *)ent->driver_data, pci_name(pdev));
card->cfg = pci_resource_start(pdev, 0);
card->irq = pdev->irq;
pci_set_drvdata(pdev, card);
err = setup_instance(card);
if (err)
pci_set_drvdata(pdev, NULL);
return err;
}
static void
sfax_remove_pci(struct pci_dev *pdev)
{
struct sfax_hw *card = pci_get_drvdata(pdev);
if (card)
release_card(card);
else
pr_debug("%s: drvdata already removed\n", __func__);
}
static struct pci_device_id sfaxpci_ids[] = {
{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER,
0, 0, (unsigned long) "Pyramid Speedfax + PCI"
},
{ PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER,
0, 0, (unsigned long) "Sedlbauer Speedfax + PCI"
},
{ }
};
MODULE_DEVICE_TABLE(pci, sfaxpci_ids);
static struct pci_driver sfaxpci_driver = {
.name = "speedfax+ pci",
.probe = sfaxpci_probe,
.remove = sfax_remove_pci,
.id_table = sfaxpci_ids,
};
static int __init
Speedfax_init(void)
{
int err;
pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n",
SPEEDFAX_REV);
err = pci_register_driver(&sfaxpci_driver);
return err;
}
static void __exit
Speedfax_cleanup(void)
{
pci_unregister_driver(&sfaxpci_driver);
}
module_init(Speedfax_init);
module_exit(Speedfax_cleanup);

File diff suppressed because it is too large Load Diff

View File

@@ -1,177 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Winbond W6692 specific defines
*
* Author Karsten Keil <keil@isdn4linux.de>
* based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
*
* Copyright 2009 by Karsten Keil <keil@isdn4linux.de>
*/
/* Specifications of W6692 registers */
#define W_D_RFIFO 0x00 /* R */
#define W_D_XFIFO 0x04 /* W */
#define W_D_CMDR 0x08 /* W */
#define W_D_MODE 0x0c /* R/W */
#define W_D_TIMR 0x10 /* R/W */
#define W_ISTA 0x14 /* R_clr */
#define W_IMASK 0x18 /* R/W */
#define W_D_EXIR 0x1c /* R_clr */
#define W_D_EXIM 0x20 /* R/W */
#define W_D_STAR 0x24 /* R */
#define W_D_RSTA 0x28 /* R */
#define W_D_SAM 0x2c /* R/W */
#define W_D_SAP1 0x30 /* R/W */
#define W_D_SAP2 0x34 /* R/W */
#define W_D_TAM 0x38 /* R/W */
#define W_D_TEI1 0x3c /* R/W */
#define W_D_TEI2 0x40 /* R/W */
#define W_D_RBCH 0x44 /* R */
#define W_D_RBCL 0x48 /* R */
#define W_TIMR2 0x4c /* W */
#define W_L1_RC 0x50 /* R/W */
#define W_D_CTL 0x54 /* R/W */
#define W_CIR 0x58 /* R */
#define W_CIX 0x5c /* W */
#define W_SQR 0x60 /* R */
#define W_SQX 0x64 /* W */
#define W_PCTL 0x68 /* R/W */
#define W_MOR 0x6c /* R */
#define W_MOX 0x70 /* R/W */
#define W_MOSR 0x74 /* R_clr */
#define W_MOCR 0x78 /* R/W */
#define W_GCR 0x7c /* R/W */
#define W_B_RFIFO 0x80 /* R */
#define W_B_XFIFO 0x84 /* W */
#define W_B_CMDR 0x88 /* W */
#define W_B_MODE 0x8c /* R/W */
#define W_B_EXIR 0x90 /* R_clr */
#define W_B_EXIM 0x94 /* R/W */
#define W_B_STAR 0x98 /* R */
#define W_B_ADM1 0x9c /* R/W */
#define W_B_ADM2 0xa0 /* R/W */
#define W_B_ADR1 0xa4 /* R/W */
#define W_B_ADR2 0xa8 /* R/W */
#define W_B_RBCL 0xac /* R */
#define W_B_RBCH 0xb0 /* R */
#define W_XADDR 0xf4 /* R/W */
#define W_XDATA 0xf8 /* R/W */
#define W_EPCTL 0xfc /* W */
/* W6692 register bits */
#define W_D_CMDR_XRST 0x01
#define W_D_CMDR_XME 0x02
#define W_D_CMDR_XMS 0x08
#define W_D_CMDR_STT 0x10
#define W_D_CMDR_RRST 0x40
#define W_D_CMDR_RACK 0x80
#define W_D_MODE_RLP 0x01
#define W_D_MODE_DLP 0x02
#define W_D_MODE_MFD 0x04
#define W_D_MODE_TEE 0x08
#define W_D_MODE_TMS 0x10
#define W_D_MODE_RACT 0x40
#define W_D_MODE_MMS 0x80
#define W_INT_B2_EXI 0x01
#define W_INT_B1_EXI 0x02
#define W_INT_D_EXI 0x04
#define W_INT_XINT0 0x08
#define W_INT_XINT1 0x10
#define W_INT_D_XFR 0x20
#define W_INT_D_RME 0x40
#define W_INT_D_RMR 0x80
#define W_D_EXI_WEXP 0x01
#define W_D_EXI_TEXP 0x02
#define W_D_EXI_ISC 0x04
#define W_D_EXI_MOC 0x08
#define W_D_EXI_TIN2 0x10
#define W_D_EXI_XCOL 0x20
#define W_D_EXI_XDUN 0x40
#define W_D_EXI_RDOV 0x80
#define W_D_STAR_DRDY 0x10
#define W_D_STAR_XBZ 0x20
#define W_D_STAR_XDOW 0x80
#define W_D_RSTA_RMB 0x10
#define W_D_RSTA_CRCE 0x20
#define W_D_RSTA_RDOV 0x40
#define W_D_CTL_SRST 0x20
#define W_CIR_SCC 0x80
#define W_CIR_ICC 0x40
#define W_CIR_COD_MASK 0x0f
#define W_PCTL_PCX 0x01
#define W_PCTL_XMODE 0x02
#define W_PCTL_OE0 0x04
#define W_PCTL_OE1 0x08
#define W_PCTL_OE2 0x10
#define W_PCTL_OE3 0x20
#define W_PCTL_OE4 0x40
#define W_PCTL_OE5 0x80
#define W_B_CMDR_XRST 0x01
#define W_B_CMDR_XME 0x02
#define W_B_CMDR_XMS 0x04
#define W_B_CMDR_RACT 0x20
#define W_B_CMDR_RRST 0x40
#define W_B_CMDR_RACK 0x80
#define W_B_MODE_FTS0 0x01
#define W_B_MODE_FTS1 0x02
#define W_B_MODE_SW56 0x04
#define W_B_MODE_BSW0 0x08
#define W_B_MODE_BSW1 0x10
#define W_B_MODE_EPCM 0x20
#define W_B_MODE_ITF 0x40
#define W_B_MODE_MMS 0x80
#define W_B_EXI_XDUN 0x01
#define W_B_EXI_XFR 0x02
#define W_B_EXI_RDOV 0x10
#define W_B_EXI_RME 0x20
#define W_B_EXI_RMR 0x40
#define W_B_STAR_XBZ 0x01
#define W_B_STAR_XDOW 0x04
#define W_B_STAR_RMB 0x10
#define W_B_STAR_CRCE 0x20
#define W_B_STAR_RDOV 0x40
#define W_B_RBCH_LOV 0x20
/* W6692 Layer1 commands */
#define W_L1CMD_ECK 0x00
#define W_L1CMD_RST 0x01
#define W_L1CMD_SCP 0x04
#define W_L1CMD_SSP 0x02
#define W_L1CMD_AR8 0x08
#define W_L1CMD_AR10 0x09
#define W_L1CMD_EAL 0x0a
#define W_L1CMD_DRC 0x0f
/* W6692 Layer1 indications */
#define W_L1IND_CE 0x07
#define W_L1IND_DRD 0x00
#define W_L1IND_LD 0x04
#define W_L1IND_ARD 0x08
#define W_L1IND_TI 0x0a
#define W_L1IND_ATI 0x0b
#define W_L1IND_AI8 0x0c
#define W_L1IND_AI10 0x0d
#define W_L1IND_CD 0x0f
/* FIFO thresholds */
#define W_D_FIFO_THRESH 64
#define W_B_FIFO_THRESH 64

View File

@@ -1,48 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# modularer ISDN driver
#
menuconfig MISDN
tristate "Modular ISDN driver"
help
Enable support for the modular ISDN driver.
if MISDN != n
config MISDN_DSP
tristate "Digital Audio Processing of transparent data"
depends on MISDN
select BITREVERSE
help
Enable support for digital audio processing capability.
This module may be used for special applications that require
cross connecting of bchannels, conferencing, dtmf decoding,
echo cancellation, tone generation, and Blowfish encryption and
decryption. It may use hardware features if available.
E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu
and get more information about this module and its usage.
If unsure, say 'N'.
config MISDN_L1OIP
tristate "ISDN over IP tunnel"
depends on MISDN
help
Enable support for ISDN over IP tunnel.
It features:
- dynamic IP exchange, if one or both peers have dynamic IPs
- BRI (S0) and PRI (S2M) interface
- layer 1 control via network keepalive frames
- direct tunneling of physical interface via IP
NOTE: This protocol is called 'Layer 1 over IP' and is not
compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is
provided in the source code.
source "drivers/isdn/hardware/mISDN/Kconfig"
endif #MISDN

View File

@@ -1,14 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the modular ISDN driver
#
obj-$(CONFIG_MISDN) += mISDN_core.o
obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o
obj-$(CONFIG_MISDN_L1OIP) += l1oip.o
# multi objects
mISDN_core-objs := core.o fsm.o socket.o clock.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o
mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o
l1oip-objs := l1oip_core.o l1oip_codec.o

View File

@@ -1,197 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 by Andreas Eversberg <andreas@eversberg.eu>
*
* Quick API description:
*
* A clock source registers using mISDN_register_clock:
* name = text string to name clock source
* priority = value to priorize clock sources (0 = default)
* ctl = callback function to enable/disable clock source
* priv = private pointer of clock source
* return = pointer to clock source structure;
*
* Note: Callback 'ctl' can be called before mISDN_register_clock returns!
* Also it can be called during mISDN_unregister_clock.
*
* A clock source calls mISDN_clock_update with given samples elapsed, if
* enabled. If function call is delayed, tv must be set with the timestamp
* of the actual event.
*
* A clock source unregisters using mISDN_unregister_clock.
*
* To get current clock, call mISDN_clock_get. The signed short value
* counts the number of samples since. Time since last clock event is added.
*/
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/spinlock.h>
#include <linux/ktime.h>
#include <linux/mISDNif.h>
#include <linux/export.h>
#include "core.h"
static u_int *debug;
static LIST_HEAD(iclock_list);
static DEFINE_RWLOCK(iclock_lock);
static u16 iclock_count; /* counter of last clock */
static ktime_t iclock_timestamp; /* time stamp of last clock */
static int iclock_timestamp_valid; /* already received one timestamp */
static struct mISDNclock *iclock_current;
void
mISDN_init_clock(u_int *dp)
{
debug = dp;
iclock_timestamp = ktime_get();
}
static void
select_iclock(void)
{
struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL;
int pri = -128;
list_for_each_entry(iclock, &iclock_list, list) {
if (iclock->pri > pri) {
pri = iclock->pri;
bestclock = iclock;
}
if (iclock_current == iclock)
lastclock = iclock;
}
if (lastclock && bestclock != lastclock) {
/* last used clock source still exists but changes, disable */
if (*debug & DEBUG_CLOCK)
printk(KERN_DEBUG "Old clock source '%s' disable.\n",
lastclock->name);
lastclock->ctl(lastclock->priv, 0);
}
if (bestclock && bestclock != iclock_current) {
/* new clock source selected, enable */
if (*debug & DEBUG_CLOCK)
printk(KERN_DEBUG "New clock source '%s' enable.\n",
bestclock->name);
bestclock->ctl(bestclock->priv, 1);
}
if (bestclock != iclock_current) {
/* no clock received yet */
iclock_timestamp_valid = 0;
}
iclock_current = bestclock;
}
struct mISDNclock
*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv)
{
u_long flags;
struct mISDNclock *iclock;
if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri);
iclock = kzalloc_obj(struct mISDNclock, GFP_ATOMIC);
if (!iclock) {
printk(KERN_ERR "%s: No memory for clock entry.\n", __func__);
return NULL;
}
strscpy(iclock->name, name, sizeof(iclock->name));
iclock->pri = pri;
iclock->priv = priv;
iclock->ctl = ctl;
write_lock_irqsave(&iclock_lock, flags);
list_add_tail(&iclock->list, &iclock_list);
select_iclock();
write_unlock_irqrestore(&iclock_lock, flags);
return iclock;
}
EXPORT_SYMBOL(mISDN_register_clock);
void
mISDN_unregister_clock(struct mISDNclock *iclock)
{
u_long flags;
if (*debug & (DEBUG_CORE | DEBUG_CLOCK))
printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name,
iclock->pri);
write_lock_irqsave(&iclock_lock, flags);
if (iclock_current == iclock) {
if (*debug & DEBUG_CLOCK)
printk(KERN_DEBUG
"Current clock source '%s' unregisters.\n",
iclock->name);
iclock->ctl(iclock->priv, 0);
}
list_del(&iclock->list);
select_iclock();
write_unlock_irqrestore(&iclock_lock, flags);
}
EXPORT_SYMBOL(mISDN_unregister_clock);
void
mISDN_clock_update(struct mISDNclock *iclock, int samples, ktime_t *timestamp)
{
u_long flags;
ktime_t timestamp_now;
u16 delta;
write_lock_irqsave(&iclock_lock, flags);
if (iclock_current != iclock) {
printk(KERN_ERR "%s: '%s' sends us clock updates, but we do "
"listen to '%s'. This is a bug!\n", __func__,
iclock->name,
iclock_current ? iclock_current->name : "nothing");
iclock->ctl(iclock->priv, 0);
write_unlock_irqrestore(&iclock_lock, flags);
return;
}
if (iclock_timestamp_valid) {
/* increment sample counter by given samples */
iclock_count += samples;
if (timestamp) { /* timestamp must be set, if function call is delayed */
iclock_timestamp = *timestamp;
} else {
iclock_timestamp = ktime_get();
}
} else {
/* calc elapsed time by system clock */
if (timestamp) { /* timestamp must be set, if function call is delayed */
timestamp_now = *timestamp;
} else {
timestamp_now = ktime_get();
}
delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
(NSEC_PER_SEC / 8000));
/* add elapsed time to counter and set new timestamp */
iclock_count += delta;
iclock_timestamp = timestamp_now;
iclock_timestamp_valid = 1;
if (*debug & DEBUG_CLOCK)
printk("Received first clock from source '%s'.\n",
iclock_current ? iclock_current->name : "nothing");
}
write_unlock_irqrestore(&iclock_lock, flags);
}
EXPORT_SYMBOL(mISDN_clock_update);
unsigned short
mISDN_clock_get(void)
{
u_long flags;
ktime_t timestamp_now;
u16 delta;
u16 count;
read_lock_irqsave(&iclock_lock, flags);
/* calc elapsed time by system clock */
timestamp_now = ktime_get();
delta = ktime_divns(ktime_sub(timestamp_now, iclock_timestamp),
(NSEC_PER_SEC / 8000));
/* add elapsed time to counter */
count = iclock_count + delta;
read_unlock_irqrestore(&iclock_lock, flags);
return count;
}
EXPORT_SYMBOL(mISDN_clock_get);

View File

@@ -1,400 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/mISDNif.h>
#include "core.h"
static u_int debug;
MODULE_AUTHOR("Karsten Keil");
MODULE_DESCRIPTION("Modular ISDN core driver");
MODULE_LICENSE("GPL");
module_param(debug, uint, S_IRUGO | S_IWUSR);
static u64 device_ids;
#define MAX_DEVICE_ID 63
static LIST_HEAD(Bprotocols);
static DEFINE_RWLOCK(bp_lock);
static void mISDN_dev_release(struct device *dev)
{
/* nothing to do: the device is part of its parent's data structure */
}
static ssize_t id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->id);
}
static DEVICE_ATTR_RO(id);
static ssize_t nrbchan_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->nrbchan);
}
static DEVICE_ATTR_RO(nrbchan);
static ssize_t d_protocols_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->Dprotocols);
}
static DEVICE_ATTR_RO(d_protocols);
static ssize_t b_protocols_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols());
}
static DEVICE_ATTR_RO(b_protocols);
static ssize_t protocol_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return -ENODEV;
return sprintf(buf, "%d\n", mdev->D.protocol);
}
static DEVICE_ATTR_RO(protocol);
static ssize_t name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
strcpy(buf, dev_name(dev));
return strlen(buf);
}
static DEVICE_ATTR_RO(name);
#if 0 /* hangs */
static ssize_t name_set(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int err = 0;
char *out = kmalloc(count + 1, GFP_KERNEL);
if (!out)
return -ENOMEM;
memcpy(out, buf, count);
if (count && out[count - 1] == '\n')
out[--count] = 0;
if (count)
err = device_rename(dev, out);
kfree(out);
return (err < 0) ? err : count;
}
static DEVICE_ATTR_RW(name);
#endif
static ssize_t channelmap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
char *bp = buf;
int i;
for (i = 0; i <= mdev->nrbchan; i++)
*bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0';
return bp - buf;
}
static DEVICE_ATTR_RO(channelmap);
static struct attribute *mISDN_attrs[] = {
&dev_attr_id.attr,
&dev_attr_d_protocols.attr,
&dev_attr_b_protocols.attr,
&dev_attr_protocol.attr,
&dev_attr_channelmap.attr,
&dev_attr_nrbchan.attr,
&dev_attr_name.attr,
NULL,
};
ATTRIBUTE_GROUPS(mISDN);
static int mISDN_uevent(const struct device *dev, struct kobj_uevent_env *env)
{
const struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return 0;
if (add_uevent_var(env, "nchans=%d", mdev->nrbchan))
return -ENOMEM;
return 0;
}
static struct class mISDN_class = {
.name = "mISDN",
.dev_uevent = mISDN_uevent,
.dev_groups = mISDN_groups,
.dev_release = mISDN_dev_release,
};
static int
_get_mdevice(struct device *dev, const void *id)
{
struct mISDNdevice *mdev = dev_to_mISDN(dev);
if (!mdev)
return 0;
if (mdev->id != *(const u_int *)id)
return 0;
return 1;
}
struct mISDNdevice
*get_mdevice(u_int id)
{
return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id,
_get_mdevice));
}
static int
_get_mdevice_count(struct device *dev, void *cnt)
{
*(int *)cnt += 1;
return 0;
}
int
get_mdevice_count(void)
{
int cnt = 0;
class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count);
return cnt;
}
static int
get_free_devid(void)
{
u_int i;
for (i = 0; i <= MAX_DEVICE_ID; i++)
if (!test_and_set_bit(i, (u_long *)&device_ids))
break;
if (i > MAX_DEVICE_ID)
return -EBUSY;
return i;
}
int
mISDN_register_device(struct mISDNdevice *dev,
struct device *parent, char *name)
{
int err;
err = get_free_devid();
if (err < 0)
return err;
dev->id = err;
device_initialize(&dev->dev);
if (name && name[0])
dev_set_name(&dev->dev, "%s", name);
else
dev_set_name(&dev->dev, "mISDN%d", dev->id);
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "mISDN_register %s %d\n",
dev_name(&dev->dev), dev->id);
dev->dev.class = &mISDN_class;
err = create_stack(dev);
if (err)
goto error1;
dev->dev.platform_data = dev;
dev->dev.parent = parent;
dev_set_drvdata(&dev->dev, dev);
err = device_add(&dev->dev);
if (err)
goto error3;
return 0;
error3:
delete_stack(dev);
error1:
put_device(&dev->dev);
return err;
}
EXPORT_SYMBOL(mISDN_register_device);
void
mISDN_unregister_device(struct mISDNdevice *dev) {
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "mISDN_unregister %s %d\n",
dev_name(&dev->dev), dev->id);
/* sysfs_remove_link(&dev->dev.kobj, "device"); */
device_del(&dev->dev);
dev_set_drvdata(&dev->dev, NULL);
test_and_clear_bit(dev->id, (u_long *)&device_ids);
delete_stack(dev);
put_device(&dev->dev);
}
EXPORT_SYMBOL(mISDN_unregister_device);
u_int
get_all_Bprotocols(void)
{
struct Bprotocol *bp;
u_int m = 0;
read_lock(&bp_lock);
list_for_each_entry(bp, &Bprotocols, list)
m |= bp->Bprotocols;
read_unlock(&bp_lock);
return m;
}
struct Bprotocol *
get_Bprotocol4mask(u_int m)
{
struct Bprotocol *bp;
read_lock(&bp_lock);
list_for_each_entry(bp, &Bprotocols, list)
if (bp->Bprotocols & m) {
read_unlock(&bp_lock);
return bp;
}
read_unlock(&bp_lock);
return NULL;
}
int
mISDN_register_Bprotocol(struct Bprotocol *bp)
{
u_long flags;
struct Bprotocol *old;
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "%s: %s/%x\n", __func__,
bp->name, bp->Bprotocols);
old = get_Bprotocol4mask(bp->Bprotocols);
if (old) {
printk(KERN_WARNING
"register duplicate protocol old %s/%x new %s/%x\n",
old->name, old->Bprotocols, bp->name, bp->Bprotocols);
return -EBUSY;
}
write_lock_irqsave(&bp_lock, flags);
list_add_tail(&bp->list, &Bprotocols);
write_unlock_irqrestore(&bp_lock, flags);
return 0;
}
EXPORT_SYMBOL(mISDN_register_Bprotocol);
void
mISDN_unregister_Bprotocol(struct Bprotocol *bp)
{
u_long flags;
if (debug & DEBUG_CORE)
printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
bp->Bprotocols);
write_lock_irqsave(&bp_lock, flags);
list_del(&bp->list);
write_unlock_irqrestore(&bp_lock, flags);
}
EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
static const char *msg_no_channel = "<no channel>";
static const char *msg_no_stack = "<no stack>";
static const char *msg_no_stackdev = "<no stack device>";
const char *mISDNDevName4ch(struct mISDNchannel *ch)
{
if (!ch)
return msg_no_channel;
if (!ch->st)
return msg_no_stack;
if (!ch->st->dev)
return msg_no_stackdev;
return dev_name(&ch->st->dev->dev);
};
EXPORT_SYMBOL(mISDNDevName4ch);
static int
mISDNInit(void)
{
int err;
printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
mISDN_init_clock(&debug);
mISDN_initstack(&debug);
err = class_register(&mISDN_class);
if (err)
goto error1;
err = mISDN_inittimer(&debug);
if (err)
goto error2;
err = Isdnl1_Init(&debug);
if (err)
goto error3;
err = Isdnl2_Init(&debug);
if (err)
goto error4;
err = misdn_sock_init(&debug);
if (err)
goto error5;
return 0;
error5:
Isdnl2_cleanup();
error4:
Isdnl1_cleanup();
error3:
mISDN_timer_cleanup();
error2:
class_unregister(&mISDN_class);
error1:
return err;
}
static void mISDN_cleanup(void)
{
misdn_sock_cleanup();
Isdnl2_cleanup();
Isdnl1_cleanup();
mISDN_timer_cleanup();
class_unregister(&mISDN_class);
printk(KERN_DEBUG "mISDNcore unloaded\n");
}
module_init(mISDNInit);
module_exit(mISDN_cleanup);

View File

@@ -1,69 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef mISDN_CORE_H
#define mISDN_CORE_H
extern struct mISDNdevice *get_mdevice(u_int);
extern int get_mdevice_count(void);
/* stack status flag */
#define mISDN_STACK_ACTION_MASK 0x0000ffff
#define mISDN_STACK_COMMAND_MASK 0x000f0000
#define mISDN_STACK_STATUS_MASK 0xfff00000
/* action bits 0-15 */
#define mISDN_STACK_WORK 0
#define mISDN_STACK_SETUP 1
#define mISDN_STACK_CLEARING 2
#define mISDN_STACK_RESTART 3
#define mISDN_STACK_WAKEUP 4
#define mISDN_STACK_ABORT 15
/* command bits 16-19 */
#define mISDN_STACK_STOPPED 16
#define mISDN_STACK_INIT 17
#define mISDN_STACK_THREADSTART 18
/* status bits 20-31 */
#define mISDN_STACK_BCHANNEL 20
#define mISDN_STACK_ACTIVE 29
#define mISDN_STACK_RUNNING 30
#define mISDN_STACK_KILLED 31
/* manager options */
#define MGR_OPT_USER 24
#define MGR_OPT_NETWORK 25
extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *,
u_int, struct sockaddr_mISDN *);
extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *,
u_int, struct sockaddr_mISDN *);
extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *,
u_int, struct sockaddr_mISDN *);
extern int create_stack(struct mISDNdevice *);
extern int create_teimanager(struct mISDNdevice *);
extern void delete_teimanager(struct mISDNchannel *);
extern void delete_channel(struct mISDNchannel *);
extern void delete_stack(struct mISDNdevice *);
extern void mISDN_initstack(u_int *);
extern int misdn_sock_init(u_int *);
extern void misdn_sock_cleanup(void);
extern void add_layer2(struct mISDNchannel *, struct mISDNstack *);
extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *);
extern u_int get_all_Bprotocols(void);
struct Bprotocol *get_Bprotocol4mask(u_int);
extern int mISDN_inittimer(u_int *);
extern void mISDN_timer_cleanup(void);
extern int Isdnl1_Init(u_int *);
extern void Isdnl1_cleanup(void);
extern int Isdnl2_Init(u_int *);
extern void Isdnl2_cleanup(void);
extern void mISDN_init_clock(u_int *);
#endif

View File

@@ -1,277 +0,0 @@
/*
* Audio support data for ISDN4Linux.
*
* Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#define DEBUG_DSP_CTRL 0x0001
#define DEBUG_DSP_CORE 0x0002
#define DEBUG_DSP_DTMF 0x0004
#define DEBUG_DSP_CMX 0x0010
#define DEBUG_DSP_TONE 0x0020
#define DEBUG_DSP_BLOWFISH 0x0040
#define DEBUG_DSP_DELAY 0x0100
#define DEBUG_DSP_CLOCK 0x0200
#define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */
/* options may be:
*
* bit 0 = use ulaw instead of alaw
* bit 1 = enable hfc hardware acceleration for all channels
*
*/
#define DSP_OPT_ULAW (1 << 0)
#define DSP_OPT_NOHARDWARE (1 << 1)
#include <linux/timer.h>
#include <linux/workqueue.h>
#include "dsp_ecdis.h"
extern int dsp_options;
extern int dsp_debug;
extern int dsp_poll;
extern int dsp_tics;
extern spinlock_t dsp_lock;
extern struct work_struct dsp_workq;
extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */
/***************
* audio stuff *
***************/
extern s32 dsp_audio_alaw_to_s32[256];
extern s32 dsp_audio_ulaw_to_s32[256];
extern s32 *dsp_audio_law_to_s32;
extern u8 dsp_audio_s16_to_law[65536];
extern u8 dsp_audio_alaw_to_ulaw[256];
extern u8 dsp_audio_mix_law[65536];
extern u8 dsp_audio_seven2law[128];
extern u8 dsp_audio_law2seven[256];
extern void dsp_audio_generate_law_tables(void);
extern void dsp_audio_generate_s2law_table(void);
extern void dsp_audio_generate_seven(void);
extern void dsp_audio_generate_mix_table(void);
extern void dsp_audio_generate_ulaw_samples(void);
extern void dsp_audio_generate_volume_changes(void);
extern u8 dsp_silence;
/*************
* cmx stuff *
*************/
#define MAX_POLL 256 /* maximum number of send-chunks */
#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */
#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */
#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */
/* how many seconds will we check the lowest delay until the jitter buffer
is reduced by that delay */
#define MAX_SECONDS_JITTER_CHECK 5
extern struct timer_list dsp_spl_tl;
/* the datatype need to match jiffies datatype */
extern unsigned long dsp_spl_jiffies;
/* the structure of conferences:
*
* each conference has a unique number, given by user space.
* the conferences are linked in a chain.
* each conference has members linked in a chain.
* each dsplayer points to a member, each member points to a dsplayer.
*/
/* all members within a conference (this is linked 1:1 with the dsp) */
struct dsp;
struct dsp_conf_member {
struct list_head list;
struct dsp *dsp;
};
/* the list of all conferences */
struct dsp_conf {
struct list_head list;
u32 id;
/* all cmx stacks with the same ID are
connected */
struct list_head mlist;
int software; /* conf is processed by software */
int hardware; /* conf is processed by hardware */
/* note: if both unset, has only one member */
};
/**************
* DTMF stuff *
**************/
#define DSP_DTMF_NPOINTS 102
#define ECHOCAN_BUFF_SIZE 0x400 /* must be 2**n */
#define ECHOCAN_BUFF_MASK 0x3ff /* -1 */
struct dsp_dtmf {
int enable; /* dtmf is enabled */
int treshold; /* above this is dtmf (square of) */
int software; /* dtmf uses software decoding */
int hardware; /* dtmf uses hardware decoding */
int size; /* number of bytes in buffer */
signed short buffer[DSP_DTMF_NPOINTS];
/* buffers one full dtmf frame */
u8 lastwhat, lastdigit;
int count;
u8 digits[16]; /* dtmf result */
};
/******************
* pipeline stuff *
******************/
struct dsp_pipeline {
rwlock_t lock;
struct list_head list;
int inuse;
};
/***************
* tones stuff *
***************/
struct dsp_tone {
int software; /* tones are generated by software */
int hardware; /* tones are generated by hardware */
int tone;
void *pattern;
int count;
int index;
struct timer_list tl;
};
/***************
* echo stuff *
***************/
struct dsp_echo {
int software; /* echo is generated by software */
int hardware; /* echo is generated by hardware */
};
/*****************
* general stuff *
*****************/
struct dsp {
struct list_head list;
struct mISDNchannel ch;
struct mISDNchannel *up;
unsigned char name[64];
int b_active;
struct dsp_echo echo;
int rx_disabled; /* what the user wants */
int rx_is_off; /* what the card is */
int tx_mix;
struct dsp_tone tone;
struct dsp_dtmf dtmf;
int tx_volume, rx_volume;
/* queue for sending frames */
struct work_struct workq;
struct sk_buff_head sendq;
int hdlc; /* if mode is hdlc */
int data_pending; /* currently an unconfirmed frame */
/* conference stuff */
u32 conf_id;
struct dsp_conf *conf;
struct dsp_conf_member
*member;
/* buffer stuff */
int rx_W; /* current write pos for data without timestamp */
int rx_R; /* current read pos for transmit clock */
int rx_init; /* if set, pointers will be adjusted first */
int tx_W; /* current write pos for transmit data */
int tx_R; /* current read pos for transmit clock */
int rx_delay[MAX_SECONDS_JITTER_CHECK];
int tx_delay[MAX_SECONDS_JITTER_CHECK];
u8 tx_buff[CMX_BUFF_SIZE];
u8 rx_buff[CMX_BUFF_SIZE];
int last_tx; /* if set, we transmitted last poll interval */
int cmx_delay; /* initial delay of buffers,
or 0 for dynamic jitter buffer */
int tx_dejitter; /* if set, dejitter tx buffer */
int tx_data; /* enables tx-data of CMX to upper layer */
/* hardware stuff */
struct dsp_features features;
int features_rx_off; /* set if rx_off is featured */
int features_fill_empty; /* set if fill_empty is featured */
int pcm_slot_rx; /* current PCM slot (or -1) */
int pcm_bank_rx;
int pcm_slot_tx;
int pcm_bank_tx;
int hfc_conf; /* unique id of current conference (or -1) */
/* encryption stuff */
int bf_enable;
u32 bf_p[18];
u32 bf_s[1024];
int bf_crypt_pos;
u8 bf_data_in[9];
u8 bf_crypt_out[9];
int bf_decrypt_in_pos;
int bf_decrypt_out_pos;
u8 bf_crypt_inring[16];
u8 bf_data_out[9];
int bf_sync;
struct dsp_pipeline
pipeline;
};
/* functions */
extern void dsp_change_volume(struct sk_buff *skb, int volume);
extern struct list_head dsp_ilist;
extern struct list_head conf_ilist;
extern void dsp_cmx_debug(struct dsp *dsp);
extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp);
extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id);
extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb);
extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb);
extern void dsp_cmx_send(struct timer_list *arg);
extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb);
extern int dsp_cmx_del_conf_member(struct dsp *dsp);
extern int dsp_cmx_del_conf(struct dsp_conf *conf);
extern void dsp_dtmf_goertzel_init(struct dsp *dsp);
extern void dsp_dtmf_hardware(struct dsp *dsp);
extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len,
int fmt);
extern int dsp_tone(struct dsp *dsp, int tone);
extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len);
extern void dsp_tone_timeout(struct timer_list *t);
extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len);
extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len);
extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen);
extern void dsp_bf_cleanup(struct dsp *dsp);
extern int dsp_pipeline_module_init(void);
extern void dsp_pipeline_module_exit(void);
extern int dsp_pipeline_init(struct dsp_pipeline *pipeline);
extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline);
extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg);
extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data,
int len);
extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data,
int len, unsigned int txlen);

View File

@@ -1,421 +0,0 @@
/*
* Audio support data for mISDN_dsp.
*
* Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
* Rewritten by Peter
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/delay.h>
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
#include <linux/export.h>
#include <linux/bitrev.h>
#include "core.h"
#include "dsp.h"
/* ulaw[unsigned char] -> signed 16-bit */
s32 dsp_audio_ulaw_to_s32[256];
/* alaw[unsigned char] -> signed 16-bit */
s32 dsp_audio_alaw_to_s32[256];
s32 *dsp_audio_law_to_s32;
EXPORT_SYMBOL(dsp_audio_law_to_s32);
/* signed 16-bit -> law */
u8 dsp_audio_s16_to_law[65536];
EXPORT_SYMBOL(dsp_audio_s16_to_law);
/* alaw -> ulaw */
u8 dsp_audio_alaw_to_ulaw[256];
/* ulaw -> alaw */
static u8 dsp_audio_ulaw_to_alaw[256];
u8 dsp_silence;
/*****************************************************
* generate table for conversion of s16 to alaw/ulaw *
*****************************************************/
#define AMI_MASK 0x55
static inline unsigned char linear2alaw(short int linear)
{
int mask;
int seg;
int pcm_val;
static int seg_end[8] = {
0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
};
pcm_val = linear;
if (pcm_val >= 0) {
/* Sign (7th) bit = 1 */
mask = AMI_MASK | 0x80;
} else {
/* Sign bit = 0 */
mask = AMI_MASK;
pcm_val = -pcm_val;
}
/* Convert the scaled magnitude to segment number. */
for (seg = 0; seg < 8; seg++) {
if (pcm_val <= seg_end[seg])
break;
}
/* Combine the sign, segment, and quantization bits. */
return ((seg << 4) |
((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
}
static inline short int alaw2linear(unsigned char alaw)
{
int i;
int seg;
alaw ^= AMI_MASK;
i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
seg = (((int) alaw & 0x70) >> 4);
if (seg)
i = (i + 0x100) << (seg - 1);
return (short int) ((alaw & 0x80) ? i : -i);
}
static inline short int ulaw2linear(unsigned char ulaw)
{
short mu, e, f, y;
static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
mu = 255 - ulaw;
e = (mu & 0x70) / 16;
f = mu & 0x0f;
y = f * (1 << (e + 3));
y += etab[e];
if (mu & 0x80)
y = -y;
return y;
}
#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */
static unsigned char linear2ulaw(short sample)
{
static int exp_lut[256] = {
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
int sign, exponent, mantissa;
unsigned char ulawbyte;
/* Get the sample into sign-magnitude. */
sign = (sample >> 8) & 0x80; /* set aside the sign */
if (sign != 0)
sample = -sample; /* get magnitude */
/* Convert from 16 bit linear to ulaw. */
sample = sample + BIAS;
exponent = exp_lut[(sample >> 7) & 0xFF];
mantissa = (sample >> (exponent + 3)) & 0x0F;
ulawbyte = ~(sign | (exponent << 4) | mantissa);
return ulawbyte;
}
void dsp_audio_generate_law_tables(void)
{
int i;
for (i = 0; i < 256; i++)
dsp_audio_alaw_to_s32[i] = alaw2linear(bitrev8((u8)i));
for (i = 0; i < 256; i++)
dsp_audio_ulaw_to_s32[i] = ulaw2linear(bitrev8((u8)i));
for (i = 0; i < 256; i++) {
dsp_audio_alaw_to_ulaw[i] =
linear2ulaw(dsp_audio_alaw_to_s32[i]);
dsp_audio_ulaw_to_alaw[i] =
linear2alaw(dsp_audio_ulaw_to_s32[i]);
}
}
void
dsp_audio_generate_s2law_table(void)
{
int i;
if (dsp_options & DSP_OPT_ULAW) {
/* generating ulaw-table */
for (i = -32768; i < 32768; i++) {
dsp_audio_s16_to_law[i & 0xffff] =
bitrev8(linear2ulaw(i));
}
} else {
/* generating alaw-table */
for (i = -32768; i < 32768; i++) {
dsp_audio_s16_to_law[i & 0xffff] =
bitrev8(linear2alaw(i));
}
}
}
/*
* the seven bit sample is the number of every second alaw-sample ordered by
* aplitude. 0x00 is negative, 0x7f is positive amplitude.
*/
u8 dsp_audio_seven2law[128];
u8 dsp_audio_law2seven[256];
/********************************************************************
* generate table for conversion law from/to 7-bit alaw-like sample *
********************************************************************/
void
dsp_audio_generate_seven(void)
{
int i, j, k;
u8 spl;
u8 sorted_alaw[256];
/* generate alaw table, sorted by the linear value */
for (i = 0; i < 256; i++) {
j = 0;
for (k = 0; k < 256; k++) {
if (dsp_audio_alaw_to_s32[k]
< dsp_audio_alaw_to_s32[i])
j++;
}
sorted_alaw[j] = i;
}
/* generate tabels */
for (i = 0; i < 256; i++) {
/* spl is the source: the law-sample (converted to alaw) */
spl = i;
if (dsp_options & DSP_OPT_ULAW)
spl = dsp_audio_ulaw_to_alaw[i];
/* find the 7-bit-sample */
for (j = 0; j < 256; j++) {
if (sorted_alaw[j] == spl)
break;
}
/* write 7-bit audio value */
dsp_audio_law2seven[i] = j >> 1;
}
for (i = 0; i < 128; i++) {
spl = sorted_alaw[i << 1];
if (dsp_options & DSP_OPT_ULAW)
spl = dsp_audio_alaw_to_ulaw[spl];
dsp_audio_seven2law[i] = spl;
}
}
/* mix 2*law -> law */
u8 dsp_audio_mix_law[65536];
/******************************************************
* generate mix table to mix two law samples into one *
******************************************************/
void
dsp_audio_generate_mix_table(void)
{
int i, j;
s32 sample;
i = 0;
while (i < 256) {
j = 0;
while (j < 256) {
sample = dsp_audio_law_to_s32[i];
sample += dsp_audio_law_to_s32[j];
if (sample > 32767)
sample = 32767;
if (sample < -32768)
sample = -32768;
dsp_audio_mix_law[(i << 8) | j] =
dsp_audio_s16_to_law[sample & 0xffff];
j++;
}
i++;
}
}
/*************************************
* generate different volume changes *
*************************************/
static u8 dsp_audio_reduce8[256];
static u8 dsp_audio_reduce7[256];
static u8 dsp_audio_reduce6[256];
static u8 dsp_audio_reduce5[256];
static u8 dsp_audio_reduce4[256];
static u8 dsp_audio_reduce3[256];
static u8 dsp_audio_reduce2[256];
static u8 dsp_audio_reduce1[256];
static u8 dsp_audio_increase1[256];
static u8 dsp_audio_increase2[256];
static u8 dsp_audio_increase3[256];
static u8 dsp_audio_increase4[256];
static u8 dsp_audio_increase5[256];
static u8 dsp_audio_increase6[256];
static u8 dsp_audio_increase7[256];
static u8 dsp_audio_increase8[256];
static u8 *dsp_audio_volume_change[16] = {
dsp_audio_reduce8,
dsp_audio_reduce7,
dsp_audio_reduce6,
dsp_audio_reduce5,
dsp_audio_reduce4,
dsp_audio_reduce3,
dsp_audio_reduce2,
dsp_audio_reduce1,
dsp_audio_increase1,
dsp_audio_increase2,
dsp_audio_increase3,
dsp_audio_increase4,
dsp_audio_increase5,
dsp_audio_increase6,
dsp_audio_increase7,
dsp_audio_increase8,
};
void
dsp_audio_generate_volume_changes(void)
{
register s32 sample;
int i;
int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 };
int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 };
i = 0;
while (i < 256) {
dsp_audio_reduce8[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff];
dsp_audio_reduce7[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff];
dsp_audio_reduce6[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff];
dsp_audio_reduce5[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff];
dsp_audio_reduce4[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff];
dsp_audio_reduce3[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff];
dsp_audio_reduce2[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff];
dsp_audio_reduce1[i] = dsp_audio_s16_to_law[
(dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[0] / denum[0];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase1[i] = dsp_audio_s16_to_law[sample & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[1] / denum[1];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase2[i] = dsp_audio_s16_to_law[sample & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[2] / denum[2];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase3[i] = dsp_audio_s16_to_law[sample & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[3] / denum[3];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase4[i] = dsp_audio_s16_to_law[sample & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[4] / denum[4];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase5[i] = dsp_audio_s16_to_law[sample & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[5] / denum[5];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase6[i] = dsp_audio_s16_to_law[sample & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[6] / denum[6];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase7[i] = dsp_audio_s16_to_law[sample & 0xffff];
sample = dsp_audio_law_to_s32[i] * num[7] / denum[7];
if (sample < -32768)
sample = -32768;
else if (sample > 32767)
sample = 32767;
dsp_audio_increase8[i] = dsp_audio_s16_to_law[sample & 0xffff];
i++;
}
}
/**************************************
* change the volume of the given skb *
**************************************/
/* this is a helper function for changing volume of skb. the range may be
* -8 to 8, which is a shift to the power of 2. 0 == no volume, 3 == volume*8
*/
void
dsp_change_volume(struct sk_buff *skb, int volume)
{
u8 *volume_change;
int i, ii;
u8 *p;
int shift;
if (volume == 0)
return;
/* get correct conversion table */
if (volume < 0) {
shift = volume + 8;
if (shift < 0)
shift = 0;
} else {
shift = volume + 7;
if (shift > 15)
shift = 15;
}
volume_change = dsp_audio_volume_change[shift];
i = 0;
ii = skb->len;
p = skb->data;
/* change volume */
while (i < ii) {
*p = volume_change[*p];
p++;
i++;
}
}

View File

@@ -1,51 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* SpanDSP - a series of DSP components for telephony
*
* biquad.h - General telephony bi-quad section routines (currently this just
* handles canonic/type 2 form)
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2001 Steve Underwood
*
* All rights reserved.
*/
struct biquad2_state {
int32_t gain;
int32_t a1;
int32_t a2;
int32_t b1;
int32_t b2;
int32_t z1;
int32_t z2;
};
static inline void biquad2_init(struct biquad2_state *bq,
int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2)
{
bq->gain = gain;
bq->a1 = a1;
bq->a2 = a2;
bq->b1 = b1;
bq->b2 = b2;
bq->z1 = 0;
bq->z2 = 0;
}
static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample)
{
int32_t y;
int32_t z0;
z0 = sample * bq->gain + bq->z1 * bq->a1 + bq->z2 * bq->a2;
y = z0 + bq->z1 * bq->b1 + bq->z2 * bq->b2;
bq->z2 = bq->z1;
bq->z1 = z0 >> 15;
y >>= 15;
return y;
}

View File

@@ -1,667 +0,0 @@
/*
* Blowfish encryption/decryption for mISDN_dsp.
*
* Copyright Andreas Eversberg (jolly@eversberg.eu)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
#include "core.h"
#include "dsp.h"
/*
* how to encode a sample stream to 64-bit blocks that will be encryped
*
* first of all, data is collected until a block of 9 samples are received.
* of course, a packet may have much more than 9 sample, but is may have
* not excacly the multiple of 9 samples. if there is a rest, the next
* received data will complete the block.
*
* the block is then converted to 9 uLAW samples without the least sigificant
* bit. the result is a 7-bit encoded sample.
*
* the samples will be reoganised to form 8 bytes of data:
* (5(6) means: encoded sample no. 5, bit 6)
*
* 0(6) 0(5) 0(4) 0(3) 0(2) 0(1) 0(0) 1(6)
* 1(5) 1(4) 1(3) 1(2) 1(1) 1(0) 2(6) 2(5)
* 2(4) 2(3) 2(2) 2(1) 2(0) 3(6) 3(5) 3(4)
* 3(3) 3(2) 3(1) 3(0) 4(6) 4(5) 4(4) 4(3)
* 4(2) 4(1) 4(0) 5(6) 5(5) 5(4) 5(3) 5(2)
* 5(1) 5(0) 6(6) 6(5) 6(4) 6(3) 6(2) 6(1)
* 6(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
* 8(6) 8(5) 8(4) 8(3) 8(2) 8(1) 8(0)
*
* the missing bit 0 of the last byte is filled with some
* random noise, to fill all 8 bytes.
*
* the 8 bytes will be encrypted using blowfish.
*
* the result will be converted into 9 bytes. the bit 7 is used for
* checksumme (CS) for sync (0, 1) and for the last bit:
* (5(6) means: crypted byte 5, bit 6)
*
* 1 0(7) 0(6) 0(5) 0(4) 0(3) 0(2) 0(1)
* 0 0(0) 1(7) 1(6) 1(5) 1(4) 1(3) 1(2)
* 0 1(1) 1(0) 2(7) 2(6) 2(5) 2(4) 2(3)
* 0 2(2) 2(1) 2(0) 3(7) 3(6) 3(5) 3(4)
* 0 3(3) 3(2) 3(1) 3(0) 4(7) 4(6) 4(5)
* CS 4(4) 4(3) 4(2) 4(1) 4(0) 5(7) 5(6)
* CS 5(5) 5(4) 5(3) 5(2) 5(1) 5(0) 6(7)
* CS 6(6) 6(5) 6(4) 6(3) 6(2) 6(1) 6(0)
* 7(0) 7(6) 7(5) 7(4) 7(3) 7(2) 7(1) 7(0)
*
* the checksum is used to detect transmission errors and frame drops.
*
* synchronisation of received block is done by shifting the upper bit of each
* byte (bit 7) to a shift register. if the rigister has the first five bits
* (10000), this is used to find the sync. only if sync has been found, the
* current block of 9 received bytes are decrypted. before that the check
* sum is calculated. if it is incorrect the block is dropped.
* this will avoid loud noise due to corrupt encrypted data.
*
* if the last block is corrupt, the current decoded block is repeated
* until a valid block has been received.
*/
/*
* some blowfish parts are taken from the
* crypto-api for faster implementation
*/
static const u32 bf_pbox[16 + 2] = {
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b,
};
static const u32 bf_sbox[256 * 4] = {
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
};
/*
* Round loop unrolling macros, S is a pointer to a S-Box array
* organized in 4 unsigned longs at a row.
*/
#define GET32_3(x) (((x) & 0xff))
#define GET32_2(x) (((x) >> (8)) & (0xff))
#define GET32_1(x) (((x) >> (16)) & (0xff))
#define GET32_0(x) (((x) >> (24)) & (0xff))
#define bf_F(x) (((S[GET32_0(x)] + S[256 + GET32_1(x)]) ^ \
S[512 + GET32_2(x)]) + S[768 + GET32_3(x)])
#define EROUND(a, b, n) do { b ^= P[n]; a ^= bf_F(b); } while (0)
#define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0)
/*
* encrypt isdn data frame
* every block with 9 samples is encrypted
*/
void
dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len)
{
int i = 0, j = dsp->bf_crypt_pos;
u8 *bf_data_in = dsp->bf_data_in;
u8 *bf_crypt_out = dsp->bf_crypt_out;
u32 *P = dsp->bf_p;
u32 *S = dsp->bf_s;
u32 yl, yr;
u32 cs;
u8 nibble;
while (i < len) {
/* collect a block of 9 samples */
if (j < 9) {
bf_data_in[j] = *data;
*data++ = bf_crypt_out[j++];
i++;
continue;
}
j = 0;
/* transcode 9 samples xlaw to 8 bytes */
yl = dsp_audio_law2seven[bf_data_in[0]];
yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[1]];
yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[2]];
yl = (yl << 7) | dsp_audio_law2seven[bf_data_in[3]];
nibble = dsp_audio_law2seven[bf_data_in[4]];
yr = nibble;
yl = (yl << 4) | (nibble >> 3);
yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[5]];
yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[6]];
yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[7]];
yr = (yr << 7) | dsp_audio_law2seven[bf_data_in[8]];
yr = (yr << 1) | (bf_data_in[0] & 1);
/* fill unused bit with random noise of audio input */
/* encrypt */
EROUND(yr, yl, 0);
EROUND(yl, yr, 1);
EROUND(yr, yl, 2);
EROUND(yl, yr, 3);
EROUND(yr, yl, 4);
EROUND(yl, yr, 5);
EROUND(yr, yl, 6);
EROUND(yl, yr, 7);
EROUND(yr, yl, 8);
EROUND(yl, yr, 9);
EROUND(yr, yl, 10);
EROUND(yl, yr, 11);
EROUND(yr, yl, 12);
EROUND(yl, yr, 13);
EROUND(yr, yl, 14);
EROUND(yl, yr, 15);
yl ^= P[16];
yr ^= P[17];
/* calculate 3-bit checksumme */
cs = yl ^ (yl >> 3) ^ (yl >> 6) ^ (yl >> 9) ^ (yl >> 12) ^ (yl >> 15)
^ (yl >> 18) ^ (yl >> 21) ^ (yl >> 24) ^ (yl >> 27) ^ (yl >> 30)
^ (yr << 2) ^ (yr >> 1) ^ (yr >> 4) ^ (yr >> 7) ^ (yr >> 10)
^ (yr >> 13) ^ (yr >> 16) ^ (yr >> 19) ^ (yr >> 22) ^ (yr >> 25)
^ (yr >> 28) ^ (yr >> 31);
/*
* transcode 8 crypted bytes to 9 data bytes with sync
* and checksum information
*/
bf_crypt_out[0] = (yl >> 25) | 0x80;
bf_crypt_out[1] = (yl >> 18) & 0x7f;
bf_crypt_out[2] = (yl >> 11) & 0x7f;
bf_crypt_out[3] = (yl >> 4) & 0x7f;
bf_crypt_out[4] = ((yl << 3) & 0x78) | ((yr >> 29) & 0x07);
bf_crypt_out[5] = ((yr >> 22) & 0x7f) | ((cs << 5) & 0x80);
bf_crypt_out[6] = ((yr >> 15) & 0x7f) | ((cs << 6) & 0x80);
bf_crypt_out[7] = ((yr >> 8) & 0x7f) | (cs << 7);
bf_crypt_out[8] = yr;
}
/* write current count */
dsp->bf_crypt_pos = j;
}
/*
* decrypt isdn data frame
* every block with 9 bytes is decrypted
*/
void
dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len)
{
int i = 0;
u8 j = dsp->bf_decrypt_in_pos;
u8 k = dsp->bf_decrypt_out_pos;
u8 *bf_crypt_inring = dsp->bf_crypt_inring;
u8 *bf_data_out = dsp->bf_data_out;
u16 sync = dsp->bf_sync;
u32 *P = dsp->bf_p;
u32 *S = dsp->bf_s;
u32 yl, yr;
u8 nibble;
u8 cs, cs0, cs1, cs2;
while (i < len) {
/*
* shift upper bit and rotate data to buffer ring
* send current decrypted data
*/
sync = (sync << 1) | ((*data) >> 7);
bf_crypt_inring[j++ & 15] = *data;
*data++ = bf_data_out[k++];
i++;
if (k == 9)
k = 0; /* repeat if no sync has been found */
/* check if not in sync */
if ((sync & 0x1f0) != 0x100)
continue;
j -= 9;
/* transcode receive data to 64 bit block of encrypted data */
yl = bf_crypt_inring[j++ & 15];
yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
yl = (yl << 7) | bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
yr = nibble;
yl = (yl << 4) | (nibble >> 3);
cs2 = bf_crypt_inring[j++ & 15];
yr = (yr << 7) | (cs2 & 0x7f);
cs1 = bf_crypt_inring[j++ & 15];
yr = (yr << 7) | (cs1 & 0x7f);
cs0 = bf_crypt_inring[j++ & 15];
yr = (yr << 7) | (cs0 & 0x7f);
yr = (yr << 8) | bf_crypt_inring[j++ & 15];
/* calculate 3-bit checksumme */
cs = yl ^ (yl >> 3) ^ (yl >> 6) ^ (yl >> 9) ^ (yl >> 12) ^ (yl >> 15)
^ (yl >> 18) ^ (yl >> 21) ^ (yl >> 24) ^ (yl >> 27) ^ (yl >> 30)
^ (yr << 2) ^ (yr >> 1) ^ (yr >> 4) ^ (yr >> 7) ^ (yr >> 10)
^ (yr >> 13) ^ (yr >> 16) ^ (yr >> 19) ^ (yr >> 22) ^ (yr >> 25)
^ (yr >> 28) ^ (yr >> 31);
/* check if frame is valid */
if ((cs & 0x7) != (((cs2 >> 5) & 4) | ((cs1 >> 6) & 2) | (cs0 >> 7))) {
if (dsp_debug & DEBUG_DSP_BLOWFISH)
printk(KERN_DEBUG
"DSP BLOWFISH: received corrupt frame, "
"checksumme is not correct\n");
continue;
}
/* decrypt */
yr ^= P[17];
yl ^= P[16];
DROUND(yl, yr, 15);
DROUND(yr, yl, 14);
DROUND(yl, yr, 13);
DROUND(yr, yl, 12);
DROUND(yl, yr, 11);
DROUND(yr, yl, 10);
DROUND(yl, yr, 9);
DROUND(yr, yl, 8);
DROUND(yl, yr, 7);
DROUND(yr, yl, 6);
DROUND(yl, yr, 5);
DROUND(yr, yl, 4);
DROUND(yl, yr, 3);
DROUND(yr, yl, 2);
DROUND(yl, yr, 1);
DROUND(yr, yl, 0);
/* transcode 8 crypted bytes to 9 sample bytes */
bf_data_out[0] = dsp_audio_seven2law[(yl >> 25) & 0x7f];
bf_data_out[1] = dsp_audio_seven2law[(yl >> 18) & 0x7f];
bf_data_out[2] = dsp_audio_seven2law[(yl >> 11) & 0x7f];
bf_data_out[3] = dsp_audio_seven2law[(yl >> 4) & 0x7f];
bf_data_out[4] = dsp_audio_seven2law[((yl << 3) & 0x78) |
((yr >> 29) & 0x07)];
bf_data_out[5] = dsp_audio_seven2law[(yr >> 22) & 0x7f];
bf_data_out[6] = dsp_audio_seven2law[(yr >> 15) & 0x7f];
bf_data_out[7] = dsp_audio_seven2law[(yr >> 8) & 0x7f];
bf_data_out[8] = dsp_audio_seven2law[(yr >> 1) & 0x7f];
k = 0; /* start with new decoded frame */
}
/* write current count and sync */
dsp->bf_decrypt_in_pos = j;
dsp->bf_decrypt_out_pos = k;
dsp->bf_sync = sync;
}
/* used to encrypt S and P boxes */
static inline void
encrypt_block(const u32 *P, const u32 *S, u32 *dst, u32 *src)
{
u32 yl = src[0];
u32 yr = src[1];
EROUND(yr, yl, 0);
EROUND(yl, yr, 1);
EROUND(yr, yl, 2);
EROUND(yl, yr, 3);
EROUND(yr, yl, 4);
EROUND(yl, yr, 5);
EROUND(yr, yl, 6);
EROUND(yl, yr, 7);
EROUND(yr, yl, 8);
EROUND(yl, yr, 9);
EROUND(yr, yl, 10);
EROUND(yl, yr, 11);
EROUND(yr, yl, 12);
EROUND(yl, yr, 13);
EROUND(yr, yl, 14);
EROUND(yl, yr, 15);
yl ^= P[16];
yr ^= P[17];
dst[0] = yr;
dst[1] = yl;
}
/*
* initialize the dsp for encryption and decryption using the same key
* Calculates the blowfish S and P boxes for encryption and decryption.
* The margin of keylen must be 4-56 bytes.
* returns 0 if ok.
*/
int
dsp_bf_init(struct dsp *dsp, const u8 *key, uint keylen)
{
short i, j, count;
u32 data[2], temp;
u32 *P = (u32 *)dsp->bf_p;
u32 *S = (u32 *)dsp->bf_s;
if (keylen < 4 || keylen > 56)
return 1;
/* Set dsp states */
i = 0;
while (i < 9) {
dsp->bf_crypt_out[i] = 0xff;
dsp->bf_data_out[i] = dsp_silence;
i++;
}
dsp->bf_crypt_pos = 0;
dsp->bf_decrypt_in_pos = 0;
dsp->bf_decrypt_out_pos = 0;
dsp->bf_sync = 0x1ff;
dsp->bf_enable = 1;
/* Copy the initialization s-boxes */
for (i = 0, count = 0; i < 256; i++)
for (j = 0; j < 4; j++, count++)
S[count] = bf_sbox[count];
/* Set the p-boxes */
for (i = 0; i < 16 + 2; i++)
P[i] = bf_pbox[i];
/* Actual subkey generation */
for (j = 0, i = 0; i < 16 + 2; i++) {
temp = (((u32)key[j] << 24) |
((u32)key[(j + 1) % keylen] << 16) |
((u32)key[(j + 2) % keylen] << 8) |
((u32)key[(j + 3) % keylen]));
P[i] = P[i] ^ temp;
j = (j + 4) % keylen;
}
data[0] = 0x00000000;
data[1] = 0x00000000;
for (i = 0; i < 16 + 2; i += 2) {
encrypt_block(P, S, data, data);
P[i] = data[0];
P[i + 1] = data[1];
}
for (i = 0; i < 4; i++) {
for (j = 0, count = i * 256; j < 256; j += 2, count += 2) {
encrypt_block(P, S, data, data);
S[count] = data[0];
S[count + 1] = data[1];
}
}
return 0;
}
/*
* turn encryption off
*/
void
dsp_bf_cleanup(struct dsp *dsp)
{
dsp->bf_enable = 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,313 +0,0 @@
/*
* DTMF decoder.
*
* Copyright by Andreas Eversberg (jolly@eversberg.eu)
* based on different decoders such as ISDN4Linux
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
#include "core.h"
#include "dsp.h"
#define NCOEFF 8 /* number of frequencies to be analyzed */
/* For DTMF recognition:
* 2 * cos(2 * PI * k / N) precalculated for all k
*/
static u64 cos2pik[NCOEFF] =
{
/* k << 15 (source: hfc-4s/8s documentation (www.colognechip.de)) */
55960, 53912, 51402, 48438, 38146, 32650, 26170, 18630
};
/* digit matrix */
static char dtmf_matrix[4][4] =
{
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
/* dtmf detection using goertzel algorithm
* init function
*/
void dsp_dtmf_goertzel_init(struct dsp *dsp)
{
dsp->dtmf.size = 0;
dsp->dtmf.lastwhat = '\0';
dsp->dtmf.lastdigit = '\0';
dsp->dtmf.count = 0;
}
/* check for hardware or software features
*/
void dsp_dtmf_hardware(struct dsp *dsp)
{
int hardware = 1;
if (!dsp->dtmf.enable)
return;
if (!dsp->features.hfc_dtmf)
hardware = 0;
/* check for volume change */
if (dsp->tx_volume) {
if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
"because tx_volume is changed\n",
__func__, dsp->name);
hardware = 0;
}
if (dsp->rx_volume) {
if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
"because rx_volume is changed\n",
__func__, dsp->name);
hardware = 0;
}
/* check if encryption is enabled */
if (dsp->bf_enable) {
if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
"because encryption is enabled\n",
__func__, dsp->name);
hardware = 0;
}
/* check if pipeline exists */
if (dsp->pipeline.inuse) {
if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
"because pipeline exists.\n",
__func__, dsp->name);
hardware = 0;
}
dsp->dtmf.hardware = hardware;
dsp->dtmf.software = !hardware;
}
/*************************************************************
* calculate the coefficients of the given sample and decode *
*************************************************************/
/* the given sample is decoded. if the sample is not long enough for a
* complete frame, the decoding is finished and continued with the next
* call of this function.
*
* the algorithm is very good for detection with a minimum of errors. i
* tested it allot. it even works with very short tones (40ms). the only
* disadvantage is, that it doesn't work good with different volumes of both
* tones. this will happen, if accoustically coupled dialers are used.
* it sometimes detects tones during speech, which is normal for decoders.
* use sequences to given commands during calls.
*
* dtmf - points to a structure of the current dtmf state
* spl and len - the sample
* fmt - 0 = alaw, 1 = ulaw, 2 = coefficients from HFC DTMF hw-decoder
*/
u8
*dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len, int fmt)
{
u8 what;
int size;
signed short *buf;
s32 sk, sk1, sk2;
int k, n, i;
s32 *hfccoeff;
s32 result[NCOEFF], tresh, treshl;
int lowgroup, highgroup;
s64 cos2pik_;
dsp->dtmf.digits[0] = '\0';
/* Note: The function will loop until the buffer has not enough samples
* left to decode a full frame.
*/
again:
/* convert samples */
size = dsp->dtmf.size;
buf = dsp->dtmf.buffer;
switch (fmt) {
case 0: /* alaw */
case 1: /* ulaw */
while (size < DSP_DTMF_NPOINTS && len) {
buf[size++] = dsp_audio_law_to_s32[*data++];
len--;
}
break;
case 2: /* HFC coefficients */
default:
if (len < 64) {
if (len > 0)
printk(KERN_ERR "%s: coefficients have invalid "
"size. (is=%d < must=%d)\n",
__func__, len, 64);
return dsp->dtmf.digits;
}
hfccoeff = (s32 *)data;
for (k = 0; k < NCOEFF; k++) {
sk2 = (*hfccoeff++) >> 4;
sk = (*hfccoeff++) >> 4;
if (sk > 32767 || sk < -32767 || sk2 > 32767
|| sk2 < -32767)
printk(KERN_WARNING
"DTMF-Detection overflow\n");
/* compute |X(k)|**2 */
result[k] =
(sk * sk) -
(((cos2pik[k] * sk) >> 15) * sk2) +
(sk2 * sk2);
}
data += 64;
len -= 64;
goto coefficients;
break;
}
dsp->dtmf.size = size;
if (size < DSP_DTMF_NPOINTS)
return dsp->dtmf.digits;
dsp->dtmf.size = 0;
/* now we have a full buffer of signed long samples - we do goertzel */
for (k = 0; k < NCOEFF; k++) {
sk = 0;
sk1 = 0;
sk2 = 0;
buf = dsp->dtmf.buffer;
cos2pik_ = cos2pik[k];
for (n = 0; n < DSP_DTMF_NPOINTS; n++) {
sk = ((cos2pik_ * sk1) >> 15) - sk2 + (*buf++);
sk2 = sk1;
sk1 = sk;
}
sk >>= 8;
sk2 >>= 8;
if (sk > 32767 || sk < -32767 || sk2 > 32767 || sk2 < -32767)
printk(KERN_WARNING "DTMF-Detection overflow\n");
/* compute |X(k)|**2 */
result[k] =
(sk * sk) -
(((cos2pik[k] * sk) >> 15) * sk2) +
(sk2 * sk2);
}
/* our (squared) coefficients have been calculated, we need to process
* them.
*/
coefficients:
tresh = 0;
for (i = 0; i < NCOEFF; i++) {
if (result[i] < 0)
result[i] = 0;
if (result[i] > dsp->dtmf.treshold) {
if (result[i] > tresh)
tresh = result[i];
}
}
if (tresh == 0) {
what = 0;
goto storedigit;
}
if (dsp_debug & DEBUG_DSP_DTMFCOEFF) {
s32 tresh_100 = tresh/100;
if (tresh_100 == 0) {
tresh_100 = 1;
printk(KERN_DEBUG
"tresh(%d) too small set tresh/100 to 1\n",
tresh);
}
printk(KERN_DEBUG "a %3d %3d %3d %3d %3d %3d %3d %3d"
" tr:%3d r %3d %3d %3d %3d %3d %3d %3d %3d\n",
result[0] / 10000, result[1] / 10000, result[2] / 10000,
result[3] / 10000, result[4] / 10000, result[5] / 10000,
result[6] / 10000, result[7] / 10000, tresh / 10000,
result[0] / (tresh_100), result[1] / (tresh_100),
result[2] / (tresh_100), result[3] / (tresh_100),
result[4] / (tresh_100), result[5] / (tresh_100),
result[6] / (tresh_100), result[7] / (tresh_100));
}
/* calc digit (lowgroup/highgroup) */
lowgroup = -1;
highgroup = -1;
treshl = tresh >> 3; /* tones which are not on, must be below 9 dB */
tresh = tresh >> 2; /* touchtones must match within 6 dB */
for (i = 0; i < NCOEFF; i++) {
if (result[i] < treshl)
continue; /* ignore */
if (result[i] < tresh) {
lowgroup = -1;
highgroup = -1;
break; /* noise in between */
}
/* good level found. This is allowed only one time per group */
if (i < NCOEFF / 2) {
/* lowgroup */
if (lowgroup >= 0) {
/* Bad. Another tone found. */
lowgroup = -1;
break;
} else
lowgroup = i;
} else {
/* higroup */
if (highgroup >= 0) {
/* Bad. Another tone found. */
highgroup = -1;
break;
} else
highgroup = i - (NCOEFF / 2);
}
}
/* get digit or null */
what = 0;
if (lowgroup >= 0 && highgroup >= 0)
what = dtmf_matrix[lowgroup][highgroup];
storedigit:
if (what && (dsp_debug & DEBUG_DSP_DTMF))
printk(KERN_DEBUG "DTMF what: %c\n", what);
if (dsp->dtmf.lastwhat != what)
dsp->dtmf.count = 0;
/* the tone (or no tone) must remain 3 times without change */
if (dsp->dtmf.count == 2) {
if (dsp->dtmf.lastdigit != what) {
dsp->dtmf.lastdigit = what;
if (what) {
if (dsp_debug & DEBUG_DSP_DTMF)
printk(KERN_DEBUG "DTMF digit: %c\n",
what);
if ((strlen(dsp->dtmf.digits) + 1)
< sizeof(dsp->dtmf.digits)) {
dsp->dtmf.digits[strlen(
dsp->dtmf.digits) + 1] = '\0';
dsp->dtmf.digits[strlen(
dsp->dtmf.digits)] = what;
}
}
}
} else
dsp->dtmf.count++;
dsp->dtmf.lastwhat = what;
goto again;
}

View File

@@ -1,96 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* SpanDSP - a series of DSP components for telephony
*
* ec_disable_detector.h - A detector which should eventually meet the
* G.164/G.165 requirements for detecting the
* 2100Hz echo cancellor disable tone.
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2001 Steve Underwood
*
* All rights reserved.
*/
#include "dsp_biquad.h"
struct ec_disable_detector_state {
struct biquad2_state notch;
int notch_level;
int channel_level;
int tone_present;
int tone_cycle_duration;
int good_cycles;
int hit;
};
#define FALSE 0
#define TRUE (!FALSE)
static inline void
echo_can_disable_detector_init(struct ec_disable_detector_state *det)
{
/* Elliptic notch */
/* This is actually centred at 2095Hz, but gets the balance we want, due
to the asymmetric walls of the notch */
biquad2_init(&det->notch,
(int32_t)(-0.7600000 * 32768.0),
(int32_t)(-0.1183852 * 32768.0),
(int32_t)(-0.5104039 * 32768.0),
(int32_t)(0.1567596 * 32768.0),
(int32_t)(1.0000000 * 32768.0));
det->channel_level = 0;
det->notch_level = 0;
det->tone_present = FALSE;
det->tone_cycle_duration = 0;
det->good_cycles = 0;
det->hit = 0;
}
/*- End of function --------------------------------------------------------*/
static inline int
echo_can_disable_detector_update(struct ec_disable_detector_state *det,
int16_t amp)
{
int16_t notched;
notched = biquad2(&det->notch, amp);
/* Estimate the overall energy in the channel, and the energy in
the notch (i.e. overall channel energy - tone energy => noise).
Use abs instead of multiply for speed (is it really faster?).
Damp the overall energy a little more for a stable result.
Damp the notch energy a little less, so we don't damp out the
blip every time the phase reverses */
det->channel_level += ((abs(amp) - det->channel_level) >> 5);
det->notch_level += ((abs(notched) - det->notch_level) >> 4);
if (det->channel_level > 280) {
/* There is adequate energy in the channel.
Is it mostly at 2100Hz? */
if (det->notch_level * 6 < det->channel_level) {
/* The notch says yes, so we have the tone. */
if (!det->tone_present) {
/* Do we get a kick every 450+-25ms? */
if (det->tone_cycle_duration >= 425 * 8
&& det->tone_cycle_duration <= 475 * 8) {
det->good_cycles++;
if (det->good_cycles > 2)
det->hit = TRUE;
}
det->tone_cycle_duration = 0;
}
det->tone_present = TRUE;
} else
det->tone_present = FALSE;
det->tone_cycle_duration++;
} else {
det->tone_present = FALSE;
det->tone_cycle_duration = 0;
det->good_cycles = 0;
}
return det->hit;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@@ -1,122 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* dsp_hwec.c:
* builtin mISDN dsp pipeline element for enabling the hw echocanceller
*
* Copyright (C) 2007, Nadi Sarrar
*
* Nadi Sarrar <nadi@beronet.com>
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/mISDNdsp.h>
#include <linux/mISDNif.h>
#include "core.h"
#include "dsp.h"
#include "dsp_hwec.h"
static struct mISDN_dsp_element_arg args[] = {
{ "deftaps", "128", "Set the number of taps of cancellation." },
};
static struct mISDN_dsp_element dsp_hwec_p = {
.name = "hwec",
.new = NULL,
.free = NULL,
.process_tx = NULL,
.process_rx = NULL,
.num_args = ARRAY_SIZE(args),
.args = args,
};
struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p;
void dsp_hwec_enable(struct dsp *dsp, const char *arg)
{
int deftaps = 128,
len;
struct mISDN_ctrl_req cq;
if (!dsp) {
printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n",
__func__);
return;
}
if (!arg)
goto _do;
len = strlen(arg);
if (!len)
goto _do;
{
char *dup, *next, *tok, *name, *val;
int tmp;
dup = next = kstrdup(arg, GFP_ATOMIC);
if (!dup)
return;
while ((tok = strsep(&next, ","))) {
if (!strlen(tok))
continue;
name = strsep(&tok, "=");
val = tok;
if (!val)
continue;
if (!strcmp(name, "deftaps")) {
if (sscanf(val, "%d", &tmp) == 1)
deftaps = tmp;
}
}
kfree(dup);
}
_do:
printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n",
__func__, deftaps);
memset(&cq, 0, sizeof(cq));
cq.op = MISDN_CTRL_HFC_ECHOCAN_ON;
cq.p1 = deftaps;
if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
__func__);
return;
}
}
void dsp_hwec_disable(struct dsp *dsp)
{
struct mISDN_ctrl_req cq;
if (!dsp) {
printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n",
__func__);
return;
}
printk(KERN_DEBUG "%s: disabling hwec\n", __func__);
memset(&cq, 0, sizeof(cq));
cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF;
if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
__func__);
return;
}
}
int dsp_hwec_init(void)
{
mISDN_dsp_element_register(dsp_hwec);
return 0;
}
void dsp_hwec_exit(void)
{
mISDN_dsp_element_unregister(dsp_hwec);
}

View File

@@ -1,10 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* dsp_hwec.h
*/
extern struct mISDN_dsp_element *dsp_hwec;
extern void dsp_hwec_enable(struct dsp *dsp, const char *arg);
extern void dsp_hwec_disable(struct dsp *dsp);
extern int dsp_hwec_init(void);
extern void dsp_hwec_exit(void);

View File

@@ -1,300 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* dsp_pipeline.c: pipelined audio processing
*
* Copyright (C) 2007, Nadi Sarrar
*
* Nadi Sarrar <nadi@beronet.com>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
#include <linux/export.h>
#include "dsp.h"
#include "dsp_hwec.h"
struct dsp_pipeline_entry {
struct mISDN_dsp_element *elem;
void *p;
struct list_head list;
};
struct dsp_element_entry {
struct mISDN_dsp_element *elem;
struct device dev;
struct list_head list;
};
static LIST_HEAD(dsp_elements);
/* sysfs */
static const struct class elements_class = {
.name = "dsp_pipeline",
};
static ssize_t
attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
{
struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
int i;
char *p = buf;
*buf = 0;
for (i = 0; i < elem->num_args; i++)
p += sprintf(p, "Name: %s\n%s%s%sDescription: %s\n\n",
elem->args[i].name,
elem->args[i].def ? "Default: " : "",
elem->args[i].def ? elem->args[i].def : "",
elem->args[i].def ? "\n" : "",
elem->args[i].desc);
return p - buf;
}
static struct device_attribute element_attributes[] = {
__ATTR(args, 0444, attr_show_args, NULL),
};
static void
mISDN_dsp_dev_release(struct device *dev)
{
struct dsp_element_entry *entry =
container_of(dev, struct dsp_element_entry, dev);
list_del(&entry->list);
kfree(entry);
}
int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
{
struct dsp_element_entry *entry;
int ret, i;
if (!elem)
return -EINVAL;
entry = kzalloc_obj(struct dsp_element_entry, GFP_ATOMIC);
if (!entry)
return -ENOMEM;
INIT_LIST_HEAD(&entry->list);
entry->elem = elem;
entry->dev.class = &elements_class;
entry->dev.release = mISDN_dsp_dev_release;
dev_set_drvdata(&entry->dev, elem);
dev_set_name(&entry->dev, "%s", elem->name);
ret = device_register(&entry->dev);
if (ret) {
printk(KERN_ERR "%s: failed to register %s\n",
__func__, elem->name);
goto err1;
}
list_add_tail(&entry->list, &dsp_elements);
for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) {
ret = device_create_file(&entry->dev,
&element_attributes[i]);
if (ret) {
printk(KERN_ERR "%s: failed to create device file\n",
__func__);
goto err2;
}
}
return 0;
err2:
device_unregister(&entry->dev);
return ret;
err1:
put_device(&entry->dev);
return ret;
}
EXPORT_SYMBOL(mISDN_dsp_element_register);
void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
{
struct dsp_element_entry *entry, *n;
if (!elem)
return;
list_for_each_entry_safe(entry, n, &dsp_elements, list)
if (entry->elem == elem) {
device_unregister(&entry->dev);
return;
}
printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
}
EXPORT_SYMBOL(mISDN_dsp_element_unregister);
int dsp_pipeline_module_init(void)
{
int err;
err = class_register(&elements_class);
if (err)
return err;
dsp_hwec_init();
return 0;
}
void dsp_pipeline_module_exit(void)
{
struct dsp_element_entry *entry, *n;
dsp_hwec_exit();
class_unregister(&elements_class);
list_for_each_entry_safe(entry, n, &dsp_elements, list) {
list_del(&entry->list);
printk(KERN_WARNING "%s: element was still registered: %s\n",
__func__, entry->elem->name);
kfree(entry);
}
}
int dsp_pipeline_init(struct dsp_pipeline *pipeline)
{
if (!pipeline)
return -EINVAL;
INIT_LIST_HEAD(&pipeline->list);
return 0;
}
static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
{
struct dsp_pipeline_entry *entry, *n;
list_for_each_entry_safe(entry, n, &pipeline->list, list) {
list_del(&entry->list);
if (entry->elem == dsp_hwec)
dsp_hwec_disable(container_of(pipeline, struct dsp,
pipeline));
else
entry->elem->free(entry->p);
kfree(entry);
}
}
void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
{
if (!pipeline)
return;
_dsp_pipeline_destroy(pipeline);
}
int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
{
int found = 0;
char *dup, *next, *tok, *name, *args;
struct dsp_element_entry *entry, *n;
struct dsp_pipeline_entry *pipeline_entry;
struct mISDN_dsp_element *elem;
if (!pipeline)
return -EINVAL;
if (!list_empty(&pipeline->list))
_dsp_pipeline_destroy(pipeline);
dup = next = kstrdup(cfg, GFP_ATOMIC);
if (!dup)
return 0;
while ((tok = strsep(&next, "|"))) {
if (!strlen(tok))
continue;
name = strsep(&tok, "(");
args = strsep(&tok, ")");
if (args && !*args)
args = NULL;
list_for_each_entry_safe(entry, n, &dsp_elements, list)
if (!strcmp(entry->elem->name, name)) {
elem = entry->elem;
pipeline_entry = kmalloc_obj(struct dsp_pipeline_entry,
GFP_ATOMIC);
if (!pipeline_entry) {
printk(KERN_ERR "%s: failed to add "
"entry to pipeline: %s (out of "
"memory)\n", __func__, elem->name);
goto _out;
}
pipeline_entry->elem = elem;
if (elem == dsp_hwec) {
/* This is a hack to make the hwec
available as a pipeline module */
dsp_hwec_enable(container_of(pipeline,
struct dsp, pipeline), args);
list_add_tail(&pipeline_entry->list,
&pipeline->list);
} else {
pipeline_entry->p = elem->new(args);
if (pipeline_entry->p) {
list_add_tail(&pipeline_entry->
list, &pipeline->list);
} else {
printk(KERN_ERR "%s: failed "
"to add entry to pipeline: "
"%s (new() returned NULL)\n",
__func__, elem->name);
kfree(pipeline_entry);
}
}
found = 1;
break;
}
if (found)
found = 0;
else
printk(KERN_ERR "%s: element not found, skipping: "
"%s\n", __func__, name);
}
_out:
if (!list_empty(&pipeline->list))
pipeline->inuse = 1;
else
pipeline->inuse = 0;
kfree(dup);
return 0;
}
void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
{
struct dsp_pipeline_entry *entry;
if (!pipeline)
return;
list_for_each_entry(entry, &pipeline->list, list)
if (entry->elem->process_tx)
entry->elem->process_tx(entry->p, data, len);
}
void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,
unsigned int txlen)
{
struct dsp_pipeline_entry *entry;
if (!pipeline)
return;
list_for_each_entry_reverse(entry, &pipeline->list, list)
if (entry->elem->process_rx)
entry->elem->process_rx(entry->p, data, len, txlen);
}

View File

@@ -1,550 +0,0 @@
/*
* Audio support data for ISDN4Linux.
*
* Copyright Andreas Eversberg (jolly@eversberg.eu)
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#include <linux/gfp.h>
#include <linux/mISDNif.h>
#include <linux/mISDNdsp.h>
#include "core.h"
#include "dsp.h"
#define DATA_S sample_silence
#define SIZE_S (&sizeof_silence)
#define DATA_GA sample_german_all
#define SIZE_GA (&sizeof_german_all)
#define DATA_GO sample_german_old
#define SIZE_GO (&sizeof_german_old)
#define DATA_DT sample_american_dialtone
#define SIZE_DT (&sizeof_american_dialtone)
#define DATA_RI sample_american_ringing
#define SIZE_RI (&sizeof_american_ringing)
#define DATA_BU sample_american_busy
#define SIZE_BU (&sizeof_american_busy)
#define DATA_S1 sample_special1
#define SIZE_S1 (&sizeof_special1)
#define DATA_S2 sample_special2
#define SIZE_S2 (&sizeof_special2)
#define DATA_S3 sample_special3
#define SIZE_S3 (&sizeof_special3)
/***************/
/* tones loops */
/***************/
/* all tones are alaw encoded */
/* the last sample+1 is in phase with the first sample. the error is low */
static u8 sample_german_all[] = {
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
0x80, 0xab, 0x81, 0x6d, 0xfd, 0xdd, 0x5d, 0x9d,
0x4d, 0xd1, 0x89, 0x88, 0xd0, 0x4c, 0x9c, 0x5c,
0xdc, 0xfc, 0x6c,
};
static u32 sizeof_german_all = sizeof(sample_german_all);
static u8 sample_german_old[] = {
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
0xec, 0x68, 0xe1, 0x6d, 0x6d, 0x91, 0x51, 0xed,
0x6d, 0x01, 0x1e, 0x10, 0x0c, 0x90, 0x60, 0x70,
0x8c,
};
static u32 sizeof_german_old = sizeof(sample_german_old);
static u8 sample_american_dialtone[] = {
0x2a, 0x18, 0x90, 0x6c, 0x4c, 0xbc, 0x4c, 0x6c,
0x10, 0x58, 0x32, 0xb9, 0x31, 0x2d, 0x8d, 0x0d,
0x8d, 0x2d, 0x31, 0x99, 0x0f, 0x28, 0x60, 0xf0,
0xd0, 0x50, 0xd0, 0x30, 0x60, 0x08, 0x8e, 0x67,
0x09, 0x19, 0x21, 0xe1, 0xd9, 0xb9, 0x29, 0x67,
0x83, 0x02, 0xce, 0xbe, 0xee, 0x1a, 0x1b, 0xef,
0xbf, 0xcf, 0x03, 0x82, 0x66, 0x28, 0xb8, 0xd8,
0xe0, 0x20, 0x18, 0x08, 0x66, 0x8f, 0x09, 0x61,
0x31, 0xd1, 0x51, 0xd1, 0xf1, 0x61, 0x29, 0x0e,
0x98, 0x30, 0x2c, 0x8c, 0x0c, 0x8c, 0x2c, 0x30,
0xb8, 0x33, 0x59, 0x11, 0x6d, 0x4d, 0xbd, 0x4d,
0x6d, 0x91, 0x19,
};
static u32 sizeof_american_dialtone = sizeof(sample_american_dialtone);
static u8 sample_american_ringing[] = {
0x2a, 0xe0, 0xac, 0x0c, 0xbc, 0x4c, 0x8c, 0x90,
0x48, 0xc7, 0xc1, 0xed, 0xcd, 0x4d, 0xcd, 0xed,
0xc1, 0xb7, 0x08, 0x30, 0xec, 0xcc, 0xcc, 0x8c,
0x10, 0x58, 0x1a, 0x99, 0x71, 0xed, 0x8d, 0x8d,
0x2d, 0x41, 0x89, 0x9e, 0x20, 0x70, 0x2c, 0xec,
0x2c, 0x70, 0x20, 0x86, 0x77, 0xe1, 0x31, 0x11,
0xd1, 0xf1, 0x81, 0x09, 0xa3, 0x56, 0x58, 0x00,
0x40, 0xc0, 0x60, 0x38, 0x46, 0x43, 0x57, 0x39,
0xd9, 0x59, 0x99, 0xc9, 0x77, 0x2f, 0x2e, 0xc6,
0xd6, 0x28, 0xd6, 0x36, 0x26, 0x2e, 0x8a, 0xa3,
0x43, 0x63, 0x4b, 0x4a, 0x62, 0x42, 0xa2, 0x8b,
0x2f, 0x27, 0x37, 0xd7, 0x29, 0xd7, 0xc7, 0x2f,
0x2e, 0x76, 0xc8, 0x98, 0x58, 0xd8, 0x38, 0x56,
0x42, 0x47, 0x39, 0x61, 0xc1, 0x41, 0x01, 0x59,
0x57, 0xa2, 0x08, 0x80, 0xf0, 0xd0, 0x10, 0x30,
0xe0, 0x76, 0x87, 0x21, 0x71, 0x2d, 0xed, 0x2d,
0x71, 0x21, 0x9f, 0x88, 0x40, 0x2c, 0x8c, 0x8c,
0xec, 0x70, 0x98, 0x1b, 0x59, 0x11, 0x8d, 0xcd,
0xcd, 0xed, 0x31, 0x09, 0xb6, 0xc0, 0xec, 0xcc,
0x4c, 0xcc, 0xec, 0xc0, 0xc6, 0x49, 0x91, 0x8d,
0x4d, 0xbd, 0x0d, 0xad, 0xe1,
};
static u32 sizeof_american_ringing = sizeof(sample_american_ringing);
static u8 sample_american_busy[] = {
0x2a, 0x00, 0x6c, 0x4c, 0x4c, 0x6c, 0xb0, 0x66,
0x99, 0x11, 0x6d, 0x8d, 0x2d, 0x41, 0xd7, 0x96,
0x60, 0xf0, 0x70, 0x40, 0x58, 0xf6, 0x53, 0x57,
0x09, 0x89, 0xd7, 0x5f, 0xe3, 0x2a, 0xe3, 0x5f,
0xd7, 0x89, 0x09, 0x57, 0x53, 0xf6, 0x58, 0x40,
0x70, 0xf0, 0x60, 0x96, 0xd7, 0x41, 0x2d, 0x8d,
0x6d, 0x11, 0x99, 0x66, 0xb0, 0x6c, 0x4c, 0x4c,
0x6c, 0x00, 0x2a, 0x01, 0x6d, 0x4d, 0x4d, 0x6d,
0xb1, 0x67, 0x98, 0x10, 0x6c, 0x8c, 0x2c, 0x40,
0xd6, 0x97, 0x61, 0xf1, 0x71, 0x41, 0x59, 0xf7,
0x52, 0x56, 0x08, 0x88, 0xd6, 0x5e, 0xe2, 0x2a,
0xe2, 0x5e, 0xd6, 0x88, 0x08, 0x56, 0x52, 0xf7,
0x59, 0x41, 0x71, 0xf1, 0x61, 0x97, 0xd6, 0x40,
0x2c, 0x8c, 0x6c, 0x10, 0x98, 0x67, 0xb1, 0x6d,
0x4d, 0x4d, 0x6d, 0x01,
};
static u32 sizeof_american_busy = sizeof(sample_american_busy);
static u8 sample_special1[] = {
0x2a, 0x2c, 0xbc, 0x6c, 0xd6, 0x71, 0xbd, 0x0d,
0xd9, 0x80, 0xcc, 0x4c, 0x40, 0x39, 0x0d, 0xbd,
0x11, 0x86, 0xec, 0xbc, 0xec, 0x0e, 0x51, 0xbd,
0x8d, 0x89, 0x30, 0x4c, 0xcc, 0xe0, 0xe1, 0xcd,
0x4d, 0x31, 0x88, 0x8c, 0xbc, 0x50, 0x0f, 0xed,
0xbd, 0xed, 0x87, 0x10, 0xbc, 0x0c, 0x38, 0x41,
0x4d, 0xcd, 0x81, 0xd8, 0x0c, 0xbc, 0x70, 0xd7,
0x6d, 0xbd, 0x2d,
};
static u32 sizeof_special1 = sizeof(sample_special1);
static u8 sample_special2[] = {
0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
0x2a, 0xcc, 0x8c, 0xd7, 0x4d, 0x2d, 0x18, 0xbc,
0x10, 0xc1, 0xbd, 0xc1, 0x10, 0xbc, 0x18, 0x2d,
0x4d, 0xd7, 0x8c, 0xcc, 0x2a, 0xcd, 0x8d, 0xd6,
0x4c, 0x2c, 0x19, 0xbd, 0x11, 0xc0, 0xbc, 0xc0,
0x11, 0xbd, 0x19, 0x2c, 0x4c, 0xd6, 0x8d, 0xcd,
};
static u32 sizeof_special2 = sizeof(sample_special2);
static u8 sample_special3[] = {
0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
0x2a, 0xbc, 0x18, 0xcd, 0x11, 0x2c, 0x8c, 0xc1,
0x4d, 0xd6, 0xbc, 0xd6, 0x4d, 0xc1, 0x8c, 0x2c,
0x11, 0xcd, 0x18, 0xbc, 0x2a, 0xbd, 0x19, 0xcc,
0x10, 0x2d, 0x8d, 0xc0, 0x4c, 0xd7, 0xbd, 0xd7,
0x4c, 0xc0, 0x8d, 0x2d, 0x10, 0xcc, 0x19, 0xbd,
};
static u32 sizeof_special3 = sizeof(sample_special3);
static u8 sample_silence[] = {
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a,
};
static u32 sizeof_silence = sizeof(sample_silence);
struct tones_samples {
u32 *len;
u8 *data;
};
static struct
tones_samples samples[] = {
{&sizeof_german_all, sample_german_all},
{&sizeof_german_old, sample_german_old},
{&sizeof_american_dialtone, sample_american_dialtone},
{&sizeof_american_ringing, sample_american_ringing},
{&sizeof_american_busy, sample_american_busy},
{&sizeof_special1, sample_special1},
{&sizeof_special2, sample_special2},
{&sizeof_special3, sample_special3},
{NULL, NULL},
};
/***********************************
* generate ulaw from alaw samples *
***********************************/
void
dsp_audio_generate_ulaw_samples(void)
{
int i, j;
i = 0;
while (samples[i].len) {
j = 0;
while (j < (*samples[i].len)) {
samples[i].data[j] =
dsp_audio_alaw_to_ulaw[samples[i].data[j]];
j++;
}
i++;
}
}
/****************************
* tone sequence definition *
****************************/
static struct pattern {
int tone;
u8 *data[10];
u32 *siz[10];
u32 seq[10];
} pattern[] = {
{TONE_GERMAN_DIALTONE,
{DATA_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDDIALTONE,
{DATA_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_DIALTONE,
{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_DIALPBX,
{DATA_GA, DATA_S, DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL,
NULL},
{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL,
NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_GERMAN_OLDDIALPBX,
{DATA_GO, DATA_S, DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL,
NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL,
NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_AMERICAN_DIALPBX,
{DATA_DT, DATA_S, DATA_DT, DATA_S, DATA_DT, DATA_S, NULL, NULL, NULL,
NULL},
{SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, NULL, NULL, NULL,
NULL},
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
{TONE_GERMAN_RINGING,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDRINGING,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_RINGING,
{DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_RINGPBX,
{DATA_GA, DATA_S, DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDRINGPBX,
{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_RINGPBX,
{DATA_RI, DATA_S, DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_BUSY,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDBUSY,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_BUSY,
{DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_BU, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_HANGUP,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDHANGUP,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_HANGUP,
{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_SPECIAL_INFO,
{DATA_S1, DATA_S2, DATA_S3, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_GASSENBESETZT,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_AUFSCHALTTON,
{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
{0,
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
};
/******************
* copy tone data *
******************/
/* an sk_buff is generated from the number of samples needed.
* the count will be changed and may begin from 0 each pattern period.
* the clue is to precalculate the pointers and legths to use only one
* memcpy per function call, or two memcpy if the tone sequence changes.
*
* pattern - the type of the pattern
* count - the sample from the beginning of the pattern (phase)
* len - the number of bytes
*
* return - the sk_buff with the sample
*
* if tones has finished (e.g. knocking tone), dsp->tones is turned off
*/
void dsp_tone_copy(struct dsp *dsp, u8 *data, int len)
{
int index, count, start, num;
struct pattern *pat;
struct dsp_tone *tone = &dsp->tone;
/* if we have no tone, we copy silence */
if (!tone->tone) {
memset(data, dsp_silence, len);
return;
}
/* process pattern */
pat = (struct pattern *)tone->pattern;
/* points to the current pattern */
index = tone->index; /* gives current sequence index */
count = tone->count; /* gives current sample */
/* copy sample */
while (len) {
/* find sample to start with */
while (42) {
/* wrap around */
if (!pat->seq[index]) {
count = 0;
index = 0;
}
/* check if we are currently playing this tone */
if (count < pat->seq[index])
break;
if (dsp_debug & DEBUG_DSP_TONE)
printk(KERN_DEBUG "%s: reaching next sequence "
"(index=%d)\n", __func__, index);
count -= pat->seq[index];
index++;
}
/* calculate start and number of samples */
start = count % (*(pat->siz[index]));
num = len;
if (num + count > pat->seq[index])
num = pat->seq[index] - count;
if (num + start > (*(pat->siz[index])))
num = (*(pat->siz[index])) - start;
/* copy memory */
memcpy(data, pat->data[index] + start, num);
/* reduce length */
data += num;
count += num;
len -= num;
}
tone->index = index;
tone->count = count;
/* return sk_buff */
return;
}
/*******************************
* send HW message to hfc card *
*******************************/
static void
dsp_tone_hw_message(struct dsp *dsp, u8 *sample, int len)
{
struct sk_buff *nskb;
/* unlocking is not required, because we don't expect a response */
nskb = _alloc_mISDN_skb(PH_CONTROL_REQ,
(len) ? HFC_SPL_LOOP_ON : HFC_SPL_LOOP_OFF, len, sample,
GFP_ATOMIC);
if (nskb) {
if (dsp->ch.peer) {
if (dsp->ch.recv(dsp->ch.peer, nskb))
dev_kfree_skb(nskb);
} else
dev_kfree_skb(nskb);
}
}
/*****************
* timer expires *
*****************/
void
dsp_tone_timeout(struct timer_list *t)
{
struct dsp *dsp = timer_container_of(dsp, t, tone.tl);
struct dsp_tone *tone = &dsp->tone;
struct pattern *pat = (struct pattern *)tone->pattern;
int index = tone->index;
if (!tone->tone)
return;
index++;
if (!pat->seq[index])
index = 0;
tone->index = index;
/* set next tone */
if (pat->data[index] == DATA_S)
dsp_tone_hw_message(dsp, NULL, 0);
else
dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index]));
/* set timer */
tone->tl.expires = jiffies + (pat->seq[index] * HZ) / 8000;
add_timer(&tone->tl);
}
/********************
* set/release tone *
********************/
/*
* tones are relaized by streaming or by special loop commands if supported
* by hardware. when hardware is used, the patterns will be controlled by
* timers.
*/
int
dsp_tone(struct dsp *dsp, int tone)
{
struct pattern *pat;
int i;
struct dsp_tone *tonet = &dsp->tone;
tonet->software = 0;
tonet->hardware = 0;
/* we turn off the tone */
if (!tone) {
if (dsp->features.hfc_loops && timer_pending(&tonet->tl))
timer_delete(&tonet->tl);
if (dsp->features.hfc_loops)
dsp_tone_hw_message(dsp, NULL, 0);
tonet->tone = 0;
return 0;
}
pat = NULL;
i = 0;
while (pattern[i].tone) {
if (pattern[i].tone == tone) {
pat = &pattern[i];
break;
}
i++;
}
if (!pat) {
printk(KERN_WARNING "dsp: given tone 0x%x is invalid\n", tone);
return -EINVAL;
}
if (dsp_debug & DEBUG_DSP_TONE)
printk(KERN_DEBUG "%s: now starting tone %d (index=%d)\n",
__func__, tone, 0);
tonet->tone = tone;
tonet->pattern = pat;
tonet->index = 0;
tonet->count = 0;
if (dsp->features.hfc_loops) {
tonet->hardware = 1;
/* set first tone */
dsp_tone_hw_message(dsp, pat->data[0], *(pat->siz[0]));
/* set timer */
if (timer_pending(&tonet->tl))
timer_delete(&tonet->tl);
tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000;
add_timer(&tonet->tl);
} else {
tonet->software = 1;
}
return 0;
}

View File

@@ -1,176 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* finite state machine implementation
*
* Author Karsten Keil <kkeil@novell.com>
*
* Thanks to Jan den Ouden
* Fritz Elfert
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/string.h>
#include "fsm.h"
#define FSM_TIMER_DEBUG 0
int
mISDN_FsmNew(struct Fsm *fsm,
struct FsmNode *fnlist, int fncount)
{
int i;
fsm->jumpmatrix =
kzalloc(array3_size(sizeof(FSMFNPTR), fsm->state_count,
fsm->event_count),
GFP_KERNEL);
if (fsm->jumpmatrix == NULL)
return -ENOMEM;
for (i = 0; i < fncount; i++)
if ((fnlist[i].state >= fsm->state_count) ||
(fnlist[i].event >= fsm->event_count)) {
printk(KERN_ERR
"mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
i, (long)fnlist[i].state, (long)fsm->state_count,
(long)fnlist[i].event, (long)fsm->event_count);
} else
fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
return 0;
}
EXPORT_SYMBOL(mISDN_FsmNew);
void
mISDN_FsmFree(struct Fsm *fsm)
{
kfree((void *) fsm->jumpmatrix);
}
EXPORT_SYMBOL(mISDN_FsmFree);
int
mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
{
FSMFNPTR r;
if ((fi->state >= fi->fsm->state_count) ||
(event >= fi->fsm->event_count)) {
printk(KERN_ERR
"mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
(long)fi->state, (long)fi->fsm->state_count, event,
(long)fi->fsm->event_count);
return 1;
}
r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
if (r) {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
r(fi, event, arg);
return 0;
} else {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s no action",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
return 1;
}
}
EXPORT_SYMBOL(mISDN_FsmEvent);
void
mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
{
fi->state = newstate;
if (fi->debug)
fi->printdebug(fi, "ChangeState %s",
fi->fsm->strState[newstate]);
}
EXPORT_SYMBOL(mISDN_FsmChangeState);
static void
FsmExpireTimer(struct timer_list *t)
{
struct FsmTimer *ft = timer_container_of(ft, t, tl);
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
#endif
mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
}
void
mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
{
ft->fi = fi;
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
#endif
timer_setup(&ft->tl, FsmExpireTimer, 0);
}
EXPORT_SYMBOL(mISDN_FsmInitTimer);
void
mISDN_FsmDelTimer(struct FsmTimer *ft, int where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
(long) ft, where);
#endif
timer_delete(&ft->tl);
}
EXPORT_SYMBOL(mISDN_FsmDelTimer);
int
mISDN_FsmAddTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmAddTimer %lx %d %d",
(long) ft, millisec, where);
#endif
if (timer_pending(&ft->tl)) {
if (ft->fi->debug) {
printk(KERN_WARNING
"mISDN_FsmAddTimer: timer already active!\n");
ft->fi->printdebug(ft->fi,
"mISDN_FsmAddTimer already active!");
}
return -1;
}
ft->event = event;
ft->arg = arg;
ft->tl.expires = jiffies + (millisec * HZ) / 1000;
add_timer(&ft->tl);
return 0;
}
EXPORT_SYMBOL(mISDN_FsmAddTimer);
void
mISDN_FsmRestartTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmRestartTimer %lx %d %d",
(long) ft, millisec, where);
#endif
if (timer_pending(&ft->tl))
timer_delete(&ft->tl);
ft->event = event;
ft->arg = arg;
ft->tl.expires = jiffies + (millisec * HZ) / 1000;
add_timer(&ft->tl);
}
EXPORT_SYMBOL(mISDN_FsmRestartTimer);

View File

@@ -1,58 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Thanks to Jan den Ouden
* Fritz Elfert
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef _MISDN_FSM_H
#define _MISDN_FSM_H
#include <linux/timer.h>
/* Statemachine */
struct FsmInst;
typedef void (*FSMFNPTR)(struct FsmInst *, int, void *);
struct Fsm {
FSMFNPTR *jumpmatrix;
int state_count, event_count;
char **strEvent, **strState;
};
struct FsmInst {
struct Fsm *fsm;
int state;
int debug;
void *userdata;
int userint;
void (*printdebug) (struct FsmInst *, char *, ...);
};
struct FsmNode {
int state, event;
void (*routine) (struct FsmInst *, int, void *);
};
struct FsmTimer {
struct FsmInst *fi;
struct timer_list tl;
int event;
void *arg;
};
extern int mISDN_FsmNew(struct Fsm *, struct FsmNode *, int);
extern void mISDN_FsmFree(struct Fsm *);
extern int mISDN_FsmEvent(struct FsmInst *, int , void *);
extern void mISDN_FsmChangeState(struct FsmInst *, int);
extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *);
extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int);
extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int);
extern void mISDN_FsmDelTimer(struct FsmTimer *, int);
#endif

View File

@@ -1,516 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/mISDNhw.h>
static void
dchannel_bh(struct work_struct *ws)
{
struct dchannel *dch = container_of(ws, struct dchannel, workq);
struct sk_buff *skb;
int err;
if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
while ((skb = skb_dequeue(&dch->rqueue))) {
if (likely(dch->dev.D.peer)) {
err = dch->dev.D.recv(dch->dev.D.peer, skb);
if (err)
dev_kfree_skb(skb);
} else
dev_kfree_skb(skb);
}
}
if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
if (dch->phfunc)
dch->phfunc(dch);
}
}
static void
bchannel_bh(struct work_struct *ws)
{
struct bchannel *bch = container_of(ws, struct bchannel, workq);
struct sk_buff *skb;
int err;
if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
while ((skb = skb_dequeue(&bch->rqueue))) {
bch->rcount--;
if (likely(bch->ch.peer)) {
err = bch->ch.recv(bch->ch.peer, skb);
if (err)
dev_kfree_skb(skb);
} else
dev_kfree_skb(skb);
}
}
}
int
mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
{
test_and_set_bit(FLG_HDLC, &ch->Flags);
ch->maxlen = maxlen;
ch->hw = NULL;
ch->rx_skb = NULL;
ch->tx_skb = NULL;
ch->tx_idx = 0;
ch->phfunc = phf;
skb_queue_head_init(&ch->squeue);
skb_queue_head_init(&ch->rqueue);
INIT_LIST_HEAD(&ch->dev.bchannels);
INIT_WORK(&ch->workq, dchannel_bh);
return 0;
}
EXPORT_SYMBOL(mISDN_initdchannel);
int
mISDN_initbchannel(struct bchannel *ch, unsigned short maxlen,
unsigned short minlen)
{
ch->Flags = 0;
ch->minlen = minlen;
ch->next_minlen = minlen;
ch->init_minlen = minlen;
ch->maxlen = maxlen;
ch->next_maxlen = maxlen;
ch->init_maxlen = maxlen;
ch->hw = NULL;
ch->rx_skb = NULL;
ch->tx_skb = NULL;
ch->tx_idx = 0;
skb_queue_head_init(&ch->rqueue);
ch->rcount = 0;
ch->next_skb = NULL;
INIT_WORK(&ch->workq, bchannel_bh);
return 0;
}
EXPORT_SYMBOL(mISDN_initbchannel);
int
mISDN_freedchannel(struct dchannel *ch)
{
if (ch->tx_skb) {
dev_kfree_skb(ch->tx_skb);
ch->tx_skb = NULL;
}
if (ch->rx_skb) {
dev_kfree_skb(ch->rx_skb);
ch->rx_skb = NULL;
}
skb_queue_purge(&ch->squeue);
skb_queue_purge(&ch->rqueue);
flush_work(&ch->workq);
return 0;
}
EXPORT_SYMBOL(mISDN_freedchannel);
void
mISDN_clear_bchannel(struct bchannel *ch)
{
if (ch->tx_skb) {
dev_kfree_skb(ch->tx_skb);
ch->tx_skb = NULL;
}
ch->tx_idx = 0;
if (ch->rx_skb) {
dev_kfree_skb(ch->rx_skb);
ch->rx_skb = NULL;
}
if (ch->next_skb) {
dev_kfree_skb(ch->next_skb);
ch->next_skb = NULL;
}
test_and_clear_bit(FLG_TX_BUSY, &ch->Flags);
test_and_clear_bit(FLG_TX_NEXT, &ch->Flags);
test_and_clear_bit(FLG_ACTIVE, &ch->Flags);
test_and_clear_bit(FLG_FILLEMPTY, &ch->Flags);
test_and_clear_bit(FLG_TX_EMPTY, &ch->Flags);
test_and_clear_bit(FLG_RX_OFF, &ch->Flags);
ch->dropcnt = 0;
ch->minlen = ch->init_minlen;
ch->next_minlen = ch->init_minlen;
ch->maxlen = ch->init_maxlen;
ch->next_maxlen = ch->init_maxlen;
skb_queue_purge(&ch->rqueue);
ch->rcount = 0;
}
EXPORT_SYMBOL(mISDN_clear_bchannel);
void
mISDN_freebchannel(struct bchannel *ch)
{
cancel_work_sync(&ch->workq);
mISDN_clear_bchannel(ch);
}
EXPORT_SYMBOL(mISDN_freebchannel);
int
mISDN_ctrl_bchannel(struct bchannel *bch, struct mISDN_ctrl_req *cq)
{
int ret = 0;
switch (cq->op) {
case MISDN_CTRL_GETOP:
cq->op = MISDN_CTRL_RX_BUFFER | MISDN_CTRL_FILL_EMPTY |
MISDN_CTRL_RX_OFF;
break;
case MISDN_CTRL_FILL_EMPTY:
if (cq->p1) {
memset(bch->fill, cq->p2 & 0xff, MISDN_BCH_FILL_SIZE);
test_and_set_bit(FLG_FILLEMPTY, &bch->Flags);
} else {
test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
}
break;
case MISDN_CTRL_RX_OFF:
/* read back dropped byte count */
cq->p2 = bch->dropcnt;
if (cq->p1)
test_and_set_bit(FLG_RX_OFF, &bch->Flags);
else
test_and_clear_bit(FLG_RX_OFF, &bch->Flags);
bch->dropcnt = 0;
break;
case MISDN_CTRL_RX_BUFFER:
if (cq->p2 > MISDN_CTRL_RX_SIZE_IGNORE)
bch->next_maxlen = cq->p2;
if (cq->p1 > MISDN_CTRL_RX_SIZE_IGNORE)
bch->next_minlen = cq->p1;
/* we return the old values */
cq->p1 = bch->minlen;
cq->p2 = bch->maxlen;
break;
default:
pr_info("mISDN unhandled control %x operation\n", cq->op);
ret = -EINVAL;
break;
}
return ret;
}
EXPORT_SYMBOL(mISDN_ctrl_bchannel);
static inline u_int
get_sapi_tei(u_char *p)
{
u_int sapi, tei;
sapi = *p >> 2;
tei = p[1] >> 1;
return sapi | (tei << 8);
}
void
recv_Dchannel(struct dchannel *dch)
{
struct mISDNhead *hh;
if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
dev_kfree_skb(dch->rx_skb);
dch->rx_skb = NULL;
return;
}
hh = mISDN_HEAD_P(dch->rx_skb);
hh->prim = PH_DATA_IND;
hh->id = get_sapi_tei(dch->rx_skb->data);
skb_queue_tail(&dch->rqueue, dch->rx_skb);
dch->rx_skb = NULL;
schedule_event(dch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Dchannel);
void
recv_Echannel(struct dchannel *ech, struct dchannel *dch)
{
struct mISDNhead *hh;
if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */
dev_kfree_skb(ech->rx_skb);
ech->rx_skb = NULL;
return;
}
hh = mISDN_HEAD_P(ech->rx_skb);
hh->prim = PH_DATA_E_IND;
hh->id = get_sapi_tei(ech->rx_skb->data);
skb_queue_tail(&dch->rqueue, ech->rx_skb);
ech->rx_skb = NULL;
schedule_event(dch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Echannel);
void
recv_Bchannel(struct bchannel *bch, unsigned int id, bool force)
{
struct mISDNhead *hh;
/* if allocation did fail upper functions still may call us */
if (unlikely(!bch->rx_skb))
return;
if (unlikely(!bch->rx_skb->len)) {
/* we have no data to send - this may happen after recovery
* from overflow or too small allocation.
* We need to free the buffer here */
dev_kfree_skb(bch->rx_skb);
bch->rx_skb = NULL;
} else {
if (test_bit(FLG_TRANSPARENT, &bch->Flags) &&
(bch->rx_skb->len < bch->minlen) && !force)
return;
hh = mISDN_HEAD_P(bch->rx_skb);
hh->prim = PH_DATA_IND;
hh->id = id;
if (bch->rcount >= 64) {
printk(KERN_WARNING
"B%d receive queue overflow - flushing!\n",
bch->nr);
skb_queue_purge(&bch->rqueue);
}
bch->rcount++;
skb_queue_tail(&bch->rqueue, bch->rx_skb);
bch->rx_skb = NULL;
schedule_event(bch, FLG_RECVQUEUE);
}
}
EXPORT_SYMBOL(recv_Bchannel);
void
recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
{
skb_queue_tail(&dch->rqueue, skb);
schedule_event(dch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Dchannel_skb);
void
recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
{
if (bch->rcount >= 64) {
printk(KERN_WARNING "B-channel %p receive queue overflow, "
"flushing!\n", bch);
skb_queue_purge(&bch->rqueue);
bch->rcount = 0;
}
bch->rcount++;
skb_queue_tail(&bch->rqueue, skb);
schedule_event(bch, FLG_RECVQUEUE);
}
EXPORT_SYMBOL(recv_Bchannel_skb);
static void
confirm_Dsend(struct dchannel *dch)
{
struct sk_buff *skb;
skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
0, NULL, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "%s: no skb id %x\n", __func__,
mISDN_HEAD_ID(dch->tx_skb));
return;
}
skb_queue_tail(&dch->rqueue, skb);
schedule_event(dch, FLG_RECVQUEUE);
}
int
get_next_dframe(struct dchannel *dch)
{
dch->tx_idx = 0;
dch->tx_skb = skb_dequeue(&dch->squeue);
if (dch->tx_skb) {
confirm_Dsend(dch);
return 1;
}
dch->tx_skb = NULL;
test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
return 0;
}
EXPORT_SYMBOL(get_next_dframe);
static void
confirm_Bsend(struct bchannel *bch)
{
struct sk_buff *skb;
if (bch->rcount >= 64) {
printk(KERN_WARNING "B-channel %p receive queue overflow, "
"flushing!\n", bch);
skb_queue_purge(&bch->rqueue);
bch->rcount = 0;
}
skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
0, NULL, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "%s: no skb id %x\n", __func__,
mISDN_HEAD_ID(bch->tx_skb));
return;
}
bch->rcount++;
skb_queue_tail(&bch->rqueue, skb);
schedule_event(bch, FLG_RECVQUEUE);
}
int
get_next_bframe(struct bchannel *bch)
{
bch->tx_idx = 0;
if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
bch->tx_skb = bch->next_skb;
if (bch->tx_skb) {
bch->next_skb = NULL;
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
/* confirm imediately to allow next data */
confirm_Bsend(bch);
return 1;
} else {
test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
printk(KERN_WARNING "B TX_NEXT without skb\n");
}
}
bch->tx_skb = NULL;
test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
return 0;
}
EXPORT_SYMBOL(get_next_bframe);
void
queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
{
struct mISDNhead *hh;
if (!skb) {
_queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
} else {
if (ch->peer) {
hh = mISDN_HEAD_P(skb);
hh->prim = pr;
hh->id = id;
if (!ch->recv(ch->peer, skb))
return;
}
dev_kfree_skb(skb);
}
}
EXPORT_SYMBOL(queue_ch_frame);
int
dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
{
/* check oversize */
if (skb->len <= 0) {
printk(KERN_WARNING "%s: skb too small\n", __func__);
return -EINVAL;
}
if (skb->len > ch->maxlen) {
printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
__func__, skb->len, ch->maxlen);
return -EINVAL;
}
/* HW lock must be obtained */
if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
skb_queue_tail(&ch->squeue, skb);
return 0;
} else {
/* write to fifo */
ch->tx_skb = skb;
ch->tx_idx = 0;
return 1;
}
}
EXPORT_SYMBOL(dchannel_senddata);
int
bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
{
/* check oversize */
if (skb->len <= 0) {
printk(KERN_WARNING "%s: skb too small\n", __func__);
return -EINVAL;
}
if (skb->len > ch->maxlen) {
printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
__func__, skb->len, ch->maxlen);
return -EINVAL;
}
/* HW lock must be obtained */
/* check for pending next_skb */
if (ch->next_skb) {
printk(KERN_WARNING
"%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
__func__, skb->len, ch->next_skb->len);
return -EBUSY;
}
if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
ch->next_skb = skb;
return 0;
} else {
/* write to fifo */
ch->tx_skb = skb;
ch->tx_idx = 0;
confirm_Bsend(ch);
return 1;
}
}
EXPORT_SYMBOL(bchannel_senddata);
/* The function allocates a new receive skb on demand with a size for the
* requirements of the current protocol. It returns the tailroom of the
* receive skb or an error.
*/
int
bchannel_get_rxbuf(struct bchannel *bch, int reqlen)
{
int len;
if (bch->rx_skb) {
len = skb_tailroom(bch->rx_skb);
if (len < reqlen) {
pr_warn("B%d no space for %d (only %d) bytes\n",
bch->nr, reqlen, len);
if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
/* send what we have now and try a new buffer */
recv_Bchannel(bch, 0, true);
} else {
/* on HDLC we have to drop too big frames */
return -EMSGSIZE;
}
} else {
return len;
}
}
/* update current min/max length first */
if (unlikely(bch->maxlen != bch->next_maxlen))
bch->maxlen = bch->next_maxlen;
if (unlikely(bch->minlen != bch->next_minlen))
bch->minlen = bch->next_minlen;
if (unlikely(reqlen > bch->maxlen))
return -EMSGSIZE;
if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
if (reqlen >= bch->minlen) {
len = reqlen;
} else {
len = 2 * bch->minlen;
if (len > bch->maxlen)
len = bch->maxlen;
}
} else {
/* with HDLC we do not know the length yet */
len = bch->maxlen;
}
bch->rx_skb = mI_alloc_skb(len, GFP_ATOMIC);
if (!bch->rx_skb) {
pr_warn("B%d receive no memory for %d bytes\n", bch->nr, len);
len = -ENOMEM;
}
return len;
}
EXPORT_SYMBOL(bchannel_get_rxbuf);

View File

@@ -1,92 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* see notice in l1oip.c
*/
/* debugging */
#define DEBUG_L1OIP_INIT 0x00010000
#define DEBUG_L1OIP_SOCKET 0x00020000
#define DEBUG_L1OIP_MGR 0x00040000
#define DEBUG_L1OIP_MSG 0x00080000
/* enable to disorder received bchannels by sequence 2143658798... */
/*
#define REORDER_DEBUG
*/
/* frames */
#define L1OIP_MAX_LEN 2048 /* max packet size form l2 */
#define L1OIP_MAX_PERFRAME 1400 /* max data size in one frame */
/* timers */
#define L1OIP_KEEPALIVE 15
#define L1OIP_TIMEOUT 65
/* socket */
#define L1OIP_DEFAULTPORT 931
/* channel structure */
struct l1oip_chan {
struct dchannel *dch;
struct bchannel *bch;
u32 tx_counter; /* counts xmit bytes/packets */
u32 rx_counter; /* counts recv bytes/packets */
u32 codecstate; /* used by codec to save data */
#ifdef REORDER_DEBUG
int disorder_flag;
struct sk_buff *disorder_skb;
u32 disorder_cnt;
#endif
};
/* card structure */
struct l1oip {
struct list_head list;
/* card */
int registered; /* if registered with mISDN */
char name[MISDN_MAX_IDLEN];
int idx; /* card index */
int pri; /* 1=pri, 0=bri */
int d_idx; /* current dchannel number */
int b_num; /* number of bchannels */
u32 id; /* id of connection */
int ondemand; /* if transmis. is on demand */
int bundle; /* bundle channels in one frm */
int codec; /* codec to use for transmis. */
int limit; /* limit number of bchannels */
bool shutdown; /* if card is released */
/* timer */
struct timer_list keep_tl;
struct timer_list timeout_tl;
int timeout_on;
struct work_struct workq;
/* socket */
struct socket *socket; /* if set, socket is created */
struct completion socket_complete;/* completion of sock thread */
struct task_struct *socket_thread;
spinlock_t socket_lock; /* access sock outside thread */
u32 remoteip; /* if all set, ip is assigned */
u16 localport; /* must always be set */
u16 remoteport; /* must always be set */
struct sockaddr_in sin_local; /* local socket name */
struct sockaddr_in sin_remote; /* remote socket name */
struct msghdr sendmsg; /* ip message to send */
struct kvec sendiov; /* iov for message */
/* frame */
struct l1oip_chan chan[128]; /* channel instances */
};
extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state);
extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result);
extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result);
extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result);
extern void l1oip_4bit_free(void);
extern int l1oip_4bit_alloc(int ulaw);

View File

@@ -1,358 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* l1oip_codec.c generic codec using lookup table
* -> conversion from a-Law to u-Law
* -> conversion from u-Law to a-Law
* -> compression by reducing the number of sample resolution to 4
*
* NOTE: It is not compatible with any standard codec like ADPCM.
*
* Author Andreas Eversberg (jolly@eversberg.eu)
*
*/
/*
How the codec works:
--------------------
The volume is increased to increase the dynamic range of the audio signal.
Each sample is converted to a-LAW with only 16 steps of level resolution.
A pair of two samples are stored in one byte.
The first byte is stored in the upper bits, the second byte is stored in the
lower bits.
To speed up compression and decompression, two lookup tables are formed:
- 16 bits index for two samples (law encoded) with 8 bit compressed result.
- 8 bits index for one compressed data with 16 bits decompressed result.
NOTE: The bytes are handled as they are law-encoded.
*/
#include <linux/vmalloc.h>
#include <linux/mISDNif.h>
#include <linux/in.h>
#include "core.h"
#include "l1oip.h"
/* definitions of codec. don't use calculations, code may run slower. */
static u8 *table_com;
static u16 *table_dec;
/* alaw -> ulaw */
static u8 alaw_to_ulaw[256] =
{
0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49,
0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57,
0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41,
0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f,
0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d,
0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b,
0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45,
0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53,
0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47,
0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55,
0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f,
0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e,
0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b,
0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59,
0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43,
0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51,
0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a,
0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58,
0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42,
0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50,
0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e,
0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c,
0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46,
0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54,
0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48,
0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56,
0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40,
0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f,
0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c,
0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a,
0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44,
0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52
};
/* ulaw -> alaw */
static u8 ulaw_to_alaw[256] =
{
0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35,
0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25,
0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d,
0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d,
0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31,
0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21,
0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9,
0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9,
0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47,
0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf,
0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f,
0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33,
0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23,
0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b,
0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b,
0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b,
0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34,
0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24,
0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c,
0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c,
0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30,
0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20,
0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8,
0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8,
0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46,
0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde,
0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e,
0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32,
0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22,
0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a,
0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a,
0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a
};
/* alaw -> 4bit compression */
static u8 alaw_to_4bit[256] = {
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02,
0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
};
/* 4bit -> alaw decompression */
static u8 _4bit_to_alaw[16] = {
0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b,
0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c,
};
/* ulaw -> 4bit compression */
static u8 ulaw_to_4bit[256] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a,
0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};
/* 4bit -> ulaw decompression */
static u8 _4bit_to_ulaw[16] = {
0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71,
0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f,
};
/*
* Compresses data to the result buffer
* The result size must be at least half of the input buffer.
* The number of samples also must be even!
*/
int
l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state)
{
int ii, i = 0, o = 0;
if (!len)
return 0;
/* send saved byte and first input byte */
if (*state) {
*result++ = table_com[(((*state) << 8) & 0xff00) | (*data++)];
len--;
o++;
}
ii = len >> 1;
while (i < ii) {
*result++ = table_com[(data[0]<<8) | (data[1])];
data += 2;
i++;
o++;
}
/* if len has an odd number, we save byte for next call */
if (len & 1)
*state = 0x100 + *data;
else
*state = 0;
return o;
}
/* Decompress data to the result buffer
* The result size must be the number of sample in packet. (2 * input data)
* The number of samples in the result are even!
*/
int
l1oip_4bit_to_law(u8 *data, int len, u8 *result)
{
int i = 0;
u16 r;
while (i < len) {
r = table_dec[*data++];
*result++ = r >> 8;
*result++ = r;
i++;
}
return len << 1;
}
/*
* law conversion
*/
int
l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result)
{
int i = 0;
while (i < len) {
*result++ = alaw_to_ulaw[*data++];
i++;
}
return len;
}
int
l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result)
{
int i = 0;
while (i < len) {
*result++ = ulaw_to_alaw[*data++];
i++;
}
return len;
}
/*
* generate/free compression and decompression table
*/
void
l1oip_4bit_free(void)
{
vfree(table_dec);
vfree(table_com);
table_com = NULL;
table_dec = NULL;
}
int
l1oip_4bit_alloc(int ulaw)
{
int i1, i2, c, sample;
/* in case, it is called again */
if (table_dec)
return 0;
/* alloc conversion tables */
table_com = vzalloc(65536);
table_dec = vzalloc(512);
if (!table_com || !table_dec) {
l1oip_4bit_free();
return -ENOMEM;
}
/* generate compression table */
i1 = 0;
while (i1 < 256) {
if (ulaw)
c = ulaw_to_4bit[i1];
else
c = alaw_to_4bit[i1];
i2 = 0;
while (i2 < 256) {
table_com[(i1 << 8) | i2] |= (c << 4);
table_com[(i2 << 8) | i1] |= c;
i2++;
}
i1++;
}
/* generate decompression table */
i1 = 0;
while (i1 < 16) {
if (ulaw)
sample = _4bit_to_ulaw[i1];
else
sample = _4bit_to_alaw[i1];
i2 = 0;
while (i2 < 16) {
table_dec[(i1 << 4) | i2] |= (sample << 8);
table_dec[(i2 << 4) | i1] |= sample;
i2++;
}
i1++;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,415 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mISDNhw.h>
#include "core.h"
#include "layer1.h"
#include "fsm.h"
static u_int *debug;
struct layer1 {
u_long Flags;
struct FsmInst l1m;
struct FsmTimer timer3;
struct FsmTimer timerX;
int delay;
int t3_value;
struct dchannel *dch;
dchannel_l1callback *dcb;
};
#define TIMER3_DEFAULT_VALUE 7000
static
struct Fsm l1fsm_s = {NULL, 0, 0, NULL, NULL};
enum {
ST_L1_F2,
ST_L1_F3,
ST_L1_F4,
ST_L1_F5,
ST_L1_F6,
ST_L1_F7,
ST_L1_F8,
};
#define L1S_STATE_COUNT (ST_L1_F8 + 1)
static char *strL1SState[] =
{
"ST_L1_F2",
"ST_L1_F3",
"ST_L1_F4",
"ST_L1_F5",
"ST_L1_F6",
"ST_L1_F7",
"ST_L1_F8",
};
enum {
EV_PH_ACTIVATE,
EV_PH_DEACTIVATE,
EV_RESET_IND,
EV_DEACT_CNF,
EV_DEACT_IND,
EV_POWER_UP,
EV_ANYSIG_IND,
EV_INFO2_IND,
EV_INFO4_IND,
EV_TIMER_DEACT,
EV_TIMER_ACT,
EV_TIMER3,
};
#define L1_EVENT_COUNT (EV_TIMER3 + 1)
static char *strL1Event[] =
{
"EV_PH_ACTIVATE",
"EV_PH_DEACTIVATE",
"EV_RESET_IND",
"EV_DEACT_CNF",
"EV_DEACT_IND",
"EV_POWER_UP",
"EV_ANYSIG_IND",
"EV_INFO2_IND",
"EV_INFO4_IND",
"EV_TIMER_DEACT",
"EV_TIMER_ACT",
"EV_TIMER3",
};
static void
l1m_debug(struct FsmInst *fi, char *fmt, ...)
{
struct layer1 *l1 = fi->userdata;
struct va_format vaf;
va_list va;
va_start(va, fmt);
vaf.fmt = fmt;
vaf.va = &va;
printk(KERN_DEBUG "%s: %pV\n", dev_name(&l1->dch->dev.dev), &vaf);
va_end(va);
}
static void
l1_reset(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_L1_F3);
}
static void
l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F3);
if (test_bit(FLG_L1_ACTIVATING, &l1->Flags))
l1->dcb(l1->dch, HW_POWERUP_REQ);
}
static void
l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F3);
mISDN_FsmRestartTimer(&l1->timerX, 550, EV_TIMER_DEACT, NULL, 2);
test_and_set_bit(FLG_L1_DEACTTIMER, &l1->Flags);
}
static void
l1_power_up_s(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
mISDN_FsmChangeState(fi, ST_L1_F4);
l1->dcb(l1->dch, INFO3_P8);
} else
mISDN_FsmChangeState(fi, ST_L1_F3);
}
static void
l1_go_F5(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_L1_F5);
}
static void
l1_go_F8(struct FsmInst *fi, int event, void *arg)
{
mISDN_FsmChangeState(fi, ST_L1_F8);
}
static void
l1_info2_ind(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F6);
l1->dcb(l1->dch, INFO3_P8);
}
static void
l1_info4_ind(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmChangeState(fi, ST_L1_F7);
l1->dcb(l1->dch, INFO3_P8);
if (test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags))
mISDN_FsmDelTimer(&l1->timerX, 4);
if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) {
if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags))
mISDN_FsmDelTimer(&l1->timer3, 3);
mISDN_FsmRestartTimer(&l1->timerX, 110, EV_TIMER_ACT, NULL, 2);
test_and_set_bit(FLG_L1_ACTTIMER, &l1->Flags);
}
}
static void
l1_timer3(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags);
if (test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
l1->dcb(l1->dch, HW_D_NOBLOCKED);
l1->dcb(l1->dch, PH_DEACTIVATE_IND);
}
if (l1->l1m.state != ST_L1_F6) {
mISDN_FsmChangeState(fi, ST_L1_F3);
/* do not force anything here, we need send INFO 0 */
}
}
static void
l1_timer_act(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags);
test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags);
l1->dcb(l1->dch, PH_ACTIVATE_IND);
}
static void
l1_timer_deact(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
test_and_clear_bit(FLG_L1_DEACTTIMER, &l1->Flags);
test_and_clear_bit(FLG_L1_ACTIVATED, &l1->Flags);
if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
l1->dcb(l1->dch, HW_D_NOBLOCKED);
l1->dcb(l1->dch, PH_DEACTIVATE_IND);
l1->dcb(l1->dch, HW_DEACT_REQ);
}
static void
l1_activate_s(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
mISDN_FsmRestartTimer(&l1->timer3, l1->t3_value, EV_TIMER3, NULL, 2);
test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
/* Tell HW to send INFO 1 */
l1->dcb(l1->dch, HW_RESET_REQ);
}
static void
l1_activate_no(struct FsmInst *fi, int event, void *arg)
{
struct layer1 *l1 = fi->userdata;
if ((!test_bit(FLG_L1_DEACTTIMER, &l1->Flags)) &&
(!test_bit(FLG_L1_T3RUN, &l1->Flags))) {
test_and_clear_bit(FLG_L1_ACTIVATING, &l1->Flags);
if (test_and_clear_bit(FLG_L1_DBLOCKED, &l1->Flags))
l1->dcb(l1->dch, HW_D_NOBLOCKED);
l1->dcb(l1->dch, PH_DEACTIVATE_IND);
}
}
static struct FsmNode L1SFnList[] =
{
{ST_L1_F3, EV_PH_ACTIVATE, l1_activate_s},
{ST_L1_F6, EV_PH_ACTIVATE, l1_activate_no},
{ST_L1_F8, EV_PH_ACTIVATE, l1_activate_no},
{ST_L1_F3, EV_RESET_IND, l1_reset},
{ST_L1_F4, EV_RESET_IND, l1_reset},
{ST_L1_F5, EV_RESET_IND, l1_reset},
{ST_L1_F6, EV_RESET_IND, l1_reset},
{ST_L1_F7, EV_RESET_IND, l1_reset},
{ST_L1_F8, EV_RESET_IND, l1_reset},
{ST_L1_F3, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F4, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F5, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F6, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F7, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F8, EV_DEACT_CNF, l1_deact_cnf},
{ST_L1_F6, EV_DEACT_IND, l1_deact_req_s},
{ST_L1_F7, EV_DEACT_IND, l1_deact_req_s},
{ST_L1_F8, EV_DEACT_IND, l1_deact_req_s},
{ST_L1_F3, EV_POWER_UP, l1_power_up_s},
{ST_L1_F4, EV_ANYSIG_IND, l1_go_F5},
{ST_L1_F6, EV_ANYSIG_IND, l1_go_F8},
{ST_L1_F7, EV_ANYSIG_IND, l1_go_F8},
{ST_L1_F3, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F4, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F5, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F7, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F8, EV_INFO2_IND, l1_info2_ind},
{ST_L1_F3, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F4, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F5, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F6, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F8, EV_INFO4_IND, l1_info4_ind},
{ST_L1_F3, EV_TIMER3, l1_timer3},
{ST_L1_F4, EV_TIMER3, l1_timer3},
{ST_L1_F5, EV_TIMER3, l1_timer3},
{ST_L1_F6, EV_TIMER3, l1_timer3},
{ST_L1_F8, EV_TIMER3, l1_timer3},
{ST_L1_F7, EV_TIMER_ACT, l1_timer_act},
{ST_L1_F3, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F4, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F5, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F6, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F7, EV_TIMER_DEACT, l1_timer_deact},
{ST_L1_F8, EV_TIMER_DEACT, l1_timer_deact},
};
static void
release_l1(struct layer1 *l1) {
mISDN_FsmDelTimer(&l1->timerX, 0);
mISDN_FsmDelTimer(&l1->timer3, 0);
if (l1->dch)
l1->dch->l1 = NULL;
module_put(THIS_MODULE);
kfree(l1);
}
int
l1_event(struct layer1 *l1, u_int event)
{
int err = 0;
if (!l1)
return -EINVAL;
switch (event) {
case HW_RESET_IND:
mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
break;
case HW_DEACT_IND:
mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL);
break;
case HW_POWERUP_IND:
mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL);
break;
case HW_DEACT_CNF:
mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
break;
case ANYSIGNAL:
mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
break;
case LOSTFRAMING:
mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
break;
case INFO2:
mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL);
break;
case INFO4_P8:
mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
break;
case INFO4_P10:
mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
break;
case PH_ACTIVATE_REQ:
if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
l1->dcb(l1->dch, PH_ACTIVATE_IND);
else {
test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags);
mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL);
}
break;
case CLOSE_CHANNEL:
release_l1(l1);
break;
default:
if ((event & ~HW_TIMER3_VMASK) == HW_TIMER3_VALUE) {
int val = event & HW_TIMER3_VMASK;
if (val < 5)
val = 5;
if (val > 30)
val = 30;
l1->t3_value = val;
break;
}
if (*debug & DEBUG_L1)
printk(KERN_DEBUG "%s %x unhandled\n",
__func__, event);
err = -EINVAL;
}
return err;
}
EXPORT_SYMBOL(l1_event);
int
create_l1(struct dchannel *dch, dchannel_l1callback *dcb) {
struct layer1 *nl1;
nl1 = kzalloc_obj(struct layer1, GFP_ATOMIC);
if (!nl1) {
printk(KERN_ERR "kmalloc struct layer1 failed\n");
return -ENOMEM;
}
nl1->l1m.fsm = &l1fsm_s;
nl1->l1m.state = ST_L1_F3;
nl1->Flags = 0;
nl1->t3_value = TIMER3_DEFAULT_VALUE;
nl1->l1m.debug = *debug & DEBUG_L1_FSM;
nl1->l1m.userdata = nl1;
nl1->l1m.userint = 0;
nl1->l1m.printdebug = l1m_debug;
nl1->dch = dch;
nl1->dcb = dcb;
mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer3);
mISDN_FsmInitTimer(&nl1->l1m, &nl1->timerX);
__module_get(THIS_MODULE);
dch->l1 = nl1;
return 0;
}
EXPORT_SYMBOL(create_l1);
int
Isdnl1_Init(u_int *deb)
{
debug = deb;
l1fsm_s.state_count = L1S_STATE_COUNT;
l1fsm_s.event_count = L1_EVENT_COUNT;
l1fsm_s.strEvent = strL1Event;
l1fsm_s.strState = strL1SState;
return mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
}
void
Isdnl1_cleanup(void)
{
mISDN_FsmFree(&l1fsm_s);
}

View File

@@ -1,16 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* Layer 1 defines
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#define FLG_L1_ACTIVATING 1
#define FLG_L1_ACTIVATED 2
#define FLG_L1_DEACTTIMER 3
#define FLG_L1_ACTTIMER 4
#define FLG_L1_T3RUN 5
#define FLG_L1_PULL_REQ 6
#define FLG_L1_UINT 7
#define FLG_L1_DBLOCKED 8

File diff suppressed because it is too large Load Diff

View File

@@ -1,131 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Layer 2 defines
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/mISDNif.h>
#include <linux/skbuff.h>
#include "fsm.h"
#define MAX_WINDOW 8
struct manager {
struct mISDNchannel ch;
struct mISDNchannel bcast;
u_long options;
struct list_head layer2;
rwlock_t lock;
struct FsmInst deact;
struct FsmTimer datimer;
struct sk_buff_head sendq;
struct mISDNchannel *up;
u_int nextid;
u_int lastid;
};
struct teimgr {
int ri;
int rcnt;
struct FsmInst tei_m;
struct FsmTimer timer;
int tval, nval;
struct layer2 *l2;
struct manager *mgr;
};
struct laddr {
u_char A;
u_char B;
};
struct layer2 {
struct list_head list;
struct mISDNchannel ch;
u_long flag;
int id;
struct mISDNchannel *up;
signed char sapi;
signed char tei;
struct laddr addr;
u_int maxlen;
struct teimgr *tm;
u_int vs, va, vr;
int rc;
u_int window;
u_int sow;
struct FsmInst l2m;
struct FsmTimer t200, t203;
int T200, N200, T203;
u_int next_id;
u_int down_id;
struct sk_buff *windowar[MAX_WINDOW];
struct sk_buff_head i_queue;
struct sk_buff_head ui_queue;
struct sk_buff_head down_queue;
struct sk_buff_head tmp_queue;
};
enum {
ST_L2_1,
ST_L2_2,
ST_L2_3,
ST_L2_4,
ST_L2_5,
ST_L2_6,
ST_L2_7,
ST_L2_8,
};
#define L2_STATE_COUNT (ST_L2_8 + 1)
extern struct layer2 *create_l2(struct mISDNchannel *, u_int,
u_long, int, int);
extern int tei_l2(struct layer2 *, u_int, u_long arg);
/* from tei.c */
extern int l2_tei(struct layer2 *, u_int, u_long arg);
extern void TEIrelease(struct layer2 *);
extern int TEIInit(u_int *);
extern void TEIFree(void);
#define MAX_L2HEADER_LEN 4
#define RR 0x01
#define RNR 0x05
#define REJ 0x09
#define SABME 0x6f
#define SABM 0x2f
#define DM 0x0f
#define UI 0x03
#define DISC 0x43
#define UA 0x63
#define FRMR 0x87
#define XID 0xaf
#define CMD 0
#define RSP 1
#define LC_FLUSH_WAIT 1
#define FLG_LAPB 0
#define FLG_LAPD 1
#define FLG_ORIG 2
#define FLG_MOD128 3
#define FLG_PEND_REL 4
#define FLG_L3_INIT 5
#define FLG_T200_RUN 6
#define FLG_ACK_PEND 7
#define FLG_REJEXC 8
#define FLG_OWN_BUSY 9
#define FLG_PEER_BUSY 10
#define FLG_DCHAN_BUSY 11
#define FLG_L1_ACTIV 12
#define FLG_ESTAB_PEND 13
#define FLG_PTP 14
#define FLG_FIXED_TEI 15
#define FLG_L2BLOCK 16
#define FLG_L1_NOTREADY 17
#define FLG_LAPD_NET 18

View File

@@ -1,825 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/mISDNif.h>
#include <linux/slab.h>
#include <linux/export.h>
#include "core.h"
static u_int *debug;
static struct proto mISDN_proto = {
.name = "misdn",
.owner = THIS_MODULE,
.obj_size = sizeof(struct mISDN_sock)
};
#define _pms(sk) ((struct mISDN_sock *)sk)
static struct mISDN_sock_list data_sockets = {
.lock = __RW_LOCK_UNLOCKED(data_sockets.lock)
};
static struct mISDN_sock_list base_sockets = {
.lock = __RW_LOCK_UNLOCKED(base_sockets.lock)
};
#define L2_HEADER_LEN 4
static inline struct sk_buff *
_l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
{
struct sk_buff *skb;
skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
if (likely(skb))
skb_reserve(skb, L2_HEADER_LEN);
return skb;
}
static void
mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk)
{
write_lock_bh(&l->lock);
sk_add_node(sk, &l->head);
write_unlock_bh(&l->lock);
}
static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk)
{
write_lock_bh(&l->lock);
sk_del_node_init(sk);
write_unlock_bh(&l->lock);
}
static int
mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
{
struct mISDN_sock *msk;
int err;
msk = container_of(ch, struct mISDN_sock, ch);
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
if (msk->sk.sk_state == MISDN_CLOSED)
return -EUNATCH;
__net_timestamp(skb);
err = sock_queue_rcv_skb(&msk->sk, skb);
if (err)
printk(KERN_WARNING "%s: error %d\n", __func__, err);
return err;
}
static int
mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
{
struct mISDN_sock *msk;
msk = container_of(ch, struct mISDN_sock, ch);
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
switch (cmd) {
case CLOSE_CHANNEL:
msk->sk.sk_state = MISDN_CLOSED;
break;
}
return 0;
}
static inline void
mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
{
struct __kernel_old_timeval tv;
if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
skb_get_timestamp(skb, &tv);
put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
}
}
static int
mISDN_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
int flags)
{
struct sk_buff *skb;
struct sock *sk = sock->sk;
int copied, err;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
__func__, (int)len, flags, _pms(sk)->ch.nr,
sk->sk_protocol);
if (flags & (MSG_OOB))
return -EOPNOTSUPP;
if (sk->sk_state == MISDN_CLOSED)
return 0;
skb = skb_recv_datagram(sk, flags, &err);
if (!skb)
return err;
if (msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);
maddr->family = AF_ISDN;
maddr->dev = _pms(sk)->dev->id;
if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
(sk->sk_protocol == ISDN_P_LAPD_NT)) {
maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff;
maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
} else {
maddr->channel = _pms(sk)->ch.nr;
maddr->sapi = _pms(sk)->ch.addr & 0xFF;
maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF;
}
msg->msg_namelen = sizeof(*maddr);
}
copied = skb->len + MISDN_HEADER_LEN;
if (len < copied) {
if (flags & MSG_PEEK)
refcount_dec(&skb->users);
else
skb_queue_head(&sk->sk_receive_queue, skb);
return -ENOSPC;
}
memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
MISDN_HEADER_LEN);
err = skb_copy_datagram_msg(skb, 0, msg, copied);
mISDN_sock_cmsg(sk, msg, skb);
skb_free_datagram(sk, skb);
return err ? : copied;
}
static int
mISDN_sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
struct sk_buff *skb;
int err = -ENOMEM;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
__func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
sk->sk_protocol);
if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE))
return -EINVAL;
if (len < MISDN_HEADER_LEN)
return -EINVAL;
if (sk->sk_state != MISDN_BOUND)
return -EBADFD;
lock_sock(sk);
skb = _l2_alloc_skb(len, GFP_KERNEL);
if (!skb)
goto done;
if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
goto done;
}
memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
skb_pull(skb, MISDN_HEADER_LEN);
if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
/* if we have a address, we use it */
DECLARE_SOCKADDR(struct sockaddr_mISDN *, maddr, msg->msg_name);
mISDN_HEAD_ID(skb) = maddr->channel;
} else { /* use default for L2 messages */
if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
(sk->sk_protocol == ISDN_P_LAPD_NT))
mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
}
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: ID:%x\n",
__func__, mISDN_HEAD_ID(skb));
err = -ENODEV;
if (!_pms(sk)->ch.peer)
goto done;
err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb);
if (err)
goto done;
else {
skb = NULL;
err = len;
}
done:
kfree_skb(skb);
release_sock(sk);
return err;
}
static int
data_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
if (!sk)
return 0;
switch (sk->sk_protocol) {
case ISDN_P_TE_S0:
case ISDN_P_NT_S0:
case ISDN_P_TE_E1:
case ISDN_P_NT_E1:
if (sk->sk_state == MISDN_BOUND)
delete_channel(&_pms(sk)->ch);
else
mISDN_sock_unlink(&data_sockets, sk);
break;
case ISDN_P_LAPD_TE:
case ISDN_P_LAPD_NT:
case ISDN_P_B_RAW:
case ISDN_P_B_HDLC:
case ISDN_P_B_X75SLP:
case ISDN_P_B_L2DTMF:
case ISDN_P_B_L2DSP:
case ISDN_P_B_L2DSPHDLC:
delete_channel(&_pms(sk)->ch);
mISDN_sock_unlink(&data_sockets, sk);
break;
}
lock_sock(sk);
sock_orphan(sk);
skb_queue_purge(&sk->sk_receive_queue);
release_sock(sk);
sock_put(sk);
return 0;
}
static int
data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p)
{
struct mISDN_ctrl_req cq;
int err = -EINVAL, val[2];
struct mISDNchannel *bchan, *next;
lock_sock(sk);
if (!_pms(sk)->dev) {
err = -ENODEV;
goto done;
}
switch (cmd) {
case IMCTRLREQ:
if (copy_from_user(&cq, p, sizeof(cq))) {
err = -EFAULT;
break;
}
if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
list_for_each_entry_safe(bchan, next,
&_pms(sk)->dev->bchannels, list) {
if (bchan->nr == cq.channel) {
err = bchan->ctrl(bchan,
CONTROL_CHANNEL, &cq);
break;
}
}
} else
err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D,
CONTROL_CHANNEL, &cq);
if (err)
break;
if (copy_to_user(p, &cq, sizeof(cq)))
err = -EFAULT;
break;
case IMCLEAR_L2:
if (sk->sk_protocol != ISDN_P_LAPD_NT) {
err = -EINVAL;
break;
}
val[0] = cmd;
if (get_user(val[1], (int __user *)p)) {
err = -EFAULT;
break;
}
err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
CONTROL_CHANNEL, val);
break;
case IMHOLD_L1:
if (sk->sk_protocol != ISDN_P_LAPD_NT
&& sk->sk_protocol != ISDN_P_LAPD_TE) {
err = -EINVAL;
break;
}
val[0] = cmd;
if (get_user(val[1], (int __user *)p)) {
err = -EFAULT;
break;
}
err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
CONTROL_CHANNEL, val);
break;
default:
err = -EINVAL;
break;
}
done:
release_sock(sk);
return err;
}
static int
data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
int err = 0, id;
struct sock *sk = sock->sk;
struct mISDNdevice *dev;
struct mISDNversion ver;
switch (cmd) {
case IMGETVERSION:
ver.major = MISDN_MAJOR_VERSION;
ver.minor = MISDN_MINOR_VERSION;
ver.release = MISDN_RELEASE;
if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
err = -EFAULT;
break;
case IMGETCOUNT:
id = get_mdevice_count();
if (put_user(id, (int __user *)arg))
err = -EFAULT;
break;
case IMGETDEVINFO:
if (get_user(id, (int __user *)arg)) {
err = -EFAULT;
break;
}
dev = get_mdevice(id);
if (dev) {
struct mISDN_devinfo di;
memset(&di, 0, sizeof(di));
di.id = dev->id;
di.Dprotocols = dev->Dprotocols;
di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
di.protocol = dev->D.protocol;
memcpy(di.channelmap, dev->channelmap,
sizeof(di.channelmap));
di.nrbchan = dev->nrbchan;
strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
if (copy_to_user((void __user *)arg, &di, sizeof(di)))
err = -EFAULT;
} else
err = -ENODEV;
break;
default:
if (sk->sk_state == MISDN_BOUND)
err = data_sock_ioctl_bound(sk, cmd,
(void __user *)arg);
else
err = -ENOTCONN;
}
return err;
}
static int data_sock_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err = 0, opt = 0;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s(%p, %d, %x, optval, %d)\n", __func__, sock,
level, optname, optlen);
lock_sock(sk);
switch (optname) {
case MISDN_TIME_STAMP:
err = copy_safe_from_sockptr(&opt, sizeof(opt),
optval, optlen);
if (err)
break;
if (opt)
_pms(sk)->cmask |= MISDN_TIME_STAMP;
else
_pms(sk)->cmask &= ~MISDN_TIME_STAMP;
break;
default:
err = -ENOPROTOOPT;
break;
}
release_sock(sk);
return err;
}
static int data_sock_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
int len, opt;
if (get_user(len, optlen))
return -EFAULT;
if (len != sizeof(char))
return -EINVAL;
switch (optname) {
case MISDN_TIME_STAMP:
if (_pms(sk)->cmask & MISDN_TIME_STAMP)
opt = 1;
else
opt = 0;
if (put_user(opt, optval))
return -EFAULT;
break;
default:
return -ENOPROTOOPT;
}
return 0;
}
static int
data_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct sock *sk = sock->sk;
struct sock *csk;
int err = 0;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
if (addr_len != sizeof(struct sockaddr_mISDN))
return -EINVAL;
if (!maddr || maddr->family != AF_ISDN)
return -EINVAL;
lock_sock(sk);
if (_pms(sk)->dev) {
err = -EALREADY;
goto done;
}
_pms(sk)->dev = get_mdevice(maddr->dev);
if (!_pms(sk)->dev) {
err = -ENODEV;
goto done;
}
if (sk->sk_protocol < ISDN_P_B_START) {
read_lock_bh(&data_sockets.lock);
sk_for_each(csk, &data_sockets.head) {
if (sk == csk)
continue;
if (_pms(csk)->dev != _pms(sk)->dev)
continue;
if (csk->sk_protocol >= ISDN_P_B_START)
continue;
if (IS_ISDN_P_TE(csk->sk_protocol)
== IS_ISDN_P_TE(sk->sk_protocol))
continue;
read_unlock_bh(&data_sockets.lock);
err = -EBUSY;
goto done;
}
read_unlock_bh(&data_sockets.lock);
}
_pms(sk)->ch.send = mISDN_send;
_pms(sk)->ch.ctrl = mISDN_ctrl;
switch (sk->sk_protocol) {
case ISDN_P_TE_S0:
case ISDN_P_NT_S0:
case ISDN_P_TE_E1:
case ISDN_P_NT_E1:
mISDN_sock_unlink(&data_sockets, sk);
err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch,
sk->sk_protocol, maddr);
if (err)
mISDN_sock_link(&data_sockets, sk);
break;
case ISDN_P_LAPD_TE:
case ISDN_P_LAPD_NT:
err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch,
sk->sk_protocol, maddr);
break;
case ISDN_P_B_RAW:
case ISDN_P_B_HDLC:
case ISDN_P_B_X75SLP:
case ISDN_P_B_L2DTMF:
case ISDN_P_B_L2DSP:
case ISDN_P_B_L2DSPHDLC:
err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch,
sk->sk_protocol, maddr);
break;
default:
err = -EPROTONOSUPPORT;
}
if (err)
goto done;
sk->sk_state = MISDN_BOUND;
_pms(sk)->ch.protocol = sk->sk_protocol;
done:
release_sock(sk);
return err;
}
static int
data_sock_getname(struct socket *sock, struct sockaddr *addr,
int peer)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct sock *sk = sock->sk;
if (!_pms(sk)->dev)
return -EBADFD;
lock_sock(sk);
maddr->family = AF_ISDN;
maddr->dev = _pms(sk)->dev->id;
maddr->channel = _pms(sk)->ch.nr;
maddr->sapi = _pms(sk)->ch.addr & 0xff;
maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
release_sock(sk);
return sizeof(*maddr);
}
static const struct proto_ops data_sock_ops = {
.family = PF_ISDN,
.owner = THIS_MODULE,
.release = data_sock_release,
.ioctl = data_sock_ioctl,
.bind = data_sock_bind,
.getname = data_sock_getname,
.sendmsg = mISDN_sock_sendmsg,
.recvmsg = mISDN_sock_recvmsg,
.poll = datagram_poll,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = data_sock_setsockopt,
.getsockopt = data_sock_getsockopt,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
static int
data_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
{
struct sock *sk;
if (sock->type != SOCK_DGRAM)
return -ESOCKTNOSUPPORT;
sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &data_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = MISDN_OPEN;
mISDN_sock_link(&data_sockets, sk);
return 0;
}
static int
base_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
if (!sk)
return 0;
mISDN_sock_unlink(&base_sockets, sk);
sock_orphan(sk);
sock_put(sk);
return 0;
}
static int
base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
int err = 0, id;
struct mISDNdevice *dev;
struct mISDNversion ver;
switch (cmd) {
case IMGETVERSION:
ver.major = MISDN_MAJOR_VERSION;
ver.minor = MISDN_MINOR_VERSION;
ver.release = MISDN_RELEASE;
if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
err = -EFAULT;
break;
case IMGETCOUNT:
id = get_mdevice_count();
if (put_user(id, (int __user *)arg))
err = -EFAULT;
break;
case IMGETDEVINFO:
if (get_user(id, (int __user *)arg)) {
err = -EFAULT;
break;
}
dev = get_mdevice(id);
if (dev) {
struct mISDN_devinfo di;
memset(&di, 0, sizeof(di));
di.id = dev->id;
di.Dprotocols = dev->Dprotocols;
di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
di.protocol = dev->D.protocol;
memcpy(di.channelmap, dev->channelmap,
sizeof(di.channelmap));
di.nrbchan = dev->nrbchan;
strscpy(di.name, dev_name(&dev->dev), sizeof(di.name));
if (copy_to_user((void __user *)arg, &di, sizeof(di)))
err = -EFAULT;
} else
err = -ENODEV;
break;
case IMSETDEVNAME:
{
struct mISDN_devrename dn;
if (copy_from_user(&dn, (void __user *)arg,
sizeof(dn))) {
err = -EFAULT;
break;
}
dn.name[sizeof(dn.name) - 1] = '\0';
dev = get_mdevice(dn.id);
if (dev)
err = device_rename(&dev->dev, dn.name);
else
err = -ENODEV;
}
break;
default:
err = -EINVAL;
}
return err;
}
static int
base_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct sock *sk = sock->sk;
int err = 0;
if (addr_len < sizeof(struct sockaddr_mISDN))
return -EINVAL;
if (!maddr || maddr->family != AF_ISDN)
return -EINVAL;
lock_sock(sk);
if (_pms(sk)->dev) {
err = -EALREADY;
goto done;
}
_pms(sk)->dev = get_mdevice(maddr->dev);
if (!_pms(sk)->dev) {
err = -ENODEV;
goto done;
}
sk->sk_state = MISDN_BOUND;
done:
release_sock(sk);
return err;
}
static const struct proto_ops base_sock_ops = {
.family = PF_ISDN,
.owner = THIS_MODULE,
.release = base_sock_release,
.ioctl = base_sock_ioctl,
.bind = base_sock_bind,
.getname = sock_no_getname,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
static int
base_sock_create(struct net *net, struct socket *sock, int protocol, int kern)
{
struct sock *sk;
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
if (!capable(CAP_NET_RAW))
return -EPERM;
sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto, kern);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &base_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = MISDN_OPEN;
mISDN_sock_link(&base_sockets, sk);
return 0;
}
static int
mISDN_sock_create(struct net *net, struct socket *sock, int proto, int kern)
{
int err = -EPROTONOSUPPORT;
switch (proto) {
case ISDN_P_BASE:
err = base_sock_create(net, sock, proto, kern);
break;
case ISDN_P_TE_S0:
case ISDN_P_NT_S0:
case ISDN_P_TE_E1:
case ISDN_P_NT_E1:
case ISDN_P_LAPD_TE:
case ISDN_P_LAPD_NT:
case ISDN_P_B_RAW:
case ISDN_P_B_HDLC:
case ISDN_P_B_X75SLP:
case ISDN_P_B_L2DTMF:
case ISDN_P_B_L2DSP:
case ISDN_P_B_L2DSPHDLC:
err = data_sock_create(net, sock, proto, kern);
break;
default:
return err;
}
return err;
}
static const struct net_proto_family mISDN_sock_family_ops = {
.owner = THIS_MODULE,
.family = PF_ISDN,
.create = mISDN_sock_create,
};
int
misdn_sock_init(u_int *deb)
{
int err;
debug = deb;
err = sock_register(&mISDN_sock_family_ops);
if (err)
printk(KERN_ERR "%s: error(%d)\n", __func__, err);
return err;
}
void
misdn_sock_cleanup(void)
{
sock_unregister(PF_ISDN);
}

View File

@@ -1,654 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/slab.h>
#include <linux/mISDNif.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/sched/cputime.h>
#include <linux/signal.h>
#include "core.h"
static u_int *debug;
static inline void
_queue_message(struct mISDNstack *st, struct sk_buff *skb)
{
struct mISDNhead *hh = mISDN_HEAD_P(skb);
if (*debug & DEBUG_QUEUE_FUNC)
printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
__func__, hh->prim, hh->id, skb);
skb_queue_tail(&st->msgq, skb);
if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) {
test_and_set_bit(mISDN_STACK_WORK, &st->status);
wake_up_interruptible(&st->workq);
}
}
static int
mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
{
_queue_message(ch->st, skb);
return 0;
}
static struct mISDNchannel *
get_channel4id(struct mISDNstack *st, u_int id)
{
struct mISDNchannel *ch;
mutex_lock(&st->lmutex);
list_for_each_entry(ch, &st->layer2, list) {
if (id == ch->nr)
goto unlock;
}
ch = NULL;
unlock:
mutex_unlock(&st->lmutex);
return ch;
}
static void
send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb)
{
struct sock *sk;
struct sk_buff *cskb = NULL;
read_lock(&sl->lock);
sk_for_each(sk, &sl->head) {
if (sk->sk_state != MISDN_BOUND)
continue;
if (!cskb)
cskb = skb_copy(skb, GFP_ATOMIC);
if (!cskb) {
printk(KERN_WARNING "%s no skb\n", __func__);
break;
}
if (!sock_queue_rcv_skb(sk, cskb))
cskb = NULL;
}
read_unlock(&sl->lock);
dev_kfree_skb(cskb);
}
static void
send_layer2(struct mISDNstack *st, struct sk_buff *skb)
{
struct sk_buff *cskb;
struct mISDNhead *hh = mISDN_HEAD_P(skb);
struct mISDNchannel *ch;
int ret;
if (!st)
return;
mutex_lock(&st->lmutex);
if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */
list_for_each_entry(ch, &st->layer2, list) {
if (list_is_last(&ch->list, &st->layer2)) {
cskb = skb;
skb = NULL;
} else {
cskb = skb_copy(skb, GFP_KERNEL);
}
if (cskb) {
ret = ch->send(ch, cskb);
if (ret) {
if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s ch%d prim(%x) addr(%x)"
" err %d\n",
__func__, ch->nr,
hh->prim, ch->addr, ret);
dev_kfree_skb(cskb);
}
} else {
printk(KERN_WARNING "%s ch%d addr %x no mem\n",
__func__, ch->nr, ch->addr);
goto out;
}
}
} else {
list_for_each_entry(ch, &st->layer2, list) {
if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) {
ret = ch->send(ch, skb);
if (!ret)
skb = NULL;
goto out;
}
}
ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb);
if (!ret)
skb = NULL;
else if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s mgr prim(%x) err %d\n",
__func__, hh->prim, ret);
}
out:
mutex_unlock(&st->lmutex);
dev_kfree_skb(skb);
}
static inline int
send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
{
struct mISDNhead *hh = mISDN_HEAD_P(skb);
struct mISDNchannel *ch;
int lm;
lm = hh->prim & MISDN_LAYERMASK;
if (*debug & DEBUG_QUEUE_FUNC)
printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
__func__, hh->prim, hh->id, skb);
if (lm == 0x1) {
if (!hlist_empty(&st->l1sock.head)) {
__net_timestamp(skb);
send_socklist(&st->l1sock, skb);
}
return st->layer1->send(st->layer1, skb);
} else if (lm == 0x2) {
if (!hlist_empty(&st->l1sock.head))
send_socklist(&st->l1sock, skb);
send_layer2(st, skb);
return 0;
} else if (lm == 0x4) {
ch = get_channel4id(st, hh->id);
if (ch)
return ch->send(ch, skb);
else
printk(KERN_WARNING
"%s: dev(%s) prim(%x) id(%x) no channel\n",
__func__, dev_name(&st->dev->dev), hh->prim,
hh->id);
} else if (lm == 0x8) {
WARN_ON(lm == 0x8);
ch = get_channel4id(st, hh->id);
if (ch)
return ch->send(ch, skb);
else
printk(KERN_WARNING
"%s: dev(%s) prim(%x) id(%x) no channel\n",
__func__, dev_name(&st->dev->dev), hh->prim,
hh->id);
} else {
/* broadcast not handled yet */
printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
__func__, dev_name(&st->dev->dev), hh->prim);
}
return -ESRCH;
}
static void
do_clear_stack(struct mISDNstack *st)
{
}
static int
mISDNStackd(void *data)
{
struct mISDNstack *st = data;
#ifdef MISDN_MSG_STATS
u64 utime, stime;
#endif
int err = 0;
sigfillset(&current->blocked);
if (*debug & DEBUG_MSG_THREAD)
printk(KERN_DEBUG "mISDNStackd %s started\n",
dev_name(&st->dev->dev));
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
for (;;) {
struct sk_buff *skb;
if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) {
test_and_clear_bit(mISDN_STACK_WORK, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
} else
test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
while (test_bit(mISDN_STACK_WORK, &st->status)) {
skb = skb_dequeue(&st->msgq);
if (!skb) {
test_and_clear_bit(mISDN_STACK_WORK,
&st->status);
/* test if a race happens */
skb = skb_dequeue(&st->msgq);
if (!skb)
continue;
test_and_set_bit(mISDN_STACK_WORK,
&st->status);
}
#ifdef MISDN_MSG_STATS
st->msg_cnt++;
#endif
err = send_msg_to_layer(st, skb);
if (unlikely(err)) {
if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s: %s prim(%x) id(%x) "
"send call(%d)\n",
__func__, dev_name(&st->dev->dev),
mISDN_HEAD_PRIM(skb),
mISDN_HEAD_ID(skb), err);
dev_kfree_skb(skb);
continue;
}
if (unlikely(test_bit(mISDN_STACK_STOPPED,
&st->status))) {
test_and_clear_bit(mISDN_STACK_WORK,
&st->status);
test_and_clear_bit(mISDN_STACK_RUNNING,
&st->status);
break;
}
}
if (test_bit(mISDN_STACK_CLEARING, &st->status)) {
test_and_set_bit(mISDN_STACK_STOPPED, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
do_clear_stack(st);
test_and_clear_bit(mISDN_STACK_CLEARING, &st->status);
test_and_set_bit(mISDN_STACK_RESTART, &st->status);
}
if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) {
test_and_clear_bit(mISDN_STACK_STOPPED, &st->status);
test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
if (!skb_queue_empty(&st->msgq))
test_and_set_bit(mISDN_STACK_WORK,
&st->status);
}
if (test_bit(mISDN_STACK_ABORT, &st->status))
break;
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
#ifdef MISDN_MSG_STATS
st->sleep_cnt++;
#endif
test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
wait_event_interruptible(st->workq, (st->status &
mISDN_STACK_ACTION_MASK));
if (*debug & DEBUG_MSG_THREAD)
printk(KERN_DEBUG "%s: %s wake status %08lx\n",
__func__, dev_name(&st->dev->dev), st->status);
test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
if (test_bit(mISDN_STACK_STOPPED, &st->status)) {
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
#ifdef MISDN_MSG_STATS
st->stopped_cnt++;
#endif
}
}
#ifdef MISDN_MSG_STATS
printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
"msg %d sleep %d stopped\n",
dev_name(&st->dev->dev), st->msg_cnt, st->sleep_cnt,
st->stopped_cnt);
task_cputime(st->thread, &utime, &stime);
printk(KERN_DEBUG
"mISDNStackd daemon for %s utime(%llu) stime(%llu)\n",
dev_name(&st->dev->dev), utime, stime);
printk(KERN_DEBUG
"mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
dev_name(&st->dev->dev), st->thread->nvcsw, st->thread->nivcsw);
printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
dev_name(&st->dev->dev));
#endif
test_and_set_bit(mISDN_STACK_KILLED, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
test_and_clear_bit(mISDN_STACK_ABORT, &st->status);
skb_queue_purge(&st->msgq);
st->thread = NULL;
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
return 0;
}
static int
l1_receive(struct mISDNchannel *ch, struct sk_buff *skb)
{
if (!ch->st)
return -ENODEV;
__net_timestamp(skb);
_queue_message(ch->st, skb);
return 0;
}
void
set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei)
{
ch->addr = sapi | (tei << 8);
}
void
__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
{
list_add_tail(&ch->list, &st->layer2);
}
void
add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
{
mutex_lock(&st->lmutex);
__add_layer2(ch, st);
mutex_unlock(&st->lmutex);
}
static int
st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
{
if (!ch->st || !ch->st->layer1)
return -EINVAL;
return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg);
}
int
create_stack(struct mISDNdevice *dev)
{
struct mISDNstack *newst;
int err;
DECLARE_COMPLETION_ONSTACK(done);
newst = kzalloc_obj(struct mISDNstack);
if (!newst) {
printk(KERN_ERR "kmalloc mISDN_stack failed\n");
return -ENOMEM;
}
newst->dev = dev;
INIT_LIST_HEAD(&newst->layer2);
INIT_HLIST_HEAD(&newst->l1sock.head);
rwlock_init(&newst->l1sock.lock);
init_waitqueue_head(&newst->workq);
skb_queue_head_init(&newst->msgq);
mutex_init(&newst->lmutex);
dev->D.st = newst;
err = create_teimanager(dev);
if (err) {
printk(KERN_ERR "kmalloc teimanager failed\n");
kfree(newst);
return err;
}
dev->teimgr->peer = &newst->own;
dev->teimgr->recv = mISDN_queue_message;
dev->teimgr->st = newst;
newst->layer1 = &dev->D;
dev->D.recv = l1_receive;
dev->D.peer = &newst->own;
newst->own.st = newst;
newst->own.ctrl = st_own_ctrl;
newst->own.send = mISDN_queue_message;
newst->own.recv = mISDN_queue_message;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s)\n", __func__,
dev_name(&newst->dev->dev));
newst->notify = &done;
newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
dev_name(&newst->dev->dev));
if (IS_ERR(newst->thread)) {
err = PTR_ERR(newst->thread);
printk(KERN_ERR
"mISDN:cannot create kernel thread for %s (%d)\n",
dev_name(&newst->dev->dev), err);
delete_teimanager(dev->teimgr);
kfree(newst);
} else
wait_for_completion(&done);
return err;
}
int
connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
struct channel_req rq;
int err;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(&dev->dev), protocol, adr->dev,
adr->channel, adr->sapi, adr->tei);
switch (protocol) {
case ISDN_P_NT_S0:
case ISDN_P_NT_E1:
case ISDN_P_TE_S0:
case ISDN_P_TE_E1:
ch->recv = mISDN_queue_message;
ch->peer = &dev->D.st->own;
ch->st = dev->D.st;
rq.protocol = protocol;
rq.adr.channel = adr->channel;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err,
dev->id);
if (err)
return err;
write_lock_bh(&dev->D.st->l1sock.lock);
sk_add_node(&msk->sk, &dev->D.st->l1sock.head);
write_unlock_bh(&dev->D.st->l1sock.lock);
break;
default:
return -ENOPROTOOPT;
}
return 0;
}
int
connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct channel_req rq, rq2;
int pmask, err;
struct Bprotocol *bp;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(&dev->dev), protocol,
adr->dev, adr->channel, adr->sapi,
adr->tei);
ch->st = dev->D.st;
pmask = 1 << (protocol & ISDN_P_B_MASK);
if (pmask & dev->Bprotocols) {
rq.protocol = protocol;
rq.adr = *adr;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
if (err)
return err;
ch->recv = rq.ch->send;
ch->peer = rq.ch;
rq.ch->recv = ch->send;
rq.ch->peer = ch;
rq.ch->st = dev->D.st;
} else {
bp = get_Bprotocol4mask(pmask);
if (!bp)
return -ENOPROTOOPT;
rq2.protocol = protocol;
rq2.adr = *adr;
rq2.ch = ch;
err = bp->create(&rq2);
if (err)
return err;
ch->recv = rq2.ch->send;
ch->peer = rq2.ch;
rq2.ch->st = dev->D.st;
rq.protocol = rq2.protocol;
rq.adr = *adr;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
if (err) {
rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL);
return err;
}
rq2.ch->recv = rq.ch->send;
rq2.ch->peer = rq.ch;
rq.ch->recv = rq2.ch->send;
rq.ch->peer = rq2.ch;
rq.ch->st = dev->D.st;
}
ch->protocol = protocol;
ch->nr = rq.ch->nr;
return 0;
}
int
create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct channel_req rq;
int err;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(&dev->dev), protocol,
adr->dev, adr->channel, adr->sapi,
adr->tei);
rq.protocol = ISDN_P_TE_S0;
if (dev->Dprotocols & (1 << ISDN_P_TE_E1))
rq.protocol = ISDN_P_TE_E1;
switch (protocol) {
case ISDN_P_LAPD_NT:
rq.protocol = ISDN_P_NT_S0;
if (dev->Dprotocols & (1 << ISDN_P_NT_E1))
rq.protocol = ISDN_P_NT_E1;
fallthrough;
case ISDN_P_LAPD_TE:
ch->recv = mISDN_queue_message;
ch->peer = &dev->D.st->own;
ch->st = dev->D.st;
rq.adr.channel = 0;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
if (err)
break;
rq.protocol = protocol;
rq.adr = *adr;
rq.ch = ch;
err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err);
if (!err) {
if ((protocol == ISDN_P_LAPD_NT) && !rq.ch)
break;
add_layer2(rq.ch, dev->D.st);
rq.ch->recv = mISDN_queue_message;
rq.ch->peer = &dev->D.st->own;
rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */
}
break;
default:
err = -EPROTONOSUPPORT;
}
return err;
}
void
delete_channel(struct mISDNchannel *ch)
{
struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
struct mISDNchannel *pch;
if (!ch->st) {
printk(KERN_WARNING "%s: no stack\n", __func__);
return;
}
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
dev_name(&ch->st->dev->dev), ch->protocol);
if (ch->protocol >= ISDN_P_B_START) {
if (ch->peer) {
ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL);
ch->peer = NULL;
}
return;
}
switch (ch->protocol) {
case ISDN_P_NT_S0:
case ISDN_P_TE_S0:
case ISDN_P_NT_E1:
case ISDN_P_TE_E1:
write_lock_bh(&ch->st->l1sock.lock);
sk_del_node_init(&msk->sk);
write_unlock_bh(&ch->st->l1sock.lock);
ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL);
break;
case ISDN_P_LAPD_TE:
pch = get_channel4id(ch->st, ch->nr);
if (pch) {
mutex_lock(&ch->st->lmutex);
list_del(&pch->list);
mutex_unlock(&ch->st->lmutex);
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
pch = ch->st->dev->teimgr;
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
} else
printk(KERN_WARNING "%s: no l2 channel\n",
__func__);
break;
case ISDN_P_LAPD_NT:
pch = ch->st->dev->teimgr;
if (pch) {
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
} else
printk(KERN_WARNING "%s: no l2 channel\n",
__func__);
break;
default:
break;
}
return;
}
void
delete_stack(struct mISDNdevice *dev)
{
struct mISDNstack *st = dev->D.st;
DECLARE_COMPLETION_ONSTACK(done);
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s)\n", __func__,
dev_name(&st->dev->dev));
if (dev->teimgr)
delete_teimanager(dev->teimgr);
if (st->thread) {
if (st->notify) {
printk(KERN_WARNING "%s: notifier in use\n",
__func__);
complete(st->notify);
}
st->notify = &done;
test_and_set_bit(mISDN_STACK_ABORT, &st->status);
test_and_set_bit(mISDN_STACK_WAKEUP, &st->status);
wake_up_interruptible(&st->workq);
wait_for_completion(&done);
}
if (!list_empty(&st->layer2))
printk(KERN_WARNING "%s: layer2 list not empty\n",
__func__);
if (!hlist_empty(&st->l1sock.head))
printk(KERN_WARNING "%s: layer1 list not empty\n",
__func__);
kfree(st);
}
void
mISDN_initstack(u_int *dp)
{
debug = dp;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,295 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* general timer device for using in ISDN stacks
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include <linux/poll.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mISDNif.h>
#include <linux/mutex.h>
#include <linux/sched/signal.h>
#include "core.h"
static DEFINE_MUTEX(mISDN_mutex);
static u_int *debug;
struct mISDNtimerdev {
int next_id;
struct list_head pending;
struct list_head expired;
wait_queue_head_t wait;
u_int work;
spinlock_t lock; /* protect lists */
};
struct mISDNtimer {
struct list_head list;
struct mISDNtimerdev *dev;
struct timer_list tl;
int id;
};
static int
mISDN_open(struct inode *ino, struct file *filep)
{
struct mISDNtimerdev *dev;
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
dev = kmalloc_obj(struct mISDNtimerdev);
if (!dev)
return -ENOMEM;
dev->next_id = 1;
INIT_LIST_HEAD(&dev->pending);
INIT_LIST_HEAD(&dev->expired);
spin_lock_init(&dev->lock);
dev->work = 0;
init_waitqueue_head(&dev->wait);
filep->private_data = dev;
return nonseekable_open(ino, filep);
}
static int
mISDN_close(struct inode *ino, struct file *filep)
{
struct mISDNtimerdev *dev = filep->private_data;
struct list_head *list = &dev->pending;
struct mISDNtimer *timer, *next;
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
spin_lock_irq(&dev->lock);
while (!list_empty(list)) {
timer = list_first_entry(list, struct mISDNtimer, list);
spin_unlock_irq(&dev->lock);
timer_shutdown_sync(&timer->tl);
spin_lock_irq(&dev->lock);
/* it might have been moved to ->expired */
list_del(&timer->list);
kfree(timer);
}
spin_unlock_irq(&dev->lock);
list_for_each_entry_safe(timer, next, &dev->expired, list) {
kfree(timer);
}
kfree(dev);
return 0;
}
static ssize_t
mISDN_read(struct file *filep, char __user *buf, size_t count, loff_t *off)
{
struct mISDNtimerdev *dev = filep->private_data;
struct list_head *list = &dev->expired;
struct mISDNtimer *timer;
int ret = 0;
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
filep, buf, (int)count, off);
if (count < sizeof(int))
return -ENOSPC;
spin_lock_irq(&dev->lock);
while (list_empty(list) && (dev->work == 0)) {
spin_unlock_irq(&dev->lock);
if (filep->f_flags & O_NONBLOCK)
return -EAGAIN;
wait_event_interruptible(dev->wait, (READ_ONCE(dev->work) ||
!list_empty(list)));
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&dev->lock);
}
if (dev->work)
WRITE_ONCE(dev->work, 0);
if (!list_empty(list)) {
timer = list_first_entry(list, struct mISDNtimer, list);
list_del(&timer->list);
spin_unlock_irq(&dev->lock);
if (put_user(timer->id, (int __user *)buf))
ret = -EFAULT;
else
ret = sizeof(int);
kfree(timer);
} else {
spin_unlock_irq(&dev->lock);
}
return ret;
}
static __poll_t
mISDN_poll(struct file *filep, poll_table *wait)
{
struct mISDNtimerdev *dev = filep->private_data;
__poll_t mask = EPOLLERR;
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
if (dev) {
u32 work;
poll_wait(filep, &dev->wait, wait);
mask = 0;
work = READ_ONCE(dev->work);
if (work || !list_empty(&dev->expired))
mask |= (EPOLLIN | EPOLLRDNORM);
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
work, list_empty(&dev->expired));
}
return mask;
}
static void
dev_expire_timer(struct timer_list *t)
{
struct mISDNtimer *timer = timer_container_of(timer, t, tl);
u_long flags;
spin_lock_irqsave(&timer->dev->lock, flags);
if (timer->id >= 0)
list_move_tail(&timer->list, &timer->dev->expired);
wake_up_interruptible(&timer->dev->wait);
spin_unlock_irqrestore(&timer->dev->lock, flags);
}
static int
misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
{
int id;
struct mISDNtimer *timer;
if (!timeout) {
WRITE_ONCE(dev->work, 1);
wake_up_interruptible(&dev->wait);
id = 0;
} else {
timer = kzalloc_obj(struct mISDNtimer);
if (!timer)
return -ENOMEM;
timer->dev = dev;
timer_setup(&timer->tl, dev_expire_timer, 0);
spin_lock_irq(&dev->lock);
id = timer->id = dev->next_id++;
if (dev->next_id < 0)
dev->next_id = 1;
list_add_tail(&timer->list, &dev->pending);
timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
add_timer(&timer->tl);
spin_unlock_irq(&dev->lock);
}
return id;
}
static int
misdn_del_timer(struct mISDNtimerdev *dev, int id)
{
struct mISDNtimer *timer;
spin_lock_irq(&dev->lock);
list_for_each_entry(timer, &dev->pending, list) {
if (timer->id == id) {
list_del_init(&timer->list);
timer->id = -1;
spin_unlock_irq(&dev->lock);
timer_shutdown_sync(&timer->tl);
kfree(timer);
return id;
}
}
spin_unlock_irq(&dev->lock);
return 0;
}
static long
mISDN_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
{
struct mISDNtimerdev *dev = filep->private_data;
int id, tout, ret = 0;
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
filep, cmd, arg);
mutex_lock(&mISDN_mutex);
switch (cmd) {
case IMADDTIMER:
if (get_user(tout, (int __user *)arg)) {
ret = -EFAULT;
break;
}
id = misdn_add_timer(dev, tout);
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s add %d id %d\n", __func__,
tout, id);
if (id < 0) {
ret = id;
break;
}
if (put_user(id, (int __user *)arg))
ret = -EFAULT;
break;
case IMDELTIMER:
if (get_user(id, (int __user *)arg)) {
ret = -EFAULT;
break;
}
if (*debug & DEBUG_TIMER)
printk(KERN_DEBUG "%s del id %d\n", __func__, id);
id = misdn_del_timer(dev, id);
if (put_user(id, (int __user *)arg))
ret = -EFAULT;
break;
default:
ret = -EINVAL;
}
mutex_unlock(&mISDN_mutex);
return ret;
}
static const struct file_operations mISDN_fops = {
.owner = THIS_MODULE,
.read = mISDN_read,
.poll = mISDN_poll,
.unlocked_ioctl = mISDN_ioctl,
.open = mISDN_open,
.release = mISDN_close,
};
static struct miscdevice mISDNtimer = {
.minor = MISC_DYNAMIC_MINOR,
.name = "mISDNtimer",
.fops = &mISDN_fops,
};
int
mISDN_inittimer(u_int *deb)
{
int err;
debug = deb;
err = misc_register(&mISDNtimer);
if (err)
printk(KERN_WARNING "mISDN: Could not register timer device\n");
return err;
}
void mISDN_timer_cleanup(void)
{
misc_deregister(&mISDNtimer);
}

View File

@@ -1,95 +0,0 @@
/* $Id: capilli.h,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
*
* Kernel CAPI 2.0 Driver Interface for Linux
*
* 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 __CAPILLI_H__
#define __CAPILLI_H__
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/capi.h>
#include <linux/kernelcapi.h>
typedef struct capiloaddatapart {
int user; /* data in userspace ? */
int len;
unsigned char *data;
} capiloaddatapart;
typedef struct capiloaddata {
capiloaddatapart firmware;
capiloaddatapart configuration;
} capiloaddata;
typedef struct capicardparams {
unsigned int port;
unsigned irq;
int cardtype;
int cardnr;
unsigned int membase;
} capicardparams;
struct capi_ctr {
/* filled in before calling attach_capi_ctr */
struct module *owner;
void *driverdata; /* driver specific */
char name[32]; /* name of controller */
char *driver_name; /* name of driver */
int (*load_firmware)(struct capi_ctr *, capiloaddata *);
void (*reset_ctr)(struct capi_ctr *);
void (*register_appl)(struct capi_ctr *, u16 appl,
capi_register_params *);
void (*release_appl)(struct capi_ctr *, u16 appl);
u16 (*send_message)(struct capi_ctr *, struct sk_buff *skb);
char *(*procinfo)(struct capi_ctr *);
int (*proc_show)(struct seq_file *, void *);
/* filled in before calling ready callback */
u8 manu[CAPI_MANUFACTURER_LEN]; /* CAPI_GET_MANUFACTURER */
capi_version version; /* CAPI_GET_VERSION */
capi_profile profile; /* CAPI_GET_PROFILE */
u8 serial[CAPI_SERIAL_LEN]; /* CAPI_GET_SERIAL */
/* management information for kcapi */
unsigned long nrecvctlpkt;
unsigned long nrecvdatapkt;
unsigned long nsentctlpkt;
unsigned long nsentdatapkt;
int cnr; /* controller number */
unsigned short state; /* controller state */
int blocked; /* output blocked */
int traceflag; /* capi trace */
struct proc_dir_entry *procent;
char procfn[128];
};
int attach_capi_ctr(struct capi_ctr *);
int detach_capi_ctr(struct capi_ctr *);
void capi_ctr_ready(struct capi_ctr * card);
void capi_ctr_down(struct capi_ctr * card);
void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb);
// ---------------------------------------------------------------------------
// needed for AVM capi drivers
struct capi_driver {
char name[32]; /* driver name */
char revision[32];
/* management information for kcapi */
struct list_head list;
};
#endif /* __CAPILLI_H__ */

View File

@@ -1,60 +0,0 @@
/* $Id: capiutil.h,v 1.5.6.2 2001/09/23 22:24:33 kai Exp $
*
* CAPI 2.0 defines & types
*
* From CAPI 2.0 Development Kit AVM 1995 (msg.c)
* Rewritten for Linux 1996 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 __CAPIUTIL_H__
#define __CAPIUTIL_H__
#include <asm/types.h>
#define CAPIMSG_BASELEN 8
#define CAPIMSG_U8(m, off) (m[off])
#define CAPIMSG_U16(m, off) (m[off]|(m[(off)+1]<<8))
#define CAPIMSG_U32(m, off) (m[off]|(m[(off)+1]<<8)|(m[(off)+2]<<16)|(m[(off)+3]<<24))
#define CAPIMSG_LEN(m) CAPIMSG_U16(m,0)
#define CAPIMSG_APPID(m) CAPIMSG_U16(m,2)
#define CAPIMSG_COMMAND(m) CAPIMSG_U8(m,4)
#define CAPIMSG_SUBCOMMAND(m) CAPIMSG_U8(m,5)
#define CAPIMSG_CMD(m) (((m[4])<<8)|(m[5]))
#define CAPIMSG_MSGID(m) CAPIMSG_U16(m,6)
#define CAPIMSG_CONTROLLER(m) (m[8] & 0x7f)
#define CAPIMSG_CONTROL(m) CAPIMSG_U32(m, 8)
#define CAPIMSG_NCCI(m) CAPIMSG_CONTROL(m)
#define CAPIMSG_DATALEN(m) CAPIMSG_U16(m,16) /* DATA_B3_REQ */
static inline void capimsg_setu8(void *m, int off, __u8 val)
{
((__u8 *)m)[off] = val;
}
static inline void capimsg_setu16(void *m, int off, __u16 val)
{
((__u8 *)m)[off] = val & 0xff;
((__u8 *)m)[off+1] = (val >> 8) & 0xff;
}
static inline void capimsg_setu32(void *m, int off, __u32 val)
{
((__u8 *)m)[off] = val & 0xff;
((__u8 *)m)[off+1] = (val >> 8) & 0xff;
((__u8 *)m)[off+2] = (val >> 16) & 0xff;
((__u8 *)m)[off+3] = (val >> 24) & 0xff;
}
#define CAPIMSG_SETLEN(m, len) capimsg_setu16(m, 0, len)
#define CAPIMSG_SETAPPID(m, applid) capimsg_setu16(m, 2, applid)
#define CAPIMSG_SETCOMMAND(m,cmd) capimsg_setu8(m, 4, cmd)
#define CAPIMSG_SETSUBCOMMAND(m, cmd) capimsg_setu8(m, 5, cmd)
#define CAPIMSG_SETMSGID(m, msgid) capimsg_setu16(m, 6, msgid)
#define CAPIMSG_SETCONTROL(m, contr) capimsg_setu32(m, 8, contr)
#define CAPIMSG_SETDATALEN(m, len) capimsg_setu16(m, 16, len)
#endif /* __CAPIUTIL_H__ */

View File

@@ -1,45 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* $Id: kernelcapi.h,v 1.8.6.2 2001/02/07 11:31:31 kai Exp $
*
* Kernel CAPI 2.0 Interface for Linux
*
* (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
*
*/
#ifndef __KERNELCAPI_H__
#define __KERNELCAPI_H__
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <linux/notifier.h>
#include <uapi/linux/kernelcapi.h>
#define CAPI_NOERROR 0x0000
#define CAPI_TOOMANYAPPLS 0x1001
#define CAPI_LOGBLKSIZETOSMALL 0x1002
#define CAPI_BUFFEXECEEDS64K 0x1003
#define CAPI_MSGBUFSIZETOOSMALL 0x1004
#define CAPI_ANZLOGCONNNOTSUPPORTED 0x1005
#define CAPI_REGRESERVED 0x1006
#define CAPI_REGBUSY 0x1007
#define CAPI_REGOSRESOURCEERR 0x1008
#define CAPI_REGNOTINSTALLED 0x1009
#define CAPI_REGCTRLERNOTSUPPORTEXTEQUIP 0x100a
#define CAPI_REGCTRLERONLYSUPPORTEXTEQUIP 0x100b
#define CAPI_ILLAPPNR 0x1101
#define CAPI_ILLCMDORSUBCMDORMSGTOSMALL 0x1102
#define CAPI_SENDQUEUEFULL 0x1103
#define CAPI_RECEIVEQUEUEEMPTY 0x1104
#define CAPI_RECEIVEOVERFLOW 0x1105
#define CAPI_UNKNOWNNOTPAR 0x1106
#define CAPI_MSGBUSY 0x1107
#define CAPI_MSGOSRESOURCEERR 0x1108
#define CAPI_MSGNOTINSTALLED 0x1109
#define CAPI_MSGCTRLERNOTSUPPORTEXTEQUIP 0x110a
#define CAPI_MSGCTRLERONLYSUPPORTEXTEQUIP 0x110b
#endif /* __KERNELCAPI_H__ */

View File

@@ -1,40 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __mISDNdsp_H__
#define __mISDNdsp_H__
struct mISDN_dsp_element_arg {
char *name;
char *def;
char *desc;
};
struct mISDN_dsp_element {
char *name;
void *(*new)(const char *arg);
void (*free)(void *p);
void (*process_tx)(void *p, unsigned char *data, int len);
void (*process_rx)(void *p, unsigned char *data, int len,
unsigned int txlen);
int num_args;
struct mISDN_dsp_element_arg
*args;
};
extern int mISDN_dsp_element_register(struct mISDN_dsp_element *elem);
extern void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem);
struct dsp_features {
int hfc_id; /* unique id to identify the chip (or -1) */
int hfc_dtmf; /* set if HFCmulti card supports dtmf */
int hfc_conf; /* set if HFCmulti card supports conferences */
int hfc_loops; /* set if card supports tone loops */
int hfc_echocanhw; /* set if card supports echocancelation*/
int pcm_id; /* unique id to identify the pcm bus (or -1) */
int pcm_slots; /* number of slots on the pcm bus */
int pcm_banks; /* number of IO banks of pcm bus */
int unclocked; /* data is not clocked (has jitter/loss) */
int unordered; /* data is unordered (packets have index) */
};
#endif

View File

@@ -1,192 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Basic declarations for the mISDN HW channels
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef MISDNHW_H
#define MISDNHW_H
#include <linux/mISDNif.h>
#include <linux/timer.h>
/*
* HW DEBUG 0xHHHHGGGG
* H - hardware driver specific bits
* G - for all drivers
*/
#define DEBUG_HW 0x00000001
#define DEBUG_HW_OPEN 0x00000002
#define DEBUG_HW_DCHANNEL 0x00000100
#define DEBUG_HW_DFIFO 0x00000200
#define DEBUG_HW_BCHANNEL 0x00001000
#define DEBUG_HW_BFIFO 0x00002000
#define MAX_DFRAME_LEN_L1 300
#define MAX_MON_FRAME 32
#define MAX_LOG_SPACE 2048
#define MISDN_COPY_SIZE 32
/* channel->Flags bit field */
#define FLG_TX_BUSY 0 /* tx_buf in use */
#define FLG_TX_NEXT 1 /* next_skb in use */
#define FLG_L1_BUSY 2 /* L1 is permanent busy */
#define FLG_L2_ACTIVATED 3 /* activated from L2 */
#define FLG_OPEN 5 /* channel is in use */
#define FLG_ACTIVE 6 /* channel is activated */
#define FLG_BUSY_TIMER 7
/* channel type */
#define FLG_DCHANNEL 8 /* channel is D-channel */
#define FLG_BCHANNEL 9 /* channel is B-channel */
#define FLG_ECHANNEL 10 /* channel is E-channel */
#define FLG_TRANSPARENT 12 /* channel use transparent data */
#define FLG_HDLC 13 /* channel use hdlc data */
#define FLG_L2DATA 14 /* channel use L2 DATA primitivs */
#define FLG_ORIGIN 15 /* channel is on origin site */
/* channel specific stuff */
#define FLG_FILLEMPTY 16 /* fill fifo on first frame (empty) */
/* arcofi specific */
#define FLG_ARCOFI_TIMER 17
#define FLG_ARCOFI_ERROR 18
/* isar specific */
#define FLG_INITIALIZED 17
#define FLG_DLEETX 18
#define FLG_LASTDLE 19
#define FLG_FIRST 20
#define FLG_LASTDATA 21
#define FLG_NMD_DATA 22
#define FLG_FTI_RUN 23
#define FLG_LL_OK 24
#define FLG_LL_CONN 25
#define FLG_DTMFSEND 26
#define FLG_TX_EMPTY 27
/* stop sending received data upstream */
#define FLG_RX_OFF 28
/* workq events */
#define FLG_RECVQUEUE 30
#define FLG_PHCHANGE 31
#define schedule_event(s, ev) do { \
test_and_set_bit(ev, &((s)->Flags)); \
schedule_work(&((s)->workq)); \
} while (0)
struct dchannel {
struct mISDNdevice dev;
u_long Flags;
struct work_struct workq;
void (*phfunc) (struct dchannel *);
u_int state;
void *l1;
void *hw;
int slot; /* multiport card channel slot */
struct timer_list timer;
/* receive data */
struct sk_buff *rx_skb;
int maxlen;
/* send data */
struct sk_buff_head squeue;
struct sk_buff_head rqueue;
struct sk_buff *tx_skb;
int tx_idx;
int debug;
/* statistics */
int err_crc;
int err_tx;
int err_rx;
};
typedef int (dchannel_l1callback)(struct dchannel *, u_int);
extern int create_l1(struct dchannel *, dchannel_l1callback *);
/* private L1 commands */
#define INFO0 0x8002
#define INFO1 0x8102
#define INFO2 0x8202
#define INFO3_P8 0x8302
#define INFO3_P10 0x8402
#define INFO4_P8 0x8502
#define INFO4_P10 0x8602
#define LOSTFRAMING 0x8702
#define ANYSIGNAL 0x8802
#define HW_POWERDOWN 0x8902
#define HW_RESET_REQ 0x8a02
#define HW_POWERUP_REQ 0x8b02
#define HW_DEACT_REQ 0x8c02
#define HW_ACTIVATE_REQ 0x8e02
#define HW_D_NOBLOCKED 0x8f02
#define HW_RESET_IND 0x9002
#define HW_POWERUP_IND 0x9102
#define HW_DEACT_IND 0x9202
#define HW_ACTIVATE_IND 0x9302
#define HW_DEACT_CNF 0x9402
#define HW_TESTLOOP 0x9502
#define HW_TESTRX_RAW 0x9602
#define HW_TESTRX_HDLC 0x9702
#define HW_TESTRX_OFF 0x9802
#define HW_TIMER3_IND 0x9902
#define HW_TIMER3_VALUE 0x9a00
#define HW_TIMER3_VMASK 0x00FF
struct layer1;
extern int l1_event(struct layer1 *, u_int);
#define MISDN_BCH_FILL_SIZE 4
struct bchannel {
struct mISDNchannel ch;
int nr;
u_long Flags;
struct work_struct workq;
u_int state;
void *hw;
int slot; /* multiport card channel slot */
struct timer_list timer;
/* receive data */
u8 fill[MISDN_BCH_FILL_SIZE];
struct sk_buff *rx_skb;
unsigned short maxlen;
unsigned short init_maxlen; /* initial value */
unsigned short next_maxlen; /* pending value */
unsigned short minlen; /* for transparent data */
unsigned short init_minlen; /* initial value */
unsigned short next_minlen; /* pending value */
/* send data */
struct sk_buff *next_skb;
struct sk_buff *tx_skb;
struct sk_buff_head rqueue;
int rcount;
int tx_idx;
int debug;
/* statistics */
int err_crc;
int err_tx;
int err_rx;
int dropcnt;
};
extern int mISDN_initdchannel(struct dchannel *, int, void *);
extern int mISDN_initbchannel(struct bchannel *, unsigned short,
unsigned short);
extern int mISDN_freedchannel(struct dchannel *);
extern void mISDN_clear_bchannel(struct bchannel *);
extern void mISDN_freebchannel(struct bchannel *);
extern int mISDN_ctrl_bchannel(struct bchannel *, struct mISDN_ctrl_req *);
extern void queue_ch_frame(struct mISDNchannel *, u_int,
int, struct sk_buff *);
extern int dchannel_senddata(struct dchannel *, struct sk_buff *);
extern int bchannel_senddata(struct bchannel *, struct sk_buff *);
extern int bchannel_get_rxbuf(struct bchannel *, int);
extern void recv_Dchannel(struct dchannel *);
extern void recv_Echannel(struct dchannel *, struct dchannel *);
extern void recv_Bchannel(struct bchannel *, unsigned int, bool);
extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *);
extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *);
extern int get_next_bframe(struct bchannel *);
extern int get_next_dframe(struct dchannel *);
#endif

View File

@@ -1,603 +0,0 @@
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* version 2.1 as published by the Free Software Foundation.
*
* This code 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 LESSER GENERAL PUBLIC LICENSE for more details.
*
*/
#ifndef mISDNIF_H
#define mISDNIF_H
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/socket.h>
/*
* ABI Version 32 bit
*
* <8 bit> Major version
* - changed if any interface become backwards incompatible
*
* <8 bit> Minor version
* - changed if any interface is extended but backwards compatible
*
* <16 bit> Release number
* - should be incremented on every checkin
*/
#define MISDN_MAJOR_VERSION 1
#define MISDN_MINOR_VERSION 1
#define MISDN_RELEASE 29
/* primitives for information exchange
* generell format
* <16 bit 0 >
* <8 bit command>
* BIT 8 = 1 LAYER private
* BIT 7 = 1 answer
* BIT 6 = 1 DATA
* <8 bit target layer mask>
*
* Layer = 00 is reserved for general commands
Layer = 01 L2 -> HW
Layer = 02 HW -> L2
Layer = 04 L3 -> L2
Layer = 08 L2 -> L3
* Layer = FF is reserved for broadcast commands
*/
#define MISDN_CMDMASK 0xff00
#define MISDN_LAYERMASK 0x00ff
/* generell commands */
#define OPEN_CHANNEL 0x0100
#define CLOSE_CHANNEL 0x0200
#define CONTROL_CHANNEL 0x0300
#define CHECK_DATA 0x0400
/* layer 2 -> layer 1 */
#define PH_ACTIVATE_REQ 0x0101
#define PH_DEACTIVATE_REQ 0x0201
#define PH_DATA_REQ 0x2001
#define MPH_ACTIVATE_REQ 0x0501
#define MPH_DEACTIVATE_REQ 0x0601
#define MPH_INFORMATION_REQ 0x0701
#define PH_CONTROL_REQ 0x0801
/* layer 1 -> layer 2 */
#define PH_ACTIVATE_IND 0x0102
#define PH_ACTIVATE_CNF 0x4102
#define PH_DEACTIVATE_IND 0x0202
#define PH_DEACTIVATE_CNF 0x4202
#define PH_DATA_IND 0x2002
#define PH_DATA_E_IND 0x3002
#define MPH_ACTIVATE_IND 0x0502
#define MPH_DEACTIVATE_IND 0x0602
#define MPH_INFORMATION_IND 0x0702
#define PH_DATA_CNF 0x6002
#define PH_CONTROL_IND 0x0802
#define PH_CONTROL_CNF 0x4802
/* layer 3 -> layer 2 */
#define DL_ESTABLISH_REQ 0x1004
#define DL_RELEASE_REQ 0x1104
#define DL_DATA_REQ 0x3004
#define DL_UNITDATA_REQ 0x3104
#define DL_INFORMATION_REQ 0x0004
/* layer 2 -> layer 3 */
#define DL_ESTABLISH_IND 0x1008
#define DL_ESTABLISH_CNF 0x5008
#define DL_RELEASE_IND 0x1108
#define DL_RELEASE_CNF 0x5108
#define DL_DATA_IND 0x3008
#define DL_UNITDATA_IND 0x3108
#define DL_INFORMATION_IND 0x0008
/* intern layer 2 management */
#define MDL_ASSIGN_REQ 0x1804
#define MDL_ASSIGN_IND 0x1904
#define MDL_REMOVE_REQ 0x1A04
#define MDL_REMOVE_IND 0x1B04
#define MDL_STATUS_UP_IND 0x1C04
#define MDL_STATUS_DOWN_IND 0x1D04
#define MDL_STATUS_UI_IND 0x1E04
#define MDL_ERROR_IND 0x1F04
#define MDL_ERROR_RSP 0x5F04
/* intern layer 2 */
#define DL_TIMER200_IND 0x7004
#define DL_TIMER203_IND 0x7304
#define DL_INTERN_MSG 0x7804
/* DL_INFORMATION_IND types */
#define DL_INFO_L2_CONNECT 0x0001
#define DL_INFO_L2_REMOVED 0x0002
/* PH_CONTROL types */
/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */
#define DTMF_TONE_VAL 0x2000
#define DTMF_TONE_MASK 0x007F
#define DTMF_TONE_START 0x2100
#define DTMF_TONE_STOP 0x2200
#define DTMF_HFC_COEF 0x4000
#define DSP_CONF_JOIN 0x2403
#define DSP_CONF_SPLIT 0x2404
#define DSP_RECEIVE_OFF 0x2405
#define DSP_RECEIVE_ON 0x2406
#define DSP_ECHO_ON 0x2407
#define DSP_ECHO_OFF 0x2408
#define DSP_MIX_ON 0x2409
#define DSP_MIX_OFF 0x240a
#define DSP_DELAY 0x240b
#define DSP_JITTER 0x240c
#define DSP_TXDATA_ON 0x240d
#define DSP_TXDATA_OFF 0x240e
#define DSP_TX_DEJITTER 0x240f
#define DSP_TX_DEJ_OFF 0x2410
#define DSP_TONE_PATT_ON 0x2411
#define DSP_TONE_PATT_OFF 0x2412
#define DSP_VOL_CHANGE_TX 0x2413
#define DSP_VOL_CHANGE_RX 0x2414
#define DSP_BF_ENABLE_KEY 0x2415
#define DSP_BF_DISABLE 0x2416
#define DSP_BF_ACCEPT 0x2416
#define DSP_BF_REJECT 0x2417
#define DSP_PIPELINE_CFG 0x2418
#define HFC_VOL_CHANGE_TX 0x2601
#define HFC_VOL_CHANGE_RX 0x2602
#define HFC_SPL_LOOP_ON 0x2603
#define HFC_SPL_LOOP_OFF 0x2604
/* for T30 FAX and analog modem */
#define HW_MOD_FRM 0x4000
#define HW_MOD_FRH 0x4001
#define HW_MOD_FTM 0x4002
#define HW_MOD_FTH 0x4003
#define HW_MOD_FTS 0x4004
#define HW_MOD_CONNECT 0x4010
#define HW_MOD_OK 0x4011
#define HW_MOD_NOCARR 0x4012
#define HW_MOD_FCERROR 0x4013
#define HW_MOD_READY 0x4014
#define HW_MOD_LASTDATA 0x4015
/* DSP_TONE_PATT_ON parameter */
#define TONE_OFF 0x0000
#define TONE_GERMAN_DIALTONE 0x0001
#define TONE_GERMAN_OLDDIALTONE 0x0002
#define TONE_AMERICAN_DIALTONE 0x0003
#define TONE_GERMAN_DIALPBX 0x0004
#define TONE_GERMAN_OLDDIALPBX 0x0005
#define TONE_AMERICAN_DIALPBX 0x0006
#define TONE_GERMAN_RINGING 0x0007
#define TONE_GERMAN_OLDRINGING 0x0008
#define TONE_AMERICAN_RINGPBX 0x000b
#define TONE_GERMAN_RINGPBX 0x000c
#define TONE_GERMAN_OLDRINGPBX 0x000d
#define TONE_AMERICAN_RINGING 0x000e
#define TONE_GERMAN_BUSY 0x000f
#define TONE_GERMAN_OLDBUSY 0x0010
#define TONE_AMERICAN_BUSY 0x0011
#define TONE_GERMAN_HANGUP 0x0012
#define TONE_GERMAN_OLDHANGUP 0x0013
#define TONE_AMERICAN_HANGUP 0x0014
#define TONE_SPECIAL_INFO 0x0015
#define TONE_GERMAN_GASSENBESETZT 0x0016
#define TONE_GERMAN_AUFSCHALTTON 0x0016
/* MPH_INFORMATION_IND */
#define L1_SIGNAL_LOS_OFF 0x0010
#define L1_SIGNAL_LOS_ON 0x0011
#define L1_SIGNAL_AIS_OFF 0x0012
#define L1_SIGNAL_AIS_ON 0x0013
#define L1_SIGNAL_RDI_OFF 0x0014
#define L1_SIGNAL_RDI_ON 0x0015
#define L1_SIGNAL_SLIP_RX 0x0020
#define L1_SIGNAL_SLIP_TX 0x0021
/*
* protocol ids
* D channel 1-31
* B channel 33 - 63
*/
#define ISDN_P_NONE 0
#define ISDN_P_BASE 0
#define ISDN_P_TE_S0 0x01
#define ISDN_P_NT_S0 0x02
#define ISDN_P_TE_E1 0x03
#define ISDN_P_NT_E1 0x04
#define ISDN_P_TE_UP0 0x05
#define ISDN_P_NT_UP0 0x06
#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \
(p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE))
#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \
(p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT))
#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0))
#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1))
#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0))
#define ISDN_P_LAPD_TE 0x10
#define ISDN_P_LAPD_NT 0x11
#define ISDN_P_B_MASK 0x1f
#define ISDN_P_B_START 0x20
#define ISDN_P_B_RAW 0x21
#define ISDN_P_B_HDLC 0x22
#define ISDN_P_B_X75SLP 0x23
#define ISDN_P_B_L2DTMF 0x24
#define ISDN_P_B_L2DSP 0x25
#define ISDN_P_B_L2DSPHDLC 0x26
#define ISDN_P_B_T30_FAX 0x27
#define ISDN_P_B_MODEM_ASYNC 0x28
#define OPTION_L2_PMX 1
#define OPTION_L2_PTP 2
#define OPTION_L2_FIXEDTEI 3
#define OPTION_L2_CLEANUP 4
#define OPTION_L1_HOLD 5
/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
#define MISDN_MAX_IDLEN 20
struct mISDNhead {
unsigned int prim;
unsigned int id;
} __packed;
#define MISDN_HEADER_LEN sizeof(struct mISDNhead)
#define MAX_DATA_SIZE 2048
#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN)
#define MAX_DFRAME_LEN 260
#define MISDN_ID_ADDR_MASK 0xFFFF
#define MISDN_ID_TEI_MASK 0xFF00
#define MISDN_ID_SAPI_MASK 0x00FF
#define MISDN_ID_TEI_ANY 0x7F00
#define MISDN_ID_ANY 0xFFFF
#define MISDN_ID_NONE 0xFFFE
#define GROUP_TEI 127
#define TEI_SAPI 63
#define CTRL_SAPI 0
#define MISDN_MAX_CHANNEL 127
#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3)
#define SOL_MISDN 0
struct sockaddr_mISDN {
sa_family_t family;
unsigned char dev;
unsigned char channel;
unsigned char sapi;
unsigned char tei;
};
struct mISDNversion {
unsigned char major;
unsigned char minor;
unsigned short release;
};
struct mISDN_devinfo {
u_int id;
u_int Dprotocols;
u_int Bprotocols;
u_int protocol;
u_char channelmap[MISDN_CHMAP_SIZE];
u_int nrbchan;
char name[MISDN_MAX_IDLEN];
};
struct mISDN_devrename {
u_int id;
char name[MISDN_MAX_IDLEN]; /* new name */
};
/* MPH_INFORMATION_REQ payload */
struct ph_info_ch {
__u32 protocol;
__u64 Flags;
};
struct ph_info_dch {
struct ph_info_ch ch;
__u16 state;
__u16 num_bch;
};
struct ph_info {
struct ph_info_dch dch;
struct ph_info_ch bch[];
};
/* timer device ioctl */
#define IMADDTIMER _IOR('I', 64, int)
#define IMDELTIMER _IOR('I', 65, int)
/* socket ioctls */
#define IMGETVERSION _IOR('I', 66, int)
#define IMGETCOUNT _IOR('I', 67, int)
#define IMGETDEVINFO _IOR('I', 68, int)
#define IMCTRLREQ _IOR('I', 69, int)
#define IMCLEAR_L2 _IOR('I', 70, int)
#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename)
#define IMHOLD_L1 _IOR('I', 72, int)
static inline int
test_channelmap(u_int nr, u_char *map)
{
if (nr <= MISDN_MAX_CHANNEL)
return map[nr >> 3] & (1 << (nr & 7));
else
return 0;
}
static inline void
set_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] |= (1 << (nr & 7));
}
static inline void
clear_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] &= ~(1 << (nr & 7));
}
/* CONTROL_CHANNEL parameters */
#define MISDN_CTRL_GETOP 0x0000
#define MISDN_CTRL_LOOP 0x0001
#define MISDN_CTRL_CONNECT 0x0002
#define MISDN_CTRL_DISCONNECT 0x0004
#define MISDN_CTRL_RX_BUFFER 0x0008
#define MISDN_CTRL_PCMCONNECT 0x0010
#define MISDN_CTRL_PCMDISCONNECT 0x0020
#define MISDN_CTRL_SETPEER 0x0040
#define MISDN_CTRL_UNSETPEER 0x0080
#define MISDN_CTRL_RX_OFF 0x0100
#define MISDN_CTRL_FILL_EMPTY 0x0200
#define MISDN_CTRL_GETPEER 0x0400
#define MISDN_CTRL_L1_TIMER3 0x0800
#define MISDN_CTRL_HW_FEATURES_OP 0x2000
#define MISDN_CTRL_HW_FEATURES 0x2001
#define MISDN_CTRL_HFC_OP 0x4000
#define MISDN_CTRL_HFC_PCM_CONN 0x4001
#define MISDN_CTRL_HFC_PCM_DISC 0x4002
#define MISDN_CTRL_HFC_CONF_JOIN 0x4003
#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004
#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005
#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006
#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007
#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008
#define MISDN_CTRL_HFC_WD_INIT 0x4009
#define MISDN_CTRL_HFC_WD_RESET 0x400A
/* special RX buffer value for MISDN_CTRL_RX_BUFFER request.p1 is the minimum
* buffer size request.p2 the maximum. Using MISDN_CTRL_RX_SIZE_IGNORE will
* not change the value, but still read back the actual stetting.
*/
#define MISDN_CTRL_RX_SIZE_IGNORE -1
/* socket options */
#define MISDN_TIME_STAMP 0x0001
struct mISDN_ctrl_req {
int op;
int channel;
int p1;
int p2;
};
/* muxer options */
#define MISDN_OPT_ALL 1
#define MISDN_OPT_TEIMGR 2
#ifdef __KERNEL__
#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/net.h>
#include <net/sock.h>
#include <linux/completion.h>
#define DEBUG_CORE 0x000000ff
#define DEBUG_CORE_FUNC 0x00000002
#define DEBUG_SOCKET 0x00000004
#define DEBUG_MANAGER 0x00000008
#define DEBUG_SEND_ERR 0x00000010
#define DEBUG_MSG_THREAD 0x00000020
#define DEBUG_QUEUE_FUNC 0x00000040
#define DEBUG_L1 0x0000ff00
#define DEBUG_L1_FSM 0x00000200
#define DEBUG_L2 0x00ff0000
#define DEBUG_L2_FSM 0x00020000
#define DEBUG_L2_CTRL 0x00040000
#define DEBUG_L2_RECV 0x00080000
#define DEBUG_L2_TEI 0x00100000
#define DEBUG_L2_TEIFSM 0x00200000
#define DEBUG_TIMER 0x01000000
#define DEBUG_CLOCK 0x02000000
#define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0])
#define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim)
#define mISDN_HEAD_ID(s) (((struct mISDNhead *)&s->cb[0])->id)
/* socket states */
#define MISDN_OPEN 1
#define MISDN_BOUND 2
#define MISDN_CLOSED 3
struct mISDNchannel;
struct mISDNdevice;
struct mISDNstack;
struct mISDNclock;
struct channel_req {
u_int protocol;
struct sockaddr_mISDN adr;
struct mISDNchannel *ch;
};
typedef int (ctrl_func_t)(struct mISDNchannel *, u_int, void *);
typedef int (send_func_t)(struct mISDNchannel *, struct sk_buff *);
typedef int (create_func_t)(struct channel_req *);
struct Bprotocol {
struct list_head list;
char *name;
u_int Bprotocols;
create_func_t *create;
};
struct mISDNchannel {
struct list_head list;
u_int protocol;
u_int nr;
u_long opt;
u_int addr;
struct mISDNstack *st;
struct mISDNchannel *peer;
send_func_t *send;
send_func_t *recv;
ctrl_func_t *ctrl;
};
struct mISDN_sock_list {
struct hlist_head head;
rwlock_t lock;
};
struct mISDN_sock {
struct sock sk;
struct mISDNchannel ch;
u_int cmask;
struct mISDNdevice *dev;
};
struct mISDNdevice {
struct mISDNchannel D;
u_int id;
u_int Dprotocols;
u_int Bprotocols;
u_int nrbchan;
u_char channelmap[MISDN_CHMAP_SIZE];
struct list_head bchannels;
struct mISDNchannel *teimgr;
struct device dev;
};
struct mISDNstack {
u_long status;
struct mISDNdevice *dev;
struct task_struct *thread;
struct completion *notify;
wait_queue_head_t workq;
struct sk_buff_head msgq;
struct list_head layer2;
struct mISDNchannel *layer1;
struct mISDNchannel own;
struct mutex lmutex; /* protect lists */
struct mISDN_sock_list l1sock;
#ifdef MISDN_MSG_STATS
u_int msg_cnt;
u_int sleep_cnt;
u_int stopped_cnt;
#endif
};
typedef int (clockctl_func_t)(void *, int);
struct mISDNclock {
struct list_head list;
char name[64];
int pri;
clockctl_func_t *ctl;
void *priv;
};
/* global alloc/queue functions */
static inline struct sk_buff *
mI_alloc_skb(unsigned int len, gfp_t gfp_mask)
{
struct sk_buff *skb;
skb = alloc_skb(len + MISDN_HEADER_LEN, gfp_mask);
if (likely(skb))
skb_reserve(skb, MISDN_HEADER_LEN);
return skb;
}
static inline struct sk_buff *
_alloc_mISDN_skb(u_int prim, u_int id, u_int len, void *dp, gfp_t gfp_mask)
{
struct sk_buff *skb = mI_alloc_skb(len, gfp_mask);
struct mISDNhead *hh;
if (!skb)
return NULL;
if (len)
skb_put_data(skb, dp, len);
hh = mISDN_HEAD_P(skb);
hh->prim = prim;
hh->id = id;
return skb;
}
static inline void
_queue_data(struct mISDNchannel *ch, u_int prim,
u_int id, u_int len, void *dp, gfp_t gfp_mask)
{
struct sk_buff *skb;
if (!ch->peer)
return;
skb = _alloc_mISDN_skb(prim, id, len, dp, gfp_mask);
if (!skb)
return;
if (ch->recv(ch->peer, skb))
dev_kfree_skb(skb);
}
/* global register/unregister functions */
extern int mISDN_register_device(struct mISDNdevice *,
struct device *parent, char *name);
extern void mISDN_unregister_device(struct mISDNdevice *);
extern int mISDN_register_Bprotocol(struct Bprotocol *);
extern void mISDN_unregister_Bprotocol(struct Bprotocol *);
extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *,
void *);
extern void mISDN_unregister_clock(struct mISDNclock *);
static inline struct mISDNdevice *dev_to_mISDN(const struct device *dev)
{
if (dev)
return dev_get_drvdata(dev);
else
return NULL;
}
extern void set_channel_address(struct mISDNchannel *, u_int, u_int);
extern void mISDN_clock_update(struct mISDNclock *, int, ktime_t *);
extern unsigned short mISDN_clock_get(void);
extern const char *mISDNDevName4ch(struct mISDNchannel *);
#endif /* __KERNEL__ */
#endif /* mISDNIF_H */

View File

@@ -1,134 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/* $Id: capi.h,v 1.4.6.1 2001/09/23 22:25:05 kai Exp $
*
* CAPI 2.0 Interface for Linux
*
* Copyright 1997 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 __LINUX_CAPI_H__
#define __LINUX_CAPI_H__
#include <linux/types.h>
#include <linux/ioctl.h>
#ifndef __KERNEL__
#include <linux/kernelcapi.h>
#endif
/*
* CAPI_REGISTER
*/
typedef struct capi_register_params { /* CAPI_REGISTER */
__u32 level3cnt; /* No. of simulatneous user data connections */
__u32 datablkcnt; /* No. of buffered data messages */
__u32 datablklen; /* Size of buffered data messages */
} capi_register_params;
#define CAPI_REGISTER _IOW('C',0x01,struct capi_register_params)
/*
* CAPI_GET_MANUFACTURER
*/
#define CAPI_MANUFACTURER_LEN 64
#define CAPI_GET_MANUFACTURER _IOWR('C',0x06,int) /* broken: wanted size 64 (CAPI_MANUFACTURER_LEN) */
/*
* CAPI_GET_VERSION
*/
typedef struct capi_version {
__u32 majorversion;
__u32 minorversion;
__u32 majormanuversion;
__u32 minormanuversion;
} capi_version;
#define CAPI_GET_VERSION _IOWR('C',0x07,struct capi_version)
/*
* CAPI_GET_SERIAL
*/
#define CAPI_SERIAL_LEN 8
#define CAPI_GET_SERIAL _IOWR('C',0x08,int) /* broken: wanted size 8 (CAPI_SERIAL_LEN) */
/*
* CAPI_GET_PROFILE
*/
typedef struct capi_profile {
__u16 ncontroller; /* number of installed controller */
__u16 nbchannel; /* number of B-Channels */
__u32 goptions; /* global options */
__u32 support1; /* B1 protocols support */
__u32 support2; /* B2 protocols support */
__u32 support3; /* B3 protocols support */
__u32 reserved[6]; /* reserved */
__u32 manu[5]; /* manufacturer specific information */
} capi_profile;
#define CAPI_GET_PROFILE _IOWR('C',0x09,struct capi_profile)
typedef struct capi_manufacturer_cmd {
unsigned long cmd;
void __user *data;
} capi_manufacturer_cmd;
/*
* CAPI_MANUFACTURER_CMD
*/
#define CAPI_MANUFACTURER_CMD _IOWR('C',0x20, struct capi_manufacturer_cmd)
/*
* CAPI_GET_ERRCODE
* capi errcode is set, * if read, write, or ioctl returns EIO,
* ioctl returns errcode directly, and in arg, if != 0
*/
#define CAPI_GET_ERRCODE _IOR('C',0x21, __u16)
/*
* CAPI_INSTALLED
*/
#define CAPI_INSTALLED _IOR('C',0x22, __u16)
/*
* member contr is input for
* CAPI_GET_MANUFACTURER, CAPI_GET_VERSION, CAPI_GET_SERIAL
* and CAPI_GET_PROFILE
*/
typedef union capi_ioctl_struct {
__u32 contr;
capi_register_params rparams;
__u8 manufacturer[CAPI_MANUFACTURER_LEN];
capi_version version;
__u8 serial[CAPI_SERIAL_LEN];
capi_profile profile;
capi_manufacturer_cmd cmd;
__u16 errcode;
} capi_ioctl_struct;
/*
* Middleware extension
*/
#define CAPIFLAG_HIGHJACKING 0x0001
#define CAPI_GET_FLAGS _IOR('C',0x23, unsigned)
#define CAPI_SET_FLAGS _IOR('C',0x24, unsigned)
#define CAPI_CLR_FLAGS _IOR('C',0x25, unsigned)
#define CAPI_NCCI_OPENCOUNT _IOR('C',0x26, unsigned)
#define CAPI_NCCI_GETUNIT _IOR('C',0x27, unsigned)
#endif /* __LINUX_CAPI_H__ */

View File

@@ -1,117 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/* $Id: capicmd.h,v 1.2.6.2 2001/09/23 22:24:33 kai Exp $
*
* CAPI 2.0 Interface for Linux
*
* Copyright 1997 by Carsten Paeth <calle@calle.de>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#ifndef __CAPICMD_H__
#define __CAPICMD_H__
#define CAPI_MSG_BASELEN 8
#define CAPI_DATA_B3_REQ_LEN (CAPI_MSG_BASELEN+4+4+2+2+2)
#define CAPI_DATA_B3_RESP_LEN (CAPI_MSG_BASELEN+4+2)
#define CAPI_DISCONNECT_B3_RESP_LEN (CAPI_MSG_BASELEN+4)
/*----- CAPI commands -----*/
#define CAPI_ALERT 0x01
#define CAPI_CONNECT 0x02
#define CAPI_CONNECT_ACTIVE 0x03
#define CAPI_CONNECT_B3_ACTIVE 0x83
#define CAPI_CONNECT_B3 0x82
#define CAPI_CONNECT_B3_T90_ACTIVE 0x88
#define CAPI_DATA_B3 0x86
#define CAPI_DISCONNECT_B3 0x84
#define CAPI_DISCONNECT 0x04
#define CAPI_FACILITY 0x80
#define CAPI_INFO 0x08
#define CAPI_LISTEN 0x05
#define CAPI_MANUFACTURER 0xff
#define CAPI_RESET_B3 0x87
#define CAPI_SELECT_B_PROTOCOL 0x41
/*----- CAPI subcommands -----*/
#define CAPI_REQ 0x80
#define CAPI_CONF 0x81
#define CAPI_IND 0x82
#define CAPI_RESP 0x83
/*----- CAPI combined commands -----*/
#define CAPICMD(cmd,subcmd) (((cmd)<<8)|(subcmd))
#define CAPI_DISCONNECT_REQ CAPICMD(CAPI_DISCONNECT,CAPI_REQ)
#define CAPI_DISCONNECT_CONF CAPICMD(CAPI_DISCONNECT,CAPI_CONF)
#define CAPI_DISCONNECT_IND CAPICMD(CAPI_DISCONNECT,CAPI_IND)
#define CAPI_DISCONNECT_RESP CAPICMD(CAPI_DISCONNECT,CAPI_RESP)
#define CAPI_ALERT_REQ CAPICMD(CAPI_ALERT,CAPI_REQ)
#define CAPI_ALERT_CONF CAPICMD(CAPI_ALERT,CAPI_CONF)
#define CAPI_CONNECT_REQ CAPICMD(CAPI_CONNECT,CAPI_REQ)
#define CAPI_CONNECT_CONF CAPICMD(CAPI_CONNECT,CAPI_CONF)
#define CAPI_CONNECT_IND CAPICMD(CAPI_CONNECT,CAPI_IND)
#define CAPI_CONNECT_RESP CAPICMD(CAPI_CONNECT,CAPI_RESP)
#define CAPI_CONNECT_ACTIVE_REQ CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_REQ)
#define CAPI_CONNECT_ACTIVE_CONF CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_CONF)
#define CAPI_CONNECT_ACTIVE_IND CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_IND)
#define CAPI_CONNECT_ACTIVE_RESP CAPICMD(CAPI_CONNECT_ACTIVE,CAPI_RESP)
#define CAPI_SELECT_B_PROTOCOL_REQ CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_REQ)
#define CAPI_SELECT_B_PROTOCOL_CONF CAPICMD(CAPI_SELECT_B_PROTOCOL,CAPI_CONF)
#define CAPI_CONNECT_B3_ACTIVE_REQ CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_REQ)
#define CAPI_CONNECT_B3_ACTIVE_CONF CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_CONF)
#define CAPI_CONNECT_B3_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_IND)
#define CAPI_CONNECT_B3_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_ACTIVE,CAPI_RESP)
#define CAPI_CONNECT_B3_REQ CAPICMD(CAPI_CONNECT_B3,CAPI_REQ)
#define CAPI_CONNECT_B3_CONF CAPICMD(CAPI_CONNECT_B3,CAPI_CONF)
#define CAPI_CONNECT_B3_IND CAPICMD(CAPI_CONNECT_B3,CAPI_IND)
#define CAPI_CONNECT_B3_RESP CAPICMD(CAPI_CONNECT_B3,CAPI_RESP)
#define CAPI_CONNECT_B3_T90_ACTIVE_IND CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_IND)
#define CAPI_CONNECT_B3_T90_ACTIVE_RESP CAPICMD(CAPI_CONNECT_B3_T90_ACTIVE,CAPI_RESP)
#define CAPI_DATA_B3_REQ CAPICMD(CAPI_DATA_B3,CAPI_REQ)
#define CAPI_DATA_B3_CONF CAPICMD(CAPI_DATA_B3,CAPI_CONF)
#define CAPI_DATA_B3_IND CAPICMD(CAPI_DATA_B3,CAPI_IND)
#define CAPI_DATA_B3_RESP CAPICMD(CAPI_DATA_B3,CAPI_RESP)
#define CAPI_DISCONNECT_B3_REQ CAPICMD(CAPI_DISCONNECT_B3,CAPI_REQ)
#define CAPI_DISCONNECT_B3_CONF CAPICMD(CAPI_DISCONNECT_B3,CAPI_CONF)
#define CAPI_DISCONNECT_B3_IND CAPICMD(CAPI_DISCONNECT_B3,CAPI_IND)
#define CAPI_DISCONNECT_B3_RESP CAPICMD(CAPI_DISCONNECT_B3,CAPI_RESP)
#define CAPI_RESET_B3_REQ CAPICMD(CAPI_RESET_B3,CAPI_REQ)
#define CAPI_RESET_B3_CONF CAPICMD(CAPI_RESET_B3,CAPI_CONF)
#define CAPI_RESET_B3_IND CAPICMD(CAPI_RESET_B3,CAPI_IND)
#define CAPI_RESET_B3_RESP CAPICMD(CAPI_RESET_B3,CAPI_RESP)
#define CAPI_LISTEN_REQ CAPICMD(CAPI_LISTEN,CAPI_REQ)
#define CAPI_LISTEN_CONF CAPICMD(CAPI_LISTEN,CAPI_CONF)
#define CAPI_MANUFACTURER_REQ CAPICMD(CAPI_MANUFACTURER,CAPI_REQ)
#define CAPI_MANUFACTURER_CONF CAPICMD(CAPI_MANUFACTURER,CAPI_CONF)
#define CAPI_MANUFACTURER_IND CAPICMD(CAPI_MANUFACTURER,CAPI_IND)
#define CAPI_MANUFACTURER_RESP CAPICMD(CAPI_MANUFACTURER,CAPI_RESP)
#define CAPI_FACILITY_REQ CAPICMD(CAPI_FACILITY,CAPI_REQ)
#define CAPI_FACILITY_CONF CAPICMD(CAPI_FACILITY,CAPI_CONF)
#define CAPI_FACILITY_IND CAPICMD(CAPI_FACILITY,CAPI_IND)
#define CAPI_FACILITY_RESP CAPICMD(CAPI_FACILITY,CAPI_RESP)
#define CAPI_INFO_REQ CAPICMD(CAPI_INFO,CAPI_REQ)
#define CAPI_INFO_CONF CAPICMD(CAPI_INFO,CAPI_CONF)
#define CAPI_INFO_IND CAPICMD(CAPI_INFO,CAPI_IND)
#define CAPI_INFO_RESP CAPICMD(CAPI_INFO,CAPI_RESP)
#endif /* __CAPICMD_H__ */

View File

@@ -1,48 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* $Id: kernelcapi.h,v 1.8.6.2 2001/02/07 11:31:31 kai Exp $
*
* Kernel CAPI 2.0 Interface for Linux
*
* (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
*
*/
#ifndef _UAPI__KERNELCAPI_H__
#define _UAPI__KERNELCAPI_H__
#define CAPI_MAXAPPL 240 /* maximum number of applications */
#define CAPI_MAXCONTR 32 /* maximum number of controller */
#define CAPI_MAXDATAWINDOW 8
typedef struct kcapi_flagdef {
int contr;
int flag;
} kcapi_flagdef;
typedef struct kcapi_carddef {
char driver[32];
unsigned int port;
unsigned irq;
unsigned int membase;
int cardnr;
} kcapi_carddef;
/* new ioctls >= 10 */
#define KCAPI_CMD_TRACE 10
#define KCAPI_CMD_ADDCARD 11 /* OBSOLETE */
/*
* flag > 2 => trace also data
* flag & 1 => show trace
*/
#define KCAPI_TRACE_OFF 0
#define KCAPI_TRACE_SHORT_NO_DATA 1
#define KCAPI_TRACE_FULL_NO_DATA 2
#define KCAPI_TRACE_SHORT 3
#define KCAPI_TRACE_FULL 4
#endif /* _UAPI__KERNELCAPI_H__ */

View File

@@ -33,7 +33,6 @@ menuconfig BT
HCI Device drivers (Interface to the hardware)
RFCOMM Module (RFCOMM Protocol)
BNEP Module (Bluetooth Network Encapsulation Protocol)
CMTP Module (CAPI Message Transport Protocol)
HIDP Module (Human Interface Device Protocol)
Say Y here to compile Bluetooth support into the kernel or say M to
@@ -58,8 +57,6 @@ source "net/bluetooth/rfcomm/Kconfig"
source "net/bluetooth/bnep/Kconfig"
source "net/bluetooth/cmtp/Kconfig"
source "net/bluetooth/hidp/Kconfig"
config BT_LE

View File

@@ -6,7 +6,6 @@
obj-$(CONFIG_BT) += bluetooth.o
obj-$(CONFIG_BT_RFCOMM) += rfcomm/
obj-$(CONFIG_BT_BNEP) += bnep/
obj-$(CONFIG_BT_CMTP) += cmtp/
obj-$(CONFIG_BT_HIDP) += hidp/
obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o

View File

@@ -1,12 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
config BT_CMTP
tristate "CMTP protocol support (DEPRECATED)"
depends on BT_BREDR && ISDN_CAPI && DEPRECATED
help
CMTP (CAPI Message Transport Protocol) is a transport layer
for CAPI messages. CMTP is required for the Bluetooth Common
ISDN Access Profile.
Say Y here to compile CMTP support into the kernel or say M to
compile it as module (cmtp).

View File

@@ -1,8 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the Linux Bluetooth CMTP layer
#
obj-$(CONFIG_BT_CMTP) += cmtp.o
cmtp-objs := core.o sock.o capi.o

View File

@@ -1,579 +0,0 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/export.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/wait.h>
#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include <linux/isdn/capicmd.h>
#include <linux/isdn/capiutil.h>
#include "cmtp.h"
#define CAPI_INTEROPERABILITY 0x20
#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ)
#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF)
#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND)
#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP)
#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4)
#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2)
#define CAPI_FUNCTION_REGISTER 0
#define CAPI_FUNCTION_RELEASE 1
#define CAPI_FUNCTION_GET_PROFILE 2
#define CAPI_FUNCTION_GET_MANUFACTURER 3
#define CAPI_FUNCTION_GET_VERSION 4
#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5
#define CAPI_FUNCTION_MANUFACTURER 6
#define CAPI_FUNCTION_LOOPBACK 7
#define CMTP_MSGNUM 1
#define CMTP_APPLID 2
#define CMTP_MAPPING 3
static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl)
{
struct cmtp_application *app = kzalloc_obj(*app);
BT_DBG("session %p application %p appl %u", session, app, appl);
if (!app)
return NULL;
app->state = BT_OPEN;
app->appl = appl;
list_add_tail(&app->list, &session->applications);
return app;
}
static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app)
{
BT_DBG("session %p application %p", session, app);
if (app) {
list_del(&app->list);
kfree(app);
}
}
static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
{
struct cmtp_application *app;
list_for_each_entry(app, &session->applications, list) {
switch (pattern) {
case CMTP_MSGNUM:
if (app->msgnum == value)
return app;
break;
case CMTP_APPLID:
if (app->appl == value)
return app;
break;
case CMTP_MAPPING:
if (app->mapping == value)
return app;
break;
}
}
return NULL;
}
static int cmtp_msgnum_get(struct cmtp_session *session)
{
session->msgnum++;
if ((session->msgnum & 0xff) > 200)
session->msgnum = CMTP_INITIAL_MSGNUM + 1;
return session->msgnum;
}
static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct cmtp_scb *scb = (void *) skb->cb;
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
scb->id = -1;
scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3);
skb_queue_tail(&session->transmit, skb);
wake_up_interruptible(sk_sleep(session->sock->sk));
}
static void cmtp_send_interopmsg(struct cmtp_session *session,
__u8 subcmd, __u16 appl, __u16 msgnum,
__u16 function, unsigned char *buf, int len)
{
struct sk_buff *skb;
unsigned char *s;
BT_DBG("session %p subcmd 0x%02x appl %u msgnum %u", session, subcmd, appl, msgnum);
skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC);
if (!skb) {
BT_ERR("Can't allocate memory for interoperability packet");
return;
}
s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len);
capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len);
capimsg_setu16(s, 2, appl);
capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY);
capimsg_setu8 (s, 5, subcmd);
capimsg_setu16(s, 6, msgnum);
/* Interoperability selector (Bluetooth Device Management) */
capimsg_setu16(s, 8, 0x0001);
capimsg_setu8 (s, 10, 3 + len);
capimsg_setu16(s, 11, function);
capimsg_setu8 (s, 13, len);
if (len > 0)
memcpy(s + 14, buf, len);
cmtp_send_capimsg(session, skb);
}
static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = &session->ctrl;
struct cmtp_application *application;
__u16 appl, msgnum, func, info;
__u32 controller;
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
switch (CAPIMSG_SUBCOMMAND(skb->data)) {
case CAPI_CONF:
if (skb->len < CAPI_MSG_BASELEN + 10)
break;
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5);
info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8);
switch (func) {
case CAPI_FUNCTION_REGISTER:
msgnum = CAPIMSG_MSGID(skb->data);
application = cmtp_application_get(session, CMTP_MSGNUM, msgnum);
if (application) {
application->state = BT_CONNECTED;
application->msgnum = 0;
application->mapping = CAPIMSG_APPID(skb->data);
wake_up_interruptible(&session->wait);
}
break;
case CAPI_FUNCTION_RELEASE:
appl = CAPIMSG_APPID(skb->data);
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
application->state = BT_CLOSED;
application->msgnum = 0;
wake_up_interruptible(&session->wait);
}
break;
case CAPI_FUNCTION_GET_PROFILE:
if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile))
break;
controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11);
msgnum = CAPIMSG_MSGID(skb->data);
if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) {
session->ncontroller = controller;
wake_up_interruptible(&session->wait);
break;
}
if (!info && ctrl) {
memcpy(&ctrl->profile,
skb->data + CAPI_MSG_BASELEN + 11,
sizeof(capi_profile));
session->state = BT_CONNECTED;
capi_ctr_ready(ctrl);
}
break;
case CAPI_FUNCTION_GET_MANUFACTURER:
if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 14)
strscpy_pad(ctrl->manu,
skb->data + CAPI_MSG_BASELEN + 15,
skb->data[CAPI_MSG_BASELEN + 14]);
break;
case CAPI_FUNCTION_GET_VERSION:
if (skb->len < CAPI_MSG_BASELEN + 32)
break;
if (!info && ctrl) {
ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16);
ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20);
ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24);
ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28);
}
break;
case CAPI_FUNCTION_GET_SERIAL_NUMBER:
if (!info && ctrl && skb->len > CAPI_MSG_BASELEN + 16)
strscpy_pad(ctrl->serial,
skb->data + CAPI_MSG_BASELEN + 17,
skb->data[CAPI_MSG_BASELEN + 16]);
break;
}
break;
case CAPI_IND:
if (skb->len < CAPI_MSG_BASELEN + 6)
break;
func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3);
if (func == CAPI_FUNCTION_LOOPBACK) {
int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6,
skb->data[CAPI_MSG_BASELEN + 5]);
appl = CAPIMSG_APPID(skb->data);
msgnum = CAPIMSG_MSGID(skb->data);
cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func,
skb->data + CAPI_MSG_BASELEN + 6, len);
}
break;
}
kfree_skb(skb);
}
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
{
struct capi_ctr *ctrl = &session->ctrl;
struct cmtp_application *application;
__u16 appl;
__u32 contr;
BT_DBG("session %p skb %p len %u", session, skb, skb->len);
if (skb->len < CAPI_MSG_BASELEN)
return;
if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) {
cmtp_recv_interopmsg(session, skb);
return;
}
if (session->flags & BIT(CMTP_LOOPBACK)) {
kfree_skb(skb);
return;
}
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
application = cmtp_application_get(session, CMTP_MAPPING, appl);
if (application) {
appl = application->appl;
CAPIMSG_SETAPPID(skb->data, appl);
} else {
BT_ERR("Can't find application with id %u", appl);
kfree_skb(skb);
return;
}
if ((contr & 0x7f) == 0x01) {
contr = (contr & 0xffffff80) | session->num;
CAPIMSG_SETCONTROL(skb->data, contr);
}
capi_ctr_handle_message(ctrl, appl, skb);
}
static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
{
BT_DBG("ctrl %p data %p", ctrl, data);
return 0;
}
static void cmtp_reset_ctr(struct capi_ctr *ctrl)
{
struct cmtp_session *session = ctrl->driverdata;
BT_DBG("ctrl %p", ctrl);
capi_ctr_down(ctrl);
atomic_inc(&session->terminate);
wake_up_process(session->task);
}
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
{
DECLARE_WAITQUEUE(wait, current);
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
unsigned long timeo = CMTP_INTEROP_TIMEOUT;
unsigned char buf[8];
int err = 0, nconn, want = rp->level3cnt;
BT_DBG("ctrl %p appl %u level3cnt %u datablkcnt %u datablklen %u",
ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen);
application = cmtp_application_add(session, appl);
if (!application) {
BT_ERR("Can't allocate memory for new application");
return;
}
if (want < 0)
nconn = ctrl->profile.nbchannel * -want;
else
nconn = want;
if (nconn == 0)
nconn = ctrl->profile.nbchannel;
capimsg_setu16(buf, 0, nconn);
capimsg_setu16(buf, 2, rp->datablkcnt);
capimsg_setu16(buf, 4, rp->datablklen);
application->state = BT_CONFIG;
application->msgnum = cmtp_msgnum_get(session);
cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum,
CAPI_FUNCTION_REGISTER, buf, 6);
add_wait_queue(&session->wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (!timeo) {
err = -EAGAIN;
break;
}
if (application->state == BT_CLOSED) {
err = -application->err;
break;
}
if (application->state == BT_CONNECTED)
break;
if (signal_pending(current)) {
err = -EINTR;
break;
}
timeo = schedule_timeout(timeo);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&session->wait, &wait);
if (err) {
cmtp_application_del(session, application);
return;
}
}
static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
BT_DBG("ctrl %p appl %u", ctrl, appl);
application = cmtp_application_get(session, CMTP_APPLID, appl);
if (!application) {
BT_ERR("Can't find application");
return;
}
application->msgnum = cmtp_msgnum_get(session);
cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum,
CAPI_FUNCTION_RELEASE, NULL, 0);
wait_event_interruptible_timeout(session->wait,
(application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT);
cmtp_application_del(session, application);
}
static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
{
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *application;
__u16 appl;
__u32 contr;
BT_DBG("ctrl %p skb %p", ctrl, skb);
appl = CAPIMSG_APPID(skb->data);
contr = CAPIMSG_CONTROL(skb->data);
application = cmtp_application_get(session, CMTP_APPLID, appl);
if ((!application) || (application->state != BT_CONNECTED)) {
BT_ERR("Can't find application with id %u", appl);
return CAPI_ILLAPPNR;
}
CAPIMSG_SETAPPID(skb->data, application->mapping);
if ((contr & 0x7f) == session->num) {
contr = (contr & 0xffffff80) | 0x01;
CAPIMSG_SETCONTROL(skb->data, contr);
}
cmtp_send_capimsg(session, skb);
return CAPI_NOERROR;
}
static char *cmtp_procinfo(struct capi_ctr *ctrl)
{
return "CAPI Message Transport Protocol";
}
static int cmtp_proc_show(struct seq_file *m, void *v)
{
struct capi_ctr *ctrl = m->private;
struct cmtp_session *session = ctrl->driverdata;
struct cmtp_application *app;
seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
seq_printf(m, "addr %s\n", session->name);
seq_printf(m, "ctrl %d\n", session->num);
list_for_each_entry(app, &session->applications, list) {
seq_printf(m, "appl %u -> %u\n", app->appl, app->mapping);
}
return 0;
}
int cmtp_attach_device(struct cmtp_session *session)
{
unsigned char buf[4];
long ret;
BT_DBG("session %p", session);
capimsg_setu32(buf, 0, 0);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM,
CAPI_FUNCTION_GET_PROFILE, buf, 4);
ret = wait_event_interruptible_timeout(session->wait,
session->ncontroller, CMTP_INTEROP_TIMEOUT);
BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name);
if (!ret)
return -ETIMEDOUT;
if (!session->ncontroller)
return -ENODEV;
if (session->ncontroller > 1)
BT_INFO("Setting up only CAPI controller 1");
session->ctrl.owner = THIS_MODULE;
session->ctrl.driverdata = session;
strcpy(session->ctrl.name, session->name);
session->ctrl.driver_name = "cmtp";
session->ctrl.load_firmware = cmtp_load_firmware;
session->ctrl.reset_ctr = cmtp_reset_ctr;
session->ctrl.register_appl = cmtp_register_appl;
session->ctrl.release_appl = cmtp_release_appl;
session->ctrl.send_message = cmtp_send_message;
session->ctrl.procinfo = cmtp_procinfo;
session->ctrl.proc_show = cmtp_proc_show;
if (attach_capi_ctr(&session->ctrl) < 0) {
BT_ERR("Can't attach new controller");
return -EBUSY;
}
session->num = session->ctrl.cnr;
BT_DBG("session %p num %d", session, session->num);
capimsg_setu32(buf, 0, 1);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_MANUFACTURER, buf, 4);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_VERSION, buf, 4);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4);
cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session),
CAPI_FUNCTION_GET_PROFILE, buf, 4);
return 0;
}
void cmtp_detach_device(struct cmtp_session *session)
{
BT_DBG("session %p", session);
detach_capi_ctr(&session->ctrl);
}

View File

@@ -1,129 +0,0 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#ifndef __CMTP_H
#define __CMTP_H
#include <linux/types.h>
#include <net/bluetooth/bluetooth.h>
#define BTNAMSIZ 21
/* CMTP ioctl defines */
#define CMTPCONNADD _IOW('C', 200, int)
#define CMTPCONNDEL _IOW('C', 201, int)
#define CMTPGETCONNLIST _IOR('C', 210, int)
#define CMTPGETCONNINFO _IOR('C', 211, int)
#define CMTP_LOOPBACK 0
struct cmtp_connadd_req {
int sock; /* Connected socket */
__u32 flags;
};
struct cmtp_conndel_req {
bdaddr_t bdaddr;
__u32 flags;
};
struct cmtp_conninfo {
bdaddr_t bdaddr;
__u32 flags;
__u16 state;
int num;
};
struct cmtp_connlist_req {
__u32 cnum;
struct cmtp_conninfo __user *ci;
};
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock);
int cmtp_del_connection(struct cmtp_conndel_req *req);
int cmtp_get_connlist(struct cmtp_connlist_req *req);
int cmtp_get_conninfo(struct cmtp_conninfo *ci);
/* CMTP session defines */
#define CMTP_INTEROP_TIMEOUT (HZ * 5)
#define CMTP_INITIAL_MSGNUM 0xff00
struct cmtp_session {
struct list_head list;
struct socket *sock;
bdaddr_t bdaddr;
unsigned long state;
unsigned long flags;
uint mtu;
char name[BTNAMSIZ];
atomic_t terminate;
struct task_struct *task;
wait_queue_head_t wait;
int ncontroller;
int num;
struct capi_ctr ctrl;
struct list_head applications;
unsigned long blockids;
int msgnum;
struct sk_buff_head transmit;
struct sk_buff *reassembly[16];
};
struct cmtp_application {
struct list_head list;
unsigned long state;
int err;
__u16 appl;
__u16 mapping;
__u16 msgnum;
};
struct cmtp_scb {
int id;
int data;
};
int cmtp_attach_device(struct cmtp_session *session);
void cmtp_detach_device(struct cmtp_session *session);
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
/* CMTP init defines */
int cmtp_init_sockets(void);
void cmtp_cleanup_sockets(void);
#endif /* __CMTP_H */

View File

@@ -1,519 +0,0 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/freezer.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/l2cap.h>
#include "cmtp.h"
#define VERSION "1.0"
static DECLARE_RWSEM(cmtp_session_sem);
static LIST_HEAD(cmtp_session_list);
static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
{
struct cmtp_session *session;
BT_DBG("");
list_for_each_entry(session, &cmtp_session_list, list)
if (!bacmp(bdaddr, &session->bdaddr))
return session;
return NULL;
}
static void __cmtp_link_session(struct cmtp_session *session)
{
list_add(&session->list, &cmtp_session_list);
}
static void __cmtp_unlink_session(struct cmtp_session *session)
{
list_del(&session->list);
}
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
{
u32 valid_flags = BIT(CMTP_LOOPBACK);
memset(ci, 0, sizeof(*ci));
bacpy(&ci->bdaddr, &session->bdaddr);
ci->flags = session->flags & valid_flags;
ci->state = session->state;
ci->num = session->num;
}
static inline int cmtp_alloc_block_id(struct cmtp_session *session)
{
int i, id = -1;
for (i = 0; i < 16; i++)
if (!test_and_set_bit(i, &session->blockids)) {
id = i;
break;
}
return id;
}
static inline void cmtp_free_block_id(struct cmtp_session *session, int id)
{
clear_bit(id, &session->blockids);
}
static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count)
{
struct sk_buff *skb = session->reassembly[id], *nskb;
int size;
BT_DBG("session %p buf %p count %d", session, buf, count);
size = (skb) ? skb->len + count : count;
nskb = alloc_skb(size, GFP_ATOMIC);
if (!nskb) {
BT_ERR("Can't allocate memory for CAPI message");
return;
}
if (skb && (skb->len > 0))
skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len);
skb_put_data(nskb, buf, count);
session->reassembly[id] = nskb;
kfree_skb(skb);
}
static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
{
__u8 hdr, hdrlen, id;
__u16 len;
BT_DBG("session %p skb %p len %d", session, skb, skb->len);
while (skb->len > 0) {
hdr = skb->data[0];
switch (hdr & 0xc0) {
case 0x40:
hdrlen = 2;
len = skb->data[1];
break;
case 0x80:
hdrlen = 3;
len = skb->data[1] | (skb->data[2] << 8);
break;
default:
hdrlen = 1;
len = 0;
break;
}
id = (hdr & 0x3c) >> 2;
BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id);
if (hdrlen + len > skb->len) {
BT_ERR("Wrong size or header information in CMTP frame");
break;
}
if (len == 0) {
skb_pull(skb, hdrlen);
continue;
}
switch (hdr & 0x03) {
case 0x00:
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
cmtp_recv_capimsg(session, session->reassembly[id]);
session->reassembly[id] = NULL;
break;
case 0x01:
cmtp_add_msgpart(session, id, skb->data + hdrlen, len);
break;
default:
kfree_skb(session->reassembly[id]);
session->reassembly[id] = NULL;
break;
}
skb_pull(skb, hdrlen + len);
}
kfree_skb(skb);
return 0;
}
static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len)
{
struct socket *sock = session->sock;
struct kvec iv = { data, len };
struct msghdr msg;
BT_DBG("session %p data %p len %d", session, data, len);
if (!len)
return 0;
memset(&msg, 0, sizeof(msg));
return kernel_sendmsg(sock, &msg, &iv, 1, len);
}
static void cmtp_process_transmit(struct cmtp_session *session)
{
struct sk_buff *skb, *nskb;
unsigned char *hdr;
unsigned int size, tail;
BT_DBG("session %p", session);
nskb = alloc_skb(session->mtu, GFP_ATOMIC);
if (!nskb) {
BT_ERR("Can't allocate memory for new frame");
return;
}
while ((skb = skb_dequeue(&session->transmit))) {
struct cmtp_scb *scb = (void *) skb->cb;
tail = session->mtu - nskb->len;
if (tail < 5) {
cmtp_send_frame(session, nskb->data, nskb->len);
skb_trim(nskb, 0);
tail = session->mtu;
}
size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
if (scb->id < 0) {
scb->id = cmtp_alloc_block_id(session);
if (scb->id < 0) {
skb_queue_head(&session->transmit, skb);
break;
}
}
if (size < 256) {
hdr = skb_put(nskb, 2);
hdr[0] = 0x40
| ((scb->id << 2) & 0x3c)
| ((skb->len == size) ? 0x00 : 0x01);
hdr[1] = size;
} else {
hdr = skb_put(nskb, 3);
hdr[0] = 0x80
| ((scb->id << 2) & 0x3c)
| ((skb->len == size) ? 0x00 : 0x01);
hdr[1] = size & 0xff;
hdr[2] = size >> 8;
}
skb_copy_from_linear_data(skb, skb_put(nskb, size), size);
skb_pull(skb, size);
if (skb->len > 0) {
skb_queue_head(&session->transmit, skb);
} else {
cmtp_free_block_id(session, scb->id);
if (scb->data) {
cmtp_send_frame(session, nskb->data, nskb->len);
skb_trim(nskb, 0);
}
kfree_skb(skb);
}
}
cmtp_send_frame(session, nskb->data, nskb->len);
kfree_skb(nskb);
}
static int cmtp_session(void *arg)
{
struct cmtp_session *session = arg;
struct sock *sk = session->sock->sk;
struct sk_buff *skb;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
BT_DBG("session %p", session);
set_user_nice(current, -15);
add_wait_queue(sk_sleep(sk), &wait);
while (1) {
if (atomic_read(&session->terminate))
break;
if (sk->sk_state != BT_CONNECTED)
break;
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
if (!skb_linearize(skb))
cmtp_recv_frame(session, skb);
else
kfree_skb(skb);
}
cmtp_process_transmit(session);
/*
* wait_woken() performs the necessary memory barriers
* for us; see the header comment for this primitive.
*/
wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
remove_wait_queue(sk_sleep(sk), &wait);
down_write(&cmtp_session_sem);
if (!(session->flags & BIT(CMTP_LOOPBACK)))
cmtp_detach_device(session);
fput(session->sock->file);
__cmtp_unlink_session(session);
up_write(&cmtp_session_sem);
kfree(session);
module_put_and_kthread_exit(0);
return 0;
}
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
{
u32 valid_flags = BIT(CMTP_LOOPBACK);
struct cmtp_session *session, *s;
int i, err;
BT_DBG("");
if (!l2cap_is_socket(sock))
return -EBADFD;
if (req->flags & ~valid_flags)
return -EINVAL;
session = kzalloc_obj(struct cmtp_session);
if (!session)
return -ENOMEM;
down_write(&cmtp_session_sem);
s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst);
if (s && s->state == BT_CONNECTED) {
err = -EEXIST;
goto failed;
}
bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst);
session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
l2cap_pi(sock->sk)->chan->imtu);
BT_DBG("mtu %d", session->mtu);
sprintf(session->name, "%pMR", &session->bdaddr);
session->sock = sock;
session->state = BT_CONFIG;
init_waitqueue_head(&session->wait);
session->msgnum = CMTP_INITIAL_MSGNUM;
INIT_LIST_HEAD(&session->applications);
skb_queue_head_init(&session->transmit);
for (i = 0; i < 16; i++)
session->reassembly[i] = NULL;
session->flags = req->flags;
__cmtp_link_session(session);
__module_get(THIS_MODULE);
session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
session->num);
if (IS_ERR(session->task)) {
module_put(THIS_MODULE);
err = PTR_ERR(session->task);
goto unlink;
}
if (!(session->flags & BIT(CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
if (err < 0) {
/* Caller will call fput in case of failure, and so
* will cmtp_session kthread.
*/
get_file(session->sock->file);
atomic_inc(&session->terminate);
wake_up_interruptible(sk_sleep(session->sock->sk));
up_write(&cmtp_session_sem);
return err;
}
}
up_write(&cmtp_session_sem);
return 0;
unlink:
__cmtp_unlink_session(session);
failed:
up_write(&cmtp_session_sem);
kfree(session);
return err;
}
int cmtp_del_connection(struct cmtp_conndel_req *req)
{
u32 valid_flags = 0;
struct cmtp_session *session;
int err = 0;
BT_DBG("");
if (req->flags & ~valid_flags)
return -EINVAL;
down_read(&cmtp_session_sem);
session = __cmtp_get_session(&req->bdaddr);
if (session) {
/* Flush the transmit queue */
skb_queue_purge(&session->transmit);
/* Stop session thread */
atomic_inc(&session->terminate);
/*
* See the comment preceding the call to wait_woken()
* in cmtp_session().
*/
wake_up_interruptible(sk_sleep(session->sock->sk));
} else
err = -ENOENT;
up_read(&cmtp_session_sem);
return err;
}
int cmtp_get_connlist(struct cmtp_connlist_req *req)
{
struct cmtp_session *session;
int err = 0, n = 0;
BT_DBG("");
down_read(&cmtp_session_sem);
list_for_each_entry(session, &cmtp_session_list, list) {
struct cmtp_conninfo ci;
__cmtp_copy_session(session, &ci);
if (copy_to_user(req->ci, &ci, sizeof(ci))) {
err = -EFAULT;
break;
}
if (++n >= req->cnum)
break;
req->ci++;
}
req->cnum = n;
up_read(&cmtp_session_sem);
return err;
}
int cmtp_get_conninfo(struct cmtp_conninfo *ci)
{
struct cmtp_session *session;
int err = 0;
down_read(&cmtp_session_sem);
session = __cmtp_get_session(&ci->bdaddr);
if (session)
__cmtp_copy_session(session, ci);
else
err = -ENOENT;
up_read(&cmtp_session_sem);
return err;
}
static int __init cmtp_init(void)
{
BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
return cmtp_init_sockets();
}
static void __exit cmtp_exit(void)
{
cmtp_cleanup_sockets();
}
module_init(cmtp_init);
module_exit(cmtp_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("bt-proto-5");

View File

@@ -1,271 +0,0 @@
/*
CMTP implementation for Linux Bluetooth stack (BlueZ).
Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
SOFTWARE IS DISCLAIMED.
*/
#include <linux/export.h>
#include <linux/types.h>
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/skbuff.h>
#include <linux/socket.h>
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/compat.h>
#include <linux/gfp.h>
#include <linux/uaccess.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include "cmtp.h"
static struct bt_sock_list cmtp_sk_list = {
.lock = __RW_LOCK_UNLOCKED(cmtp_sk_list.lock)
};
static int cmtp_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
BT_DBG("sock %p sk %p", sock, sk);
if (!sk)
return 0;
bt_sock_unlink(&cmtp_sk_list, sk);
sock_orphan(sk);
sock_put(sk);
return 0;
}
static int do_cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, void __user *argp)
{
struct cmtp_connadd_req ca;
struct cmtp_conndel_req cd;
struct cmtp_connlist_req cl;
struct cmtp_conninfo ci;
struct socket *nsock;
int err;
BT_DBG("cmd %x arg %p", cmd, argp);
switch (cmd) {
case CMTPCONNADD:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
nsock = sockfd_lookup(ca.sock, &err);
if (!nsock)
return err;
if (nsock->sk->sk_state != BT_CONNECTED) {
sockfd_put(nsock);
return -EBADFD;
}
err = cmtp_add_connection(&ca, nsock);
if (!err) {
if (copy_to_user(argp, &ca, sizeof(ca)))
err = -EFAULT;
} else
sockfd_put(nsock);
return err;
case CMTPCONNDEL:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
return cmtp_del_connection(&cd);
case CMTPGETCONNLIST:
if (copy_from_user(&cl, argp, sizeof(cl)))
return -EFAULT;
if (cl.cnum <= 0)
return -EINVAL;
err = cmtp_get_connlist(&cl);
if (!err && copy_to_user(argp, &cl, sizeof(cl)))
return -EFAULT;
return err;
case CMTPGETCONNINFO:
if (copy_from_user(&ci, argp, sizeof(ci)))
return -EFAULT;
err = cmtp_get_conninfo(&ci);
if (!err && copy_to_user(argp, &ci, sizeof(ci)))
return -EFAULT;
return err;
}
return -EINVAL;
}
static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
return do_cmtp_sock_ioctl(sock, cmd, (void __user *)arg);
}
#ifdef CONFIG_COMPAT
static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
void __user *argp = compat_ptr(arg);
if (cmd == CMTPGETCONNLIST) {
struct cmtp_connlist_req cl;
u32 __user *p = argp;
u32 uci;
int err;
if (get_user(cl.cnum, p) || get_user(uci, p + 1))
return -EFAULT;
cl.ci = compat_ptr(uci);
if (cl.cnum <= 0)
return -EINVAL;
err = cmtp_get_connlist(&cl);
if (!err && put_user(cl.cnum, p))
err = -EFAULT;
return err;
}
return do_cmtp_sock_ioctl(sock, cmd, argp);
}
#endif
static const struct proto_ops cmtp_sock_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.release = cmtp_sock_release,
.ioctl = cmtp_sock_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = cmtp_sock_compat_ioctl,
#endif
.bind = sock_no_bind,
.getname = sock_no_getname,
.sendmsg = sock_no_sendmsg,
.recvmsg = sock_no_recvmsg,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.mmap = sock_no_mmap
};
static struct proto cmtp_proto = {
.name = "CMTP",
.owner = THIS_MODULE,
.obj_size = sizeof(struct bt_sock)
};
static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
BT_DBG("sock %p", sock);
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto, kern);
if (!sk)
return -ENOMEM;
sock_init_data(sock, sk);
sock->ops = &cmtp_sock_ops;
sock->state = SS_UNCONNECTED;
sock_reset_flag(sk, SOCK_ZAPPED);
sk->sk_protocol = protocol;
sk->sk_state = BT_OPEN;
bt_sock_link(&cmtp_sk_list, sk);
return 0;
}
static const struct net_proto_family cmtp_sock_family_ops = {
.family = PF_BLUETOOTH,
.owner = THIS_MODULE,
.create = cmtp_sock_create
};
int cmtp_init_sockets(void)
{
int err;
err = proto_register(&cmtp_proto, 0);
if (err < 0)
return err;
err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops);
if (err < 0) {
BT_ERR("Can't register CMTP socket");
goto error;
}
err = bt_procfs_init(&init_net, "cmtp", &cmtp_sk_list, NULL);
if (err < 0) {
BT_ERR("Failed to create CMTP proc file");
bt_sock_unregister(BTPROTO_HIDP);
goto error;
}
BT_INFO("CMTP socket layer initialized");
return 0;
error:
proto_unregister(&cmtp_proto);
return err;
}
void cmtp_cleanup_sockets(void)
{
bt_procfs_cleanup(&init_net, "cmtp");
bt_sock_unregister(BTPROTO_CMTP);
proto_unregister(&cmtp_proto);
}