mISDN/drivers/isdn/hardware/mISDN/udevice.c

1223 lines
29 KiB
C
Raw Normal View History

2001-02-11 22:46:19 +00:00
/* $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 "hisax_core.h"
#define MAX_HEADER_LEN 4
2001-03-13 02:04:37 +00:00
#define FLG_MGR_SETSTACK 1
2001-04-08 16:45:56 +00:00
#define FLG_DOWN_BUSY 2
2001-03-13 02:04:37 +00:00
2001-02-11 22:46:19 +00:00
typedef struct _hisaxdevice {
struct _hisaxdevice *prev;
struct _hisaxdevice *next;
struct inode *inode;
struct file *file;
struct wait_queue *procq;
spinlock_t slock;
int rcnt;
int wcnt;
u_char *rbuf, *rp;
u_char *wbuf, *wp;
struct _devicelayer *layer;
} hisaxdevice_t;
typedef struct _devicelayer {
struct _devicelayer *prev;
struct _devicelayer *next;
hisaxdevice_t *dev;
hisaxinstance_t inst;
2001-04-08 16:45:56 +00:00
hisaxinstance_t *slave;
hisaxif_t sif;
struct sk_buff_head queue;
struct sk_buff *last_skb;
2001-02-11 22:46:19 +00:00
int iaddr;
2001-03-13 02:04:37 +00:00
int lm_st;
int Flags;
2001-02-11 22:46:19 +00:00
} devicelayer_t;
static hisaxdevice_t *hisax_devicelist = NULL;
static rwlock_t hisax_device_lock = RW_LOCK_UNLOCKED;
static hisaxobject_t udev_obj;
static char MName[] = "UserDevice";
2001-03-13 02:04:37 +00:00
static u_char stbuf[1000];
2001-02-11 22:46:19 +00:00
static int device_debug = 0;
2001-03-03 18:17:16 +00:00
static int from_up_down(hisaxif_t *, u_int, int, int, void *);
2001-04-08 16:45:56 +00:00
static int from_peer(hisaxif_t *, u_int, int, int, void *);
static int to_peer(hisaxif_t *, u_int, int, int, void *);
2001-02-11 22:46:19 +00:00
static int
hisax_rdata(hisaxdevice_t *dev, iframe_t *iff, int use_value) {
int len = 4*sizeof(u_int);
u_char *p;
u_long flags;
if (iff->len > 0)
len += iff->len;
spin_lock_irqsave(&dev->slock, flags);
if (len < (HISAX_DEVBUF_SIZE - dev->rcnt)) {
p = dev->rp + dev->rcnt;
if (len <= 20 && use_value) {
memcpy(p, iff, len);
} else {
memcpy(p, iff, 4*sizeof(u_int));
p += 4*sizeof(u_int);
memcpy(p, iff->data.p, iff->len);
}
dev->rcnt += len;
} else
len = -ENOSPC;
spin_unlock_irqrestore(&dev->slock, flags);
if (len > 0)
wake_up_interruptible(&dev->procq);
return(len);
}
static devicelayer_t
*get_devlayer(hisaxdevice_t *dev, int addr) {
devicelayer_t *dl = dev->layer;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG "get_devlayer: addr:%x\n", addr);
while(dl) {
if (dl->iaddr == (IF_IADDRMASK & addr))
break;
dl = dl->next;
}
return(dl);
}
static int
2001-03-13 02:04:37 +00:00
stack_inst_flg(hisaxdevice_t *dev, hisaxstack_t *st, int bit, int clear)
{
int ret = -1;
devicelayer_t *dl = dev->layer;
while(dl) {
if (dl->inst.st == st) {
if (clear)
ret = test_and_clear_bit(bit, &dl->Flags);
else
ret = test_and_set_bit(bit, &dl->Flags);
break;
}
dl = dl->next;
}
return(ret);
}
static int
2001-04-08 16:45:56 +00:00
create_layer(hisaxdevice_t *dev, hisaxstack_t *st, layer_info_t *linfo, int *adr) {
2001-03-03 08:07:30 +00:00
hisaxlayer_t *layer;
2001-03-13 02:04:37 +00:00
int i, ret;
2001-03-03 08:07:30 +00:00
devicelayer_t *nl;
2001-04-08 16:45:56 +00:00
hisaxobject_t *obj;
hisaxinstance_t *inst = NULL;
2001-02-11 22:46:19 +00:00
2001-04-08 16:45:56 +00:00
if (linfo->object_id != -1) {
obj = get_object(linfo->object_id);
if (!obj) {
printk(KERN_WARNING __FUNCTION__ ": no object %x found\n",
linfo->object_id);
return(-ENODEV);
}
ret = obj->own_ctrl(st, MGR_NEWLAYER | REQUEST, &linfo->pid);
if (ret) {
printk(KERN_WARNING __FUNCTION__ ": error nl req %d\n",
ret);
return(ret);
}
layer = getlayer4lay(st, linfo->pid.layermask);
if (!layer) {
printk(KERN_WARNING __FUNCTION__ ": no layer for lm(%x)\n",
linfo->pid.layermask);
return(-EINVAL);
}
inst = layer->inst;
while (inst && inst->next)
inst = inst->next;
if (!inst) {
printk(KERN_WARNING __FUNCTION__ ": no inst in layer(%p)\n",
layer);
return(-EINVAL);
}
} else if ((layer = getlayer4lay(st, linfo->pid.layermask))) {
printk(KERN_WARNING
2001-03-13 02:04:37 +00:00
"HiSax create_layer st(%d) LM(%x) inst not empty(%p)\n",
2001-03-26 11:40:02 +00:00
st->id, linfo->pid.layermask, layer);
2001-04-08 16:45:56 +00:00
}
2001-02-11 22:46:19 +00:00
if (!(nl = kmalloc(sizeof(devicelayer_t), GFP_ATOMIC))) {
printk(KERN_ERR "kmalloc devicelayer failed\n");
return(-ENOMEM);
}
memset(nl, 0, sizeof(devicelayer_t));
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG
2001-03-13 02:04:37 +00:00
"HiSax create_layer LM(%x) nl(%p) nl inst(%p)\n",
2001-03-26 11:40:02 +00:00
linfo->pid.layermask, nl, &nl->inst);
2001-02-11 22:46:19 +00:00
nl->dev = dev;
2001-03-13 02:04:37 +00:00
memcpy(&nl->inst.pid, &linfo->pid, sizeof(hisax_pid_t));
strcpy(nl->inst.name, linfo->name);
for (i=0; i<= MAX_LAYER_NR; i++) {
2001-03-26 11:40:02 +00:00
if (linfo->pid.layermask & ISDN_LAYER(i)) {
2001-03-13 02:04:37 +00:00
if (st->pid.protocol[i] == ISDN_PID_NONE) {
st->pid.protocol[i] = linfo->pid.protocol[i];
nl->lm_st |= ISDN_LAYER(i);
}
}
2001-02-11 22:46:19 +00:00
}
2001-03-26 11:40:02 +00:00
nl->inst.down.owner = &nl->inst;
nl->inst.up.owner = &nl->inst;
2001-04-08 16:45:56 +00:00
nl->inst.obj = &udev_obj;
2001-03-13 02:04:37 +00:00
nl->inst.data = nl;
APPEND_TO_LIST(nl, dev->layer);
2001-03-26 11:40:02 +00:00
nl->inst.obj->ctrl(st, MGR_REGLAYER | INDICATION, &nl->inst);
nl->iaddr = nl->inst.id | IADDR_BIT;
2001-04-08 16:45:56 +00:00
*adr++ = nl->iaddr;
if (inst) {
skb_queue_head_init(&nl->queue);
nl->slave = inst;
*adr = inst->id;
} else
*adr = 0;
return(8);
}
static int
reconnect(devicelayer_t *dl)
{
hisaxinstance_t *peer;
if (dl->inst.down.owner != &dl->inst) {
peer = dl->inst.down.owner;
if (peer->down.fdata == dl) {
memcpy(&peer->down, &dl->inst.down, sizeof(hisaxif_t));
memset(&dl->inst.down, 0, sizeof(hisaxif_t));
} else if (peer->up.fdata == dl) {
memcpy(&peer->up, &dl->inst.down, sizeof(hisaxif_t));
memset(&dl->inst.down, 0, sizeof(hisaxif_t));
}
}
if (dl->inst.up.owner != &dl->inst) {
peer = dl->inst.up.owner;
if (peer->down.fdata == dl) {
memcpy(&peer->down, &dl->inst.up, sizeof(hisaxif_t));
memset(&dl->inst.up, 0, sizeof(hisaxif_t));
} else if (peer->up.fdata == dl) {
memcpy(&peer->up, &dl->inst.up, sizeof(hisaxif_t));
memset(&dl->inst.up, 0, sizeof(hisaxif_t));
}
}
return(0);
2001-02-11 22:46:19 +00:00
}
static int
del_layer(devicelayer_t *dl) {
hisaxinstance_t *inst = &dl->inst;
hisaxdevice_t *dev = dl->dev;
2001-03-13 02:04:37 +00:00
int i;
2001-02-11 22:46:19 +00:00
if (device_debug & DEBUG_MGR_FUNC) {
2001-03-27 15:34:20 +00:00
printk(KERN_DEBUG __FUNCTION__": dl(%p) inst(%p) LM(%x) dev(%p) nexti(%p)\n",
2001-03-26 11:40:02 +00:00
dl, inst, inst->pid.layermask, dev, inst->next);
2001-04-08 16:45:56 +00:00
printk(KERN_DEBUG __FUNCTION__": iaddr %x inst %s slave %p\n",
dl->iaddr, inst->name, dl->slave);
}
reconnect(dl);
if (dl->slave) {
discard_queue(&dl->queue);
if (dl->last_skb) {
dev_kfree_skb(dl->last_skb);
dl->last_skb = NULL;
}
dl->slave->obj->own_ctrl(dl->slave, MGR_UNREGLAYER | REQUEST,
NULL);
dl->slave = NULL;
2001-03-13 02:04:37 +00:00
}
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);
2001-02-11 22:46:19 +00:00
}
2001-03-26 11:40:02 +00:00
if (inst->up.peer) {
inst->up.peer->obj->ctrl(inst->up.peer,
MGR_DISCONNECT | REQUEST, &inst->up);
}
if (inst->down.peer) {
inst->down.peer->obj->ctrl(inst->down.peer,
MGR_DISCONNECT | REQUEST, &inst->down);
}
2001-02-11 22:46:19 +00:00
dl->iaddr = 0;
REMOVE_FROM_LISTBASE(dl, dev->layer);
2001-03-26 11:40:02 +00:00
udev_obj.ctrl(inst, MGR_UNREGLAYER | REQUEST, NULL);
2001-02-11 22:46:19 +00:00
kfree(dl);
return(0);
}
2001-03-13 02:04:37 +00:00
static int
2001-04-08 16:45:56 +00:00
connect_if_req(hisaxdevice_t *dev, iframe_t *iff) {
hisaxif_t *hif, *phif;
2001-03-13 02:04:37 +00:00
devicelayer_t *dl;
2001-04-08 16:45:56 +00:00
int stat,err=-ENXIO;
interface_info_t *ifi = (interface_info_t *)&iff->data.p;
hisaxinstance_t *inst;
hisaxinstance_t *peer;
2001-03-13 02:04:37 +00:00
if (device_debug & DEBUG_MGR_FUNC)
2001-04-08 16:45:56 +00:00
printk(KERN_DEBUG __FUNCTION__": addr:%x own(%x) peer(%X)\n",
iff->addr, ifi->owner, ifi->peer);
2001-03-13 02:04:37 +00:00
if (!(dl=get_devlayer(dev, iff->addr)))
return(-ENXIO);
2001-04-08 16:45:56 +00:00
if (!(inst = get_instance4id(ifi->owner))) {
printk(KERN_WARNING __FUNCTION__": owner(%x) not found\n",
ifi->owner);
return(-ENODEV);
}
if (!(peer = get_instance4id(ifi->peer))) {
printk(KERN_WARNING __FUNCTION__": peer(%x) not found\n",
ifi->peer);
return(-ENODEV);
}
if (inst != &dl->inst) {
printk(KERN_WARNING __FUNCTION__": owner(%x) not our\n",
ifi->owner);
return(-EINVAL);
}
if (ifi->stat == IF_UP) {
2001-03-13 02:04:37 +00:00
hif = &dl->inst.up;
2001-04-08 16:45:56 +00:00
phif = &peer->down;
stat = IF_DOWN;
} else if (ifi->stat == IF_DOWN) {
2001-03-13 02:04:37 +00:00
hif = &dl->inst.down;
2001-04-08 16:45:56 +00:00
phif = &peer->up;
stat = IF_UP;
} else {
printk(KERN_WARNING __FUNCTION__": if not UP/DOWN\n");
return(-EINVAL);
}
if (hif->stat != IF_NOACTIV)
return(-EBUSY);
if (phif->stat != IF_NOACTIV) { /* install splitter */
if (!(inst = phif->peer))
return(-EINVAL);
memcpy(&dl->inst.down, phif, sizeof(hisaxif_t));
phif->func = from_peer;
phif->fdata = dl;
if (stat == IF_UP) {
stat = IF_DOWN;
phif = &inst->down;
} else {
stat = IF_UP;
phif = &inst->up;
}
memcpy(&dl->inst.up, phif, sizeof(hisaxif_t));
phif->func = to_peer;
phif->fdata = dl;
err = 0;
if (dl->slave) {
dl->sif.stat = stat;
dl->sif.owner = &dl->inst;
dl->sif.peer = dl->slave;
err = dl->slave->obj->own_ctrl(dl->slave, MGR_SETIF |
REQUEST, &dl->sif);
2001-03-13 02:04:37 +00:00
if (err)
2001-04-08 16:45:56 +00:00
dl->sif.stat = IF_NOACTIV;
}
} else {
hif->stat = stat;
hif->peer = peer;
err = udev_obj.ctrl(&dl->inst, MGR_CONNECT | REQUEST,
hif->peer);
if (err)
hif->stat = IF_NOACTIV;
2001-03-13 02:04:37 +00:00
}
return(err);
}
static int
set_if_req(hisaxdevice_t *dev, iframe_t *iff) {
hisaxif_t *hif = NULL;
devicelayer_t *dl;
int *ip, err=-ENXIO;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": addr:%x\n", iff->addr);
2001-03-27 10:23:48 +00:00
if (iff->len != sizeof(int))
2001-03-13 02:04:37 +00:00
return(-EINVAL);
if (!(dl=get_devlayer(dev, iff->addr)))
return(-ENXIO);
ip = &iff->data.i;
if (iff->addr & IF_UP) {
hif = &dl->inst.up;
if (hif->stat == IF_NOACTIV) {
hif->stat = IF_DOWN;
2001-03-27 10:23:48 +00:00
hif->extentions = *ip++;
2001-03-26 11:40:02 +00:00
err = udev_obj.ctrl(dl->inst.st, MGR_CONNECT | REQUEST, hif);
2001-03-13 02:04:37 +00:00
if (err)
hif->stat = IF_NOACTIV;
} else
err = -EBUSY;
} else if (iff->addr & IF_DOWN) {
hif = &dl->inst.down;
if (hif->stat == IF_NOACTIV) {
hif->stat = IF_UP;
2001-03-27 10:23:48 +00:00
2001-03-26 11:40:02 +00:00
err = udev_obj.ctrl(dl->inst.st, MGR_CONNECT | REQUEST, hif);
2001-03-13 02:04:37 +00:00
if (err)
hif->stat = IF_NOACTIV;
} else
err = -EBUSY;
}
return(err);
}
static int
del_if_req(hisaxdevice_t *dev, iframe_t *iff) {
devicelayer_t *dl;
int err=-ENXIO;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": addr:%x\n", iff->addr);
if (!(dl=get_devlayer(dev, iff->addr)))
return(-ENXIO);
if (iff->addr & IF_UP)
2001-03-26 11:40:02 +00:00
err = udev_obj.ctrl(dl->inst.up.peer, MGR_DISCONNECT | REQUEST,
&dl->inst.up);
2001-03-13 02:04:37 +00:00
else if (iff->addr & IF_DOWN)
2001-03-26 11:40:02 +00:00
err = udev_obj.ctrl(dl->inst.down.peer, MGR_DISCONNECT | REQUEST,
&dl->inst.down);
2001-03-13 02:04:37 +00:00
return(err);
}
2001-02-11 22:46:19 +00:00
static int
wdata_frame(hisaxdevice_t *dev, iframe_t *iff) {
hisaxif_t *hif = NULL;
devicelayer_t *dl;
int sub, len, err=-ENXIO;
struct sk_buff *skb;
u_char *dp;
if (device_debug & DEBUG_WDATA)
2001-03-13 02:04:37 +00:00
printk(KERN_DEBUG __FUNCTION__": addr:%x\n", iff->addr);
2001-02-11 22:46:19 +00:00
if (!(dl=get_devlayer(dev, iff->addr)))
return(-ENXIO);
if (iff->addr & IF_UP) {
hif = &dl->inst.up;
if (IF_TYPE(hif) != IF_DOWN) {
hif = NULL;
}
} else if (iff->addr & IF_DOWN) {
hif = &dl->inst.down;
if (IF_TYPE(hif) != IF_UP) {
hif = NULL;
}
}
if (hif) {
if (!hif->func) {
printk(KERN_ERR "hisax no interface func for %p\n",
hif);
return(-EINVAL);
}
sub =iff->prim & SUBCOMMAND_MASK;
if (CMD_IS_DATA(iff->prim)) {
if ((sub == REQUEST) || (sub == INDICATION)) {
if (iff->len <= 0) {
printk(KERN_WARNING
"hisax data len(%d) to short\n",
iff->len);
return(-EINVAL);
}
len = iff->len;
if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) {
printk(KERN_WARNING "hisax: alloc_skb failed\n");
return(-ENOMEM);
} else
skb_reserve(skb, MAX_HEADER_LEN);
dp = &iff->data.b[0];
memcpy(skb_put(skb, len), dp, len);
2001-03-03 18:17:16 +00:00
err = hif->func(hif, iff->prim, DINFO_SKB, 0, skb);
2001-02-11 22:46:19 +00:00
if (err)
dev_kfree_skb(skb);
return(err);
}
}
if (device_debug & DEBUG_WDATA)
2001-03-26 11:40:02 +00:00
printk(KERN_DEBUG "wdata_frame: hif %p f:%p d:%p s:%p o:%p p:%p\n",
hif, hif->func, hif->fdata, hif->st, hif->owner, hif->peer);
2001-03-03 18:17:16 +00:00
err = hif->func(hif, iff->prim, iff->dinfo, iff->len, &iff->data.b[0]);
2001-02-11 22:46:19 +00:00
} else {
if (device_debug & DEBUG_WDATA)
printk(KERN_DEBUG "hisax: no matching interface\n");
}
return(err);
}
static int
hisax_wdata(hisaxdevice_t *dev, void *dp, int len) {
iframe_t *iff = dp;
iframe_t off;
hisaxstack_t *st;
devicelayer_t *dl;
2001-03-03 08:07:30 +00:00
hisaxlayer_t *layer;
2001-02-11 22:46:19 +00:00
int lay;
int err = 0;
int used = 0;
int head = 4*sizeof(u_int);
2001-03-03 08:07:30 +00:00
int *ip;
2001-03-13 02:04:37 +00:00
u_char *p;
2001-02-11 22:46:19 +00:00
if (len < head) {
printk(KERN_WARNING "hisax: if_frame(%d) too short\n", len);
return(len);
}
if (device_debug & DEBUG_WDATA)
2001-03-03 18:17:16 +00:00
printk(KERN_DEBUG "hisax_wdata: %x:%x %x %d\n",
iff->addr, iff->prim, iff->dinfo, iff->len);
2001-02-11 22:46:19 +00:00
switch(iff->prim) {
case (MGR_GETSTACK | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_GETSTACK | CONFIRM;
2001-03-03 18:17:16 +00:00
off.dinfo = 0;
2001-02-11 22:46:19 +00:00
if (iff->addr <= 0) {
off.data.i = get_stack_cnt();
off.len = sizeof(int);
err = 1;
} else if (iff->addr <= get_stack_cnt()) {
off.data.p = stbuf;
get_stack_profile(&off);
} else
off.len = 0;
hisax_rdata(dev, &off, err);
break;
case (MGR_GETLAYER | REQUEST):
used = head + sizeof(u_int);
if (len<used)
return(len);
off.addr = iff->addr;
off.prim = MGR_GETLAYER | CONFIRM;
2001-03-03 18:17:16 +00:00
off.dinfo = 0;
2001-02-11 22:46:19 +00:00
lay = iff->data.i;
off.len = 0;
2001-03-29 19:14:25 +00:00
if (LAYER_OUTRANGE(lay)) {
off.len = -EINVAL;
hisax_rdata(dev, &off, 1);
break;
} else
lay = ISDN_LAYER(lay);
2001-03-03 08:07:30 +00:00
if ((st = get_stack4id(iff->addr))) {
if ((layer = getlayer4lay(st, lay))) {
2001-03-13 02:04:37 +00:00
hisaxinstance_t *inst = layer->inst;
2001-03-03 08:07:30 +00:00
off.data.p = stbuf;
2001-03-13 02:04:37 +00:00
p = stbuf;
while(inst) {
strcpy(p, inst->name);
p += HISAX_MAX_IDLEN;
ip = (u_int *)p;
*ip++ = inst->obj->id;
*ip++ = inst->extentions;
2001-03-29 19:14:25 +00:00
*ip++ = inst->id;
if (inst->st)
*ip++ = inst->st->id;
else
*ip++ = 0;
2001-03-13 02:04:37 +00:00
p = (u_char *)ip;
memcpy(p, &inst->pid, sizeof(hisax_pid_t));
p += sizeof(hisax_pid_t);
inst = inst->next;
}
off.len = p - stbuf;
2001-02-11 22:46:19 +00:00
}
}
hisax_rdata(dev, &off, 0);
break;
2001-03-26 11:40:02 +00:00
case (MGR_NEWLAYER | REQUEST):
2001-03-13 02:04:37 +00:00
used = head + sizeof(layer_info_t);
2001-02-11 22:46:19 +00:00
if (len<used)
return(len);
off.addr = iff->addr;
2001-03-03 18:17:16 +00:00
off.dinfo = 0;
2001-03-26 11:40:02 +00:00
off.prim = MGR_NEWLAYER | CONFIRM;
2001-04-08 16:45:56 +00:00
off.len = 8;
off.data.p = stbuf;
2001-02-11 22:46:19 +00:00
if ((st = get_stack4id(iff->addr))) {
2001-03-13 02:04:37 +00:00
err = create_layer(dev, st,
2001-04-08 16:45:56 +00:00
(layer_info_t *)&iff->data.i, off.data.p);
2001-02-11 22:46:19 +00:00
if (err<0)
off.len = err;
} else
off.len = -ENODEV;
2001-04-08 16:45:56 +00:00
hisax_rdata(dev, &off, 0);
2001-02-11 22:46:19 +00:00
break;
case (MGR_DELLAYER | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_DELLAYER | CONFIRM;
2001-03-03 18:17:16 +00:00
off.dinfo = 0;
2001-02-11 22:46:19 +00:00
if ((dl=get_devlayer(dev, iff->addr)))
off.len = del_layer(dl);
else
off.len = -ENXIO;
hisax_rdata(dev, &off, 1);
break;
2001-03-13 02:04:37 +00:00
case (MGR_GETIF | REQUEST):
used = head + 2*sizeof(int);
if (len<used)
return(len);
off.addr = iff->addr;
off.prim = MGR_GETIF | CONFIRM;
off.dinfo = 0;
off.len = 0;
off.data.p = stbuf;
ip = &iff->data.i;
lay = iff->data.i;
2001-03-29 19:14:25 +00:00
ip++;
if (LAYER_OUTRANGE(lay)) {
off.len = -EINVAL;
hisax_rdata(dev, &off, 1);
break;
} else
lay = ISDN_LAYER(lay);
2001-03-13 02:04:37 +00:00
if ((st = get_stack4id(iff->addr))) {
if ((layer = getlayer4lay(st, lay))) {
hisaxinstance_t *inst = layer->inst;
lay = *ip;
ip = (int *)stbuf;
while(inst) {
if (lay & IF_UP) {
2001-03-27 10:23:48 +00:00
*ip++ = inst->up.extentions;
2001-03-29 19:14:25 +00:00
if (inst->up.owner)
*ip++ = inst->up.owner->id;
else
*ip++ = 0;
if (inst->up.peer)
*ip++ = inst->up.peer->id;
else
*ip++ = 0;
*ip++ = inst->up.stat;
2001-03-13 02:04:37 +00:00
} else if (lay & IF_DOWN) {
2001-03-27 10:23:48 +00:00
*ip++ = inst->down.extentions;
2001-03-29 19:14:25 +00:00
if (inst->down.owner)
*ip++ = inst->down.owner->id;
else
*ip++ = 0;
if (inst->down.peer)
*ip++ = inst->down.peer->id;
else
*ip++ = 0;
*ip++ = inst->down.stat;
2001-03-13 02:04:37 +00:00
}
inst = inst->next;
}
off.len = (u_char *)ip - (u_char *)stbuf;
}
}
hisax_rdata(dev, &off, 0);
break;
2001-03-26 11:40:02 +00:00
case (MGR_CONNECT | REQUEST):
2001-04-08 16:45:56 +00:00
used = head + sizeof(interface_info_t);
if (len<used)
return(len);
2001-03-13 02:04:37 +00:00
off.addr = iff->addr;
2001-03-26 11:40:02 +00:00
off.prim = MGR_CONNECT | CONFIRM;
2001-03-13 02:04:37 +00:00
off.dinfo = 0;
2001-04-08 16:45:56 +00:00
off.len = connect_if_req(dev, iff);
2001-03-13 02:04:37 +00:00
hisax_rdata(dev, &off, 1);
break;
case (MGR_SETIF | REQUEST):
used = head + iff->len;
2001-04-08 16:45:56 +00:00
if (len<used)
return(len);
2001-03-13 02:04:37 +00:00
off.addr = iff->addr;
off.prim = MGR_SETIF | CONFIRM;
off.dinfo = 0;
off.len = set_if_req(dev, iff);
hisax_rdata(dev, &off, 1);
break;
2001-03-26 11:40:02 +00:00
case (MGR_DISCONNECT | REQUEST):
2001-03-13 02:04:37 +00:00
used = head;
off.addr = iff->addr;
2001-03-26 11:40:02 +00:00
off.prim = MGR_DISCONNECT | CONFIRM;
2001-03-13 02:04:37 +00:00
off.dinfo = 0;
off.len = del_if_req(dev, iff);
hisax_rdata(dev, &off, 1);
break;
case (MGR_SETSTACK | REQUEST):
used = head + sizeof(hisax_pid_t);
if (len<used)
return(len);
off.addr = iff->addr;
off.dinfo = 0;
off.prim = MGR_SETSTACK | CONFIRM;
off.len = 0;
if ((st = get_stack4id(iff->addr))) {
stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 0);
err = udev_obj.ctrl(st, iff->prim, &iff->data.i);
if (err<0)
off.len = err;
} else
off.len = -ENODEV;
hisax_rdata(dev, &off, 1);
break;
case (MGR_CLEARSTACK | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_CLEARSTACK | CONFIRM;
off.dinfo = 0;
off.len = 0;
if ((st = get_stack4id(iff->addr))) {
stack_inst_flg(dev, st, FLG_MGR_SETSTACK, 1);
err = udev_obj.ctrl(st, iff->prim, NULL);
if (err<0)
off.len = err;
} else
off.len = -ENODEV;
hisax_rdata(dev, &off, 1);
break;
2001-02-11 22:46:19 +00:00
default:
used = head + iff->len;
if (len<used) {
printk(KERN_WARNING "hisax_wdata: framelen error prim %x %d/%d\n",
iff->prim, len, used);
used=len;
} else if (iff->addr & IADDR_BIT) {
err = wdata_frame(dev, iff);
if (err)
if (device_debug & DEBUG_WDATA)
printk(KERN_DEBUG "wdata_frame returns error %d\n", err);
} else {
printk(KERN_WARNING "hisax: prim %x addr %x not implemented\n",
iff->prim, iff->addr);
}
break;
}
return(used);
}
static int
hisax_open(struct inode *ino, struct file *filep)
{
// u_int minor = MINOR(ino->i_rdev);
hisaxdevice_t *newdev;
u_long flags;
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_open in: %p %p\n", filep, filep->private_data);
if ((newdev = (hisaxdevice_t *) kmalloc(sizeof(hisaxdevice_t), GFP_KERNEL))) {
memset(newdev, 0, sizeof(hisaxdevice_t));
newdev->file = filep;
newdev->inode = ino;
if (!(newdev->rbuf = vmalloc(HISAX_DEVBUF_SIZE))) {
kfree(newdev);
return(-ENOMEM);
}
newdev->rp = newdev->rbuf;
if (!(newdev->wbuf = vmalloc(HISAX_DEVBUF_SIZE))) {
vfree(newdev->rbuf);
kfree(newdev);
return(-ENOMEM);
}
newdev->wp = newdev->wbuf;
newdev->slock = SPIN_LOCK_UNLOCKED;
hisaxlock_core();
write_lock_irqsave(&hisax_device_lock, flags);
APPEND_TO_LIST(newdev, hisax_devicelist);
write_unlock_irqrestore(&hisax_device_lock, flags);
filep->private_data = newdev;
} else
return(-ENOMEM);
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_open out: %p %p\n", filep, filep->private_data);
return(0);
}
static int
hisax_close(struct inode *ino, struct file *filep)
{
hisaxdevice_t *dev = hisax_devicelist;
u_long flags;
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax: hisax_close %p %p\n", filep, filep->private_data);
read_lock(&hisax_device_lock);
2001-02-11 22:46:19 +00:00
while (dev) {
if (dev == filep->private_data) {
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax: dev: %p\n", dev);
/* release related stuff */
while(dev->layer)
del_layer(dev->layer);
read_unlock(&hisax_device_lock);
write_lock_irqsave(&hisax_device_lock, flags);
2001-02-11 22:46:19 +00:00
vfree(dev->rbuf);
vfree(dev->wbuf);
dev->rp = dev->rbuf = NULL;
dev->wp = dev->wbuf = NULL;
dev->rcnt = 0;
dev->wcnt = 0;
REMOVE_FROM_LISTBASE(dev, hisax_devicelist);
write_unlock_irqrestore(&hisax_device_lock, flags);
filep->private_data = NULL;
kfree(dev);
hisaxunlock_core();
return 0;
}
dev = dev->next;
}
read_unlock(&hisax_device_lock);
2001-02-11 22:46:19 +00:00
printk(KERN_WARNING "hisax: No private data while closing device\n");
return 0;
}
static ssize_t
hisax_read(struct file *file, char *buf, size_t count, loff_t * off)
{
hisaxdevice_t *dev = file->private_data;
int len = 0;
u_long flags;
if (off != &file->f_pos)
return(-ESPIPE);
if (!dev)
return(-ENODEV);
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_read: file %p count %d\n",
file, count);
spin_lock_irqsave(&dev->slock, flags);
if (!dev->rcnt) {
spin_unlock_irqrestore(&dev->slock, flags);
if (file->f_flags & O_NONBLOCK)
return(-EAGAIN);
interruptible_sleep_on(&dev->procq);
spin_lock_irqsave(&dev->slock, flags);
if (!dev->rcnt) {
spin_unlock_irqrestore(&dev->slock, flags);
return(-EAGAIN);
}
}
if (count < dev->rcnt)
len = count;
else
len = dev->rcnt;
if (copy_to_user(buf, dev->rp, len)) {
spin_unlock_irqrestore(&dev->slock, flags);
return(-EFAULT);
}
dev->rcnt -= len;
if (dev->rcnt)
dev->rp += len;
else
dev->rp = dev->rbuf;
spin_unlock_irqrestore(&dev->slock, flags);
*off += len;
return(len);
}
static loff_t
hisax_llseek(struct file *file, loff_t offset, int orig)
{
return -ESPIPE;
}
static ssize_t
hisax_write(struct file *file, const char *buf, size_t count, loff_t * off)
{
hisaxdevice_t *dev = file->private_data;
int used;
u_long flags;
if (off != &file->f_pos)
return(-ESPIPE);
if (!dev)
return(-ENODEV);
if (count>HISAX_DEVBUF_SIZE)
return(-ENOSPC);
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_write: file %p count %d\n",
file, count);
spin_lock_irqsave(&dev->slock, flags);
if (dev->wcnt) {
spin_unlock_irqrestore(&dev->slock, flags);
if (file->f_flags & O_NONBLOCK)
return(-EAGAIN);
interruptible_sleep_on(&(dev->procq));
spin_lock_irqsave(&dev->slock, flags);
if (dev->wcnt) {
spin_unlock_irqrestore(&dev->slock, flags);
return(-EAGAIN);
}
}
copy_from_user(dev->wbuf, buf, count);
dev->wcnt += count;
dev->wp = dev->wbuf;
while (dev->wcnt > 0) {
spin_unlock_irqrestore(&dev->slock, flags);
used = hisax_wdata(dev, dev->wp, dev->wcnt);
spin_lock_irqsave(&dev->slock, flags);
dev->wcnt -= used;
dev->wp += used;
}
dev->wcnt = 0; /* if go negatic due to errors */
spin_unlock_irqrestore(&dev->slock, flags);
wake_up_interruptible(&dev->procq);
return(count);
}
static unsigned int
hisax_poll(struct file *file, poll_table * wait)
{
unsigned int mask = POLLERR;
hisaxdevice_t *dev = file->private_data;
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_poll in: file %p\n", file);
if (dev) {
if (!dev->rcnt)
poll_wait(file, &(dev->procq), wait);
mask = 0;
if (dev->rcnt) {
mask |= POLLIN | POLLRDNORM;
}
if (!dev->wcnt) {
mask |= POLLOUT | POLLWRNORM;
}
}
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_poll out: file %p mask %x\n",
file, mask);
return(mask);
}
static struct file_operations hisax_fops =
{
llseek: hisax_llseek,
read: hisax_read,
write: hisax_write,
poll: hisax_poll,
// ioctl: hisax_ioctl,
open: hisax_open,
release: hisax_close,
};
static int
2001-03-03 18:17:16 +00:00
from_up_down(hisaxif_t *hif, u_int prim, int dinfo, int len, void *arg) {
2001-02-11 22:46:19 +00:00
devicelayer_t *dl;
iframe_t off;
int retval = -EINVAL;
u_int sub;
struct sk_buff *skb;
if (!hif || !hif->fdata)
return(-EINVAL);
dl = hif->fdata;
sub = prim & SUBCOMMAND_MASK;
off.addr = dl->iaddr | IF_TYPE(hif);
off.prim = prim;
off.len = 0;
2001-03-03 18:17:16 +00:00
off.dinfo = dinfo;
2001-02-11 22:46:19 +00:00
if (device_debug & DEBUG_RDATA)
2001-03-03 18:17:16 +00:00
printk(KERN_DEBUG "from_up_down: %x(%x) dinfo:%x len:%d\n",
off.prim, off.addr, dinfo, len);
2001-02-11 22:46:19 +00:00
if (CMD_IS_DATA(prim)) {
2001-03-03 18:17:16 +00:00
if (dinfo == DINFO_SKB) {
2001-02-11 22:46:19 +00:00
if ((sub == REQUEST) || (sub == INDICATION)) {
if (!(skb = arg))
return(-EINVAL);
off.len = skb->len;
off.data.p = skb->data;
retval = hisax_rdata(dl->dev, &off, 0);
if (!retval) {
if (sub == REQUEST)
sub = CONFIRM;
else if (sub == INDICATION)
sub = RESPONSE;
prim &= ~SUBCOMMAND_MASK;
prim |= sub;
if (IF_TYPE(hif) == IF_UP)
retval = dl->inst.up.func(
&dl->inst.up, prim,
2001-03-03 18:17:16 +00:00
dinfo, len, arg);
2001-02-11 22:46:19 +00:00
else if (IF_TYPE(hif) == IF_DOWN)
retval = dl->inst.down.func(
&dl->inst.down, prim,
2001-03-03 18:17:16 +00:00
dinfo, len, arg);
2001-02-11 22:46:19 +00:00
if (retval) {
dev_kfree_skb(skb);
retval = 0;
}
}
} else {
retval = hisax_rdata(dl->dev, &off, 0);
if ((skb = arg)) {
dev_kfree_skb(skb);
}
}
} else {
printk(KERN_WARNING
"from_up_down: data prim(%x) no skb type(%x)\n",
prim, len);
}
} else {
off.data.p = arg;
off.len = len;
retval = hisax_rdata(dl->dev, &off, 0);
}
return(retval);
}
2001-04-08 16:45:56 +00:00
static int
from_peer(hisaxif_t *hif, u_int prim, int dinfo, int len, void *arg) {
int ret = -EINVAL;
devicelayer_t *dl;
struct sk_buff *nskb;
printk(KERN_DEBUG __FUNCTION__ ": prim(%x) di(%x) l(%d) arg(%p)\n",
prim, dinfo, len, arg);
if (!hif)
return(-EINVAL);
dl = hif->fdata;
if (!dl)
return(ret);
if (!dl->slave || !dl->sif.func) {
if (dl->inst.down.func)
ret = dl->inst.down.func(&dl->inst.down, prim, dinfo, len,
arg);
return(ret);
}
if (dinfo == DINFO_SKB) {
if (test_bit(FLG_DOWN_BUSY, &dl->Flags)) {
if (arg == dl->last_skb) {
if ((dl->last_skb = skb_dequeue(&dl->queue))) {
dl->inst.up.func(&dl->inst.up,
dl->last_skb->priority,
DINFO_SKB, 0, dl->last_skb);
} else
test_and_clear_bit(FLG_DOWN_BUSY,
&dl->Flags);
}
}
nskb = skb_clone(arg, GFP_ATOMIC);
ret = dl->sif.func(&dl->sif, prim, dinfo, len, arg);
if (ret) {
dev_kfree_skb(nskb);
if (dl->inst.down.func)
ret = dl->inst.down.func(&dl->inst.down, prim,
dinfo, len, arg);
} else {
if (dl->inst.down.func)
ret = dl->inst.down.func(&dl->inst.down, prim,
dinfo, len, nskb);
else
dev_kfree_skb(nskb);
if (ret)
dev_kfree_skb(nskb);
ret = 0; /* first delivery was OK */
}
} else {
ret = dl->sif.func(&dl->sif, prim, dinfo, len, arg);
if (ret) {
if (dl->inst.down.func)
ret = dl->inst.down.func(&dl->inst.down, prim,
dinfo, len, arg);
} else {
if (dl->inst.down.func)
dl->inst.down.func(&dl->inst.down, prim,
dinfo, len, arg);
}
}
return(ret);
}
static int
to_peer(hisaxif_t *hif, u_int prim, int dinfo, int len, void *arg) {
int ret = -EINVAL;
devicelayer_t *dl;
struct sk_buff *skb;
printk(KERN_DEBUG __FUNCTION__ ": prim(%x) di(%x) l(%d) arg(%p)\n",
prim, dinfo, len, arg);
if (!hif)
return(-EINVAL);
dl = hif->fdata;
if (!dl)
return(ret);
if (!dl->slave) {
if (dl->inst.up.func)
ret = dl->inst.up.func(&dl->inst.up, prim, dinfo,
len, arg);
return(ret);
}
if (dinfo == DINFO_SKB) {
if (test_and_set_bit(FLG_DOWN_BUSY, &dl->Flags)) {
skb = arg;
skb->priority = prim;
skb_queue_tail(&dl->queue, skb);
return(0);
}
dl->last_skb = arg;
ret = dl->inst.up.func(&dl->inst.up, prim, dinfo,
len, arg);
} else {
ret = dl->inst.up.func(&dl->inst.up, prim, dinfo,
len, arg);
}
return(ret);
}
static int
set_if(devicelayer_t *dl, u_int prim, hisaxif_t *hif)
{
int err = 0;
if (dl->slave && (hif->owner == dl->slave)) {
hif->peer = &dl->inst;
hif->fdata = dl;
hif->func = to_peer;
if ((prim & SUBCOMMAND_MASK) == REQUEST) {
if (dl->sif.stat == IF_NOACTIV) {
if (IF_TYPE(hif) == IF_UP)
dl->sif.stat = IF_DOWN;
else
dl->sif.stat = IF_UP;
dl->sif.owner = &dl->inst;
err = hif->owner->obj->own_ctrl(hif->owner,
MGR_SETIF | INDICATION, &dl->sif);
} else {
int_errtxt("REQ own stat(%x)", dl->sif.stat);
err= -EBUSY;
}
}
} else {
err = SetIF(&dl->inst, hif, prim, from_up_down, from_up_down, dl);
}
return(err);
}
2001-02-11 22:46:19 +00:00
static int
udev_manager(void *data, u_int prim, void *arg) {
2001-03-26 11:40:02 +00:00
hisaxinstance_t *inst = data;
2001-02-11 22:46:19 +00:00
hisaxdevice_t *dev = hisax_devicelist;
devicelayer_t *dl = NULL;
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(&hisax_device_lock);
while(dev) {
dl = dev->layer;
while(dl) {
2001-03-26 11:40:02 +00:00
if (&dl->inst == inst)
2001-02-11 22:46:19 +00:00
break;
dl = dl->next;
}
if (dl)
break;
dev = dev->next;
}
if (!dl) {
printk(KERN_WARNING "dev_manager prim %x without device layer\n", prim);
goto out;
}
switch(prim) {
2001-03-26 11:40:02 +00:00
case MGR_CONNECT | REQUEST:
err = ConnectIF(inst, arg);
2001-02-11 22:46:19 +00:00
break;
2001-03-26 11:40:02 +00:00
case MGR_SETIF | REQUEST:
case MGR_SETIF | INDICATION:
2001-04-08 16:45:56 +00:00
err = set_if(dl, prim, arg);
2001-03-26 11:40:02 +00:00
break;
case MGR_DISCONNECT | REQUEST:
case MGR_DISCONNECT | INDICATION:
err = DisConnectIF(inst, arg);
2001-02-11 22:46:19 +00:00
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);
break;
}
out:
read_unlock(&hisax_device_lock);
return(err);
}
int init_hisaxdev (int debug) {
2001-03-04 17:08:33 +00:00
int err,i;
2001-02-11 22:46:19 +00:00
udev_obj.name = MName;
2001-03-04 17:08:33 +00:00
for (i=0; i<=MAX_LAYER_NR; i++) {
udev_obj.DPROTO.protocol[i] = ISDN_PID_ANY;
udev_obj.BPROTO.protocol[i] = ISDN_PID_ANY;
}
2001-03-04 18:17:28 +00:00
udev_obj.own_ctrl = udev_manager;
2001-02-11 22:46:19 +00:00
udev_obj.prev = NULL;
udev_obj.next = NULL;
device_debug = debug;
if (register_chrdev(HISAX_MAJOR, "hisax", &hisax_fops)) {
printk(KERN_WARNING "hisax: Could not register devices\n");
return(-EIO);
}
if ((err = HiSax_register(&udev_obj))) {
printk(KERN_ERR "Can't register %s error(%d)\n", MName, err);
}
return(err);
}
int free_hisaxdev(void) {
int err = 0;
hisaxdevice_t *dev = hisax_devicelist;
if (hisax_devicelist) {
printk(KERN_WARNING "hisax: devices open on remove\n");
while (dev) {
while(dev->layer)
del_layer(dev->layer);
REMOVE_FROM_LISTBASE(dev, hisax_devicelist);
vfree(dev->rbuf);
vfree(dev->wbuf);
kfree(dev);
dev = hisax_devicelist;
}
err = -EBUSY;
}
if ((err = HiSax_unregister(&udev_obj))) {
printk(KERN_ERR "Can't unregister UserDevice(%d)\n", err);
}
if ((err = unregister_chrdev(HISAX_MAJOR, "hisax"))) {
printk(KERN_WARNING "hisax: devices busy on remove\n");
}
return(err);
}