1003 lines
25 KiB
C
1003 lines
25 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 "hisax_core.h"
|
|
|
|
#define MAX_HEADER_LEN 4
|
|
|
|
#define FLG_MGR_SETSTACK 1
|
|
|
|
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;
|
|
int iaddr;
|
|
int lm_st;
|
|
int Flags;
|
|
} 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";
|
|
static u_char stbuf[1000];
|
|
|
|
static int device_debug = 0;
|
|
|
|
static int from_up_down(hisaxif_t *, u_int, int, int, void *);
|
|
|
|
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
|
|
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
|
|
create_layer(hisaxdevice_t *dev, hisaxstack_t *st, layer_info_t *linfo) {
|
|
hisaxlayer_t *layer;
|
|
int i, ret;
|
|
devicelayer_t *nl;
|
|
|
|
if ((layer = getlayer4lay(st, linfo->layermask))) {
|
|
printk(KERN_WARNING
|
|
"HiSax create_layer st(%d) LM(%x) inst not empty(%p)\n",
|
|
st->id, linfo->layermask, layer);
|
|
}
|
|
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
|
|
"HiSax create_layer LM(%x) nl(%p) nl inst(%p)\n",
|
|
linfo->layermask, nl, &nl->inst);
|
|
nl->dev = dev;
|
|
nl->inst.layermask = linfo->layermask;
|
|
memcpy(&nl->inst.pid, &linfo->pid, sizeof(hisax_pid_t));
|
|
strcpy(nl->inst.name, linfo->name);
|
|
for (i=0; i<= MAX_LAYER_NR; i++) {
|
|
if (linfo->layermask & ISDN_LAYER(i)) {
|
|
if (st->pid.protocol[i] == ISDN_PID_NONE) {
|
|
st->pid.protocol[i] = linfo->pid.protocol[i];
|
|
nl->lm_st |= ISDN_LAYER(i);
|
|
}
|
|
}
|
|
}
|
|
nl->inst.down.inst = &nl->inst;
|
|
nl->inst.up.inst = &nl->inst;
|
|
if (linfo->object_id != -1) {
|
|
kfree(nl);
|
|
return(-ENODEV);
|
|
} else {
|
|
nl->inst.obj = &udev_obj;
|
|
}
|
|
nl->inst.data = nl;
|
|
APPEND_TO_LIST(nl, dev->layer);
|
|
nl->inst.obj->ctrl(st, MGR_ADDLAYER | INDICATION, &nl->inst);
|
|
nl->iaddr = st->id | IADDR_BIT |
|
|
(IF_LAYERMASK & (nl->inst.layermask << 16));
|
|
ret = nl->iaddr;
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
del_layer(devicelayer_t *dl) {
|
|
hisaxif_t hif;
|
|
hisaxinstance_t *inst = &dl->inst;
|
|
hisaxdevice_t *dev = dl->dev;
|
|
int i;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC) {
|
|
printk(KERN_DEBUG "del_layer: dl(%p) inst(%p) lay(%x) dev(%p) nexti(%p)\n",
|
|
dl, inst, inst->layermask, dev, inst->next);
|
|
printk(KERN_DEBUG "del_layer iaddr %x inst %s\n",
|
|
dl->iaddr, inst->name);
|
|
printk(KERN_DEBUG "del_layer up LM(%x) p(%X)\n",
|
|
inst->up.layermask, inst->up.protocol);
|
|
printk(KERN_DEBUG "del_layer down LM(%x) p(%X)\n",
|
|
inst->down.layermask,inst->down.protocol);
|
|
}
|
|
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);
|
|
}
|
|
memset(&hif, 0, sizeof(hisaxif_t));
|
|
hif.fdata = dl;
|
|
hif.func = from_up_down;
|
|
hif.protocol = inst->up.protocol;
|
|
hif.layermask = inst->up.layermask;
|
|
udev_obj.ctrl(inst->st, MGR_DELIF | REQUEST, &hif);
|
|
hif.fdata = dl;
|
|
hif.func = from_up_down;
|
|
hif.protocol = inst->down.protocol;
|
|
hif.layermask = inst->down.layermask;
|
|
udev_obj.ctrl(inst->st, MGR_DELIF | REQUEST, &hif);
|
|
dl->iaddr = 0;
|
|
REMOVE_FROM_LISTBASE(dl, dev->layer);
|
|
udev_obj.ctrl(inst->st, MGR_DELLAYER | REQUEST, inst);
|
|
kfree(dl);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
add_if_req(hisaxdevice_t *dev, iframe_t *iff) {
|
|
hisaxif_t *hif = NULL;
|
|
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) {
|
|
hif = &dl->inst.up;
|
|
if (hif->stat == IF_NOACTIV) {
|
|
hif->stat = IF_DOWN;
|
|
hif->layermask = get_up_layer(dl->inst.layermask);
|
|
hif->protocol = get_protocol(dl->inst.st, hif->layermask);
|
|
err = udev_obj.ctrl(dl->inst.st, MGR_ADDIF | REQUEST, hif);
|
|
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;
|
|
hif->layermask = get_down_layer(dl->inst.layermask);
|
|
hif->protocol = get_protocol(dl->inst.st, hif->layermask);
|
|
err = udev_obj.ctrl(dl->inst.st, MGR_ADDIF | REQUEST, hif);
|
|
if (err)
|
|
hif->stat = IF_NOACTIV;
|
|
} else
|
|
err = -EBUSY;
|
|
}
|
|
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);
|
|
if (iff->len != 2*sizeof(int))
|
|
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;
|
|
hif->layermask = *ip++;
|
|
hif->protocol = *ip;
|
|
err = udev_obj.ctrl(dl->inst.st, MGR_ADDIF | REQUEST, hif);
|
|
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;
|
|
hif->layermask = *ip++;
|
|
hif->protocol = *ip;
|
|
err = udev_obj.ctrl(dl->inst.st, MGR_ADDIF | REQUEST, hif);
|
|
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)
|
|
err = DelIF(&dl->inst, &dl->inst.up, from_up_down, dl);
|
|
else if (iff->addr & IF_DOWN)
|
|
err = DelIF(&dl->inst, &dl->inst.down, from_up_down, dl);
|
|
return(err);
|
|
}
|
|
|
|
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)
|
|
printk(KERN_DEBUG __FUNCTION__": addr:%x\n", iff->addr);
|
|
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);
|
|
err = hif->func(hif, iff->prim, DINFO_SKB, 0, skb);
|
|
if (err)
|
|
dev_kfree_skb(skb);
|
|
return(err);
|
|
}
|
|
}
|
|
if (device_debug & DEBUG_WDATA)
|
|
printk(KERN_DEBUG "wdata_frame: hif %p f:%p d:%p s:%p i:%p\n",
|
|
hif, hif->func, hif->fdata, hif->st, hif->inst);
|
|
err = hif->func(hif, iff->prim, iff->dinfo, iff->len, &iff->data.b[0]);
|
|
} 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;
|
|
hisaxlayer_t *layer;
|
|
int lay;
|
|
int err = 0;
|
|
int used = 0;
|
|
int head = 4*sizeof(u_int);
|
|
int *ip;
|
|
u_char *p;
|
|
|
|
if (len < head) {
|
|
printk(KERN_WARNING "hisax: if_frame(%d) too short\n", len);
|
|
return(len);
|
|
}
|
|
if (device_debug & DEBUG_WDATA)
|
|
printk(KERN_DEBUG "hisax_wdata: %x:%x %x %d\n",
|
|
iff->addr, iff->prim, iff->dinfo, iff->len);
|
|
switch(iff->prim) {
|
|
case (MGR_GETSTACK | REQUEST):
|
|
used = head;
|
|
off.addr = iff->addr;
|
|
off.prim = MGR_GETSTACK | CONFIRM;
|
|
off.dinfo = 0;
|
|
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;
|
|
off.dinfo = 0;
|
|
lay = iff->data.i;
|
|
off.len = 0;
|
|
if ((st = get_stack4id(iff->addr))) {
|
|
if ((layer = getlayer4lay(st, lay))) {
|
|
hisaxinstance_t *inst = layer->inst;
|
|
off.data.p = stbuf;
|
|
p = stbuf;
|
|
while(inst) {
|
|
strcpy(p, inst->name);
|
|
p += HISAX_MAX_IDLEN;
|
|
ip = (u_int *)p;
|
|
*ip++ = inst->obj->id;
|
|
*ip++ = inst->extentions;
|
|
*ip++ = inst->layermask;
|
|
p = (u_char *)ip;
|
|
memcpy(p, &inst->pid, sizeof(hisax_pid_t));
|
|
p += sizeof(hisax_pid_t);
|
|
inst = inst->next;
|
|
}
|
|
off.len = p - stbuf;
|
|
}
|
|
}
|
|
hisax_rdata(dev, &off, 0);
|
|
break;
|
|
case (MGR_ADDLAYER | REQUEST):
|
|
used = head + sizeof(layer_info_t);
|
|
if (len<used)
|
|
return(len);
|
|
off.addr = iff->addr;
|
|
off.dinfo = 0;
|
|
off.prim = MGR_ADDLAYER | CONFIRM;
|
|
off.len = 4;
|
|
if ((st = get_stack4id(iff->addr))) {
|
|
err = create_layer(dev, st,
|
|
(layer_info_t *)&iff->data.i);
|
|
if (err<0)
|
|
off.len = err;
|
|
else
|
|
off.data.i = err;
|
|
} else
|
|
off.len = -ENODEV;
|
|
hisax_rdata(dev, &off, 1);
|
|
break;
|
|
case (MGR_DELLAYER | REQUEST):
|
|
used = head;
|
|
off.addr = iff->addr;
|
|
off.prim = MGR_DELLAYER | CONFIRM;
|
|
off.dinfo = 0;
|
|
if ((dl=get_devlayer(dev, iff->addr)))
|
|
off.len = del_layer(dl);
|
|
else
|
|
off.len = -ENXIO;
|
|
hisax_rdata(dev, &off, 1);
|
|
break;
|
|
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;
|
|
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) {
|
|
*ip++ = inst->up.stat;
|
|
*ip++ = inst->up.layermask;
|
|
*ip++ = inst->up.protocol;
|
|
} else if (lay & IF_DOWN) {
|
|
*ip++ = inst->down.stat;
|
|
*ip++ = inst->down.layermask;
|
|
*ip++ = inst->down.protocol;
|
|
}
|
|
inst = inst->next;
|
|
}
|
|
off.len = (u_char *)ip - (u_char *)stbuf;
|
|
}
|
|
}
|
|
hisax_rdata(dev, &off, 0);
|
|
break;
|
|
case (MGR_ADDIF | REQUEST):
|
|
used = head;
|
|
off.addr = iff->addr;
|
|
off.prim = MGR_ADDIF | CONFIRM;
|
|
off.dinfo = 0;
|
|
off.len = add_if_req(dev, iff);
|
|
hisax_rdata(dev, &off, 1);
|
|
break;
|
|
case (MGR_SETIF | REQUEST):
|
|
used = head + iff->len;
|
|
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;
|
|
case (MGR_DELIF | REQUEST):
|
|
used = head;
|
|
off.addr = iff->addr;
|
|
off.prim = MGR_DELIF | CONFIRM;
|
|
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;
|
|
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);
|
|
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);
|
|
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);
|
|
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
|
|
from_up_down(hisaxif_t *hif, u_int prim, int dinfo, int len, void *arg) {
|
|
|
|
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;
|
|
off.dinfo = dinfo;
|
|
if (device_debug & DEBUG_RDATA)
|
|
printk(KERN_DEBUG "from_up_down: %x(%x) dinfo:%x len:%d\n",
|
|
off.prim, off.addr, dinfo, len);
|
|
if (CMD_IS_DATA(prim)) {
|
|
if (dinfo == DINFO_SKB) {
|
|
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,
|
|
dinfo, len, arg);
|
|
else if (IF_TYPE(hif) == IF_DOWN)
|
|
retval = dl->inst.down.func(
|
|
&dl->inst.down, prim,
|
|
dinfo, len, arg);
|
|
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);
|
|
}
|
|
|
|
static int
|
|
add_if(devicelayer_t *dl, hisaxif_t *hif) {
|
|
int err;
|
|
hisaxinstance_t *inst = &dl->inst;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "userdev add_if lay %x/%x prot %x\n",
|
|
hif->layermask, hif->stat, hif->protocol);
|
|
hif->fdata = dl;
|
|
if (IF_TYPE(hif) == IF_UP) {
|
|
hif->func = from_up_down;
|
|
if (inst->up.stat == IF_NOACTIV) {
|
|
inst->up.stat = IF_DOWN;
|
|
inst->up.layermask = get_up_layer(inst->layermask);
|
|
inst->up.protocol = get_protocol(inst->st,
|
|
inst->up.layermask);
|
|
err = udev_obj.ctrl(inst->st, MGR_ADDIF | REQUEST, &inst->up);
|
|
if (err)
|
|
inst->up.stat = IF_NOACTIV;
|
|
}
|
|
} else if (IF_TYPE(hif) == IF_DOWN) {
|
|
hif->func = from_up_down;
|
|
if (inst->down.stat == IF_NOACTIV) {
|
|
inst->down.stat = IF_UP;
|
|
inst->down.layermask = get_down_layer(inst->layermask);
|
|
inst->down.protocol = get_protocol(inst->st,
|
|
inst->down.layermask);
|
|
err = udev_obj.ctrl(inst->st, MGR_ADDIF | REQUEST, &inst->down);
|
|
if (err)
|
|
inst->down.stat = IF_NOACTIV;
|
|
}
|
|
} else
|
|
return(-EINVAL);
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
del_if(devicelayer_t *dl, hisaxif_t *hif) {
|
|
int err;
|
|
hisaxinstance_t *inst = &dl->inst;
|
|
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "dev del_if lay %x/%x %p/%p\n",
|
|
hif->layermask, hif->stat, hif->func, hif->fdata);
|
|
if ((hif->func == inst->up.func) && (hif->fdata == inst->up.fdata)) {
|
|
inst->up.stat = IF_NOACTIV;
|
|
inst->up.protocol = ISDN_PID_NONE;
|
|
err = udev_obj.ctrl(inst->st, MGR_ADDIF | REQUEST, &inst->up);
|
|
} else if ((hif->func == inst->down.func) && (hif->fdata == inst->down.fdata)) {
|
|
inst->down.stat = IF_NOACTIV;
|
|
inst->down.protocol = ISDN_PID_NONE;
|
|
err = udev_obj.ctrl(inst->st, MGR_ADDIF | REQUEST, &inst->down);
|
|
} else {
|
|
if (device_debug & DEBUG_MGR_FUNC)
|
|
printk(KERN_DEBUG "device del_if no if found\n");
|
|
return(-EINVAL);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
static int
|
|
udev_manager(void *data, u_int prim, void *arg) {
|
|
hisaxstack_t *st = data;
|
|
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) {
|
|
if (dl->inst.st == st)
|
|
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) {
|
|
case MGR_ADDIF | REQUEST:
|
|
err = add_if(dl, arg);
|
|
break;
|
|
case MGR_DELIF | REQUEST:
|
|
err = del_if(dl, arg);
|
|
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) {
|
|
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;
|
|
}
|
|
udev_obj.own_ctrl = udev_manager;
|
|
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);
|
|
}
|