Add port of mISDN kernel driver for use in user space

This commit is contained in:
Andreas Eversberg 2022-03-27 16:26:14 +02:00
parent b34219a725
commit 7ebde07432
26 changed files with 8252 additions and 1 deletions

1
.gitignore vendored
View File

@ -40,4 +40,5 @@ src/liboptions/liboptions.a
src/libosmocc/libosmocc.a
src/libsample/libsample.a
src/libtimer/libtimer.a
src/libmisdn/libmisdn.a
src/isdn/osmo-cc-misdn-endpoint

View File

@ -74,7 +74,7 @@ AC_SUBST(SYMBOL_VISIBILITY)
dnl Generate the output
AM_CONFIG_HEADER(config.h)
AC_CHECK_HEADERS([mISDN/mbuffer.h], , [AC_MSG_FAILURE(Missing mISDN user library. Please install mISDNuser!)])
#AC_CHECK_HEADERS([mISDN/mbuffer.h], , [AC_MSG_FAILURE(Missing mISDN user library. Please install mISDNuser!)])
AC_CHECK_LIB([m], [main])
AC_CHECK_LIB([pthread], [main])
@ -87,6 +87,7 @@ AC_OUTPUT(
src/libjitter/Makefile
src/libosmocc/Makefile
src/libg711/Makefile
src/libmisdn/Makefile
src/isdn/Makefile
src/Makefile
Makefile)

View File

@ -8,5 +8,6 @@ SUBDIRS = \
libjitter \
libosmocc \
libg711 \
libmisdn \
isdn

17
src/libmisdn/Makefile.am Normal file
View File

@ -0,0 +1,17 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libmisdn.a
libmisdn_a_SOURCES = \
printk.c \
layer1.c \
layer2.c \
tei.c \
fsm.c \
stack.c \
socket.c \
core.c \
hwchannel.c
AM_CPPFLAGS += -D__MISDNL1L2__

25
src/libmisdn/bitops.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef M_BITOPS_H
#define M_BITOPS_H
static inline int test_bit(unsigned int nr, unsigned long *addr)
{
return 1UL & (*addr >> nr);
}
static inline int test_and_set_bit(unsigned int nr, unsigned long *addr)
{
unsigned long old = *addr;
*addr |= 1UL << nr;
return 1UL & (old >> nr);
}
static inline int test_and_clear_bit(unsigned int nr, unsigned long *addr)
{
unsigned long old = *addr;
*addr &= ~(1UL << nr);
return 1UL & (old >> nr);
}
#endif

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_CONTAINER_OF_H
#define _LINUX_CONTAINER_OF_H
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
#define typeof_member(T, m) typeof(((T*)0)->m)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
((type *)(__mptr - offsetof(type, member))); })
/**
* container_of_safe - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
* If IS_ERR_OR_NULL(ptr), ptr is returned unchanged.
*/
#define container_of_safe(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
IS_ERR_OR_NULL(__mptr) ? ERR_CAST(__mptr) : \
((type *)(__mptr - offsetof(type, member))); })
#endif /* _LINUX_CONTAINER_OF_H */

429
src/libmisdn/core.c Normal file
View File

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

77
src/libmisdn/core.h Normal file
View File

@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef mISDN_CORE_H
#define mISDN_CORE_H
#ifdef __MISDNL1L2__
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 int work_stack(struct mISDNdevice *dev);
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 Isdnl1_Init(u_int *);
extern void Isdnl1_cleanup(void);
extern int Isdnl2_Init(u_int *);
extern void Isdnl2_cleanup(void);
extern void mISDN_init_clock(u_int *);
#endif
extern int mISDNInit(uint debug);
extern void mISDN_cleanup(void);
extern void mISDN_work(void);
#endif

177
src/libmisdn/fsm.c Normal file
View File

