mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-30 14:06:51 +07:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/kkeil/ISDN-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/kkeil/ISDN-2.6: Add layer1 over IP support Add mISDN HFC multiport driver Add mISDN HFC PCI driver Add mISDN DSP Add mISDN core files Define AF_ISDN and PF_ISDN Add mISDN driver
This commit is contained in:
commit
5c7c204aec
6
Documentation/isdn/README.mISDN
Normal file
6
Documentation/isdn/README.mISDN
Normal file
@ -0,0 +1,6 @@
|
||||
mISDN is a new modular ISDN driver, in the long term it should replace
|
||||
the old I4L driver architecture for passiv 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.
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
menuconfig ISDN
|
||||
tristate "ISDN support"
|
||||
bool "ISDN support"
|
||||
depends on NET
|
||||
depends on !S390
|
||||
---help---
|
||||
@ -21,6 +21,8 @@ menuconfig ISDN
|
||||
|
||||
if ISDN
|
||||
|
||||
source "drivers/isdn/mISDN/Kconfig"
|
||||
|
||||
menuconfig ISDN_I4L
|
||||
tristate "Old ISDN4Linux (deprecated)"
|
||||
---help---
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
obj-$(CONFIG_ISDN_I4L) += i4l/
|
||||
obj-$(CONFIG_ISDN_CAPI) += capi/
|
||||
obj-$(CONFIG_MISDN) += mISDN/
|
||||
obj-$(CONFIG_ISDN_CAPI) += hardware/
|
||||
obj-$(CONFIG_ISDN_DIVERSION) += divert/
|
||||
obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/
|
||||
|
@ -4,3 +4,4 @@
|
||||
|
||||
obj-$(CONFIG_CAPI_AVM) += avm/
|
||||
obj-$(CONFIG_CAPI_EICON) += eicon/
|
||||
obj-$(CONFIG_MISDN) += mISDN/
|
||||
|
25
drivers/isdn/hardware/mISDN/Kconfig
Normal file
25
drivers/isdn/hardware/mISDN/Kconfig
Normal file
@ -0,0 +1,25 @@
|
||||
#
|
||||
# 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
|
||||
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)
|
||||
|
7
drivers/isdn/hardware/mISDN/Makefile
Normal file
7
drivers/isdn/hardware/mISDN/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for the modular ISDN hardware drivers
|
||||
#
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
|
||||
obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
|
1204
drivers/isdn/hardware/mISDN/hfc_multi.h
Normal file
1204
drivers/isdn/hardware/mISDN/hfc_multi.h
Normal file
File diff suppressed because it is too large
Load Diff
228
drivers/isdn/hardware/mISDN/hfc_pci.h
Normal file
228
drivers/isdn/hardware/mISDN/hfc_pci.h
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* specific defines for CCD's HFC 2BDS0 PCI chips
|
||||
*
|
||||
* Author Werner Cornelius (werner@isdn4linux.de)
|
||||
*
|
||||
* Copyright 1999 by Werner Cornelius (werner@isdn4linux.de)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* thresholds for transparent B-channel mode
|
||||
* change mask and threshold simultaneously
|
||||
*/
|
||||
#define HFCPCI_BTRANS_THRESHOLD 128
|
||||
#define HFCPCI_BTRANS_MAX 256
|
||||
#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 {
|
||||
unsigned short z1; /* Z1 pointer 16 Bit */
|
||||
unsigned short 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))
|
5320
drivers/isdn/hardware/mISDN/hfcmulti.c
Normal file
5320
drivers/isdn/hardware/mISDN/hfcmulti.c
Normal file
File diff suppressed because it is too large
Load Diff
2256
drivers/isdn/hardware/mISDN/hfcpci.c
Normal file
2256
drivers/isdn/hardware/mISDN/hfcpci.c
Normal file
File diff suppressed because it is too large
Load Diff
44
drivers/isdn/mISDN/Kconfig
Normal file
44
drivers/isdn/mISDN/Kconfig
Normal file
@ -0,0 +1,44 @@
|
||||
#
|
||||
# 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
|
||||
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 cancelation, 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 informations about this module and it's 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
|
13
drivers/isdn/mISDN/Makefile
Normal file
13
drivers/isdn/mISDN/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
#
|
||||
# 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 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
|
244
drivers/isdn/mISDN/core.c
Normal file
244
drivers/isdn/mISDN/core.c
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#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_LICENSE("GPL");
|
||||
module_param(debug, uint, S_IRUGO | S_IWUSR);
|
||||
|
||||
static LIST_HEAD(devices);
|
||||
DEFINE_RWLOCK(device_lock);
|
||||
static u64 device_ids;
|
||||
#define MAX_DEVICE_ID 63
|
||||
|
||||
static LIST_HEAD(Bprotocols);
|
||||
DEFINE_RWLOCK(bp_lock);
|
||||
|
||||
struct mISDNdevice
|
||||
*get_mdevice(u_int id)
|
||||
{
|
||||
struct mISDNdevice *dev;
|
||||
|
||||
read_lock(&device_lock);
|
||||
list_for_each_entry(dev, &devices, D.list)
|
||||
if (dev->id == id) {
|
||||
read_unlock(&device_lock);
|
||||
return dev;
|
||||
}
|
||||
read_unlock(&device_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
get_mdevice_count(void)
|
||||
{
|
||||
struct mISDNdevice *dev;
|
||||
int cnt = 0;
|
||||
|
||||
read_lock(&device_lock);
|
||||
list_for_each_entry(dev, &devices, D.list)
|
||||
cnt++;
|
||||
read_unlock(&device_lock);
|
||||
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))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
mISDN_register_device(struct mISDNdevice *dev, char *name)
|
||||
{
|
||||
u_long flags;
|
||||
int err;
|
||||
|
||||
dev->id = get_free_devid();
|
||||
if (dev->id < 0)
|
||||
return -EBUSY;
|
||||
if (name && name[0])
|
||||
strcpy(dev->name, name);
|
||||
else
|
||||
sprintf(dev->name, "mISDN%d", dev->id);
|
||||
if (debug & DEBUG_CORE)
|
||||
printk(KERN_DEBUG "mISDN_register %s %d\n",
|
||||
dev->name, dev->id);
|
||||
err = create_stack(dev);
|
||||
if (err)
|
||||
return err;
|
||||
write_lock_irqsave(&device_lock, flags);
|
||||
list_add_tail(&dev->D.list, &devices);
|
||||
write_unlock_irqrestore(&device_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mISDN_register_device);
|
||||
|
||||
void
|
||||
mISDN_unregister_device(struct mISDNdevice *dev) {
|
||||
u_long flags;
|
||||
|
||||
if (debug & DEBUG_CORE)
|
||||
printk(KERN_DEBUG "mISDN_unregister %s %d\n",
|
||||
dev->name, dev->id);
|
||||
write_lock_irqsave(&device_lock, flags);
|
||||
list_del(&dev->D.list);
|
||||
write_unlock_irqrestore(&device_lock, flags);
|
||||
test_and_clear_bit(dev->id, (u_long *)&device_ids);
|
||||
delete_stack(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;
|
||||
}
|
||||
|
||||
struct Bprotocol *
|
||||
get_Bprotocol4id(u_int id)
|
||||
{
|
||||
u_int m;
|
||||
|
||||
if (id < ISDN_P_B_START || id > 63) {
|
||||
printk(KERN_WARNING "%s id not in range %d\n",
|
||||
__func__, id);
|
||||
return NULL;
|
||||
}
|
||||
m = 1 << (id & ISDN_P_B_MASK);
|
||||
return get_Bprotocol4mask(m);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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_initstack(&debug);
|
||||
err = mISDN_inittimer(&debug);
|
||||
if (err)
|
||||
goto error;
|
||||
err = l1_init(&debug);
|
||||
if (err) {
|
||||
mISDN_timer_cleanup();
|
||||
goto error;
|
||||
}
|
||||
err = Isdnl2_Init(&debug);
|
||||
if (err) {
|
||||
mISDN_timer_cleanup();
|
||||
l1_cleanup();
|
||||
goto error;
|
||||
}
|
||||
err = misdn_sock_init(&debug);
|
||||
if (err) {
|
||||
mISDN_timer_cleanup();
|
||||
l1_cleanup();
|
||||
Isdnl2_cleanup();
|
||||
}
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
|
||||
void mISDN_cleanup(void)
|
||||
{
|
||||
misdn_sock_cleanup();
|
||||
mISDN_timer_cleanup();
|
||||
l1_cleanup();
|
||||
Isdnl2_cleanup();
|
||||
|
||||
if (!list_empty(&devices))
|
||||
printk(KERN_ERR "%s devices still registered\n", __func__);
|
||||
|
||||
if (!list_empty(&Bprotocols))
|
||||
printk(KERN_ERR "%s Bprotocols still registered\n", __func__);
|
||||
printk(KERN_DEBUG "mISDNcore unloaded\n");
|
||||
}
|
||||
|
||||
module_init(mISDNInit);
|
||||
module_exit(mISDN_cleanup);
|
||||
|
77
drivers/isdn/mISDN/core.h
Normal file
77
drivers/isdn/mISDN/core.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#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);
|
||||
struct Bprotocol *get_Bprotocol4id(u_int);
|
||||
|
||||
extern int mISDN_inittimer(u_int *);
|
||||
extern void mISDN_timer_cleanup(void);
|
||||
|
||||
extern int l1_init(u_int *);
|
||||
extern void l1_cleanup(void);
|
||||
extern int Isdnl2_Init(u_int *);
|
||||
extern void Isdnl2_cleanup(void);
|
||||
|
||||
#endif
|
263
drivers/isdn/mISDN/dsp.h
Normal file
263
drivers/isdn/mISDN/dsp.h
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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_DTMFCOEFF 0x8000 /* heavy output */
|
||||
|
||||
/* options may be:
|
||||
*
|
||||
* bit 0 = use ulaw instead of alaw
|
||||
* bit 1 = enable hfc hardware accelleration 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;
|
||||
extern u32 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_BUFLEN (4*128)
|
||||
|
||||
struct dsp_dtmf {
|
||||
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]; /* just the 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;
|
||||
};
|
||||
|
||||
/*****************
|
||||
* general stuff *
|
||||
*****************/
|
||||
|
||||
struct dsp {
|
||||
struct list_head list;
|
||||
struct mISDNchannel ch;
|
||||
struct mISDNchannel *up;
|
||||
unsigned char name[64];
|
||||
int b_active;
|
||||
int echo; /* echo is enabled */
|
||||
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 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(void *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(void *arg);
|
||||
|
||||
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);
|
||||
|
434
drivers/isdn/mISDN/dsp_audio.c
Normal file
434
drivers/isdn/mISDN/dsp_audio.c
Normal file
@ -0,0 +1,434 @@
|
||||
/*
|
||||
* 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 "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 */
|
||||
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;
|
||||
}
|
||||
|
||||
static int reverse_bits(int i)
|
||||
{
|
||||
int z, j;
|
||||
z = 0;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
if ((i & (1 << j)) != 0)
|
||||
z |= 1 << (7 - j);
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
|
||||
void dsp_audio_generate_law_tables(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i));
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(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] =
|
||||
reverse_bits(linear2ulaw(i));
|
||||
}
|
||||
} else {
|
||||
/* generating alaw-table */
|
||||
for (i = -32768; i < 32768; i++) {
|
||||
dsp_audio_s16_to_law[i & 0xffff] =
|
||||
reverse_bits(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++;
|
||||
}
|
||||
}
|
||||
|
65
drivers/isdn/mISDN/dsp_biquad.h
Normal file
65
drivers/isdn/mISDN/dsp_biquad.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
672
drivers/isdn/mISDN/dsp_blowfish.c
Normal file
672
drivers/isdn/mISDN/dsp_blowfish.c
Normal file
@ -0,0 +1,672 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
struct bf_ctx {
|
||||
u32 p[18];
|
||||
u32 s[1024];
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
1886
drivers/isdn/mISDN/dsp_cmx.c
Normal file
1886
drivers/isdn/mISDN/dsp_cmx.c
Normal file
File diff suppressed because it is too large
Load Diff
1191
drivers/isdn/mISDN/dsp_core.c
Normal file
1191
drivers/isdn/mISDN/dsp_core.c
Normal file
File diff suppressed because it is too large
Load Diff
303
drivers/isdn/mISDN/dsp_dtmf.c
Normal file
303
drivers/isdn/mISDN/dsp_dtmf.c
Normal file
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* 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->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 speach, 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)
|
||||
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 inbetween */
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
110
drivers/isdn/mISDN/dsp_ecdis.h
Normal file
110
drivers/isdn/mISDN/dsp_ecdis.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 ------------------------------------------------------------*/
|
138
drivers/isdn/mISDN/dsp_hwec.c
Normal file
138
drivers/isdn/mISDN/dsp_hwec.c
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* dsp_hwec.c:
|
||||
* builtin mISDN dsp pipeline element for enabling the hw echocanceller
|
||||
*
|
||||
* Copyright (C) 2007, Nadi Sarrar
|
||||
*
|
||||
* Nadi Sarrar <nadi@beronet.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called LICENSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 = sizeof(args) / sizeof(struct mISDN_dsp_element_arg),
|
||||
.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[len + 1];
|
||||
char *dup, *tok, *name, *val;
|
||||
int tmp;
|
||||
|
||||
strcpy(_dup, arg);
|
||||
dup = _dup;
|
||||
|
||||
while ((tok = strsep(&dup, ","))) {
|
||||
if (!strlen(tok))
|
||||
continue;
|
||||
name = strsep(&tok, "=");
|
||||
val = tok;
|
||||
|
||||
if (!val)
|
||||
continue;
|
||||
|
||||
if (!strcmp(name, "deftaps")) {
|
||||
if (sscanf(val, "%d", &tmp) == 1)
|
||||
deftaps = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
10
drivers/isdn/mISDN/dsp_hwec.h
Normal file
10
drivers/isdn/mISDN/dsp_hwec.h
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* 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);
|
||||
|
348
drivers/isdn/mISDN/dsp_pipeline.c
Normal file
348
drivers/isdn/mISDN/dsp_pipeline.c
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* dsp_pipeline.c: pipelined audio processing
|
||||
*
|
||||
* Copyright (C) 2007, Nadi Sarrar
|
||||
*
|
||||
* Nadi Sarrar <nadi@beronet.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* The full GNU General Public License is included in this distribution in the
|
||||
* file called LICENSE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mISDNif.h>
|
||||
#include <linux/mISDNdsp.h>
|
||||
#include "dsp.h"
|
||||
#include "dsp_hwec.h"
|
||||
|
||||
/* uncomment for debugging */
|
||||
/*#define PIPELINE_DEBUG*/
|
||||
|
||||
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 struct class *elements_class;
|
||||
|
||||
static ssize_t
|
||||
attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
|
||||
ssize_t len = 0;
|
||||
int i = 0;
|
||||
|
||||
*buf = 0;
|
||||
for (; i < elem->num_args; ++i)
|
||||
len = sprintf(buf, "%sName: %s\n%s%s%sDescription: %s\n"
|
||||
"\n", buf,
|
||||
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 len;
|
||||
}
|
||||
|
||||
static struct device_attribute element_attributes[] = {
|
||||
__ATTR(args, 0444, attr_show_args, NULL),
|
||||
};
|
||||
|
||||
int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
|
||||
{
|
||||
struct dsp_element_entry *entry;
|
||||
int ret, i;
|
||||
|
||||
if (!elem)
|
||||
return -EINVAL;
|
||||
|
||||
entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
entry->elem = elem;
|
||||
|
||||
entry->dev.class = elements_class;
|
||||
dev_set_drvdata(&entry->dev, elem);
|
||||
snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name);
|
||||
ret = device_register(&entry->dev);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: failed to register %s\n",
|
||||
__func__, elem->name);
|
||||
goto err1;
|
||||
}
|
||||
|
||||
for (i = 0; i < (sizeof(element_attributes)
|
||||
/ sizeof(struct device_attribute)); ++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;
|
||||
}
|
||||
|
||||
list_add_tail(&entry->list, &dsp_elements);
|
||||
|
||||
printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
device_unregister(&entry->dev);
|
||||
err1:
|
||||
kfree(entry);
|
||||
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) {
|
||||
list_del(&entry->list);
|
||||
device_unregister(&entry->dev);
|
||||
kfree(entry);
|
||||
printk(KERN_DEBUG "%s: %s unregistered\n",
|
||||
__func__, elem->name);
|
||||
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)
|
||||
{
|
||||
elements_class = class_create(THIS_MODULE, "dsp_pipeline");
|
||||
if (IS_ERR(elements_class))
|
||||
return PTR_ERR(elements_class);
|
||||
|
||||
#ifdef PIPELINE_DEBUG
|
||||
printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
|
||||
#endif
|
||||
|
||||
dsp_hwec_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dsp_pipeline_module_exit(void)
|
||||
{
|
||||
struct dsp_element_entry *entry, *n;
|
||||
|
||||
dsp_hwec_exit();
|
||||
|
||||
class_destroy(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);
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
|
||||
}
|
||||
|
||||
int dsp_pipeline_init(struct dsp_pipeline *pipeline)
|
||||
{
|
||||
if (!pipeline)
|
||||
return -EINVAL;
|
||||
|
||||
INIT_LIST_HEAD(&pipeline->list);
|
||||
|
||||
#ifdef PIPELINE_DEBUG
|
||||
printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
#ifdef PIPELINE_DEBUG
|
||||
printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
|
||||
#endif
|
||||
}
|
||||
|
||||
int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
|
||||
{
|
||||
int len, incomplete = 0, found = 0;
|
||||
char *dup, *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);
|
||||
|
||||
if (!cfg)
|
||||
return 0;
|
||||
|
||||
len = strlen(cfg);
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
dup = kmalloc(len + 1, GFP_KERNEL);
|
||||
if (!dup)
|
||||
return 0;
|
||||
strcpy(dup, cfg);
|
||||
while ((tok = strsep(&dup, "|"))) {
|
||||
if (!strlen(tok))
|
||||
continue;
|
||||
name = strsep(&tok, "(");
|
||||
args = strsep(&tok, ")");
|
||||
if (args && !*args)
|
||||
args = 0;
|
||||
|
||||
list_for_each_entry_safe(entry, n, &dsp_elements, list)
|
||||
if (!strcmp(entry->elem->name, name)) {
|
||||
elem = entry->elem;
|
||||
|
||||
pipeline_entry = kmalloc(sizeof(struct
|
||||
dsp_pipeline_entry), GFP_KERNEL);
|
||||
if (!pipeline_entry) {
|
||||
printk(KERN_DEBUG "%s: failed to add "
|
||||
"entry to pipeline: %s (out of "
|
||||
"memory)\n", __func__, elem->name);
|
||||
incomplete = 1;
|
||||
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);
|
||||
#ifdef PIPELINE_DEBUG
|
||||
printk(KERN_DEBUG "%s: created "
|
||||
"instance of %s%s%s\n",
|
||||
__func__, name, args ?
|
||||
" with args " : "", args ?
|
||||
args : "");
|
||||
#endif
|
||||
} else {
|
||||
printk(KERN_DEBUG "%s: failed "
|
||||
"to add entry to pipeline: "
|
||||
"%s (new() returned NULL)\n",
|
||||
__func__, elem->name);
|
||||
kfree(pipeline_entry);
|
||||
incomplete = 1;
|
||||
}
|
||||
}
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (found)
|
||||
found = 0;
|
||||
else {
|
||||
printk(KERN_DEBUG "%s: element not found, skipping: "
|
||||
"%s\n", __func__, name);
|
||||
incomplete = 1;
|
||||
}
|
||||
}
|
||||
|
||||
_out:
|
||||
if (!list_empty(&pipeline->list))
|
||||
pipeline->inuse = 1;
|
||||
else
|
||||
pipeline->inuse = 0;
|
||||
|
||||
#ifdef PIPELINE_DEBUG
|
||||
printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
|
||||
__func__, incomplete ? " incomplete" : "", cfg);
|
||||
#endif
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
551
drivers/isdn/mISDN/dsp_tones.c
Normal file
551
drivers/isdn/mISDN/dsp_tones.c
Normal file
@ -0,0 +1,551 @@
|
||||
/*
|
||||
* 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/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 *
|
||||
****************************/
|
||||
|
||||
struct pattern {
|
||||
int tone;
|
||||
u8 *data[10];
|
||||
u32 *siz[10];
|
||||
u32 seq[10];
|
||||
} pattern[] = {
|
||||
{TONE_GERMAN_DIALTONE,
|
||||
{DATA_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GA, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{1900, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_OLDDIALTONE,
|
||||
{DATA_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GO, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{1998, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_AMERICAN_DIALTONE,
|
||||
{DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{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, 0, 0, 0, 0},
|
||||
{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0},
|
||||
{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, 0, 0, 0, 0},
|
||||
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0},
|
||||
{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, 0, 0, 0, 0},
|
||||
{SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, SIZE_DT, SIZE_S, 0, 0, 0, 0},
|
||||
{2000, 2000, 2000, 2000, 2000, 12000, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_RINGING,
|
||||
{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_OLDRINGING,
|
||||
{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_AMERICAN_RINGING,
|
||||
{DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_RINGPBX,
|
||||
{DATA_GA, DATA_S, DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GA, SIZE_S, SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0},
|
||||
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_OLDRINGPBX,
|
||||
{DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
|
||||
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_AMERICAN_RINGPBX,
|
||||
{DATA_RI, DATA_S, DATA_RI, DATA_S, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_RI, SIZE_S, SIZE_RI, SIZE_S, 0, 0, 0, 0, 0, 0},
|
||||
{4000, 4000, 4000, 28000, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_BUSY,
|
||||
{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_OLDBUSY,
|
||||
{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_AMERICAN_BUSY,
|
||||
{DATA_BU, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_BU, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_HANGUP,
|
||||
{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_OLDHANGUP,
|
||||
{DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_AMERICAN_HANGUP,
|
||||
{DATA_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_DT, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{8000, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_SPECIAL_INFO,
|
||||
{DATA_S1, DATA_S2, DATA_S3, DATA_S, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_S1, SIZE_S2, SIZE_S3, SIZE_S, 0, 0, 0, 0, 0, 0},
|
||||
{2666, 2666, 2666, 8002, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_GASSENBESETZT,
|
||||
{DATA_GA, DATA_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GA, SIZE_S, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{2000, 2000, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{TONE_GERMAN_AUFSCHALTTON,
|
||||
{DATA_GO, DATA_S, DATA_GO, DATA_S, 0, 0, 0, 0, 0, 0},
|
||||
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, 0, 0, 0, 0, 0, 0},
|
||||
{1000, 5000, 1000, 17000, 0, 0, 0, 0, 0, 0} },
|
||||
|
||||
{0,
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
|
||||
};
|
||||
|
||||
/******************
|
||||
* 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) {
|
||||
/* warp arround */
|
||||
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(void *arg)
|
||||
{
|
||||
struct dsp *dsp = arg;
|
||||
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, 0, 0);
|
||||
else
|
||||
dsp_tone_hw_message(dsp, pat->data[index], *(pat->siz[index]));
|
||||
/* set timer */
|
||||
init_timer(&tone->tl);
|
||||
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)
|
||||
if (timer_pending(&tonet->tl))
|
||||
del_timer(&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))
|
||||
del_timer(&tonet->tl);
|
||||
init_timer(&tonet->tl);
|
||||
tonet->tl.expires = jiffies + (pat->seq[0] * HZ) / 8000;
|
||||
add_timer(&tonet->tl);
|
||||
} else {
|
||||
tonet->software = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
183
drivers/isdn/mISDN/fsm.c
Normal file
183
drivers/isdn/mISDN/fsm.c
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include "fsm.h"
|
||||
|
||||
#define FSM_TIMER_DEBUG 0
|
||||
|
||||
void
|
||||
mISDN_FsmNew(struct Fsm *fsm,
|
||||
struct FsmNode *fnlist, int fncount)
|
||||
{
|
||||
int i;
|
||||
|
||||
fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count *
|
||||
fsm->event_count, GFP_KERNEL);
|
||||
|
||||
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;
|
||||
}
|
||||
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 FsmTimer *ft)
|
||||
{
|
||||
#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;
|
||||
ft->tl.function = (void *) FsmExpireTimer;
|
||||
ft->tl.data = (long) ft;
|
||||
#if FSM_TIMER_DEBUG
|
||||
if (ft->fi->debug)
|
||||
ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
|
||||
#endif
|
||||
init_timer(&ft->tl);
|
||||
}
|
||||
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
|
||||
del_timer(&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;
|
||||
}
|
||||
init_timer(&ft->tl);
|
||||
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))
|
||||
del_timer(&ft->tl);
|
||||
init_timer(&ft->tl);
|
||||
ft->event = event;
|
||||
ft->arg = arg;
|
||||
ft->tl.expires = jiffies + (millisec * HZ) / 1000;
|
||||
add_timer(&ft->tl);
|
||||
}
|
||||
EXPORT_SYMBOL(mISDN_FsmRestartTimer);
|
67
drivers/isdn/mISDN/fsm.h
Normal file
67
drivers/isdn/mISDN/fsm.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Thanks to Jan den Ouden
|
||||
* Fritz Elfert
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 void 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
|
365
drivers/isdn/mISDN/hwchannel.c
Normal file
365
drivers/isdn/mISDN/hwchannel.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#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))) {
|
||||
if (bch->rcount >= 64)
|
||||
printk(KERN_WARNING "B-channel %p receive "
|
||||
"queue if full, but empties...\n", bch);
|
||||
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, int maxlen)
|
||||
{
|
||||
ch->Flags = 0;
|
||||
ch->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_scheduled_work();
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mISDN_freedchannel);
|
||||
|
||||
int
|
||||
mISDN_freebchannel(struct bchannel *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;
|
||||
}
|
||||
if (ch->next_skb) {
|
||||
dev_kfree_skb(ch->next_skb);
|
||||
ch->next_skb = NULL;
|
||||
}
|
||||
skb_queue_purge(&ch->rqueue);
|
||||
ch->rcount = 0;
|
||||
flush_scheduled_work();
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mISDN_freebchannel);
|
||||
|
||||
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_Bchannel(struct bchannel *bch)
|
||||
{
|
||||
struct mISDNhead *hh;
|
||||
|
||||
hh = mISDN_HEAD_P(bch->rx_skb);
|
||||
hh->prim = PH_DATA_IND;
|
||||
hh->id = MISDN_ID_ANY;
|
||||
if (bch->rcount >= 64) {
|
||||
dev_kfree_skb(bch->rx_skb);
|
||||
bch->rx_skb = NULL;
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
||||
void
|
||||
confirm_Bsend(struct bchannel *bch)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (bch->rcount >= 64)
|
||||
return;
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL(confirm_Bsend);
|
||||
|
||||
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);
|
||||
if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
|
||||
confirm_Bsend(bch); /* not for transparent */
|
||||
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;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(bchannel_senddata);
|
91
drivers/isdn/mISDN/l1oip.h
Normal file
91
drivers/isdn/mISDN/l1oip.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 */
|
||||
|
||||
/* 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 iovec 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);
|
||||
|
374
drivers/isdn/mISDN/l1oip_codec.c
Normal file
374
drivers/isdn/mISDN/l1oip_codec.c
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
|
||||
* 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)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
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 "core.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)
|
||||
{
|
||||
if (table_dec)
|
||||
vfree(table_dec);
|
||||
if (table_com)
|
||||
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 = vmalloc(65536);
|
||||
table_dec = vmalloc(512);
|
||||
if (!table_com | !table_dec) {
|
||||
l1oip_4bit_free();
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(table_com, 0, 65536);
|
||||
memset(table_dec, 0, 512);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
1518
drivers/isdn/mISDN/l1oip_core.c
Normal file
1518
drivers/isdn/mISDN/l1oip_core.c
Normal file
File diff suppressed because it is too large
Load Diff
403
drivers/isdn/mISDN/layer1.c
Normal file
403
drivers/isdn/mISDN/layer1.c
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mISDNhw.h>
|
||||
#include "layer1.h"
|
||||
#include "fsm.h"
|
||||
|
||||
static int *debug;
|
||||
|
||||
struct layer1 {
|
||||
u_long Flags;
|
||||
struct FsmInst l1m;
|
||||
struct FsmTimer timer;
|
||||
int delay;
|
||||
struct dchannel *dch;
|
||||
dchannel_l1callback *dcb;
|
||||
};
|
||||
|
||||
#define TIMER3_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;
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
printk(KERN_DEBUG "%s: ", l1->dch->dev.name);
|
||||
vprintk(fmt, va);
|
||||
printk("\n");
|
||||
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->timer, 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->timer, 4);
|
||||
if (!test_bit(FLG_L1_ACTIVATED, &l1->Flags)) {
|
||||
if (test_and_clear_bit(FLG_L1_T3RUN, &l1->Flags))
|
||||
mISDN_FsmDelTimer(&l1->timer, 3);
|
||||
mISDN_FsmRestartTimer(&l1->timer, 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);
|
||||
l1->dcb(l1->dch, HW_POWERUP_REQ);
|
||||
}
|
||||
}
|
||||
|
||||
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->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
|
||||
test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
|
||||
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->timer, 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 (*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(sizeof(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->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->timer);
|
||||
__module_get(THIS_MODULE);
|
||||
dch->l1 = nl1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(create_l1);
|
||||
|
||||
int
|
||||
l1_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;
|
||||
mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
l1_cleanup(void)
|
||||
{
|
||||
mISDN_FsmFree(&l1fsm_s);
|
||||
}
|
26
drivers/isdn/mISDN/layer1.h
Normal file
26
drivers/isdn/mISDN/layer1.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
*
|
||||
* Layer 1 defines
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
|
2216
drivers/isdn/mISDN/layer2.c
Normal file
2216
drivers/isdn/mISDN/layer2.c
Normal file
File diff suppressed because it is too large
Load Diff
140
drivers/isdn/mISDN/layer2.h
Normal file
140
drivers/isdn/mISDN/layer2.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Layer 2 defines
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#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, u_long);
|
||||
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 release_tei(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
|
781
drivers/isdn/mISDN/socket.c
Normal file
781
drivers/isdn/mISDN/socket.c
Normal file
@ -0,0 +1,781 @@
|
||||
/*
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mISDNif.h>
|
||||
#include "core.h"
|
||||
|
||||
static 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 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 kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t len, int flags)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct sock *sk = sock->sk;
|
||||
struct sockaddr_mISDN *maddr;
|
||||
|
||||
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, flags & MSG_DONTWAIT, &err);
|
||||
if (!skb)
|
||||
return err;
|
||||
|
||||
if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
|
||||
msg->msg_namelen = sizeof(struct sockaddr_mISDN);
|
||||
maddr = (struct sockaddr_mISDN *)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;
|
||||
}
|
||||
} else {
|
||||
if (msg->msg_namelen)
|
||||
printk(KERN_WARNING "%s: too small namelen %d\n",
|
||||
__func__, msg->msg_namelen);
|
||||
msg->msg_namelen = 0;
|
||||
}
|
||||
|
||||
copied = skb->len + MISDN_HEADER_LEN;
|
||||
if (len < copied) {
|
||||
if (flags & MSG_PEEK)
|
||||
atomic_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_iovec(skb, 0, msg->msg_iov, copied);
|
||||
|
||||
mISDN_sock_cmsg(sk, msg, skb);
|
||||
|
||||
skb_free_datagram(sk, skb);
|
||||
|
||||
return err ? : copied;
|
||||
}
|
||||
|
||||
static int
|
||||
mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
|
||||
struct msghdr *msg, size_t len)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct sk_buff *skb;
|
||||
int err = -ENOMEM;
|
||||
struct sockaddr_mISDN *maddr;
|
||||
|
||||
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_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
|
||||
err = -EFAULT;
|
||||
goto drop;
|
||||
}
|
||||
|
||||
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 */
|
||||
maddr = (struct sockaddr_mISDN *)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 ||
|
||||
(err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb)))
|
||||
goto drop;
|
||||
|
||||
err = len;
|
||||
|
||||
done:
|
||||
release_sock(sk);
|
||||
return err;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
goto done;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
if (get_user(val, (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;
|
||||
|
||||
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,
|
||||
MISDN_CHMAP_SIZE * 4);
|
||||
di.nrbchan = dev->nrbchan;
|
||||
strcpy(di.name, dev->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,
|
||||
char __user *optval, int len)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
int err = 0, opt = 0;
|
||||
|
||||
if (*debug & DEBUG_SOCKET)
|
||||
printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock,
|
||||
level, optname, optval, len);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (optname) {
|
||||
case MISDN_TIME_STAMP:
|
||||
if (get_user(opt, (int __user *)optval)) {
|
||||
err = -EFAULT;
|
||||
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;
|
||||
|
||||
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 *addr, int addr_len)
|
||||
{
|
||||
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
|
||||
struct sock *sk = sock->sk;
|
||||
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;
|
||||
}
|
||||
_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 *addr_len, int peer)
|
||||
{
|
||||
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
|
||||
struct sock *sk = sock->sk;
|
||||
|
||||
if (!_pms(sk)->dev)
|
||||
return -EBADFD;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
*addr_len = sizeof(*maddr);
|
||||
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 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
if (sock->type != SOCK_DGRAM)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
|
||||
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;
|
||||
|
||||
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,
|
||||
MISDN_CHMAP_SIZE * 4);
|
||||
di.nrbchan = dev->nrbchan;
|
||||
strcpy(di.name, dev->name);
|
||||
if (copy_to_user((void __user *)arg, &di, sizeof(di)))
|
||||
err = -EFAULT;
|
||||
} else
|
||||
err = -ENODEV;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
|
||||
{
|
||||
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
|
||||
struct sock *sk = sock->sk;
|
||||
int err = 0;
|
||||
|
||||
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,
|
||||
.poll = sock_no_poll,
|
||||
.listen = sock_no_listen,
|
||||
.shutdown = sock_no_shutdown,
|
||||
.setsockopt = sock_no_setsockopt,
|
||||
.getsockopt = sock_no_getsockopt,
|
||||
.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)
|
||||
{
|
||||
struct sock *sk;
|
||||
|
||||
if (sock->type != SOCK_RAW)
|
||||
return -ESOCKTNOSUPPORT;
|
||||
|
||||
sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
|
||||
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 err = -EPROTONOSUPPORT;
|
||||
|
||||
switch (proto) {
|
||||
case ISDN_P_BASE:
|
||||
err = base_sock_create(net, sock, proto);
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
674
drivers/isdn/mISDN/stack.c
Normal file
674
drivers/isdn/mISDN/stack.c
Normal file
@ -0,0 +1,674 @@
|
||||
/*
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/mISDNif.h>
|
||||
#include <linux/kthread.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);
|
||||
}
|
||||
}
|
||||
|
||||
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 hlist_node *node;
|
||||
struct sock *sk;
|
||||
struct sk_buff *cskb = NULL;
|
||||
|
||||
read_lock(&sl->lock);
|
||||
sk_for_each(sk, node, &sl->head) {
|
||||
if (sk->sk_state != MISDN_BOUND)
|
||||
continue;
|
||||
if (!cskb)
|
||||
cskb = skb_copy(skb, GFP_KERNEL);
|
||||
if (!cskb) {
|
||||
printk(KERN_WARNING "%s no skb\n", __func__);
|
||||
break;
|
||||
}
|
||||
if (!sock_queue_rcv_skb(sk, cskb))
|
||||
cskb = NULL;
|
||||
}
|
||||
read_unlock(&sl->lock);
|
||||
if (cskb)
|
||||
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 ch%d mgr prim(%x) addr(%x) err %d\n",
|
||||
__func__, ch->nr, hh->prim, ch->addr, ret);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&st->lmutex);
|
||||
if (skb)
|
||||
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__, st->dev->name, 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__, st->dev->name, hh->prim, hh->id);
|
||||
} else {
|
||||
/* broadcast not handled yet */
|
||||
printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
|
||||
__func__, st->dev->name, hh->prim);
|
||||
}
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
static void
|
||||
do_clear_stack(struct mISDNstack *st)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
mISDNStackd(void *data)
|
||||
{
|
||||
struct mISDNstack *st = data;
|
||||
int err = 0;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
lock_kernel();
|
||||
#endif
|
||||
sigfillset(¤t->blocked);
|
||||
#ifdef CONFIG_SMP
|
||||
unlock_kernel();
|
||||
#endif
|
||||
if (*debug & DEBUG_MSG_THREAD)
|
||||
printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name);
|
||||
|
||||
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__, st->dev->name,
|
||||
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__, st->dev->name, 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",
|
||||
st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt);
|
||||
printk(KERN_DEBUG
|
||||
"mISDNStackd daemon for %s utime(%ld) stime(%ld)\n",
|
||||
st->dev->name, st->thread->utime, st->thread->stime);
|
||||
printk(KERN_DEBUG
|
||||
"mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
|
||||
st->dev->name, st->thread->nvcsw, st->thread->nivcsw);
|
||||
printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
|
||||
st->dev->name);
|
||||
#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(sizeof(struct mISDNstack), GFP_KERNEL);
|
||||
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__, newst->dev->name);
|
||||
newst->notify = &done;
|
||||
newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
|
||||
newst->dev->name);
|
||||
if (IS_ERR(newst->thread)) {
|
||||
err = PTR_ERR(newst->thread);
|
||||
printk(KERN_ERR
|
||||
"mISDN:cannot create kernel thread for %s (%d)\n",
|
||||
newst->dev->name, 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, 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:
|
||||
#ifdef PROTOCOL_CHECK
|
||||
/* this should be enhanced */
|
||||
if (!list_empty(&dev->D.st->layer2)
|
||||
&& dev->D.protocol != protocol)
|
||||
return -EBUSY;
|
||||
if (!hlist_empty(&dev->D.st->l1sock.head)
|
||||
&& dev->D.protocol != protocol)
|
||||
return -EBUSY;
|
||||
#endif
|
||||
ch->recv = mISDN_queue_message;
|
||||
ch->peer = &dev->D.st->own;
|
||||
ch->st = dev->D.st;
|
||||
rq.protocol = protocol;
|
||||
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)
|
||||
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, 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, 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;
|
||||
case ISDN_P_LAPD_TE:
|
||||
#ifdef PROTOCOL_CHECK
|
||||
/* this should be enhanced */
|
||||
if (!list_empty(&dev->D.st->layer2)
|
||||
&& dev->D.protocol != protocol)
|
||||
return -EBUSY;
|
||||
if (!hlist_empty(&dev->D.st->l1sock.head)
|
||||
&& dev->D.protocol != protocol)
|
||||
return -EBUSY;
|
||||
#endif
|
||||
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__,
|
||||
ch->st->dev->name, 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__,
|
||||
st->dev->name);
|
||||
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;
|
||||
}
|
1340
drivers/isdn/mISDN/tei.c
Normal file
1340
drivers/isdn/mISDN/tei.c
Normal file
File diff suppressed because it is too large
Load Diff
301
drivers/isdn/mISDN/timerdev.c
Normal file
301
drivers/isdn/mISDN/timerdev.c
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
*
|
||||
* general timer device for using in ISDN stacks
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/poll.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mISDNif.h>
|
||||
|
||||
static 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(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
|
||||
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;
|
||||
__module_get(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mISDN_close(struct inode *ino, struct file *filep)
|
||||
{
|
||||
struct mISDNtimerdev *dev = filep->private_data;
|
||||
struct mISDNtimer *timer, *next;
|
||||
|
||||
if (*debug & DEBUG_TIMER)
|
||||
printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
|
||||
list_for_each_entry_safe(timer, next, &dev->pending, list) {
|
||||
del_timer(&timer->tl);
|
||||
kfree(timer);
|
||||
}
|
||||
list_for_each_entry_safe(timer, next, &dev->expired, list) {
|
||||
kfree(timer);
|
||||
}
|
||||
kfree(dev);
|
||||
module_put(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
|
||||
{
|
||||
struct mISDNtimerdev *dev = filep->private_data;
|
||||
struct mISDNtimer *timer;
|
||||
u_long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (*debug & DEBUG_TIMER)
|
||||
printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
|
||||
filep, buf, (int)count, off);
|
||||
if (*off != filep->f_pos)
|
||||
return -ESPIPE;
|
||||
|
||||
if (list_empty(&dev->expired) && (dev->work == 0)) {
|
||||
if (filep->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
wait_event_interruptible(dev->wait, (dev->work ||
|
||||
!list_empty(&dev->expired)));
|
||||
if (signal_pending(current))
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
if (count < sizeof(int))
|
||||
return -ENOSPC;
|
||||
if (dev->work)
|
||||
dev->work = 0;
|
||||
if (!list_empty(&dev->expired)) {
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
timer = (struct mISDNtimer *)dev->expired.next;
|
||||
list_del(&timer->list);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
if (put_user(timer->id, (int *)buf))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = sizeof(int);
|
||||
kfree(timer);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static loff_t
|
||||
mISDN_llseek(struct file *filep, loff_t offset, int orig)
|
||||
{
|
||||
return -ESPIPE;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
mISDN_write(struct file *filep, const char *buf, size_t count, loff_t *off)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
mISDN_poll(struct file *filep, poll_table *wait)
|
||||
{
|
||||
struct mISDNtimerdev *dev = filep->private_data;
|
||||
unsigned int mask = POLLERR;
|
||||
|
||||
if (*debug & DEBUG_TIMER)
|
||||
printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
|
||||
if (dev) {
|
||||
poll_wait(filep, &dev->wait, wait);
|
||||
mask = 0;
|
||||
if (dev->work || !list_empty(&dev->expired))
|
||||
mask |= (POLLIN | POLLRDNORM);
|
||||
if (*debug & DEBUG_TIMER)
|
||||
printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
|
||||
dev->work, list_empty(&dev->expired));
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void
|
||||
dev_expire_timer(struct mISDNtimer *timer)
|
||||
{
|
||||
u_long flags;
|
||||
|
||||
spin_lock_irqsave(&timer->dev->lock, flags);
|
||||
list_del(&timer->list);
|
||||
list_add_tail(&timer->list, &timer->dev->expired);
|
||||
spin_unlock_irqrestore(&timer->dev->lock, flags);
|
||||
wake_up_interruptible(&timer->dev->wait);
|
||||
}
|
||||
|
||||
static int
|
||||
misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
|
||||
{
|
||||
int id;
|
||||
u_long flags;
|
||||
struct mISDNtimer *timer;
|
||||
|
||||
if (!timeout) {
|
||||
dev->work = 1;
|
||||
wake_up_interruptible(&dev->wait);
|
||||
id = 0;
|
||||
} else {
|
||||
timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
|
||||
if (!timer)
|
||||
return -ENOMEM;
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
timer->id = dev->next_id++;
|
||||
if (dev->next_id < 0)
|
||||
dev->next_id = 1;
|
||||
list_add_tail(&timer->list, &dev->pending);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
timer->dev = dev;
|
||||
timer->tl.data = (long)timer;
|
||||
timer->tl.function = (void *) dev_expire_timer;
|
||||
init_timer(&timer->tl);
|
||||
timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
|
||||
add_timer(&timer->tl);
|
||||
id = timer->id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static int
|
||||
misdn_del_timer(struct mISDNtimerdev *dev, int id)
|
||||
{
|
||||
u_long flags;
|
||||
struct mISDNtimer *timer;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
list_for_each_entry(timer, &dev->pending, list) {
|
||||
if (timer->id == id) {
|
||||
list_del_init(&timer->list);
|
||||
del_timer(&timer->tl);
|
||||
ret = timer->id;
|
||||
kfree(timer);
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mISDN_ioctl(struct inode *inode, 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);
|
||||
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;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file_operations mISDN_fops = {
|
||||
.llseek = mISDN_llseek,
|
||||
.read = mISDN_read,
|
||||
.write = mISDN_write,
|
||||
.poll = mISDN_poll,
|
||||
.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(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);
|
||||
}
|
37
include/linux/mISDNdsp.h
Normal file
37
include/linux/mISDNdsp.h
Normal file
@ -0,0 +1,37 @@
|
||||
#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);
|
||||
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_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
|
||||
|
193
include/linux/mISDNhw.h
Normal file
193
include/linux/mISDNhw.h
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
*
|
||||
* Author Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* Basic declarations for the mISDN HW channels
|
||||
*
|
||||
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 */
|
||||
/* arcofi specific */
|
||||
#define FLG_ARCOFI_TIMER 16
|
||||
#define FLG_ARCOFI_ERROR 17
|
||||
/* isar specific */
|
||||
#define FLG_INITIALIZED 16
|
||||
#define FLG_DLEETX 17
|
||||
#define FLG_LASTDLE 18
|
||||
#define FLG_FIRST 19
|
||||
#define FLG_LASTDATA 20
|
||||
#define FLG_NMD_DATA 21
|
||||
#define FLG_FTI_RUN 22
|
||||
#define FLG_LL_OK 23
|
||||
#define FLG_LL_CONN 24
|
||||
#define FLG_DTMFSEND 25
|
||||
|
||||
/* 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;
|
||||
/* HW access */
|
||||
u_char (*read_reg) (void *, u_char);
|
||||
void (*write_reg) (void *, u_char, u_char);
|
||||
void (*read_fifo) (void *, u_char *, int);
|
||||
void (*write_fifo) (void *, u_char *, int);
|
||||
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
|
||||
|
||||
struct layer1;
|
||||
extern int l1_event(struct layer1 *, u_int);
|
||||
|
||||
|
||||
struct bchannel {
|
||||
struct mISDNchannel ch;
|
||||
int nr;
|
||||
u_long Flags;
|
||||
struct work_struct workq;
|
||||
u_int state;
|
||||
/* HW access */
|
||||
u_char (*read_reg) (void *, u_char);
|
||||
void (*write_reg) (void *, u_char, u_char);
|
||||
void (*read_fifo) (void *, u_char *, int);
|
||||
void (*write_fifo) (void *, u_char *, int);
|
||||
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 *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;
|
||||
};
|
||||
|
||||
extern int mISDN_initdchannel(struct dchannel *, int, void *);
|
||||
extern int mISDN_initbchannel(struct bchannel *, int);
|
||||
extern int mISDN_freedchannel(struct dchannel *);
|
||||
extern int mISDN_freebchannel(struct bchannel *);
|
||||
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 void recv_Dchannel(struct dchannel *);
|
||||
extern void recv_Bchannel(struct bchannel *);
|
||||
extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *);
|
||||
extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *);
|
||||
extern void confirm_Bsend(struct bchannel *bch);
|
||||
extern int get_next_bframe(struct bchannel *);
|
||||
extern int get_next_dframe(struct dchannel *);
|
||||
|
||||
#endif
|
487
include/linux/mISDNif.h
Normal file
487
include/linux/mISDNif.h
Normal file
@ -0,0 +1,487 @@
|
||||
/*
|
||||
*
|
||||
* 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 <stdarg.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 0
|
||||
#define MISDN_RELEASE 18
|
||||
|
||||
/* 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 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 managment */
|
||||
#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
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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_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 OPTION_L2_PMX 1
|
||||
#define OPTION_L2_PTP 2
|
||||
#define OPTION_L2_FIXEDTEI 3
|
||||
#define OPTION_L2_CLEANUP 4
|
||||
|
||||
/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
|
||||
#define MISDN_MAX_IDLEN 20
|
||||
|
||||
struct mISDNhead {
|
||||
unsigned int prim;
|
||||
unsigned int id;
|
||||
} __attribute__((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_CHMAP_SIZE 4
|
||||
|
||||
#define SOL_MISDN 0
|
||||
|
||||
struct sockaddr_mISDN {
|
||||
sa_family_t family;
|
||||
unsigned char dev;
|
||||
unsigned char channel;
|
||||
unsigned char sapi;
|
||||
unsigned char tei;
|
||||
};
|
||||
|
||||
/* 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)
|
||||
|
||||
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_long channelmap[MISDN_CHMAP_SIZE];
|
||||
u_int nrbchan;
|
||||
char name[MISDN_MAX_IDLEN];
|
||||
};
|
||||
|
||||
/* 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_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_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
|
||||
|
||||
|
||||
/* 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 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 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;
|
||||
char name[MISDN_MAX_IDLEN];
|
||||
u_int Dprotocols;
|
||||
u_int Bprotocols;
|
||||
u_int nrbchan;
|
||||
u_long 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
|
||||
};
|
||||
|
||||
/* global alloc/queue dunctions */
|
||||
|
||||
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)
|
||||
memcpy(skb_put(skb, len), 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 *, char *name);
|
||||
extern void mISDN_unregister_device(struct mISDNdevice *);
|
||||
extern int mISDN_register_Bprotocol(struct Bprotocol *);
|
||||
extern void mISDN_unregister_Bprotocol(struct Bprotocol *);
|
||||
|
||||
extern void set_channel_address(struct mISDNchannel *, u_int, u_int);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* mISDNIF_H */
|
@ -1832,7 +1832,13 @@
|
||||
#define PCI_DEVICE_ID_MOXA_C320 0x3200
|
||||
|
||||
#define PCI_VENDOR_ID_CCD 0x1397
|
||||
#define PCI_DEVICE_ID_CCD_HFC4S 0x08B4
|
||||
#define PCI_SUBDEVICE_ID_CCD_PMX2S 0x1234
|
||||
#define PCI_DEVICE_ID_CCD_HFC8S 0x16B8
|
||||
#define PCI_DEVICE_ID_CCD_2BD0 0x2bd0
|
||||
#define PCI_DEVICE_ID_CCD_HFCE1 0x30B1
|
||||
#define PCI_SUBDEVICE_ID_CCD_SPD4S 0x3136
|
||||
#define PCI_SUBDEVICE_ID_CCD_SPDE1 0x3137
|
||||
#define PCI_DEVICE_ID_CCD_B000 0xb000
|
||||
#define PCI_DEVICE_ID_CCD_B006 0xb006
|
||||
#define PCI_DEVICE_ID_CCD_B007 0xb007
|
||||
@ -1842,8 +1848,32 @@
|
||||
#define PCI_DEVICE_ID_CCD_B00B 0xb00b
|
||||
#define PCI_DEVICE_ID_CCD_B00C 0xb00c
|
||||
#define PCI_DEVICE_ID_CCD_B100 0xb100
|
||||
#define PCI_SUBDEVICE_ID_CCD_IOB4ST 0xB520
|
||||
#define PCI_SUBDEVICE_ID_CCD_IOB8STR 0xB521
|
||||
#define PCI_SUBDEVICE_ID_CCD_IOB8ST 0xB522
|
||||
#define PCI_SUBDEVICE_ID_CCD_IOB1E1 0xB523
|
||||
#define PCI_SUBDEVICE_ID_CCD_SWYX4S 0xB540
|
||||
#define PCI_SUBDEVICE_ID_CCD_JH4S20 0xB550
|
||||
#define PCI_SUBDEVICE_ID_CCD_IOB8ST_1 0xB552
|
||||
#define PCI_SUBDEVICE_ID_CCD_BN4S 0xB560
|
||||
#define PCI_SUBDEVICE_ID_CCD_BN8S 0xB562
|
||||
#define PCI_SUBDEVICE_ID_CCD_BNE1 0xB563
|
||||
#define PCI_SUBDEVICE_ID_CCD_BNE1D 0xB564
|
||||
#define PCI_SUBDEVICE_ID_CCD_BNE1DP 0xB565
|
||||
#define PCI_SUBDEVICE_ID_CCD_BN2S 0xB566
|
||||
#define PCI_SUBDEVICE_ID_CCD_BN1SM 0xB567
|
||||
#define PCI_SUBDEVICE_ID_CCD_BN4SM 0xB568
|
||||
#define PCI_SUBDEVICE_ID_CCD_BN2SM 0xB569
|
||||
#define PCI_SUBDEVICE_ID_CCD_BNE1M 0xB56A
|
||||
#define PCI_SUBDEVICE_ID_CCD_BN8SP 0xB56B
|
||||
#define PCI_SUBDEVICE_ID_CCD_HFC4S 0xB620
|
||||
#define PCI_SUBDEVICE_ID_CCD_HFC8S 0xB622
|
||||
#define PCI_DEVICE_ID_CCD_B700 0xb700
|
||||
#define PCI_DEVICE_ID_CCD_B701 0xb701
|
||||
#define PCI_SUBDEVICE_ID_CCD_HFCE1 0xC523
|
||||
#define PCI_SUBDEVICE_ID_CCD_OV2S 0xE884
|
||||
#define PCI_SUBDEVICE_ID_CCD_OV4S 0xE888
|
||||
#define PCI_SUBDEVICE_ID_CCD_OV8S 0xE998
|
||||
|
||||
#define PCI_VENDOR_ID_EXAR 0x13a8
|
||||
#define PCI_DEVICE_ID_EXAR_XR17C152 0x0152
|
||||
@ -2523,6 +2553,9 @@
|
||||
|
||||
#define PCI_VENDOR_ID_3COM_2 0xa727
|
||||
|
||||
#define PCI_VENDOR_ID_DIGIUM 0xd161
|
||||
#define PCI_DEVICE_ID_DIGIUM_HFC4S 0xb410
|
||||
|
||||
#define PCI_SUBVENDOR_ID_EXSYS 0xd84d
|
||||
#define PCI_SUBDEVICE_ID_EXSYS_4014 0x4014
|
||||
#define PCI_SUBDEVICE_ID_EXSYS_4055 0x4055
|
||||
|
@ -189,7 +189,8 @@ struct ucred {
|
||||
#define AF_BLUETOOTH 31 /* Bluetooth sockets */
|
||||
#define AF_IUCV 32 /* IUCV sockets */
|
||||
#define AF_RXRPC 33 /* RxRPC sockets */
|
||||
#define AF_MAX 34 /* For now.. */
|
||||
#define AF_ISDN 34 /* mISDN sockets */
|
||||
#define AF_MAX 35 /* For now.. */
|
||||
|
||||
/* Protocol families, same as address families. */
|
||||
#define PF_UNSPEC AF_UNSPEC
|
||||
@ -225,6 +226,7 @@ struct ucred {
|
||||
#define PF_BLUETOOTH AF_BLUETOOTH
|
||||
#define PF_IUCV AF_IUCV
|
||||
#define PF_RXRPC AF_RXRPC
|
||||
#define PF_ISDN AF_ISDN
|
||||
#define PF_MAX AF_MAX
|
||||
|
||||
/* Maximum queue length specifiable by listen. */
|
||||
|
Loading…
Reference in New Issue
Block a user