mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
Add mISDN core files
Add mISDN core files Signed-off-by: Karsten Keil <kkeil@suse.de>
This commit is contained in:
parent
04578dd330
commit
1b2b03f8e5
9
drivers/isdn/mISDN/Kconfig
Normal file
9
drivers/isdn/mISDN/Kconfig
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# modularer ISDN driver
|
||||
#
|
||||
|
||||
menuconfig MISDN
|
||||
tristate "Modular ISDN driver"
|
||||
help
|
||||
Enable support for the modular ISDN driver.
|
||||
|
9
drivers/isdn/mISDN/Makefile
Normal file
9
drivers/isdn/mISDN/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# Makefile for the modular ISDN driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MISDN) += mISDN_core.o
|
||||
|
||||
# multi objects
|
||||
|
||||
mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.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
|
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);
|
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);
|
||||
}
|
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 */
|
Loading…
Reference in New Issue
Block a user