@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* finite state machine implementation
*
* Author Karsten Keil <kkeil@novell.com>
*
* Thanks to Jan den Ouden
* Fritz Elfert
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "layer2.h"
#include "core.h"
#include <stdio.h>
#include <stdarg.h>
#include "fsm.h"
#define FSM_TIMER_DEBUG 0
int
mISDN_FsmNew(struct Fsm *fsm,
struct FsmNode *fnlist, int fncount)
{
int i;
fsm->jumpmatrix =
kzalloc(sizeof(FSMFNPTR) * fsm->state_count * fsm->event_count,
GFP_KERNEL);
if (fsm->jumpmatrix == NULL)
return -ENOMEM;
for (i = 0; i < fncount; i++)
if ((fnlist[i].state >= fsm->state_count) ||
(fnlist[i].event >= fsm->event_count)) {
printk(KERN_ERR
"mISDN_FsmNew Error: %d st(%ld/%ld) ev(%ld/%ld)\n",
i, (long)fnlist[i].state, (long)fsm->state_count,
(long)fnlist[i].event, (long)fsm->event_count);
} else
fsm->jumpmatrix[fsm->state_count * fnlist[i].event +
fnlist[i].state] = (FSMFNPTR) fnlist[i].routine;
return 0;
}
EXPORT_SYMBOL(mISDN_FsmNew);
void
mISDN_FsmFree(struct Fsm *fsm)
{
kfree((void *) fsm->jumpmatrix);
}
EXPORT_SYMBOL(mISDN_FsmFree);
int
mISDN_FsmEvent(struct FsmInst *fi, int event, void *arg)
{
FSMFNPTR r;
if ((fi->state >= fi->fsm->state_count) ||
(event >= fi->fsm->event_count)) {
printk(KERN_ERR
"mISDN_FsmEvent Error st(%ld/%ld) ev(%d/%ld)\n",
(long)fi->state, (long)fi->fsm->state_count, event,
(long)fi->fsm->event_count);
return 1;
}
r = fi->fsm->jumpmatrix[fi->fsm->state_count * event + fi->state];
if (r) {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
r(fi, event, arg);
return 0;
} else {
if (fi->debug)
fi->printdebug(fi, "State %s Event %s no action",
fi->fsm->strState[fi->state],
fi->fsm->strEvent[event]);
return 1;
}
}
EXPORT_SYMBOL(mISDN_FsmEvent);
void
mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
{
fi->state = newstate;
if (fi->debug)
fi->printdebug(fi, "ChangeState %s",
fi->fsm->strState[newstate]);
}
EXPORT_SYMBOL(mISDN_FsmChangeState);
static void
FsmExpireTimer(struct timer *t)
{
struct FsmTimer *ft = t->priv;
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
#endif
timer_exit(&ft->tl);
mISDN_FsmEvent(ft->fi, ft->event, ft->arg);
}
void
mISDN_FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
{
ft->fi = fi;
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmInitTimer %lx", (long) ft);
#endif
ft->func = FsmExpireTimer;
}
EXPORT_SYMBOL(mISDN_FsmInitTimer);
void
mISDN_FsmDelTimer(struct FsmTimer *ft, int __attribute__((unused)) where)
{
#if FSM_TIMER_DEBUG
if (ft->fi->debug)
ft->fi->printdebug(ft->fi, "mISDN_FsmDelTimer %lx %d",
(long) ft, where);
#endif
timer_exit(&ft->tl);
}
EXPORT_SYMBOL(mISDN_FsmDelTimer);
int
mISDN_FsmAddTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int __attribute__((unused)) 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 (ft->tl.linked) {
if (ft->fi->debug) {
printk(KERN_WARNING
"mISDN_FsmAddTimer: timer already active!\n");
ft->fi->printdebug(ft->fi,
"mISDN_FsmAddTimer already active!");
}
return -1;
}
ft->event = event;
ft->arg = arg;
timer_init(&ft->tl, ft->func, ft);
timer_start(&ft->tl, (double)millisec / 1000);
return 0;
}
EXPORT_SYMBOL(mISDN_FsmAddTimer);
void
mISDN_FsmRestartTimer(struct FsmTimer *ft,
int millisec, int event, void *arg, int __attribute__((unused)) 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 (ft->tl.linked)
timer_exit(&ft->tl);
ft->event = event;
ft->arg = arg;
timer_init(&ft->tl, ft->func, ft);
timer_start(&ft->tl, (double)millisec / 1000.0);
}
EXPORT_SYMBOL(mISDN_FsmRestartTimer);

59
src/libmisdn/fsm.h Normal file
View File

@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Thanks to Jan den Ouden
* Fritz Elfert
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#ifndef _MISDN_FSM_H
#define _MISDN_FSM_H
#include "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 tl;
void *func;
int event;
void *arg;
};
extern int mISDN_FsmNew(struct Fsm *, struct FsmNode *, int);
extern void mISDN_FsmFree(struct Fsm *);
extern int mISDN_FsmEvent(struct FsmInst *, int , void *);
extern void mISDN_FsmChangeState(struct FsmInst *, int);
extern void mISDN_FsmInitTimer(struct FsmInst *, struct FsmTimer *);
extern int mISDN_FsmAddTimer(struct FsmTimer *, int, int, void *, int);
extern void mISDN_FsmRestartTimer(struct FsmTimer *, int, int, void *, int);
extern void mISDN_FsmDelTimer(struct FsmTimer *, int);
#endif

523
src/libmisdn/hwchannel.c Normal file
View File

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

415
src/libmisdn/layer1.c Normal file
View File

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

16
src/libmisdn/layer1.h Normal file
View File

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

2266
src/libmisdn/layer2.c Normal file

File diff suppressed because it is too large Load Diff

130
src/libmisdn/layer2.h Normal file
View File

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

192
src/libmisdn/mISDNhw.h Normal file
View File

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

656
src/libmisdn/mISDNif.h Normal file
View File

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

198
src/libmisdn/mlist.h Normal file
View File

@ -0,0 +1,198 @@
/* mlist.h
*
* Simple doubly linked list implementation and helper
* function reduce version of linux kernel list.h
*/
#ifndef _MLIST_H
#define _MLIST_H
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
/*
* list_add_head - add a new entry on the top
* @new: new entry to be added
* @head: list head to add it after
*
*/
static inline void list_add_head(struct list_head *new, struct list_head *head)
{
head->next->prev = new;
new->next = head->next;
new->prev = head;
head->next = new;
}
/**
* hlist_add_head - add a new entry at the beginning of the hlist
* @n: new entry to be added
* @h: hlist head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
/*
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
head->prev->next = new;
new->next = head;
new->prev = head->prev;
head->prev = new;
}
#define LIST_POISON1 0xdeadbee1
#define LIST_POISON2 0xdeadbee2
/*
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
*/
static inline void list_del(struct list_head *entry)
{
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
entry->next = (void *)LIST_POISON1;
entry->prev = (void *)LIST_POISON2;
}
static inline void hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
n->next = (void *)LIST_POISON1;
n->pprev = (void *)LIST_POISON2;
}
/*
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list, const struct list_head *head)
{
return list->next == head;
}
/*
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* hlist_empty - Is the specified hlist_head structure an empty hlist?
* @h: Structure to check.
*/
static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}
/*
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/*
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)
/*
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))
#endif
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
})
/**
* hlist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(pos, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))

27
src/libmisdn/printk.c Normal file
View File

@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdarg.h>
#include "printk.h"
void printk(const char *fmt, ...)
{
char buffer[4096], *b = buffer;
int s = sizeof(buffer) - 1;
int level = 1;
va_list args;
buffer[sizeof(buffer) - 1] = '\0';
/* remove debug level from format */
if (fmt[0] >= '0' && fmt[0] <= '9') {
level = fmt[0] - '0';
fmt++;
}
va_start(args, fmt);
vsnprintf(b, s, fmt, args);
va_end(args);
PDEBUG(DMISDN, level, "%s", b);
}

