2012 lines
51 KiB
C
2012 lines
51 KiB
C
/* $Id$
|
|
*
|
|
* Copyright 2000 by Karsten Keil <kkeil@isdn4linux.de>
|
|
*
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/stddef.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/config.h>
|
|
#include <linux/timer.h>
|
|
#include <linux/devfs_fs_kernel.h>
|
|
#include "core.h"
|
|
|
|
#define MAX_HEADER_LEN 4
|
|
|
|
#define FLG_MGR_SETSTACK 1
|
|
#define FLG_MGR_OWNSTACK 2
|
|
|
|
#define FLG_MGR_TIMER_INIT 1
|
|
#define FLG_MGR_TIMER_RUNING 2
|
|
|
|
#define MAX_USERDEVICE_ID MAX_LAYER_NR
|
|
|
|
typedef struct _devicelayer {
|
|
struct list_head list;
|
|
mISDNdevice_t *dev;
|
|
mISDNinstance_t inst;
|
|
mISDNinstance_t *slave;
|
|
// mISDNif_t s_up;
|
|
// mISDNif_t s_down;
|
|
u_int id;
|
|
u_int lm_st;
|
|
u_long Flags;
|
|
} devicelayer_t;
|
|
|
|
typedef struct _devicestack {
|
|
struct list_head list;
|
|
mISDNdevice_t *dev;
|
|
mISDNstack_t *st;
|
|
int extentions;
|
|
} devicestack_t;
|
|
|
|
typedef struct _mISDNtimer {
|
|
struct list_head list;
|
|
struct _mISDNdevice *dev;
|
|
struct timer_list tl;
|
|
u_int id;
|
|
u_long Flags;
|
|
} mISDNtimer_t;
|
|
|
|
typedef struct entity_item {
|
|
struct list_head head;
|
|
int entity;
|
|
} entity_item_t;
|
|
|
|
static LIST_HEAD(mISDN_devicelist);
|
|
static rwlock_t mISDN_device_lock = RW_LOCK_UNLOCKED;
|
|
|
|
static struct class *mISDN_class;
|
|
|
|
static mISDNobject_t udev_obj;
|
|
static char MName[] = "UserDevice";
|
|
|
|
static int device_debug = 0;
|
|
|
|
static int from_up_down(mISDNinstance_t *, struct sk_buff *);
|
|
|
|
// static int from_peer(mISDNif_t *, u_int, int, int, void *);
|
|
// static int to_peer(mISDNif_t *, u_int, int, int, void *);
|
|
|
|
|
|
static mISDNdevice_t *
|
|
get_mISDNdevice4minor(int minor)
|
|
{
|
|
mISDNdevice_t *dev;
|
|
|
|
read_lock(&mISDN_device_lock);
|
|
list_for_each_entry(dev, &mISDN_devicelist, list) {
|
|
if (dev->minor == minor) {
|
|
read_unlock(&mISDN_device_lock);
|
|
return(dev);
|
|
}
|
|
}
|
|
read_unlock(&mISDN_device_lock);
|
|
return(NULL);
|
|
}
|
|
|
|
#ifdef FIXME
|
|
static int
|
|
mISDN_rdata_raw(mISDNinstance_t *inst, struct sk_buff *skb) {
|
|
mISDNdevice_t *dev;
|
|
mISDN_head_t *hh;
|
|
u_long flags;
|
|
int retval = 0;
|
|
|
|
dev = inst->priv;
|
|
hh = mISDN_HEAD_P(skb);
|
|
if (hh->prim == (PH_DATA | INDICATION)) {
|
|
if (test_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag)) {
|
|
spin_lock_irqsave(&dev->rport.lock, flags);
|
|
if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen)
|
|
retval = -ENOSPC;
|
|
else
|
|
skb_queue_tail(&dev->rport.queue, skb);
|
|
spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
wake_up_interruptible(&dev->rport.procq);
|
|
} else {
|
|
printk(KERN_WARNING "%s: PH_DATA_IND device(%d) not read open\n",
|
|
__FUNCTION__, dev->minor);
|
|
retval = -ENOENT;
|
|
}
|
|
} else if (hh->prim == (PH_DATA | CONFIRM)) {
|
|
test_and_clear_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag);
|
|
dev_kfree_skb_any(skb);
|
|
spin_lock_irqsave(&dev->wport.lock, flags);
|
|
if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) {
|
|
spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
return(0);
|
|
}
|
|
while ((skb = skb_dequeue(&dev->wport.queue))) {
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag);
|
|
if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) {
|
|
skb_queue_head(&dev->wport.queue, skb);
|
|
break;
|
|
}
|
|
if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) {
|
|
spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
retval = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb);
|
|
spin_lock_irqsave(&dev->wport.lock, flags);
|
|
if (retval) {
|
|
printk(KERN_WARNING "%s: dev(%d) down err(%d)\n",
|
|
__FUNCTION__, dev->minor, retval);
|
|
dev_kfree_skb(skb);
|
|
} else {
|
|
test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag);
|
|
wake_up(&dev->wport.procq);
|
|
break;
|
|
}
|
|
} else {
|
|
printk(KERN_WARNING "%s: dev(%d) wport not enabled\n",
|
|
__FUNCTION__, dev->minor);
|
|
dev_kfree_skb(skb);
|
|
}
|
|
wake_up(&dev->wport.procq);
|
|
}
|
|
test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag);
|
|
spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
} else if ((hh->prim == (PH_ACTIVATE | CONFIRM)) ||
|
|
(hh->prim == (PH_ACTIVATE | INDICATION))) {
|
|
test_and_set_bit(FLG_mISDNPORT_ENABLED,
|
|
&dev->wport.Flag);
|
|
test_and_clear_bit(FLG_mISDNPORT_BLOCK,
|
|
&dev->wport.Flag);
|
|
} else if ((hh->prim == (PH_DEACTIVATE | CONFIRM)) ||
|
|
(hh->prim == (PH_DEACTIVATE | INDICATION))) {
|
|
test_and_clear_bit(FLG_mISDNPORT_ENABLED,
|
|
&dev->wport.Flag);
|
|
} else {
|
|
printk(KERN_WARNING "%s: prim(%x) dinfo(%x) not supported\n",
|
|
__FUNCTION__, hh->prim, hh->dinfo);
|
|
retval = -EINVAL;
|
|
}
|
|
if (!retval)
|
|
dev_kfree_skb_any(skb);
|
|
return(retval);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
mISDN_rdata(mISDNdevice_t *dev, struct sk_buff *skb)
|
|
{
|
|
mISDN_head_t *hp;
|
|
u_long flags;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (hp->len <= 0)
|
|
skb_trim(skb, 0);
|
|
if (device_debug & DEBUG_RDATA)
|
|
printk(KERN_DEBUG "%s: %x:%x %x %d %d\n",
|
|
__FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len);
|
|
spin_lock_irqsave(&dev->rport.lock, flags);
|
|
if (skb_queue_len(&dev->rport.queue) >= dev->rport.maxqlen) {
|
|
spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
printk(KERN_WARNING "%s: rport queue overflow %d/%d\n",
|
|
__FUNCTION__, skb_queue_len(&dev->rport.queue), dev->rport.maxqlen);
|
|
return(-ENOSPC);
|
|
}
|
|
skb_queue_tail(&dev->rport.queue, skb);
|
|
spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
wake_up_interruptible(&dev->rport.procq);
|
|
return(0);
|
|
}
|
|
static int
|
|
error_answer(mISDNdevice_t *dev, struct sk_buff *skb, int err)
|
|
{
|
|
mISDN_head_t *hp;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
hp->prim |= 1; /* CONFIRM or RESPONSE */
|
|
hp->len = err;
|
|
return(mISDN_rdata(dev, skb));
|
|
}
|
|
|
|
static devicelayer_t
|
|
*get_devlayer(mISDNdevice_t *dev, u_int addr)
|
|
{
|
|
devicelayer_t *dl;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr);
|
|
list_for_each_entry(dl, &dev->layerlist, list) {
|
|
// if (device_debug & DEBUG_MGR_FUNC)
|
|
// printk(KERN_DEBUG "%s: dl(%p) id:%x\n",
|
|
// __FUNCTION__, dl, dl->id);
|
|
if (dl->id == (INST_ID_MASK & addr))
|
|
return(dl);
|
|
if (dl->inst.id == (INST_ID_MASK & addr))
|
|
return(dl);
|
|
if (dl->slave && (dl->slave->id == (INST_ID_MASK & addr)))
|
|
return(dl);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
static u_int
|
|
get_new_devicelayer_id(mISDNdevice_t *dev, u_int addr)
|
|
{
|
|
devicelayer_t *dl;
|
|
u_int i, found;
|
|
|
|
addr |= (FLG_INSTANCE | FLG_ID_USER);
|
|
for (i=0; i<= MAX_USERDEVICE_ID; i++) {
|
|
found = 0;
|
|
list_for_each_entry(dl, &dev->layerlist, list) {
|
|
if (dl->id == (addr | i))
|
|
found++;
|
|
}
|
|
if (!found)
|
|
return(addr | i);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static devicestack_t
|
|
*get_devstack(mISDNdevice_t *dev, int addr)
|
|
{
|
|
devicestack_t *ds;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr);
|
|
list_for_each_entry(ds, &dev->stacklist, list) {
|
|
if (ds->st && (ds->st->id == (u_int)addr))
|
|
return(ds);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
static mISDNtimer_t
|
|
*get_devtimer(mISDNdevice_t *dev, int id)
|
|
{
|
|
mISDNtimer_t *ht;
|
|
|
|
if (device_debug & DEBUG_DEV_TIMER)
|
|
printk(KERN_DEBUG "%s: dev:%p id:%x\n", __FUNCTION__, dev, id);
|
|
list_for_each_entry(ht, &dev->timerlist, list) {
|
|
if (ht->id == id)
|
|
return(ht);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
static int
|
|
stack_inst_flg(mISDNdevice_t *dev, mISDNstack_t *st, int bit, int clear)
|
|
{
|
|
int ret;
|
|
devicelayer_t *dl;
|
|
|
|
list_for_each_entry(dl, &dev->layerlist, list) {
|
|
if (dl->inst.st == st) {
|
|
if (clear)
|
|
ret = test_and_clear_bit(bit, &dl->Flags);
|
|
else
|
|
ret = test_and_set_bit(bit, &dl->Flags);
|
|
return(ret);
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
static int
|
|
new_devstack(mISDNdevice_t *dev, stack_info_t *si)
|
|
{
|
|
int err;
|
|
mISDNstack_t *st;
|
|
mISDNinstance_t inst;
|
|
devicestack_t *nds;
|
|
|
|
memset(&inst, 0, sizeof(mISDNinstance_t));
|
|
st = get_stack4id(si->id);
|
|
if (si->extentions & EXT_STACK_CLONE) {
|
|
if (st) {
|
|
inst.st = st;
|
|
} else {
|
|
int_errtxt("ext(%x) st(%x)", si->extentions, si->id);
|
|
return(-EINVAL);
|
|
}
|
|
}
|
|
err = udev_obj.ctrl(NULL, MGR_NEWSTACK | REQUEST, &inst);
|
|
if (err) {
|
|
int_error();
|
|
return(err);
|
|
}
|
|
if (!(nds = kmalloc(sizeof(devicestack_t), GFP_ATOMIC))) {
|
|
printk(KERN_ERR "kmalloc devicestack failed\n");
|
|
udev_obj.ctrl(inst.st, MGR_DELSTACK | REQUEST, NULL);
|
|
return(-ENOMEM);
|
|
}
|
|
memset(nds, 0, sizeof(devicestack_t));
|
|
nds->dev = dev;
|
|
if (si->extentions & EXT_STACK_CLONE) {
|
|
// memcpy(&inst.st->pid, &st->pid, sizeof(mISDN_pid_t));
|
|
// FIXME that is a ugly idea, but I don't have a better one
|
|
inst.st->childlist.prev = &st->childlist;
|
|
} else {
|
|
memcpy(&inst.st->pid, &si->pid, sizeof(mISDN_pid_t));
|
|
}
|
|
nds->extentions = si->extentions;
|
|
inst.st->extentions |= si->extentions;
|
|
inst.st->mgr = get_instance4id(si->mgr);
|
|
nds->st = inst.st;
|
|
list_add_tail(&nds->list, &dev->stacklist);
|
|
return(inst.st->id);
|
|
}
|
|
|
|
static mISDNstack_t *
|
|
sel_channel(u_int addr, u_int channel)
|
|
{
|
|
mISDNstack_t *st;
|
|
channel_info_t ci;
|
|
|
|
st = get_stack4id(addr);
|
|
if (!st)
|
|
return(NULL);
|
|
ci.channel = channel;
|
|
ci.st.p = NULL;
|
|
if (udev_obj.ctrl(st, MGR_SELCHANNEL | REQUEST, &ci))
|
|
return(NULL);
|
|
return(ci.st.p);
|
|
}
|
|
|
|
static int
|
|
from_up_down(mISDNinstance_t *inst, struct sk_buff *skb) {
|
|
|
|
devicelayer_t *dl;
|
|
mISDN_head_t *hh;
|
|
int retval = -EINVAL;
|
|
|
|
dl = inst->privat;
|
|
hh = mISDN_HEAD_P(skb);
|
|
hh->len = skb->len;
|
|
// hh->addr = dl->iaddr;
|
|
if (device_debug & DEBUG_RDATA)
|
|
printk(KERN_DEBUG "from_up_down: %x(%x) dinfo:%x len:%d\n",
|
|
hh->prim, hh->addr, hh->dinfo, hh->len);
|
|
retval = mISDN_rdata(dl->dev, skb);
|
|
return(retval);
|
|
}
|
|
|
|
static int
|
|
create_layer(mISDNdevice_t *dev, struct sk_buff *skb)
|
|
{
|
|
layer_info_t *linfo;
|
|
mISDNstack_t *st;
|
|
int i, ret;
|
|
devicelayer_t *nl;
|
|
mISDNobject_t *obj;
|
|
mISDNinstance_t *inst = NULL;
|
|
mISDN_head_t *hp;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
linfo = (layer_info_t *)skb->data;
|
|
if (!(st = get_stack4id(linfo->st))) {
|
|
/* should be changed */
|
|
printk(KERN_WARNING "%s: no stack found for id(%08x)\n", __FUNCTION__, linfo->st);
|
|
return(-ENODEV);
|
|
}
|
|
if (linfo->object_id != -1) {
|
|
obj = get_object(linfo->object_id);
|
|
if (!obj) {
|
|
printk(KERN_WARNING "%s: no object %x found\n",
|
|
__FUNCTION__, linfo->object_id);
|
|
return(-ENODEV);
|
|
}
|
|
ret = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, &linfo->pid);
|
|
if (ret) {
|
|
printk(KERN_WARNING "%s: error nl req %d\n",
|
|
__FUNCTION__, ret);
|
|
return(ret);
|
|
}
|
|
inst = getlayer4lay(st, linfo->pid.layermask);
|
|
if (!inst) {
|
|
printk(KERN_WARNING "%s: no inst found\n", __FUNCTION__);
|
|
return(-EINVAL);
|
|
}
|
|
} else if (linfo->extentions & EXT_INST_CLONE) {
|
|
mISDN_pid_t *pid;
|
|
|
|
inst = get_instance4id(linfo->parent);
|
|
if (!inst) {
|
|
printk(KERN_WARNING "%s: no parent inst found\n", __FUNCTION__);
|
|
return(-EINVAL);
|
|
}
|
|
if (!(inst->extentions & EXT_INST_CLONE)) {
|
|
printk(KERN_WARNING "%s: inst(%08x) ext(%x) is not cloneable\n",
|
|
__FUNCTION__, inst->id, inst->extentions);
|
|
return(-ENOSYS);
|
|
}
|
|
for(i=0; i<=MAX_LAYER_NR; i++)
|
|
if (!st->i_array[i])
|
|
break;
|
|
if (i > MAX_LAYER_NR) {
|
|
printk(KERN_WARNING "%s: no free instance slot in stack id(%08x)\n",
|
|
__FUNCTION__, st->id);
|
|
return(-EBUSY);
|
|
}
|
|
pid = kmalloc(sizeof(mISDN_pid_t), GFP_ATOMIC);
|
|
if (!pid) {
|
|
printk(KERN_ERR "kmalloc pid failed\n");
|
|
return(-ENOMEM);
|
|
}
|
|
memset(pid, 0, sizeof(mISDN_pid_t));
|
|
if (inst->pid.pbuf && inst->pid.maxplen) {
|
|
pid->pbuf = kmalloc(inst->pid.maxplen, GFP_ATOMIC);
|
|
if (!pid->pbuf) {
|
|
kfree(pid);
|
|
printk(KERN_ERR "kmalloc pid->pbuf failed\n");
|
|
return(-ENOMEM);
|
|
}
|
|
memset(pid->pbuf, 0, inst->pid.maxplen);
|
|
}
|
|
copy_pid(pid, &inst->pid, pid->pbuf);
|
|
ret = inst->obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, pid);
|
|
kfree(pid->pbuf);
|
|
kfree(pid);
|
|
if (ret) {
|
|
printk(KERN_WARNING "%s: MGR_NEWLAYER | REQUEST for clone returns %d\n", __FUNCTION__, ret);
|
|
return(ret);
|
|
}
|
|
if (!st->i_array[i]) {
|
|
int_error();
|
|
return(-EINVAL);
|
|
} else {
|
|
while (inst->clone)
|
|
inst = inst->clone;
|
|
inst->clone = st->i_array[i];
|
|
st->i_array[i]->parent = inst;
|
|
inst = st->i_array[i];
|
|
}
|
|
}
|
|
#if 0
|
|
else if ((inst = getlayer4lay(st, linfo->pid.layermask))) {
|
|
if (!(linfo->extentions & EXT_INST_MIDDLE)) {
|
|
printk(KERN_WARNING
|
|
"mISDN create_layer st(%x) LM(%x) inst not empty(%08x)\n",
|
|
st->id, linfo->pid.layermask, inst->id);
|
|
return(-EBUSY);
|
|
}
|
|
}
|
|
#endif
|
|
if (!(nl = kmalloc(sizeof(devicelayer_t), GFP_ATOMIC))) {
|
|
printk(KERN_ERR "kmalloc devicelayer failed\n");
|
|
return(-ENOMEM);
|
|
}
|
|
memset(nl, 0, sizeof(devicelayer_t));
|
|
if (inst)
|
|
nl->id = get_new_devicelayer_id(dev, inst->id);
|
|
else if (st)
|
|
nl->id = get_new_devicelayer_id(dev, st->id);
|
|
else
|
|
nl->id = get_new_devicelayer_id(dev, 0);
|
|
if (!nl->id) {
|
|
int_errtxt("overflow devicelayer ids");
|
|
kfree(nl);
|
|
return(-EBUSY);
|
|
}
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG
|
|
"mISDN create_layer LM(%x) nl(%p:%08x) nl->inst(%p) inst(%p)\n",
|
|
linfo->pid.layermask, nl, nl->id, &nl->inst, inst);
|
|
nl->dev = dev;
|
|
list_add_tail(&nl->list, &dev->layerlist);
|
|
if (!inst) {
|
|
mISDN_init_instance(&nl->inst, &udev_obj, nl, from_up_down);
|
|
memcpy(&nl->inst.pid, &linfo->pid, sizeof(mISDN_pid_t));
|
|
strcpy(nl->inst.name, linfo->name);
|
|
nl->inst.extentions = linfo->extentions;
|
|
for (i=0; i<= MAX_LAYER_NR; i++) {
|
|
if (linfo->pid.layermask & ISDN_LAYER(i)) {
|
|
if (st && (st->pid.protocol[i] == ISDN_PID_NONE)) {
|
|
st->pid.protocol[i] = linfo->pid.protocol[i];
|
|
nl->lm_st |= ISDN_LAYER(i);
|
|
}
|
|
}
|
|
}
|
|
if (st && (linfo->extentions & EXT_INST_MGR)) {
|
|
st->mgr = &nl->inst;
|
|
test_and_set_bit(FLG_MGR_OWNSTACK, &nl->Flags);
|
|
}
|
|
if (st)
|
|
nl->inst.obj->ctrl(st, MGR_ADDLAYER | REQUEST, &nl->inst);
|
|
} else {
|
|
nl->slave = inst;
|
|
nl->inst.extentions |= EXT_INST_UNUSED;
|
|
}
|
|
skb_trim(skb, 0);
|
|
memcpy(skb_put(skb, sizeof(nl->id)), &nl->id, sizeof(nl->id));
|
|
if (inst)
|
|
memcpy(skb_put(skb, sizeof(inst->id)), &inst->id, sizeof(inst->id));
|
|
else
|
|
memset(skb_put(skb, sizeof(nl->id)), 0, sizeof(nl->id));
|
|
return(8);
|
|
}
|
|
|
|
static int
|
|
del_stack(devicestack_t *ds)
|
|
{
|
|
mISDNdevice_t *dev;
|
|
|
|
if (!ds) {
|
|
int_error();
|
|
return(-EINVAL);
|
|
}
|
|
dev = ds->dev;
|
|
if (device_debug & DEBUG_MGR_FUNC) {
|
|
printk(KERN_DEBUG "%s: ds(%p) dev(%p)\n",
|
|
__FUNCTION__, ds, dev);
|
|
}
|
|
if (!dev)
|
|
return(-EINVAL);
|
|
if (ds->st) {
|
|
if (ds->extentions & EXT_STACK_CLONE)
|
|
INIT_LIST_HEAD(&ds->st->childlist);
|
|
udev_obj.ctrl(ds->st, MGR_DELSTACK | REQUEST, NULL);
|
|
}
|
|
list_del(&ds->list);
|
|
kfree(ds);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
del_layer(devicelayer_t *dl)
|
|
{
|
|
mISDNinstance_t *inst = &dl->inst;
|
|
mISDNdevice_t *dev = dl->dev;
|
|
int i;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC) {
|
|
printk(KERN_DEBUG "%s: dl(%p) inst(%p) LM(%x) dev(%p)\n",
|
|
__FUNCTION__, dl, inst, inst->pid.layermask, dev);
|
|
printk(KERN_DEBUG "%s: iaddr %x inst(%08x) %s slave %p\n",
|
|
__FUNCTION__, dl->id, inst->id, inst->name, dl->slave);
|
|
}
|
|
if (dl->slave) {
|
|
if (dl->slave->obj)
|
|
dl->slave->obj->own_ctrl(dl->slave,
|
|
MGR_UNREGLAYER | REQUEST, NULL);
|
|
else
|
|
dl->slave = NULL;
|
|
}
|
|
if (dl->lm_st && inst->st) {
|
|
for (i=0; i<= MAX_LAYER_NR; i++) {
|
|
if (dl->lm_st & ISDN_LAYER(i)) {
|
|
inst->st->pid.protocol[i] = ISDN_PID_NONE;
|
|
}
|
|
}
|
|
dl->lm_st = 0;
|
|
}
|
|
if (test_and_clear_bit(FLG_MGR_SETSTACK, &dl->Flags) && inst->st) {
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "del_layer: CLEARSTACK id(%x)\n",
|
|
inst->st->id);
|
|
udev_obj.ctrl(inst->st, MGR_CLEARSTACK | REQUEST, NULL);
|
|
}
|
|
dl->id = 0;
|
|
list_del(&dl->list);
|
|
if (!(inst->extentions & EXT_INST_UNUSED)) {
|
|
udev_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
|
|
}
|
|
if (test_and_clear_bit(FLG_MGR_OWNSTACK, &dl->Flags)) {
|
|
if (dl->inst.st) {
|
|
del_stack(get_devstack(dev, dl->inst.st->id));
|
|
}
|
|
}
|
|
kfree(dl);
|
|
return(0);
|
|
}
|
|
|
|
#ifdef OBSOLATE
|
|
static mISDNinstance_t *
|
|
clone_instance(devicelayer_t *dl, mISDNstack_t *st, mISDNinstance_t *peer)
|
|
{
|
|
int err;
|
|
|
|
if (dl->slave) {
|
|
printk(KERN_WARNING "%s: layer has slave, cannot clone\n",
|
|
__FUNCTION__);
|
|
return(NULL);
|
|
}
|
|
if (!(peer->extentions & EXT_INST_CLONE)) {
|
|
printk(KERN_WARNING "%s: peer cannot clone\n", __FUNCTION__);
|
|
return(NULL);
|
|
}
|
|
dl->slave = (mISDNinstance_t *)st;
|
|
if ((err = peer->obj->own_ctrl(peer, MGR_CLONELAYER | REQUEST,
|
|
&dl->slave))) {
|
|
dl->slave = NULL;
|
|
printk(KERN_WARNING "%s: peer clone error %d\n",
|
|
__FUNCTION__, err);
|
|
return(NULL);
|
|
}
|
|
return(dl->slave);
|
|
}
|
|
|
|
static int
|
|
remove_if(devicelayer_t *dl, int stat) {
|
|
mISDNif_t *hif,*phif,*shif;
|
|
int err;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: dl(%p) stat(%x)\n", __FUNCTION__,
|
|
dl, stat);
|
|
phif = NULL;
|
|
if (stat & IF_UP) {
|
|
hif = &dl->inst.up;
|
|
shif = &dl->s_up;
|
|
if (shif->owner)
|
|
phif = &shif->owner->down;
|
|
} else if (stat & IF_DOWN) {
|
|
hif = &dl->inst.down;
|
|
shif = &dl->s_down;
|
|
if (shif->owner)
|
|
phif = &shif->owner->up;
|
|
} else {
|
|
printk(KERN_WARNING "%s: stat not UP/DOWN\n", __FUNCTION__);
|
|
return(-EINVAL);
|
|
}
|
|
err = udev_obj.ctrl(hif->peer, MGR_DISCONNECT | REQUEST, hif);
|
|
if (phif) {
|
|
memcpy(phif, shif, sizeof(mISDNif_t));
|
|
memset(shif, 0, sizeof(mISDNif_t));
|
|
}
|
|
if (hif->predecessor)
|
|
hif->predecessor->clone = hif->clone;
|
|
if (hif->clone)
|
|
hif->clone->predecessor = hif->predecessor;
|
|
return(err);
|
|
}
|
|
|
|
static int
|
|
connect_if_req(mISDNdevice_t *dev, struct sk_buff *skb)
|
|
{
|
|
devicelayer_t *dl;
|
|
interface_info_t *ifi = (interface_info_t *)skb->data;
|
|
mISDNinstance_t *owner;
|
|
mISDNinstance_t *peer;
|
|
mISDNinstance_t *pp;
|
|
mISDNif_t *hifp;
|
|
int stat;
|
|
mISDN_head_t *hp;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n",
|
|
__FUNCTION__, hp->addr, ifi->owner, ifi->peer);
|
|
if (!(dl=get_devlayer(dev, ifi->owner))) {
|
|
int_errtxt("no devive_layer for %08x", ifi->owner);
|
|
return(-ENXIO);
|
|
}
|
|
if (!(owner = get_instance4id(ifi->owner))) {
|
|
printk(KERN_WARNING "%s: owner(%x) not found\n",
|
|
__FUNCTION__, ifi->owner);
|
|
return(-ENODEV);
|
|
}
|
|
if (!(peer = get_instance4id(ifi->peer))) {
|
|
printk(KERN_WARNING "%s: peer(%x) not found\n",
|
|
__FUNCTION__, ifi->peer);
|
|
return(-ENODEV);
|
|
}
|
|
if (owner->pid.layermask < peer->pid.layermask) {
|
|
hifp = &peer->down;
|
|
stat = IF_DOWN;
|
|
} else if (owner->pid.layermask > peer->pid.layermask) {
|
|
hifp = &peer->up;
|
|
stat = IF_UP;
|
|
} else {
|
|
int_errtxt("OLM == PLM: %x", owner->pid.layermask);
|
|
return(-EINVAL);
|
|
}
|
|
if (ifi->extentions == EXT_IF_CHAIN) {
|
|
if (!(pp = hifp->peer)) {
|
|
printk(KERN_WARNING "%s: peer if has no peer\n",
|
|
__FUNCTION__);
|
|
return(-EINVAL);
|
|
}
|
|
if (stat == IF_UP) {
|
|
memcpy(&owner->up, hifp, sizeof(mISDNif_t));
|
|
memcpy(&dl->s_up, hifp, sizeof(mISDNif_t));
|
|
owner->up.owner = owner;
|
|
hifp->peer = owner;
|
|
hifp->func = from_up_down;
|
|
hifp->fdata = dl;
|
|
hifp = &pp->down;
|
|
memcpy(&owner->down, hifp, sizeof(mISDNif_t));
|
|
memcpy(&dl->s_down, hifp, sizeof(mISDNif_t));
|
|
owner->down.owner = owner;
|
|
hifp->peer = owner;
|
|
hifp->func = from_up_down;
|
|
hifp->fdata = dl;
|
|
} else {
|
|
memcpy(&owner->down, hifp, sizeof(mISDNif_t));
|
|
memcpy(&dl->s_down, hifp, sizeof(mISDNif_t));
|
|
owner->up.owner = owner;
|
|
hifp->peer = owner;
|
|
hifp->func = from_up_down;
|
|
hifp->fdata = dl;
|
|
hifp = &pp->up;
|
|
memcpy(&owner->up, hifp, sizeof(mISDNif_t));
|
|
memcpy(&dl->s_up, hifp, sizeof(mISDNif_t));
|
|
owner->down.owner = owner;
|
|
hifp->peer = owner;
|
|
hifp->func = from_up_down;
|
|
hifp->fdata = dl;
|
|
}
|
|
return(0);
|
|
}
|
|
if (ifi->extentions & EXT_IF_CREATE) {
|
|
/* create new instance if allready in use */
|
|
if (hifp->stat != IF_NOACTIV) {
|
|
if ((peer = clone_instance(dl, owner->st, peer))) {
|
|
if (stat == IF_UP)
|
|
hifp = &peer->up;
|
|
else
|
|
hifp = &peer->down;
|
|
} else {
|
|
printk(KERN_WARNING "%s: cannot create new peer instance\n",
|
|
__FUNCTION__);
|
|
return(-EBUSY);
|
|
}
|
|
}
|
|
}
|
|
if (ifi->extentions & EXT_IF_EXCLUSIV) {
|
|
if (hifp->stat != IF_NOACTIV) {
|
|
printk(KERN_WARNING "%s: peer if is in use\n",
|
|
__FUNCTION__);
|
|
return(-EBUSY);
|
|
}
|
|
}
|
|
return(mISDN_ConnectIF(owner, peer));
|
|
}
|
|
|
|
static int
|
|
set_if_req(mISDNdevice_t *dev, struct sk_buff *skb)
|
|
{
|
|
mISDNif_t *hif,*phif,*shif;
|
|
int stat;
|
|
interface_info_t *ifi = (interface_info_t *)skb->data;
|
|
devicelayer_t *dl;
|
|
mISDNinstance_t *inst, *peer;
|
|
mISDN_head_t *hp;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n",
|
|
__FUNCTION__, hp->addr, ifi->owner, ifi->peer);
|
|
if (!(dl=get_devlayer(dev, hp->addr)))
|
|
return(-ENXIO);
|
|
if (!(inst = get_instance4id(ifi->owner))) {
|
|
printk(KERN_WARNING "%s: owner(%x) not found\n",
|
|
__FUNCTION__, ifi->owner);
|
|
return(-ENODEV);
|
|
}
|
|
if (!(peer = get_instance4id(ifi->peer))) {
|
|
printk(KERN_WARNING "%s: peer(%x) not found\n",
|
|
__FUNCTION__, ifi->peer);
|
|
return(-ENODEV);
|
|
}
|
|
|
|
if (ifi->stat == IF_UP) {
|
|
hif = &dl->inst.up;
|
|
phif = &peer->down;
|
|
shif = &dl->s_up;
|
|
stat = IF_DOWN;
|
|
} else if (ifi->stat == IF_DOWN) {
|
|
hif = &dl->inst.down;
|
|
shif = &dl->s_down;
|
|
phif = &peer->up;
|
|
stat = IF_UP;
|
|
} else {
|
|
printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__);
|
|
return(-EINVAL);
|
|
}
|
|
|
|
|
|
if (shif->stat != IF_NOACTIV) {
|
|
printk(KERN_WARNING "%s: save if busy\n", __FUNCTION__);
|
|
return(-EBUSY);
|
|
}
|
|
if (hif->stat != IF_NOACTIV) {
|
|
printk(KERN_WARNING "%s: own if busy\n", __FUNCTION__);
|
|
return(-EBUSY);
|
|
}
|
|
hif->stat = stat;
|
|
hif->owner = inst;
|
|
memcpy(shif, phif, sizeof(mISDNif_t));
|
|
memset(phif, 0, sizeof(mISDNif_t));
|
|
return(peer->obj->own_ctrl(peer, hp->prim, hif));
|
|
}
|
|
|
|
static int
|
|
add_if_req(mISDNdevice_t *dev, struct sk_buff *skb)
|
|
{
|
|
mISDNif_t *hif;
|
|
interface_info_t *ifi = (interface_info_t *)skb->data;
|
|
mISDNinstance_t *inst, *peer;
|
|
mISDN_head_t *hp;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: addr:%x own(%x) peer(%x)\n",
|
|
__FUNCTION__, hp->addr, ifi->owner, ifi->peer);
|
|
if (!(inst = get_instance4id(ifi->owner))) {
|
|
printk(KERN_WARNING "%s: owner(%x) not found\n",
|
|
__FUNCTION__, ifi->owner);
|
|
return(-ENODEV);
|
|
}
|
|
if (!(peer = get_instance4id(ifi->peer))) {
|
|
printk(KERN_WARNING "%s: peer(%x) not found\n",
|
|
__FUNCTION__, ifi->peer);
|
|
return(-ENODEV);
|
|
}
|
|
|
|
if (ifi->stat == IF_DOWN) {
|
|
hif = &inst->up;
|
|
} else if (ifi->stat == IF_UP) {
|
|
hif = &inst->down;
|
|
} else {
|
|
printk(KERN_WARNING "%s: if not UP/DOWN\n", __FUNCTION__);
|
|
return(-EINVAL);
|
|
}
|
|
return(peer->obj->ctrl(peer, hp->prim, hif));
|
|
}
|
|
|
|
static int
|
|
del_if_req(mISDNdevice_t *dev, u_int addr)
|
|
{
|
|
devicelayer_t *dl;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, addr);
|
|
if (!(dl=get_devlayer(dev, addr)))
|
|
return(-ENXIO);
|
|
return(remove_if(dl, addr));
|
|
}
|
|
|
|
static void
|
|
get_if_info(struct sk_buff *skb)
|
|
{
|
|
mISDN_head_t *hp;
|
|
mISDNinstance_t *inst;
|
|
mISDNif_t *hif;
|
|
interface_info_t *ii = (interface_info_t *)skb->data;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (!(inst = get_instance4id(hp->addr & IF_ADDRMASK))) {
|
|
printk(KERN_WARNING "%s: no instance\n", __FUNCTION__);
|
|
hp->len = -ENODEV;
|
|
return;
|
|
}
|
|
if (hp->dinfo == IF_DOWN)
|
|
hif = &inst->down;
|
|
else if (hp->dinfo == IF_UP)
|
|
hif = &inst->up;
|
|
else {
|
|
printk(KERN_WARNING "%s: wrong interface %x\n",
|
|
__FUNCTION__, hp->dinfo);
|
|
hp->len = -EINVAL;
|
|
return;
|
|
}
|
|
hp->dinfo = 0;
|
|
memset(ii, 0, sizeof(interface_info_t));
|
|
if (hif->owner)
|
|
ii->owner = hif->owner->id;
|
|
if (hif->peer)
|
|
ii->peer = hif->peer->id;
|
|
ii->extentions = hif->extentions;
|
|
ii->stat = hif->stat;
|
|
hp->len = sizeof(interface_info_t);
|
|
skb_put(skb, hp->len);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
new_entity_req(mISDNdevice_t *dev, int *entity)
|
|
{
|
|
int ret;
|
|
entity_item_t *ei = kmalloc(sizeof(entity_item_t), GFP_ATOMIC);
|
|
|
|
if (!ei)
|
|
return(-ENOMEM);
|
|
ret = mISDN_alloc_entity(entity);
|
|
ei->entity = *entity;
|
|
if (ret)
|
|
kfree(entity);
|
|
else
|
|
list_add((struct list_head *)ei, &dev->entitylist);
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
del_entity_req(mISDNdevice_t *dev, int entity)
|
|
{
|
|
struct list_head *item, *nxt;
|
|
|
|
list_for_each_safe(item, nxt, &dev->entitylist) {
|
|
if (((entity_item_t *)item)->entity == entity) {
|
|
list_del(item);
|
|
mISDN_delete_entity(entity);
|
|
kfree(item);
|
|
return(0);
|
|
}
|
|
}
|
|
return(-ENODEV);
|
|
}
|
|
|
|
static void
|
|
dev_expire_timer(mISDNtimer_t *ht)
|
|
{
|
|
struct sk_buff *skb;
|
|
mISDN_head_t *hp;
|
|
|
|
if (device_debug & DEBUG_DEV_TIMER)
|
|
printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id);
|
|
if (test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags)) {
|
|
skb = alloc_stack_skb(16, 0);
|
|
if (!skb) {
|
|
printk(KERN_WARNING "%s: timer(%x) no skb\n",
|
|
__FUNCTION__, ht->id);
|
|
return;
|
|
}
|
|
hp = mISDN_HEAD_P(skb);
|
|
hp->dinfo = 0;
|
|
hp->prim = MGR_TIMER | INDICATION;
|
|
hp->addr = ht->id;
|
|
hp->len = 0;
|
|
if (mISDN_rdata(ht->dev, skb))
|
|
dev_kfree_skb(skb);
|
|
} else
|
|
printk(KERN_WARNING "%s: timer(%x) not active\n",
|
|
__FUNCTION__, ht->id);
|
|
}
|
|
|
|
static int
|
|
dev_init_timer(mISDNdevice_t *dev, u_int id)
|
|
{
|
|
mISDNtimer_t *ht;
|
|
|
|
ht = get_devtimer(dev, id);
|
|
if (!ht) {
|
|
ht = kmalloc(sizeof(mISDNtimer_t), GFP_ATOMIC);
|
|
if (!ht)
|
|
return(-ENOMEM);
|
|
ht->dev = dev;
|
|
ht->id = id;
|
|
ht->tl.data = (long) ht;
|
|
ht->tl.function = (void *) dev_expire_timer;
|
|
init_timer(&ht->tl);
|
|
list_add_tail(&ht->list, &dev->timerlist);
|
|
if (device_debug & DEBUG_DEV_TIMER)
|
|
printk(KERN_DEBUG "%s: new(%x)\n", __FUNCTION__, ht->id);
|
|
} else if (device_debug & DEBUG_DEV_TIMER)
|
|
printk(KERN_DEBUG "%s: old(%x)\n", __FUNCTION__, ht->id);
|
|
if (timer_pending(&ht->tl)) {
|
|
printk(KERN_WARNING "%s: timer(%x) pending\n", __FUNCTION__,
|
|
ht->id);
|
|
del_timer(&ht->tl);
|
|
}
|
|
init_timer(&ht->tl);
|
|
test_and_set_bit(FLG_MGR_TIMER_INIT, &ht->Flags);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
dev_add_timer(mISDNdevice_t *dev, mISDN_head_t *hp)
|
|
{
|
|
mISDNtimer_t *ht;
|
|
|
|
ht = get_devtimer(dev, hp->addr);
|
|
if (!ht) {
|
|
printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__,
|
|
hp->addr);
|
|
return(-ENODEV);
|
|
}
|
|
if (timer_pending(&ht->tl)) {
|
|
printk(KERN_WARNING "%s: timer(%x) pending\n",
|
|
__FUNCTION__, ht->id);
|
|
return(-EBUSY);
|
|
}
|
|
if (hp->dinfo < 10) {
|
|
printk(KERN_WARNING "%s: timer(%x): %d ms too short\n",
|
|
__FUNCTION__, ht->id, hp->dinfo);
|
|
return(-EINVAL);
|
|
}
|
|
if (device_debug & DEBUG_DEV_TIMER)
|
|
printk(KERN_DEBUG "%s: timer(%x) %d ms\n",
|
|
__FUNCTION__, ht->id, hp->dinfo);
|
|
init_timer(&ht->tl);
|
|
ht->tl.expires = jiffies + (hp->dinfo * HZ) / 1000;
|
|
test_and_set_bit(FLG_MGR_TIMER_RUNING, &ht->Flags);
|
|
add_timer(&ht->tl);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
dev_del_timer(mISDNdevice_t *dev, u_int id)
|
|
{
|
|
mISDNtimer_t *ht;
|
|
|
|
ht = get_devtimer(dev, id);
|
|
if (!ht) {
|
|
printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__,
|
|
id);
|
|
return(-ENODEV);
|
|
}
|
|
if (device_debug & DEBUG_DEV_TIMER)
|
|
printk(KERN_DEBUG "%s: timer(%x)\n",
|
|
__FUNCTION__, ht->id);
|
|
del_timer(&ht->tl);
|
|
if (!test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags))
|
|
printk(KERN_WARNING "%s: timer(%x) not running\n",
|
|
__FUNCTION__, ht->id);
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
dev_free_timer(mISDNtimer_t *ht)
|
|
{
|
|
if (device_debug & DEBUG_DEV_TIMER)
|
|
printk(KERN_DEBUG "%s: timer(%x)\n", __FUNCTION__, ht->id);
|
|
del_timer(&ht->tl);
|
|
list_del(&ht->list);
|
|
kfree(ht);
|
|
}
|
|
|
|
static int
|
|
dev_remove_timer(mISDNdevice_t *dev, u_int id)
|
|
{
|
|
mISDNtimer_t *ht;
|
|
|
|
ht = get_devtimer(dev, id);
|
|
if (!ht) {
|
|
printk(KERN_WARNING "%s: no timer(%x)\n", __FUNCTION__, id);
|
|
return(-ENODEV);
|
|
}
|
|
dev_free_timer(ht);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
get_status(struct sk_buff *skb)
|
|
{
|
|
mISDN_head_t *hp;
|
|
status_info_t *si = (status_info_t *)skb->data;
|
|
mISDNinstance_t *inst;
|
|
int err;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (!(inst = get_instance4id(hp->addr & INST_ID_MASK))) {
|
|
printk(KERN_WARNING "%s: no instance\n", __FUNCTION__);
|
|
err = -ENODEV;
|
|
} else {
|
|
err = inst->obj->own_ctrl(inst, MGR_STATUS | REQUEST, si);
|
|
}
|
|
if (err)
|
|
hp->len = err;
|
|
else {
|
|
hp->len = si->len + 2*sizeof(int);
|
|
skb_put(skb, hp->len);
|
|
}
|
|
return(err);
|
|
}
|
|
|
|
static void
|
|
get_layer_info(struct sk_buff *skb)
|
|
{
|
|
mISDN_head_t *hp;
|
|
mISDNinstance_t *inst;
|
|
layer_info_t *li = (layer_info_t *)skb->data;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (!(inst = get_instance4id(hp->addr & INST_ID_MASK))) {
|
|
printk(KERN_WARNING "%s: no instance\n", __FUNCTION__);
|
|
hp->len = -ENODEV;
|
|
return;
|
|
}
|
|
memset(li, 0, sizeof(layer_info_t));
|
|
if (inst->obj)
|
|
li->object_id = inst->obj->id;
|
|
strcpy(li->name, inst->name);
|
|
li->extentions = inst->extentions;
|
|
li->id = inst->id;
|
|
if (inst->st)
|
|
li->st = inst->st->id;
|
|
if (inst->parent)
|
|
li->parent = inst->parent->id;
|
|
if (inst->clone)
|
|
li->clone = inst->clone->id;
|
|
memcpy(&li->pid, &inst->pid, sizeof(mISDN_pid_t));
|
|
hp->len = sizeof(layer_info_t);
|
|
skb_put(skb, hp->len);
|
|
}
|
|
|
|
static int
|
|
wdata_frame(mISDNdevice_t *dev, struct sk_buff *skb)
|
|
{
|
|
mISDN_head_t *hp;
|
|
devicelayer_t *dl;
|
|
int err = -ENXIO;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (device_debug & DEBUG_WDATA)
|
|
printk(KERN_DEBUG "%s: addr:%x\n", __FUNCTION__, hp->addr);
|
|
if (!(dl=get_devlayer(dev, hp->addr)))
|
|
return(err);
|
|
if (device_debug & DEBUG_WDATA)
|
|
printk(KERN_DEBUG "%s: pr(%x) di(%x) l(%d)\n",
|
|
__FUNCTION__, hp->prim, hp->dinfo, hp->len);
|
|
if (hp->len < 0) {
|
|
printk(KERN_WARNING "%s: data negativ(%d)\n",
|
|
__FUNCTION__, hp->len);
|
|
return(-EINVAL);
|
|
}
|
|
err = mISDN_queue_message(&dl->inst, hp->addr, skb);
|
|
if (device_debug & DEBUG_WDATA && err)
|
|
printk(KERN_DEBUG "%s: mISDN_send_message ret(%x)\n",
|
|
__FUNCTION__, err);
|
|
return(err);
|
|
}
|
|
|
|
static int
|
|
mISDN_wdata_if(mISDNdevice_t *dev, struct sk_buff *skb)
|
|
{
|
|
struct sk_buff *nskb = NULL;
|
|
mISDN_head_t *hp;
|
|
mISDNstack_t *st;
|
|
devicelayer_t *dl;
|
|
mISDNinstance_t *inst;
|
|
int lay;
|
|
int err = 0;
|
|
|
|
hp = mISDN_HEAD_P(skb);
|
|
if (device_debug & DEBUG_WDATA)
|
|
printk(KERN_DEBUG "%s: %x:%x %x %d %d\n",
|
|
__FUNCTION__, hp->addr, hp->prim, hp->dinfo, hp->len, skb->len);
|
|
if ((hp->len > 0) && (skb->len < hp->len)) {
|
|
printk(KERN_WARNING "%s: frame(%d/%d) too short\n",
|
|
__FUNCTION__, skb->len, hp->len);
|
|
return(error_answer(dev, skb, -EINVAL));
|
|
}
|
|
switch(hp->prim) {
|
|
case (MGR_VERSION | REQUEST):
|
|
hp->prim = MGR_VERSION | CONFIRM;
|
|
hp->len = 0;
|
|
hp->dinfo = MISDN_VERSION;
|
|
break;
|
|
case (MGR_GETSTACK | REQUEST):
|
|
hp->prim = MGR_GETSTACK | CONFIRM;
|
|
hp->dinfo = 0;
|
|
if (hp->addr <= 0) {
|
|
hp->dinfo = get_stack_cnt();
|
|
hp->len = 0;
|
|
} else {
|
|
nskb = alloc_stack_skb(1000, 0);
|
|
if (!nskb)
|
|
return(error_answer(dev, skb, -ENOMEM));
|
|
memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
|
|
get_stack_info(nskb);
|
|
}
|
|
break;
|
|
case (MGR_SETSTACK | REQUEST):
|
|
if (skb->len < sizeof(mISDN_pid_t))
|
|
return(error_answer(dev, skb, -EINVAL));
|
|
hp->dinfo = 0;
|
|
if ((st = get_stack4id(hp->addr))) {
|
|
stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 0);
|
|
hp->len = udev_obj.ctrl(st, hp->prim, skb->data);
|
|
} else
|
|
hp->len = -ENODEV;
|
|
hp->prim = MGR_SETSTACK | CONFIRM;
|
|
break;
|
|
case (MGR_NEWSTACK | REQUEST):
|
|
hp->dinfo = 0;
|
|
hp->prim = MGR_NEWSTACK | CONFIRM;
|
|
hp->len = 0;
|
|
err = new_devstack(dev, (stack_info_t *)skb->data);
|
|
if (err<0)
|
|
hp->len = err;
|
|
else
|
|
hp->dinfo = err;
|
|
break;
|
|
case (MGR_CLEARSTACK | REQUEST):
|
|
hp->dinfo = 0;
|
|
if ((st = get_stack4id(hp->addr))) {
|
|
stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 1);
|
|
hp->len = udev_obj.ctrl(st, hp->prim, NULL);
|
|
} else
|
|
hp->len = -ENODEV;
|
|
hp->prim = MGR_CLEARSTACK | CONFIRM;
|
|
break;
|
|
case (MGR_SELCHANNEL | REQUEST):
|
|
hp->prim = MGR_SELCHANNEL | CONFIRM;
|
|
st = sel_channel(hp->addr, hp->dinfo);
|
|
if (st) {
|
|
hp->len = 0;
|
|
hp->dinfo = st->id;
|
|
} else {
|
|
hp->dinfo = 0;
|
|
hp->len = -ENODEV;
|
|
}
|
|
break;
|
|
case (MGR_GETLAYERID | REQUEST):
|
|
hp->prim = MGR_GETLAYERID | CONFIRM;
|
|
lay = hp->dinfo;
|
|
hp->dinfo = 0;
|
|
if (LAYER_OUTRANGE(lay)) {
|
|
hp->len = -EINVAL;
|
|
} else {
|
|
hp->len = 0;
|
|
lay = ISDN_LAYER(lay);
|
|
if ((st = get_stack4id(hp->addr))) {
|
|
if ((inst = getlayer4lay(st, lay))) {
|
|
if (inst)
|
|
hp->dinfo = inst->id;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case (MGR_GETLAYER | REQUEST):
|
|
hp->prim = MGR_GETLAYER | CONFIRM;
|
|
hp->dinfo = 0;
|
|
skb_trim(skb, 0);
|
|
if (skb_tailroom(skb) < sizeof(layer_info_t)) {
|
|
nskb = alloc_stack_skb(sizeof(layer_info_t), 0);
|
|
if (!nskb)
|
|
return(error_answer(dev, skb, -ENOMEM));
|
|
memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
|
|
get_layer_info(nskb);
|
|
} else {
|
|
get_layer_info(skb);
|
|
}
|
|
break;
|
|
case (MGR_NEWLAYER | REQUEST):
|
|
if (skb->len < sizeof(layer_info_t))
|
|
return(error_answer(dev, skb, -EINVAL));
|
|
hp->dinfo = 0;
|
|
hp->prim = MGR_NEWLAYER | CONFIRM;
|
|
hp->len = create_layer(dev, skb);
|
|
break;
|
|
case (MGR_REGLAYER | REQUEST):
|
|
lay = hp->dinfo;
|
|
hp->dinfo = 0;
|
|
if (!(st = get_stack4id(hp->addr)))
|
|
return(error_answer(dev, skb, -ENODEV));
|
|
if (!(dl = get_devlayer(dev, lay)))
|
|
return(error_answer(dev, skb, -ENODEV));
|
|
hp->prim = MGR_REGLAYER | CONFIRM;
|
|
hp->len = udev_obj.ctrl(st, MGR_REGLAYER | REQUEST, &dl->inst);
|
|
printk(KERN_DEBUG "MGR_REGLAYER | REQUEST: ret(%d)\n", hp->len);
|
|
break;
|
|
case (MGR_UNREGLAYER | REQUEST):
|
|
lay = hp->dinfo;
|
|
hp->dinfo = 0;
|
|
if (!(st = get_stack4id(hp->addr)))
|
|
return(error_answer(dev, skb, -ENODEV));
|
|
if (!(dl = get_devlayer(dev, lay)))
|
|
return(error_answer(dev, skb, -ENODEV));
|
|
hp->prim = MGR_UNREGLAYER | CONFIRM;
|
|
hp->len = udev_obj.ctrl(st, MGR_UNREGLAYER | REQUEST, &dl->inst);
|
|
break;
|
|
case (MGR_DELLAYER | REQUEST):
|
|
hp->prim = MGR_DELLAYER | CONFIRM;
|
|
hp->dinfo = 0;
|
|
if ((dl = get_devlayer(dev, hp->addr)))
|
|
hp->len = del_layer(dl);
|
|
else
|
|
hp->len = -ENXIO;
|
|
break;
|
|
#ifdef OBSOLATE
|
|
case (MGR_GETIF | REQUEST):
|
|
hp->prim = MGR_GETIF | CONFIRM;
|
|
hp->dinfo = 0;
|
|
skb_trim(skb, 0);
|
|
if (skb_tailroom(skb) < sizeof(interface_info_t)) {
|
|
nskb = alloc_stack_skb(sizeof(interface_info_t), 0);
|
|
if (!nskb)
|
|
return(error_answer(dev, skb, -ENOMEM));
|
|
memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
|
|
get_if_info(nskb);
|
|
} else {
|
|
get_if_info(skb);
|
|
}
|
|
break;
|
|
case (MGR_CONNECT | REQUEST):
|
|
if (skb->len < sizeof(interface_info_t))
|
|
return(error_answer(dev, skb, -EINVAL));
|
|
hp->len = connect_if_req(dev, skb);
|
|
hp->dinfo = 0;
|
|
hp->prim = MGR_CONNECT | CONFIRM;
|
|
break;
|
|
case (MGR_SETIF | REQUEST):
|
|
hp->len = set_if_req(dev, skb);
|
|
hp->prim = MGR_SETIF | CONFIRM;
|
|
hp->dinfo = 0;
|
|
break;
|
|
case (MGR_ADDIF | REQUEST):
|
|
hp->len = add_if_req(dev, skb);
|
|
hp->prim = MGR_ADDIF | CONFIRM;
|
|
hp->dinfo = 0;
|
|
break;
|
|
case (MGR_DISCONNECT | REQUEST):
|
|
hp->len = del_if_req(dev, hp->addr);
|
|
hp->prim = MGR_DISCONNECT | CONFIRM;
|
|
hp->dinfo = 0;
|
|
break;
|
|
#endif
|
|
case (MGR_NEWENTITY | REQUEST):
|
|
hp->prim = MGR_NEWENTITY | CONFIRM;
|
|
hp->len = new_entity_req(dev, &hp->dinfo);
|
|
break;
|
|
case (MGR_DELENTITY | REQUEST):
|
|
hp->prim = MGR_DELENTITY | CONFIRM;
|
|
hp->len = del_entity_req(dev, hp->dinfo);
|
|
break;
|
|
case (MGR_INITTIMER | REQUEST):
|
|
hp->len = dev_init_timer(dev, hp->addr);
|
|
hp->prim = MGR_INITTIMER | CONFIRM;
|
|
break;
|
|
case (MGR_ADDTIMER | REQUEST):
|
|
hp->len = dev_add_timer(dev, hp);
|
|
hp->prim = MGR_ADDTIMER | CONFIRM;
|
|
hp->dinfo = 0;
|
|
break;
|
|
case (MGR_DELTIMER | REQUEST):
|
|
hp->len = dev_del_timer(dev, hp->addr);
|
|
hp->prim = MGR_DELTIMER | CONFIRM;
|
|
break;
|
|
case (MGR_REMOVETIMER | REQUEST):
|
|
hp->len = dev_remove_timer(dev, hp->addr);
|
|
hp->prim = MGR_REMOVETIMER | CONFIRM;
|
|
hp->dinfo = 0;
|
|
break;
|
|
case (MGR_TIMER | RESPONSE):
|
|
dev_kfree_skb(skb);
|
|
return(0);
|
|
break;
|
|
case (MGR_STATUS | REQUEST):
|
|
hp->prim = MGR_STATUS | CONFIRM;
|
|
nskb = alloc_stack_skb(1000, 0);
|
|
if (!nskb)
|
|
return(error_answer(dev, skb, -ENOMEM));
|
|
memcpy(mISDN_HEAD_P(nskb), hp, sizeof(mISDN_head_t));
|
|
get_status(nskb);
|
|
hp->dinfo = 0;
|
|
break;
|
|
case (MGR_SETDEVOPT | REQUEST):
|
|
hp->prim = MGR_SETDEVOPT | CONFIRM;
|
|
hp->len = 0;
|
|
if (hp->dinfo == FLG_mISDNPORT_ONEFRAME) {
|
|
test_and_set_bit(FLG_mISDNPORT_ONEFRAME,
|
|
&dev->rport.Flag);
|
|
} else if (!hp->dinfo) {
|
|
test_and_clear_bit(FLG_mISDNPORT_ONEFRAME,
|
|
&dev->rport.Flag);
|
|
} else {
|
|
hp->len = -EINVAL;
|
|
}
|
|
hp->dinfo = 0;
|
|
break;
|
|
case (MGR_GETDEVOPT | REQUEST):
|
|
hp->prim = MGR_GETDEVOPT | CONFIRM;
|
|
hp->len = 0;
|
|
if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag))
|
|
hp->dinfo = FLG_mISDNPORT_ONEFRAME;
|
|
else
|
|
hp->dinfo = 0;
|
|
break;
|
|
default:
|
|
if (hp->addr & FLG_INSTANCE) {
|
|
err = wdata_frame(dev, skb);
|
|
if (err) {
|
|
if (device_debug & DEBUG_WDATA)
|
|
printk(KERN_DEBUG "wdata_frame returns error %d\n", err);
|
|
err = error_answer(dev, skb, err);
|
|
}
|
|
} else {
|
|
printk(KERN_WARNING "mISDN: prim %x addr %x not implemented\n",
|
|
hp->prim, hp->addr);
|
|
err = error_answer(dev, skb, -EINVAL);
|
|
}
|
|
return(err);
|
|
break;
|
|
}
|
|
if (nskb) {
|
|
err = mISDN_rdata(dev, nskb);
|
|
if (err)
|
|
kfree_skb(nskb);
|
|
else
|
|
kfree_skb(skb);
|
|
} else
|
|
err = mISDN_rdata(dev, skb);
|
|
return(err);
|
|
}
|
|
|
|
static mISDNdevice_t *
|
|
init_device(u_int minor) {
|
|
mISDNdevice_t *dev;
|
|
u_long flags;
|
|
|
|
dev = kmalloc(sizeof(mISDNdevice_t), GFP_KERNEL);
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: dev(%d) %p\n",
|
|
__FUNCTION__, minor, dev);
|
|
if (dev) {
|
|
memset(dev, 0, sizeof(mISDNdevice_t));
|
|
dev->minor = minor;
|
|
init_waitqueue_head(&dev->rport.procq);
|
|
init_waitqueue_head(&dev->wport.procq);
|
|
skb_queue_head_init(&dev->rport.queue);
|
|
skb_queue_head_init(&dev->wport.queue);
|
|
init_MUTEX(&dev->io_sema);
|
|
INIT_LIST_HEAD(&dev->layerlist);
|
|
INIT_LIST_HEAD(&dev->stacklist);
|
|
INIT_LIST_HEAD(&dev->timerlist);
|
|
INIT_LIST_HEAD(&dev->entitylist);
|
|
write_lock_irqsave(&mISDN_device_lock, flags);
|
|
list_add_tail(&dev->list, &mISDN_devicelist);
|
|
write_unlock_irqrestore(&mISDN_device_lock, flags);
|
|
}
|
|
return(dev);
|
|
}
|
|
|
|
#ifdef FIXME
|
|
mISDNdevice_t *
|
|
get_free_rawdevice(void)
|
|
{
|
|
mISDNdevice_t *dev;
|
|
u_int minor;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s:\n", __FUNCTION__);
|
|
for (minor=mISDN_MINOR_RAW_MIN; minor<=mISDN_MINOR_RAW_MAX; minor++) {
|
|
dev = get_mISDNdevice4minor(minor);
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: dev(%d) %p\n",
|
|
__FUNCTION__, minor, dev);
|
|
if (!dev) {
|
|
dev = init_device(minor);
|
|
if (!dev)
|
|
return(NULL);
|
|
dev->rport.pif.func = mISDN_rdata_raw;
|
|
dev->rport.pif.fdata = dev;
|
|
return(dev);
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
free_device(mISDNdevice_t *dev)
|
|
{
|
|
struct list_head *item, *ni;
|
|
u_long flags;
|
|
|
|
if (!dev)
|
|
return(-ENODEV);
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "%s: dev(%d)\n", __FUNCTION__, dev->minor);
|
|
/* release related stuff */
|
|
list_for_each_safe(item, ni, &dev->layerlist)
|
|
del_layer(list_entry(item, devicelayer_t, list));
|
|
list_for_each_safe(item, ni, &dev->stacklist)
|
|
del_stack(list_entry(item, devicestack_t, list));
|
|
list_for_each_safe(item, ni, &dev->timerlist)
|
|
dev_free_timer(list_entry(item, mISDNtimer_t, list));
|
|
if (!skb_queue_empty(&dev->rport.queue))
|
|
discard_queue(&dev->rport.queue);
|
|
if (!skb_queue_empty(&dev->wport.queue))
|
|
discard_queue(&dev->wport.queue);
|
|
write_lock_irqsave(&mISDN_device_lock, flags);
|
|
list_del(&dev->list);
|
|
write_unlock_irqrestore(&mISDN_device_lock, flags);
|
|
if (!list_empty(&dev->entitylist)) {
|
|
printk(KERN_WARNING "MISDN %s: entitylist not empty\n", __FUNCTION__);
|
|
list_for_each_safe(item, ni, &dev->entitylist) {
|
|
struct entity_item *ei = list_entry(item, struct entity_item, head);
|
|
list_del(item);
|
|
mISDN_delete_entity(ei->entity);
|
|
kfree(ei);
|
|
}
|
|
}
|
|
kfree(dev);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
mISDN_open(struct inode *ino, struct file *filep)
|
|
{
|
|
u_int minor = iminor(ino);
|
|
mISDNdevice_t *dev = NULL;
|
|
int isnew = 0;
|
|
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN_open in: minor(%d) %p %p mode(%x)\n",
|
|
minor, filep, filep->private_data, filep->f_mode);
|
|
if (minor) {
|
|
dev = get_mISDNdevice4minor(minor);
|
|
if (dev) {
|
|
if ((dev->open_mode & filep->f_mode) & (FMODE_READ | FMODE_WRITE))
|
|
return(-EBUSY);
|
|
} else
|
|
return(-ENODEV);
|
|
} else if ((dev = init_device(minor)))
|
|
isnew = 1;
|
|
else
|
|
return(-ENOMEM);
|
|
dev->open_mode |= filep->f_mode & (FMODE_READ | FMODE_WRITE);
|
|
if (dev->open_mode & FMODE_READ){
|
|
dev->rport.lock = SPIN_LOCK_UNLOCKED;
|
|
dev->rport.maxqlen = DEFAULT_PORT_QUEUELEN;
|
|
test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->rport.Flag);
|
|
}
|
|
if (dev->open_mode & FMODE_WRITE) {
|
|
dev->wport.lock = SPIN_LOCK_UNLOCKED;
|
|
dev->wport.maxqlen = DEFAULT_PORT_QUEUELEN;
|
|
test_and_set_bit(FLG_mISDNPORT_OPEN, &dev->wport.Flag);
|
|
}
|
|
filep->private_data = dev;
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN_open out: %p %p\n", filep, filep->private_data);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
mISDN_close(struct inode *ino, struct file *filep)
|
|
{
|
|
mISDNdevice_t *dev, *nd;
|
|
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN: mISDN_close %p %p\n", filep, filep->private_data);
|
|
read_lock(&mISDN_device_lock);
|
|
list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) {
|
|
if (dev == filep->private_data) {
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN: dev(%d) %p mode %x/%x\n",
|
|
dev->minor, dev, dev->open_mode, filep->f_mode);
|
|
dev->open_mode &= ~filep->f_mode;
|
|
read_unlock(&mISDN_device_lock);
|
|
if (filep->f_mode & FMODE_READ) {
|
|
test_and_clear_bit(FLG_mISDNPORT_OPEN,
|
|
&dev->rport.Flag);
|
|
}
|
|
if (filep->f_mode & FMODE_WRITE) {
|
|
test_and_clear_bit(FLG_mISDNPORT_OPEN,
|
|
&dev->wport.Flag);
|
|
}
|
|
filep->private_data = NULL;
|
|
if (!dev->minor)
|
|
free_device(dev);
|
|
return 0;
|
|
}
|
|
}
|
|
read_unlock(&mISDN_device_lock);
|
|
printk(KERN_WARNING "mISDN: No private data while closing device\n");
|
|
return 0;
|
|
}
|
|
|
|
static __inline__ ssize_t
|
|
do_mISDN_read(struct file *file, char *buf, size_t count, loff_t * off)
|
|
{
|
|
mISDNdevice_t *dev = file->private_data;
|
|
size_t len;
|
|
// u_long flags;
|
|
struct sk_buff *skb;
|
|
|
|
if (*off != file->f_pos)
|
|
return(-ESPIPE);
|
|
if (!access_ok(VERIFY_WRITE, buf, count))
|
|
return(-EFAULT);
|
|
if ((dev->minor == 0) && (count < mISDN_HEADER_LEN)) {
|
|
printk(KERN_WARNING "mISDN_read: count(%d) too small\n", count);
|
|
return(-ENOSPC);
|
|
}
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN_read: file(%d) %p max %d\n",
|
|
dev->minor, file, count);
|
|
if (skb_queue_empty(&dev->rport.queue)) {
|
|
if (file->f_flags & O_NONBLOCK)
|
|
return(-EAGAIN);
|
|
wait_event_interruptible(dev->rport.procq, (!skb_queue_empty(&dev->rport.queue)));
|
|
if (signal_pending(current))
|
|
return(-ERESTARTSYS);
|
|
}
|
|
// spin_lock_irqsave(&dev->rport.lock, flags);
|
|
len = 0;
|
|
while ((skb = skb_dequeue(&dev->rport.queue))) {
|
|
if (dev->minor == mISDN_CORE_DEVICE) {
|
|
if ((skb->len + mISDN_HEADER_LEN) > (count - len))
|
|
goto nospace;
|
|
if (copy_to_user(buf, skb->cb, mISDN_HEADER_LEN))
|
|
goto efault;
|
|
len += mISDN_HEADER_LEN;
|
|
buf += mISDN_HEADER_LEN;
|
|
} else {
|
|
if (skb->len > (count - len)) {
|
|
nospace:
|
|
skb_queue_head(&dev->rport.queue, skb);
|
|
if (len)
|
|
break;
|
|
// spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
return(-ENOSPC);
|
|
}
|
|
}
|
|
if (skb->len) {
|
|
if (copy_to_user(buf, skb->data, skb->len)) {
|
|
efault:
|
|
skb_queue_head(&dev->rport.queue, skb);
|
|
// spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
return(-EFAULT);
|
|
}
|
|
len += skb->len;
|
|
buf += skb->len;
|
|
}
|
|
dev_kfree_skb(skb);
|
|
if (test_bit(FLG_mISDNPORT_ONEFRAME, &dev->rport.Flag))
|
|
break;
|
|
}
|
|
*off += len;
|
|
// spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN_read: file(%d) %d\n",
|
|
dev->minor, len);
|
|
return(len);
|
|
}
|
|
|
|
static ssize_t
|
|
mISDN_read(struct file *file, char *buf, size_t count, loff_t * off)
|
|
{
|
|
mISDNdevice_t *dev = file->private_data;
|
|
ssize_t ret;
|
|
|
|
if (!dev)
|
|
return(-ENODEV);
|
|
down(&dev->io_sema);
|
|
ret = do_mISDN_read(file, buf, count, off);
|
|
up(&dev->io_sema);
|
|
return(ret);
|
|
}
|
|
|
|
static loff_t
|
|
mISDN_llseek(struct file *file, loff_t offset, int orig)
|
|
{
|
|
return -ESPIPE;
|
|
}
|
|
|
|
static __inline__ ssize_t
|
|
do_mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off)
|
|
{
|
|
mISDNdevice_t *dev = file->private_data;
|
|
size_t len;
|
|
// u_long flags;
|
|
struct sk_buff *skb;
|
|
mISDN_head_t head;
|
|
|
|
if (*off != file->f_pos)
|
|
return(-ESPIPE);
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN_write: file(%d) %p count %d queue(%d)\n",
|
|
dev->minor, file, count, skb_queue_len(&dev->wport.queue));
|
|
if (!access_ok(VERIFY_WRITE, buf, count))
|
|
return(-EFAULT);
|
|
if (dev->minor == 0) {
|
|
if (count < mISDN_HEADER_LEN)
|
|
return(-EINVAL);
|
|
}
|
|
if (skb_queue_len(&dev->wport.queue) >= dev->wport.maxqlen) {
|
|
if (file->f_flags & O_NONBLOCK)
|
|
return(-EAGAIN);
|
|
wait_event_interruptible(dev->wport.procq, (skb_queue_len(&dev->wport.queue) < dev->wport.maxqlen));
|
|
if (signal_pending(current))
|
|
return(-ERESTARTSYS);
|
|
}
|
|
// spin_lock_irqsave(&dev->wport.lock, flags);
|
|
if (dev->minor == mISDN_CORE_DEVICE) {
|
|
len = count;
|
|
while (len >= mISDN_HEADER_LEN) {
|
|
if (copy_from_user(&head.addr, buf, mISDN_HEADER_LEN)) {
|
|
// spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
return(-EFAULT);
|
|
}
|
|
if (head.len > 0)
|
|
skb = alloc_stack_skb((head.len > PORT_SKB_MINIMUM) ?
|
|
head.len : PORT_SKB_MINIMUM, PORT_SKB_RESERVE);
|
|
else
|
|
skb = alloc_stack_skb(PORT_SKB_MINIMUM, PORT_SKB_RESERVE);
|
|
if (!skb)
|
|
break;
|
|
memcpy(skb->cb, &head.addr, mISDN_HEADER_LEN);
|
|
len -= mISDN_HEADER_LEN;
|
|
buf += mISDN_HEADER_LEN;
|
|
if (head.len > 0) {
|
|
if (head.len > len) {
|
|
/* since header is complete we can handle this later */
|
|
if (copy_from_user(skb_put(skb, len), buf, len)) {
|
|
dev_kfree_skb(skb);
|
|
// spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
return(-EFAULT);
|
|
}
|
|
len = 0;
|
|
} else {
|
|
if (copy_from_user(skb_put(skb, head.len), buf, head.len)) {
|
|
dev_kfree_skb(skb);
|
|
// spin_unlock_irqrestore(&dev->rport.lock, flags);
|
|
return(-EFAULT);
|
|
}
|
|
len -= head.len;
|
|
buf += head.len;
|
|
}
|
|
}
|
|
skb_queue_tail(&dev->wport.queue, skb);
|
|
}
|
|
if (len)
|
|
printk(KERN_WARNING "%s: incomplete frame data (%d/%d)\n", __FUNCTION__, len, count);
|
|
if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) {
|
|
// spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
return(count-len);
|
|
}
|
|
// spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
while ((skb = skb_dequeue(&dev->wport.queue))) {
|
|
if (mISDN_wdata_if(dev, skb))
|
|
dev_kfree_skb(skb);
|
|
wake_up(&dev->wport.procq);
|
|
}
|
|
test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag);
|
|
} else { /* raw device */
|
|
len = 0;
|
|
#ifdef FIXME
|
|
skb = alloc_stack_skb(count, PORT_SKB_RESERVE);
|
|
if (!skb) {
|
|
// spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
return(0);
|
|
}
|
|
if (copy_from_user(skb_put(skb, count), buf, count)) {
|
|
dev_kfree_skb(skb);
|
|
// spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
return(-EFAULT);
|
|
}
|
|
skb_queue_tail(&dev->wport.queue, skb);
|
|
if (test_and_set_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag)) {
|
|
// spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
return(count);
|
|
}
|
|
while ((skb = skb_dequeue(&dev->wport.queue))) {
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "%s: wflg(%lx)\n", __FUNCTION__, dev->wport.Flag);
|
|
if (test_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag)) {
|
|
skb_queue_head(&dev->wport.queue, skb);
|
|
break;
|
|
}
|
|
if (test_bit(FLG_mISDNPORT_ENABLED, &dev->wport.Flag)) {
|
|
int ret;
|
|
// spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
ret = if_newhead(&dev->wport.pif, PH_DATA | REQUEST, (int)skb, skb);
|
|
// spin_lock_irqsave(&dev->wport.lock, flags);
|
|
if (ret) {
|
|
printk(KERN_WARNING "%s: dev(%d) down err(%d)\n",
|
|
__FUNCTION__, dev->minor, ret);
|
|
dev_kfree_skb(skb);
|
|
} else
|
|
test_and_set_bit(FLG_mISDNPORT_BLOCK, &dev->wport.Flag);
|
|
} else {
|
|
printk(KERN_WARNING "%s: dev(%d) wport not enabled\n",
|
|
__FUNCTION__, dev->minor);
|
|
dev_kfree_skb(skb);
|
|
}
|
|
wake_up(&dev->wport.procq);
|
|
}
|
|
test_and_clear_bit(FLG_mISDNPORT_BUSY, &dev->wport.Flag);
|
|
// spin_unlock_irqrestore(&dev->wport.lock, flags);
|
|
#endif
|
|
}
|
|
return(count - len);
|
|
}
|
|
|
|
static ssize_t
|
|
mISDN_write(struct file *file, const char *buf, size_t count, loff_t * off)
|
|
{
|
|
mISDNdevice_t *dev = file->private_data;
|
|
ssize_t ret;
|
|
|
|
if (!dev)
|
|
return(-ENODEV);
|
|
down(&dev->io_sema);
|
|
ret = do_mISDN_write(file, buf, count, off);
|
|
up(&dev->io_sema);
|
|
return(ret);
|
|
}
|
|
|
|
static unsigned int
|
|
mISDN_poll(struct file *file, poll_table * wait)
|
|
{
|
|
unsigned int mask = POLLERR;
|
|
mISDNdevice_t *dev = file->private_data;
|
|
mISDNport_t *rport = (file->f_mode & FMODE_READ) ?
|
|
&dev->rport : NULL;
|
|
mISDNport_t *wport = (file->f_mode & FMODE_WRITE) ?
|
|
&dev->wport : NULL;
|
|
|
|
if (dev) {
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN_poll in: file(%d) %p\n",
|
|
dev->minor, file);
|
|
if (rport) {
|
|
poll_wait(file, &rport->procq, wait);
|
|
mask = 0;
|
|
if (!skb_queue_empty(&rport->queue))
|
|
mask |= (POLLIN | POLLRDNORM);
|
|
}
|
|
if (wport) {
|
|
poll_wait(file, &wport->procq, wait);
|
|
if (mask == POLLERR)
|
|
mask = 0;
|
|
if (skb_queue_len(&wport->queue) < wport->maxqlen)
|
|
mask |= (POLLOUT | POLLWRNORM);
|
|
}
|
|
}
|
|
if (device_debug & DEBUG_DEV_OP)
|
|
printk(KERN_DEBUG "mISDN_poll out: file %p mask %x\n",
|
|
file, mask);
|
|
return(mask);
|
|
}
|
|
|
|
static struct file_operations mISDN_fops =
|
|
{
|
|
llseek: mISDN_llseek,
|
|
read: mISDN_read,
|
|
write: mISDN_write,
|
|
poll: mISDN_poll,
|
|
// ioctl: mISDN_ioctl,
|
|
open: mISDN_open,
|
|
release: mISDN_close,
|
|
};
|
|
|
|
|
|
#ifdef OBSOLATE
|
|
static int
|
|
set_if(devicelayer_t *dl, u_int prim, mISDNif_t *hif)
|
|
{
|
|
int err = 0;
|
|
|
|
err = mISDN_SetIF(&dl->inst, hif, prim, from_up_down, from_up_down, dl);
|
|
return(err);
|
|
}
|
|
#endif
|
|
|
|
static void setstack_conf(devicelayer_t *dl)
|
|
{
|
|
struct sk_buff *skb = create_link_skb(MGR_SETSTACK | INDICATION, 0, 0, NULL, 16);
|
|
mISDN_head_t *hh;
|
|
|
|
if (!skb)
|
|
return;
|
|
hh = mISDN_HEAD_P(skb);
|
|
hh->addr = dl->id;
|
|
if (mISDN_rdata(dl->dev, skb))
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
static int
|
|
udev_manager(void *data, u_int prim, void *arg) {
|
|
mISDNinstance_t *inst = data;
|
|
mISDNdevice_t *dev;
|
|
devicelayer_t *dl;
|
|
int err = -EINVAL;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "udev_manager data:%p prim:%x arg:%p\n",
|
|
data, prim, arg);
|
|
if (!data)
|
|
return(-EINVAL);
|
|
read_lock(&mISDN_device_lock);
|
|
list_for_each_entry(dev, &mISDN_devicelist, list) {
|
|
list_for_each_entry(dl, &dev->layerlist, list) {
|
|
if (&dl->inst == inst) {
|
|
err = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (!err)
|
|
break;
|
|
}
|
|
if (err) {
|
|
printk(KERN_WARNING "dev_manager prim %x without device layer\n", prim);
|
|
goto out;
|
|
}
|
|
switch(prim) {
|
|
#ifdef OBSOLATE
|
|
case MGR_CONNECT | REQUEST:
|
|
err = mISDN_ConnectIF(inst, arg);
|
|
break;
|
|
case MGR_SETIF | REQUEST:
|
|
case MGR_SETIF | INDICATION:
|
|
err = set_if(dl, prim, arg);
|
|
break;
|
|
case MGR_DISCONNECT | REQUEST:
|
|
case MGR_DISCONNECT | INDICATION:
|
|
err = mISDN_DisConnectIF(inst, arg);
|
|
break;
|
|
#endif
|
|
case MGR_REGLAYER | CONFIRM:
|
|
err = 0;
|
|
break;
|
|
case MGR_SETSTACK | INDICATION:
|
|
setstack_conf(dl);
|
|
err = 0;
|
|
break;
|
|
case MGR_RELEASE | INDICATION:
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "release_dev id %x\n",
|
|
dl->inst.st->id);
|
|
del_layer(dl);
|
|
err = 0;
|
|
break;
|
|
default:
|
|
printk(KERN_WARNING "dev_manager prim %x not handled\n", prim);
|
|
err = -EINVAL;
|
|
break;
|
|
}
|
|
out:
|
|
read_unlock(&mISDN_device_lock);
|
|
return(err);
|
|
}
|
|
|
|
int init_mISDNdev (int debug) {
|
|
int err,i;
|
|
|
|
udev_obj.name = MName;
|
|
for (i=0; i<=MAX_LAYER_NR; i++) {
|
|
udev_obj.DPROTO.protocol[i] = ISDN_PID_ANY;
|
|
udev_obj.BPROTO.protocol[i] = ISDN_PID_ANY;
|
|
}
|
|
INIT_LIST_HEAD(&udev_obj.ilist);
|
|
udev_obj.own_ctrl = udev_manager;
|
|
device_debug = debug;
|
|
if (register_chrdev(mISDN_MAJOR, "mISDN", &mISDN_fops)) {
|
|
printk(KERN_WARNING "mISDN: Could not register devices\n");
|
|
return(-EIO);
|
|
}
|
|
mISDN_class = class_create(THIS_MODULE, "mISDN");
|
|
if (IS_ERR(mISDN_class)) {
|
|
unregister_chrdev(mISDN_MAJOR, "mISDN");
|
|
return PTR_ERR(mISDN_class);
|
|
}
|
|
|
|
class_device_create(mISDN_class, MKDEV(mISDN_MAJOR, 0), NULL, "mISDN");
|
|
devfs_mk_cdev(MKDEV(mISDN_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR, "mISDN");
|
|
|
|
if ((err = mISDN_register(&udev_obj))) {
|
|
printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
|
|
class_device_destroy(mISDN_class, MKDEV(mISDN_MAJOR, 0));
|
|
class_destroy(mISDN_class);
|
|
unregister_chrdev(mISDN_MAJOR, "mISDN");
|
|
devfs_remove("mISDN");
|
|
}
|
|
return(err);
|
|
}
|
|
|
|
int free_mISDNdev(void) {
|
|
int err = 0;
|
|
mISDNdevice_t *dev, *nd;
|
|
|
|
if (!list_empty(&mISDN_devicelist)) {
|
|
printk(KERN_WARNING "mISDN: devices open on remove\n");
|
|
list_for_each_entry_safe(dev, nd, &mISDN_devicelist, list) {
|
|
free_device(dev);
|
|
}
|
|
err = -EBUSY;
|
|
}
|
|
if ((err = mISDN_unregister(&udev_obj))) {
|
|
printk(KERN_ERR "Can't unregister UserDevice(%d)\n", err);
|
|
}
|
|
class_device_destroy(mISDN_class, MKDEV(mISDN_MAJOR, 0));
|
|
class_destroy(mISDN_class);
|
|
if ((err = unregister_chrdev(mISDN_MAJOR, "mISDN"))) {
|
|
printk(KERN_WARNING "mISDN: devices busy on remove\n");
|
|
}
|
|
devfs_remove("mISDN");
|
|
return(err);
|
|
}
|