16
src/libmisdn/printk.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef MISDN_PRINTK_H
#define MISDN_PRINTK_H
#include <stdint.h>
#include "../libdebug/debug.h"
#define KERN_DEBUG "0"
#define KERN_INFO "1"
#define KERN_NOTICE "2"
#define KERN_WARNING "2"
#define KERN_ERR "3"
void printk(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
#endif

241
src/libmisdn/skbuff.h Normal file
View File

@ -0,0 +1,241 @@
#ifndef _MISDN_MSKBUFF_H
#define _MISDN_MSKBUFF_H
#include <stdlib.h>
#include <string.h>
#define GFP_ATOMIC 0
#define GFP_KERNEL 1
typedef unsigned int __bitwise gfp_t;
/*
* head data tail end
* | | len | |
* -----> --------> ----->
* headroom tailroom
*/
struct sk_buff {
struct sk_buff *next;
struct sk_buff *prev;
char cb[48];
gfp_t priority;
unsigned int len;
unsigned char *head,
*data,
*tail,
*end;
/* the buffer begins behind sk_buff structure size */
unsigned char buffer[0];
};
#define __struct_group(TAG, NAME, ATTRS, MEMBERS...) \
union { \
struct { MEMBERS } ATTRS; \
struct TAG { MEMBERS } ATTRS NAME; \
}
#define struct_group_tagged(TAG, NAME, MEMBERS...) \
__struct_group(TAG, NAME, /* no attrs */, MEMBERS)
struct sk_buff_head {
/* These two members must be first to match sk_buff. */
struct_group_tagged(sk_buff_list, list,
struct sk_buff *next;
struct sk_buff *prev;
);
__u32 qlen;
// spinlock_t lock;
};
static inline struct sk_buff *alloc_skb(unsigned int size,
gfp_t priority)
{
struct sk_buff *skb = calloc(1, sizeof(*skb) + size);
skb->head = skb->buffer;
skb->data = skb->buffer;
skb->tail = skb->buffer;
skb->end = skb->buffer + size;
skb->len = 0;
skb->priority = priority;
return skb;
}
static inline struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int headroom)
{
/* headroom is incremented, but never shrinks! this way copying is easy */
if (skb->data - skb->head > headroom)
headroom = skb->data - skb->head;
size_t osize = skb->end - skb->head;
size_t nsize = skb->end - skb->data + headroom;
struct sk_buff *n = alloc_skb(nsize, skb->priority);
if (!n)
return NULL;
n->data = n->head + headroom;
n->tail = n->data + (skb->tail - skb->data);
n->len = n->tail - n->data;
memcpy(n->end - osize, skb->head, osize);
memcpy(n->cb, skb->cb, sizeof(n->cb));
return n;
}
static inline struct sk_buff *skb_clone(struct sk_buff *skb)
{
return skb_realloc_headroom(skb, skb->data - skb->head);
}
static inline struct sk_buff *skb_copy(struct sk_buff *skb, gfp_t priority)
{
struct sk_buff *n = skb_realloc_headroom(skb, skb->data - skb->head);
if (!n)
return NULL;
n->priority = priority;
return n;
}
static inline void kfree_skb(struct sk_buff *skb)
{
free(skb);
}
static inline void __net_timestamp(struct sk_buff __attribute__((unused)) *skb)
{
// skb->tstamp = ktime_get_real();
}
#define dev_kfree_skb(a) kfree_skb(a)
static inline void skb_reserve(struct sk_buff *skb, int len)
{
skb->data += len;
skb->tail += len;
}
static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
{
return skb->tail;
}
static inline void *skb_put(struct sk_buff *skb, unsigned int len)
{
void *tmp = skb_tail_pointer(skb);
skb->tail += len;
skb->len += len;
if (skb->tail > skb->end)
abort();
return tmp;
}
static inline void *skb_put_data(struct sk_buff *skb, const void *data,
unsigned int len)
{
void *tmp = skb_put(skb, len);
memcpy(tmp, data, len);
return tmp;
}
static inline void skb_trim(struct sk_buff *skb, unsigned int len)
{
skb->tail = skb->data + len;
skb->len = len;
}
static inline void *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data -= len;
skb->len += len;
return skb->data;
}
static inline void *skb_pull(struct sk_buff *skb, unsigned int len)
{
void *tmp = skb->data;
if (skb->len < len)
abort();
skb->len -= len;
skb->data += len;
return tmp;
}
static inline void skb_queue_head_init(struct sk_buff_head *list)
{
list->prev = list->next = (struct sk_buff *)list;
list->qlen = 0;
}
static inline void skb_queue_tail(struct sk_buff_head *list,
struct sk_buff *newsk)
{
newsk->prev = list->prev;
newsk->next = (struct sk_buff *)list;
list->prev->next = newsk;
list->prev = newsk;
list->qlen++;
}
static inline void skb_queue_head(struct sk_buff_head *list,
struct sk_buff *newsk)
{
newsk->prev = (struct sk_buff *)list;
newsk->next = list->next;
list->next->prev = newsk;
list->next = newsk;
list->qlen++;
}
static inline struct sk_buff *skb_dequeue(struct sk_buff_head *list)
{
struct sk_buff *skb;
if (list->next == (struct sk_buff *)list)
return NULL;
skb = list->next;
list->next = list->next->next;
list->next->prev = (struct sk_buff *)list;
list->qlen--;
skb->prev = skb->next = NULL;
return skb;
}
static inline void skb_queue_purge(struct sk_buff_head *list)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(list)) != NULL)
kfree_skb(skb);
}
static inline int skb_queue_empty(const struct sk_buff_head *list)
{
return list->next == (const struct sk_buff *) list;
}
static inline __u32 skb_queue_len(const struct sk_buff_head *list)
{
return list->qlen;
}
static inline int skb_tailroom(const struct sk_buff *skb)
{
return skb->end - skb->tail;
}
#endif

646
src/libmisdn/socket.c Normal file
View File

@ -0,0 +1,646 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNif.h"
#include "core.h"
#include <stdio.h>
#include <stdarg.h>
#include <sys/socket.h>
#include "socket.h"
static u_int *debug;
static struct mISDN_sock_list data_sockets = {
.lock = 0 //__RW_LOCK_UNLOCKED(data_sockets.lock)
};
static struct mISDN_sock_list base_sockets = {
.lock = 0 //__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 mISDN_sock *msk)
{
write_lock_bh(&l->lock);
hlist_add_head(&msk->list, &l->head);
write_unlock_bh(&l->lock);
}
static void mISDN_sock_unlink(struct mISDN_sock_list __attribute__((unused)) *l, struct mISDN_sock *msk)
{
write_lock_bh(&l->lock);
hlist_del(&msk->list);
write_unlock_bh(&l->lock);
}
static int
mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
{
struct mISDN_sock *msk;
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_state == MISDN_CLOSED)
return -EUNATCH;
__net_timestamp(skb);
skb_queue_tail(&msk->sk_receive_queue, skb);
return 0;
}
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_state = MISDN_CLOSED;
break;
}
return 0;
}
int mISDN_data_recvmsg(void *inst, unsigned char *data, size_t len, int flags, struct sockaddr_mISDN *maddr)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct sk_buff *skb;
size_t copied;
if (flags & (MSG_OOB))
return -EOPNOTSUPP;
if (msk->sk_state == MISDN_CLOSED)
return 0;
skb = skb_dequeue(&msk->sk_receive_queue);
if (!skb)
return -EAGAIN;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
__func__, (int)len, flags, msk->ch.nr,
msk->sk_protocol);
if (maddr) {
maddr->family = AF_ISDN;
maddr->dev = msk->dev->id;
if ((msk->sk_protocol == ISDN_P_LAPD_TE) ||
(msk->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 = msk->ch.nr;
maddr->sapi = msk->ch.addr & 0xFF;
maddr->tei = (msk->ch.addr >> 8) & 0xFF;
}
}
copied = skb->len + MISDN_HEADER_LEN;
if (len < copied) {
if (flags & MSG_PEEK)
/*refcount_dec(&skb->users)*/;
else
skb_queue_head(&msk->sk_receive_queue, skb);
return -ENOSPC;
}
memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
MISDN_HEADER_LEN);
memcpy(data, skb->data, skb->len);
dev_kfree_skb(skb);
return copied;
}
int mISDN_data_sendmsg(void *inst, unsigned char *data, size_t len, int __attribute__((unused)) flags, struct sockaddr_mISDN *maddr)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct sk_buff *skb;
int err = -ENOMEM;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: len %d ch %d proto %x\n",
__func__, (int)len, msk->ch.nr,
msk->sk_protocol);
if (len < MISDN_HEADER_LEN)
return -EINVAL;
if (msk->sk_state != MISDN_BOUND)
return -EBADFD;
skb = _l2_alloc_skb(len, GFP_KERNEL);
if (!skb)
goto done;
memcpy(skb_put(skb, len), data, len);
memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
skb_pull(skb, MISDN_HEADER_LEN);
if (maddr) {
/* if we have a address, we use it */
mISDN_HEAD_ID(skb) = maddr->channel;
} else { /* use default for L2 messages */
if ((msk->sk_protocol == ISDN_P_LAPD_TE) ||
(msk->sk_protocol == ISDN_P_LAPD_NT))
mISDN_HEAD_ID(skb) = msk->ch.nr;
}
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s: ID:%x\n",
__func__, mISDN_HEAD_ID(skb));
err = -ENODEV;
if (!msk->ch.peer)
goto done;
err = msk->ch.recv(msk->ch.peer, skb);
if (err)
goto done;
else {
skb = NULL;
err = len;
}
done:
kfree_skb(skb);
return err;
}
int mISDN_data_release(void *inst)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
if (!msk)
return 0;
switch (msk->sk_protocol) {
case ISDN_P_TE_S0:
case ISDN_P_NT_S0:
case ISDN_P_TE_E1:
case ISDN_P_NT_E1:
if (msk->sk_state == MISDN_BOUND)
delete_channel(&msk->ch);
else
mISDN_sock_unlink(&data_sockets, msk);
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(&msk->ch);
mISDN_sock_unlink(&data_sockets, msk);
break;
}
skb_queue_purge(&msk->sk_receive_queue);
free(msk);
return 0;
}
static int
data_sock_ioctl_bound(struct mISDN_sock *msk, unsigned int cmd, void *p)
{
struct mISDN_ctrl_req cq;
int err = -EINVAL, val[2];
struct mISDNchannel *bchan, *next;
if (!msk->dev) {
err = -ENODEV;
goto done;
}
switch (cmd) {
case IMCTRLREQ:
if (memcpy(&cq, p, sizeof(cq))) {
err = -EFAULT;
break;
}
if ((msk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
list_for_each_entry_safe(bchan, next,
&msk->dev->bchannels, list) {
if ((int)bchan->nr == cq.channel) {
err = bchan->ctrl(bchan,
CONTROL_CHANNEL, &cq);
break;
}
}
} else
err = msk->dev->D.ctrl(&msk->dev->D,
CONTROL_CHANNEL, &cq);
if (err)
break;
if (memcpy(p, &cq, sizeof(cq)))
err = -EFAULT;
break;
case IMCLEAR_L2:
if (msk->sk_protocol != ISDN_P_LAPD_NT) {
err = -EINVAL;
break;
}
val[0] = cmd;
val[1] = *((int *)p);
err = msk->dev->teimgr->ctrl(msk->dev->teimgr,
CONTROL_CHANNEL, val);
break;
case IMHOLD_L1:
if (msk->sk_protocol != ISDN_P_LAPD_NT
&& msk->sk_protocol != ISDN_P_LAPD_TE) {
err = -EINVAL;
break;
}
val[0] = cmd;
val[1] = *((int *)p);
err = msk->dev->teimgr->ctrl(msk->dev->teimgr,
CONTROL_CHANNEL, val);
break;
default:
err = -EINVAL;
break;
}
done:
return err;
}
int mISDN_data_ioctl(void *inst, unsigned int cmd, void *arg)
{
int err = 0, id;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct mISDNdevice *dev;
struct mISDNversion ver;
switch (cmd) {
case IMGETVERSION:
ver.major = MISDN_MAJOR_VERSION;
ver.minor = MISDN_MINOR_VERSION;
ver.release = MISDN_RELEASE;
memcpy((void *)arg, &ver, sizeof(ver));
break;
case IMGETCOUNT:
id = get_mdevice_count();
*((int *)arg) = id;
break;
case IMGETDEVINFO:
id = *((int *)arg);
dev = get_mdevice(id);
if (dev) {
struct mISDN_devinfo di;
memset(&di, 0, sizeof(di));
di.id = dev->id;
di.Dprotocols = dev->Dprotocols;
di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
di.protocol = dev->D.protocol;
memcpy(di.channelmap, dev->channelmap,
sizeof(di.channelmap));
di.nrbchan = dev->nrbchan;
strncpy(di.name, dev_name(dev), sizeof(di.name));
di.name[sizeof(di.name) - 1] = '\0';
memcpy((void *)arg, &di, sizeof(di));
} else
err = -ENODEV;
break;
default:
if (msk->sk_state == MISDN_BOUND)
err = data_sock_ioctl_bound(msk, cmd, arg);
else
err = -ENOTCONN;
}
return err;
}
#if 0
int mISDN_data_setopt(void *inst, int level, int optname,
sockptr_t optval, unsigned int len)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
int err = 0, opt = 0;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s(%p, %d, %x, optval, %d)\n", __func__, sock,
level, optname, len);
switch (optname) {
case MISDN_TIME_STAMP:
if (copy_from_sockptr(&opt, optval, sizeof(int))) {
err = -EFAULT;
break;
}
if (opt)
msk->cmask |= MISDN_TIME_STAMP;
else
msk->cmask &= ~MISDN_TIME_STAMP;
break;
default:
err = -ENOPROTOOPT;
break;
}
return err;
}
int mISDN_data_getopt(void *inst, int level, int optname,
char *optval, int *optlen)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
int len, opt;
len = *optlen;
if (len != sizeof(char))
return -EINVAL;
switch (optname) {
case MISDN_TIME_STAMP:
if (msk->cmask & MISDN_TIME_STAMP)
opt = 1;
else
opt = 0;
*((int *)optval) = opt;
return -EFAULT;
break;
default:
return -ENOPROTOOPT;
}
return 0;
}
#endif
int mISDN_data_bind(void *inst, struct sockaddr *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
struct mISDN_sock *cmsk;
int err = 0;
if (*debug & DEBUG_SOCKET)
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
if (addr_len != sizeof(struct sockaddr_mISDN))
return -EINVAL;
if (!maddr || maddr->family != AF_ISDN)
return -EINVAL;
if (msk->dev) {
err = -EALREADY;
goto done;
}
msk->dev = get_mdevice(maddr->dev);
if (!msk->dev) {
err = -ENODEV;
goto done;
}
if (msk->sk_protocol < ISDN_P_B_START) {
read_lock_bh(&data_sockets.lock);
hlist_for_each_entry(cmsk, &data_sockets.head, list) {
if (msk == cmsk)
continue;
if (cmsk->dev != msk->dev)
continue;
if (cmsk->sk_protocol >= ISDN_P_B_START)
continue;
if (IS_ISDN_P_TE(cmsk->sk_protocol)
== IS_ISDN_P_TE(msk->sk_protocol))
continue;
read_unlock_bh(&data_sockets.lock);
err = -EBUSY;
goto done;
}
read_unlock_bh(&data_sockets.lock);
}
msk->ch.send = mISDN_send;
msk->ch.ctrl = mISDN_ctrl;
switch (msk->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, msk);
err = connect_layer1(msk->dev, &msk->ch,
msk->sk_protocol, maddr);
if (err)
mISDN_sock_link(&data_sockets, msk);
break;
case ISDN_P_LAPD_TE:
case ISDN_P_LAPD_NT:
err = create_l2entity(msk->dev, &msk->ch,
msk->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(msk->dev, &msk->ch,
msk->sk_protocol, maddr);
break;
default:
err = -EPROTONOSUPPORT;
}
if (err)
goto done;
msk->sk_state = MISDN_BOUND;
msk->ch.protocol = msk->sk_protocol;
done:
return err;
}
int mISDN_data_getname(void *inst, struct sockaddr *addr, int __attribute__((unused)) peer)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
if (!msk->dev)
return -EBADFD;
maddr->family = AF_ISDN;
maddr->dev = msk->dev->id;
maddr->channel = msk->ch.nr;
maddr->sapi = msk->ch.addr & 0xff;
maddr->tei = (msk->ch.addr >> 8) & 0xff;
return sizeof(*maddr);
}
int mISDN_data_create(void **inst, int protocol)
{
struct mISDN_sock *msk;
msk = calloc(1, sizeof(*msk));
if (!msk)
return -ENOMEM;
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
skb_queue_head_init(&msk->sk_receive_queue);
msk->sk_protocol = protocol;
msk->sk_state = MISDN_OPEN;
mISDN_sock_link(&data_sockets, msk);
*inst = msk;
return 0;
}
int mISDN_base_release(void *inst)
{
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
if (!msk)
return 0;
mISDN_sock_unlink(&base_sockets, msk);
free(msk);
return 0;
}
int mISDN_base_ioctl(void *inst __attribute__((unused)), unsigned int cmd, void *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;
memcpy((void *)arg, &ver, sizeof(ver));
break;
case IMGETCOUNT:
id = get_mdevice_count();
*((int *)arg) = id;
break;
case IMGETDEVINFO:
id = *((int *)arg);
dev = get_mdevice(id);
if (dev) {
struct mISDN_devinfo di;
memset(&di, 0, sizeof(di));
di.id = dev->id;
di.Dprotocols = dev->Dprotocols;
di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
di.protocol = dev->D.protocol;
memcpy(di.channelmap, dev->channelmap,
sizeof(di.channelmap));
di.nrbchan = dev->nrbchan;
strncpy(di.name, dev_name(dev), sizeof(di.name));
di.name[sizeof(di.name) - 1] = '\0';
memcpy((void *)arg, &di, sizeof(di));
} else
err = -ENODEV;
break;
case IMSETDEVNAME:
{
struct mISDN_devrename dn;
memcpy(&dn, (void *)arg, sizeof(dn));
dn.name[sizeof(dn.name) - 1] = '\0';
dev = get_mdevice(dn.id);
if (dev)
dev_set_name(dev, "%s", dn.name);
else
err = -ENODEV;
err = -EINVAL;
}
break;
default:
err = -EINVAL;
}
return err;
}
int mISDN_base_bind(void *inst, struct sockaddr *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct mISDN_sock *msk = (struct mISDN_sock *)inst;
int err = 0;
if (addr_len < (int)sizeof(struct sockaddr_mISDN))
return -EINVAL;
if (!maddr || maddr->family != AF_ISDN)
return -EINVAL;
if (msk->dev) {
err = -EALREADY;
goto done;
}
msk->dev = get_mdevice(maddr->dev);
if (!msk->dev) {
err = -ENODEV;
goto done;
}
msk->sk_state = MISDN_BOUND;
done:
return err;
}
int mISDN_base_create(void **inst, int protocol)
{
struct mISDN_sock *msk;
msk = calloc(1, sizeof(*msk));
if (!msk)
return -ENOMEM;
printk(KERN_DEBUG "%s msk=%p\n", __func__, msk);
skb_queue_head_init(&msk->sk_receive_queue);
msk->sk_protocol = protocol;
msk->sk_state = MISDN_OPEN;
mISDN_sock_link(&base_sockets, msk);
*inst = msk;
return 0;
}
int misdn_sock_init(u_int *deb)
{
debug = deb;
return 0;
}
void misdn_sock_cleanup(void)
{
}

14
src/libmisdn/socket.h Normal file
View File

@ -0,0 +1,14 @@
int mISDN_base_create(void **inst, int protocol);
int mISDN_base_bind(void *inst, struct sockaddr *addr, int addr_len);
int mISDN_base_ioctl(void *inst, unsigned int cmd, void *arg);
int mISDN_base_release(void *inst);
int mISDN_data_create(void **inst, int protocol);
int mISDN_data_getname(void *inst, struct sockaddr *addr, int peer);
int mISDN_data_bind(void *inst, struct sockaddr *addr, int addr_len);
int mISDN_data_ioctl(void *inst, unsigned int cmd, void *arg);
int mISDN_data_release(void *inst);
int mISDN_data_sendmsg(void *inst, unsigned char *data, size_t len, int flags, struct sockaddr_mISDN *maddr);
int mISDN_data_recvmsg(void *inst, unsigned char *data, size_t len, int flags, struct sockaddr_mISDN *maddr);

660
src/libmisdn/stack.c Normal file
View File

@ -0,0 +1,660 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*/
#include "mISDNif.h"
#include "core.h"
#include <stdio.h>
#include <stdarg.h>
static u_int *debug;
static inline void
_queue_message(struct mISDNstack *st, struct sk_buff *skb)
{
struct mISDNhead *hh = mISDN_HEAD_P(skb);
if (*debug & DEBUG_QUEUE_FUNC)
printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
__func__, hh->prim, hh->id, skb);
skb_queue_tail(&st->msgq, skb);
if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) {
test_and_set_bit(mISDN_STACK_WORK, &st->status);
// wake_up_interruptible(&st->workq);
}
}
static int
mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
{
_queue_message(ch->st, skb);
return 0;
}
static struct mISDNchannel *
get_channel4id(struct mISDNstack *st, u_int id)
{
struct mISDNchannel *ch;
mutex_lock(&st->lmutex);
list_for_each_entry(ch, &st->layer2, list) {
if (id == ch->nr)
goto unlock;
}
ch = NULL;
unlock:
mutex_unlock(&st->lmutex);
return ch;
}
static void
send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb)
{
struct mISDN_sock *msk;
struct sk_buff *cskb = NULL;
read_lock(&sl->lock);
hlist_for_each_entry(msk, &sl->head, list) {
if (msk->sk_state != MISDN_BOUND)
continue;
if (!cskb)
cskb = skb_copy(skb, GFP_ATOMIC);
if (!cskb) {
printk(KERN_WARNING "%s no skb\n", __func__);
break;
}
skb_queue_tail(&msk->sk_receive_queue, cskb);
cskb = NULL;
}
read_unlock(&sl->lock);
dev_kfree_skb(cskb);
}
static void
send_layer2(struct mISDNstack *st, struct sk_buff *skb)
{
struct sk_buff *cskb;
struct mISDNhead *hh = mISDN_HEAD_P(skb);
struct mISDNchannel *ch;
int ret;
if (!st)
return;
mutex_lock(&st->lmutex);
if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */
list_for_each_entry(ch, &st->layer2, list) {
if (list_is_last(&ch->list, &st->layer2)) {
cskb = skb;
skb = NULL;
} else {
cskb = skb_copy(skb, GFP_KERNEL);
}
if (cskb) {
ret = ch->send(ch, cskb);
if (ret) {
if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s ch%d prim(%x) addr(%x)"
" err %d\n",
__func__, ch->nr,
hh->prim, ch->addr, ret);
dev_kfree_skb(cskb);
}
} else {
printk(KERN_WARNING "%s ch%d addr %x no mem\n",
__func__, ch->nr, ch->addr);
goto out;
}
}
} else {
list_for_each_entry(ch, &st->layer2, list) {
if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) {
ret = ch->send(ch, skb);
if (!ret)
skb = NULL;
goto out;
}
}
ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb);
if (!ret)
skb = NULL;
else if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s mgr prim(%x) err %d\n",
__func__, hh->prim, ret);
}
out:
mutex_unlock(&st->lmutex);
dev_kfree_skb(skb);
}
static inline int
send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
{
struct mISDNhead *hh = mISDN_HEAD_P(skb);
struct mISDNchannel *ch;
int lm;
lm = hh->prim & MISDN_LAYERMASK;
if (*debug & DEBUG_QUEUE_FUNC)
printk(KERN_DEBUG "%s prim(%x) id(%x) lm(%x) %p\n",
__func__, hh->prim, hh->id, lm, skb);
if (lm == 0x1) {
if (!hlist_empty(&st->l1sock.head)) {
__net_timestamp(skb);
send_socklist(&st->l1sock, skb);
}
return st->layer1->send(st->layer1, skb);
} else if (lm == 0x2) {
if (!hlist_empty(&st->l1sock.head))
send_socklist(&st->l1sock, skb);
send_layer2(st, skb);
return 0;
} else if (lm == 0x4) {
ch = get_channel4id(st, hh->id);
if (ch)
return ch->send(ch, skb);
else
printk(KERN_WARNING
"%s: dev(%s) prim(%x) id(%x) no channel\n",
__func__, dev_name(st->dev), hh->prim,
hh->id);
} else if (lm == 0x8) {
WARN_ON(lm == 0x8);
ch = get_channel4id(st, hh->id);
if (ch)
return ch->send(ch, skb);
else
printk(KERN_WARNING
"%s: dev(%s) prim(%x) id(%x) no channel\n",
__func__, dev_name(st->dev), hh->prim,
hh->id);
} else {
/* broadcast not handled yet */
printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
__func__, dev_name(st->dev), hh->prim);
}
return -ESRCH;
}
static void
do_clear_stack(struct mISDNstack __attribute__((unused)) *st)
{
}
int
work_stack(struct mISDNdevice *dev)
{
struct mISDNstack *st = dev->D.st;
#if 0
#ifdef MISDN_MSG_STATS
u64 utime, stime;
#endif
#endif
int err = 0;
#if 0
sigfillset(&current->blocked);
if (*debug & DEBUG_MSG_THREAD)
printk(KERN_DEBUG "mISDNStackd %s started\n",
dev_name(st->dev));
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
for (;;) {
#endif
struct sk_buff *skb;
if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) {
test_and_clear_bit(mISDN_STACK_WORK, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
} else
test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
while (test_bit(mISDN_STACK_WORK, &st->status)) {
skb = skb_dequeue(&st->msgq);
if (!skb) {
test_and_clear_bit(mISDN_STACK_WORK,
&st->status);
/* test if a race happens */
skb = skb_dequeue(&st->msgq);
if (!skb)
continue;
test_and_set_bit(mISDN_STACK_WORK,
&st->status);
}
#ifdef MISDN_MSG_STATS
st->msg_cnt++;
#endif
err = send_msg_to_layer(st, skb);
if (unlikely(err)) {
if (*debug & DEBUG_SEND_ERR)
printk(KERN_DEBUG
"%s: %s prim(%x) id(%x) "
"send call(%d)\n",
__func__, dev_name(st->dev),
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 0
if (test_bit(mISDN_STACK_ABORT, &st->status))
break;
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
#endif
#ifdef MISDN_MSG_STATS
st->sleep_cnt++;
#endif
#if 0
test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
wait_event_interruptible(st->workq, (st->status &
mISDN_STACK_ACTION_MASK));
if (*debug & DEBUG_MSG_THREAD)
printk(KERN_DEBUG "%s: %s wake status %08lx\n",
__func__, dev_name(st->dev), st->status);
test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
if (test_bit(mISDN_STACK_STOPPED, &st->status)) {
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
#ifdef MISDN_MSG_STATS
st->stopped_cnt++;
#endif
}
}
#ifdef MISDN_MSG_STATS
printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
"msg %d sleep %d stopped\n",
dev_name(st->dev), st->msg_cnt, st->sleep_cnt,
st->stopped_cnt);
task_cputime(st->thread, &utime, &stime);
printk(KERN_DEBUG
"mISDNStackd daemon for %s utime(%llu) stime(%llu)\n",
dev_name(st->dev), utime, stime);
printk(KERN_DEBUG
"mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
dev_name(st->dev), st->thread->nvcsw, st->thread->nivcsw);
printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
dev_name(st->dev));
#endif
test_and_set_bit(mISDN_STACK_KILLED, &st->status);
test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
test_and_clear_bit(mISDN_STACK_ABORT, &st->status);
skb_queue_purge(&st->msgq);
st->thread = NULL;
if (st->notify != NULL) {
complete(st->notify);
st->notify = NULL;
}
#endif
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__,
dev_name(newst->dev));
#if 0
newst->notify = &done;
newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
dev_name(newst->dev));
if (IS_ERR(newst->thread)) {
err = PTR_ERR(newst->thread);
printk(KERN_ERR
"mISDN:cannot create kernel thread for %s (%d)\n",
dev_name(newst->dev), err);
delete_teimanager(dev->teimgr);
kfree(newst);
} else
wait_for_completion(&done);
#endif
return err;
}
int
connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
struct channel_req rq;
int err;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(dev), protocol, adr->dev,
adr->channel, adr->sapi, adr->tei);
switch (protocol) {
case ISDN_P_NT_S0:
case ISDN_P_NT_E1:
case ISDN_P_TE_S0:
case ISDN_P_TE_E1:
ch->recv = mISDN_queue_message;
ch->peer = &dev->D.st->own;
ch->st = dev->D.st;
rq.protocol = protocol;
rq.adr.channel = adr->channel;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err,
dev->id);
if (err)
return err;
write_lock_bh(&dev->D.st->l1sock.lock);
hlist_add_head(&msk->list, &dev->D.st->l1sock.head);
write_unlock_bh(&dev->D.st->l1sock.lock);
break;
default:
return -ENOPROTOOPT;
}
return 0;
}
int
connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct channel_req rq, rq2;
int pmask, err;
struct Bprotocol *bp;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(dev), protocol,
adr->dev, adr->channel, adr->sapi,
adr->tei);
ch->st = dev->D.st;
pmask = 1 << (protocol & ISDN_P_B_MASK);
if (pmask & dev->Bprotocols) {
rq.protocol = protocol;
rq.adr = *adr;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
if (err)
return err;
ch->recv = rq.ch->send;
ch->peer = rq.ch;
rq.ch->recv = ch->send;
rq.ch->peer = ch;
rq.ch->st = dev->D.st;
} else {
bp = get_Bprotocol4mask(pmask);
if (!bp)
return -ENOPROTOOPT;
rq2.protocol = protocol;
rq2.adr = *adr;
rq2.ch = ch;
err = bp->create(&rq2);
if (err)
return err;
ch->recv = rq2.ch->send;
ch->peer = rq2.ch;
rq2.ch->st = dev->D.st;
rq.protocol = rq2.protocol;
rq.adr = *adr;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
if (err) {
rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL);
return err;
}
rq2.ch->recv = rq.ch->send;
rq2.ch->peer = rq.ch;
rq.ch->recv = rq2.ch->send;
rq.ch->peer = rq2.ch;
rq.ch->st = dev->D.st;
}
ch->protocol = protocol;
ch->nr = rq.ch->nr;
return 0;
}
int
create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
u_int protocol, struct sockaddr_mISDN *adr)
{
struct channel_req rq;
int err;
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
__func__, dev_name(dev), 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;
/* FALLTHRU */
case ISDN_P_LAPD_TE:
ch->recv = mISDN_queue_message;
ch->peer = &dev->D.st->own;
ch->st = dev->D.st;
rq.adr.channel = 0;
err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
if (err)
break;
rq.protocol = protocol;
rq.adr = *adr;
rq.ch = ch;
err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq);
printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err);
if (!err) {
if ((protocol == ISDN_P_LAPD_NT) && !rq.ch)
break;
add_layer2(rq.ch, dev->D.st);
rq.ch->recv = mISDN_queue_message;
rq.ch->peer = &dev->D.st->own;
rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */
}
break;
default:
err = -EPROTONOSUPPORT;
}
return err;
}
void
delete_channel(struct mISDNchannel *ch)
{
struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
struct mISDNchannel *pch;
if (!ch->st) {
printk(KERN_WARNING "%s: no stack\n", __func__);
return;
}
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
dev_name(ch->st->dev), 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);
hlist_del(&msk->list);
write_unlock_bh(&ch->st->l1sock.lock);
ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL);
break;
case ISDN_P_LAPD_TE:
pch = get_channel4id(ch->st, ch->nr);
if (pch) {
mutex_lock(&ch->st->lmutex);
list_del(&pch->list);
mutex_unlock(&ch->st->lmutex);
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
pch = ch->st->dev->teimgr;
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
} else
printk(KERN_WARNING "%s: no l2 channel\n",
__func__);
break;
case ISDN_P_LAPD_NT:
pch = ch->st->dev->teimgr;
if (pch) {
pch->ctrl(pch, CLOSE_CHANNEL, NULL);
} else
printk(KERN_WARNING "%s: no l2 channel\n",
__func__);
break;
default:
break;
}
return;
}
void
delete_stack(struct mISDNdevice *dev)
{
struct mISDNstack *st = dev->D.st;
// DECLARE_COMPLETION_ONSTACK(done);
if (*debug & DEBUG_CORE_FUNC)
printk(KERN_DEBUG "%s: st(%s)\n", __func__,
dev_name(st->dev));
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;
}

1412
src/libmisdn/tei.c Normal file

File diff suppressed because it is too large Load Diff

19
src/libmisdn/timer.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _MISDN_TIMER_H
#define _MISDN_TIMER_H
#include "../libtimer/timer.h"
#if 0
#define timer_list timer
#define timer_setup(ti, fu, flags) timer_init(ti, fu, NULL)
#define add_timer timer_start
#define del_timer timer_stop
#define timer_pending timer_running
#endif
#define from_timer(var, callback_timer, timer_fieldname) \
container_of(callback_timer, typeof(*var), timer_fieldname)
#endif