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

1983 lines
48 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 "hisax_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
typedef struct _devicelayer {
struct _devicelayer *prev;
struct _devicelayer *next;
hisaxdevice_t *dev;
hisaxinstance_t inst;
hisaxinstance_t *slave;
hisaxif_t s_up;
hisaxif_t s_down;
int iaddr;
int lm_st;
int Flags;
} devicelayer_t;
typedef struct _devicestack {
struct _devicestack *prev;
struct _devicestack *next;
hisaxdevice_t *dev;
hisaxstack_t *st;
int extentions;
} devicestack_t;
typedef struct _hisaxtimer {
struct _hisaxtimer *prev;
struct _hisaxtimer *next;
struct _hisaxdevice *dev;
struct timer_list tl;
int id;
int Flags;
} hisaxtimer_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 *, struct sk_buff *);
static int hisax_wdata(hisaxdevice_t *dev);
// static int from_peer(hisaxif_t *, u_int, int, int, void *);
// static int to_peer(hisaxif_t *, u_int, int, int, void *);
static hisaxdevice_t *
get_hisaxdevice4minor(int minor)
{
hisaxdevice_t *dev = hisax_devicelist;
while(dev) {
if (dev->minor == minor)
break;
dev = dev->next;
}
return(dev);
}
static __inline__ void
p_memcpy_i(hisaxport_t *port, void *src, size_t count)
{
u_char *p = src;
size_t frag;
frag = port->buf + port->size - port->ip;
if (frag <= count) {
memcpy(port->ip, p, frag);
count -= frag;
port->cnt += frag;
port->ip = port->buf;
} else
frag = 0;
if (count) {
memcpy(port->ip, p + frag, count);
port->cnt += count;
port->ip += count;
}
}
static __inline__ void
p_memcpy_o(hisaxport_t *port, void *dst, size_t count)
{
u_char *p = dst;
size_t frag;
frag = port->buf + port->size - port->op;
if (frag <= count) {
memcpy(p, port->op, frag);
count -= frag;
port->cnt -= frag;
port->op = port->buf;
} else
frag = 0;
if (count) {
memcpy(p + frag, port->op, count);
port->cnt -= count;
port->op += count;
}
}
static __inline__ void
p_pull_o(hisaxport_t *port, size_t count)
{
size_t frag;
frag = port->buf + port->size - port->op;
if (frag <= count) {
count -= frag;
port->cnt -= frag;
port->op = port->buf;
}
if (count) {
port->cnt -= count;
port->op += count;
}
}
static size_t
next_frame_len(hisaxport_t *port)
{
size_t len;
int *lp;
if (port->cnt < IFRAME_HEAD_SIZE) {
int_errtxt("not a frameheader cnt(%d)", port->cnt);
return(0);
}
len = port->buf + port->size - port->op;
if (len < IFRAME_HEAD_SIZE) {
len = IFRAME_HEAD_SIZE - len - 4;
lp = (int *)(port->buf + len);
} else {
lp = (int *)(port->op + 12);
}
if (*lp <= 0) {
len = IFRAME_HEAD_SIZE;
} else {
len = IFRAME_HEAD_SIZE + *lp;
}
if (len > port->cnt) {
int_errtxt("size mismatch %d/%d/%d", *lp, len, port->cnt);
return(0);
}
return(len);
}
static int
hisax_rdata_raw(hisaxif_t *hif, struct sk_buff *skb) {
hisaxdevice_t *dev;
hisax_head_t *hh;
u_long flags;
int retval = 0;
if (!hif || !hif->fdata || !skb)
return(-EINVAL);
dev = hif->fdata;
if (skb->len < HISAX_FRAME_MIN)
return(-EINVAL);
hh = (hisax_head_t *)skb->data;
if (hh->prim == (PH_DATA | INDICATION)) {
if (test_bit(FLG_HISAXPORT_OPEN, &dev->rport.Flag)) {
skb_pull(skb, HISAX_HEAD_SIZE);
spin_lock_irqsave(&dev->rport.lock, flags);
if (skb->len < (dev->rport.size - dev->rport.cnt)) {
p_memcpy_i(&dev->rport, skb->data, skb->len);
} else {
skb_push(skb, HISAX_HEAD_SIZE);
retval = -ENOSPC;
}
spin_unlock_irqrestore(&dev->rport.lock, flags);
wake_up_interruptible(&dev->rport.procq);
} else {
printk(KERN_WARNING __FUNCTION__
": PH_DATA_IND device(%d) not read open\n",
dev->minor);
retval = -ENOENT;
}
} else if (hh->prim == (PH_DATA | CONFIRM)) {
test_and_clear_bit(FLG_HISAXPORT_BLOCK, &dev->wport.Flag);
hisax_wdata(dev);
} else if ((hh->prim == (PH_ACTIVATE | CONFIRM)) ||
(hh->prim == (PH_ACTIVATE | INDICATION))) {
test_and_set_bit(FLG_HISAXPORT_ENABLED,
&dev->wport.Flag);
test_and_clear_bit(FLG_HISAXPORT_BLOCK,
&dev->wport.Flag);
} else if ((hh->prim == (PH_DEACTIVATE | CONFIRM)) ||
(hh->prim == (PH_DEACTIVATE | INDICATION))) {
test_and_clear_bit(FLG_HISAXPORT_ENABLED,
&dev->wport.Flag);
} else {
printk(KERN_WARNING __FUNCTION__
": prim(%x) dinfo(%x) not supported\n",
hh->prim, hh->dinfo);
retval = -EINVAL;
}
if (!retval)
dev_kfree_skb_any(skb);
return(retval);
}
static int
hisax_rdata(hisaxdevice_t *dev, iframe_t *iff, int use_value) {
int len = 4*sizeof(u_int);
u_long flags;
hisaxport_t *port = &dev->rport;
if (iff->len > 0)
len += iff->len;
spin_lock_irqsave(&port->lock, flags);
if (len < (port->size - port->cnt)) {
if (len <= 20 && use_value) {
p_memcpy_i(port, iff, len);
} else {
p_memcpy_i(port, iff, 4*sizeof(u_int));
if (iff->len>0)
p_memcpy_i(port, iff->data.p, iff->len);
}
spin_unlock_irqrestore(&port->lock, flags);
} else {
spin_unlock_irqrestore(&port->lock, flags);
printk(KERN_WARNING __FUNCTION__ ":no rport space for %d\n",
len);
len = -ENOSPC;
}
wake_up_interruptible(&port->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 __FUNCTION__ ": addr:%x\n", addr);
while(dl) {
// if (device_debug & DEBUG_MGR_FUNC)
// printk(KERN_DEBUG __FUNCTION__ ": dl(%p) iaddr:%x\n",
// dl, dl->iaddr);
if (dl->iaddr == (IF_IADDRMASK & addr))
break;
dl = dl->next;
}
return(dl);
}
static devicestack_t
*get_devstack(hisaxdevice_t *dev, int addr)
{
devicestack_t *ds = dev->stack;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__ ": addr:%x\n", addr);
while(ds) {
if (ds->st && (ds->st->id == addr))
break;
ds = ds->next;
}
return(ds);
}
static hisaxtimer_t
*get_devtimer(hisaxdevice_t *dev, int id)
{
hisaxtimer_t *ht = dev->timer;
if (device_debug & DEBUG_DEV_TIMER)
printk(KERN_DEBUG __FUNCTION__ ": dev:%p id:%x\n", dev, id);
while(ht) {
if (ht->id == id)
break;
ht = ht->next;
}
return(ht);
}
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
new_devstack(hisaxdevice_t *dev, stack_info_t *si)
{
int err;
hisaxstack_t *st;
hisaxinstance_t inst;
devicestack_t *nds;
memset(&inst, 0, sizeof(hisaxinstance_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(hisax_pid_t));
inst.st->child = st->child;
} else {
memcpy(&inst.st->pid, &si->pid, sizeof(hisax_pid_t));
}
nds->extentions = si->extentions;
inst.st->extentions |= si->extentions;
inst.st->mgr = get_instance4id(si->mgr);
nds->st = inst.st;
APPEND_TO_LIST(nds, dev->stack);
return(inst.st->id);
}
static hisaxstack_t *
sel_channel(u_int addr, u_int channel)
{
hisaxstack_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
create_layer(hisaxdevice_t *dev, layer_info_t *linfo, int *adr)
{
hisaxlayer_t *layer;
hisaxstack_t *st;
int i, ret;
devicelayer_t *nl;
hisaxobject_t *obj;
hisaxinstance_t *inst = NULL;
if (!(st = get_stack4id(linfo->st))) {
int_error();
return(-ENODEV);
}
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;
if (!inst) {
printk(KERN_WARNING __FUNCTION__ ": no inst in layer(%p)\n",
layer);
return(-EINVAL);
}
} else if ((layer = getlayer4lay(st, linfo->pid.layermask))) {
if (!(linfo->extentions & EXT_INST_MIDDLE)) {
printk(KERN_WARNING
"HiSax create_layer st(%x) LM(%x) inst not empty(%p)\n",
st->id, linfo->pid.layermask, layer);
return(-EBUSY);
}
}
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->pid.layermask, nl, &nl->inst);
nl->dev = dev;
memcpy(&nl->inst.pid, &linfo->pid, sizeof(hisax_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);
}
nl->inst.down.owner = &nl->inst;
nl->inst.up.owner = &nl->inst;
nl->inst.obj = &udev_obj;
nl->inst.data = nl;
APPEND_TO_LIST(nl, dev->layer);
nl->inst.obj->ctrl(st, MGR_REGLAYER | INDICATION, &nl->inst);
nl->iaddr = nl->inst.id;
*adr++ = nl->iaddr;
if (inst) {
nl->slave = inst;
} else
*adr = 0;
return(8);
}
static int
remove_if(devicelayer_t *dl, int stat) {
hisaxif_t *hif,*phif,*shif;
int err;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__":dl(%p) stat(%x)\n", 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 __FUNCTION__": stat not UP/DOWN\n");
return(-EINVAL);
}
err = udev_obj.ctrl(hif->peer, MGR_DISCONNECT | REQUEST, hif);
if (phif) {
memcpy(phif, shif, sizeof(hisaxif_t));
memset(shif, 0, sizeof(hisaxif_t));
}
REMOVE_FROM_LIST(hif);
return(err);
}
static int
del_stack(devicestack_t *ds)
{
hisaxdevice_t *dev;
if (!ds) {
int_error();
return(-EINVAL);
}
dev = ds->dev;
if (device_debug & DEBUG_MGR_FUNC) {
printk(KERN_DEBUG __FUNCTION__": ds(%p) dev(%p)\n",
ds, dev);
}
if (!dev)
return(-EINVAL);
if (ds->st) {
if (ds->extentions & EXT_STACK_CLONE)
ds->st->child = NULL;
udev_obj.ctrl(ds->st, MGR_DELSTACK | REQUEST, NULL);
}
REMOVE_FROM_LISTBASE(ds, dev->stack);
kfree(ds);
return(0);
}
static int
del_layer(devicelayer_t *dl) {
hisaxinstance_t *inst = &dl->inst;
hisaxdevice_t *dev = dl->dev;
int i;
if (device_debug & DEBUG_MGR_FUNC) {
printk(KERN_DEBUG __FUNCTION__": dl(%p) inst(%p) LM(%x) dev(%p) nexti(%p)\n",
dl, inst, inst->pid.layermask, dev, inst->next);
printk(KERN_DEBUG __FUNCTION__": iaddr %x inst %s slave %p\n",
dl->iaddr, inst->name, dl->slave);
}
remove_if(dl, IF_UP);
remove_if(dl, IF_DOWN);
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);
}
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);
}
dl->iaddr = 0;
REMOVE_FROM_LISTBASE(dl, dev->layer);
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);
}
static hisaxinstance_t *
clone_instance(devicelayer_t *dl, hisaxstack_t *st, hisaxinstance_t *peer) {
int err;
if (dl->slave) {
printk(KERN_WARNING __FUNCTION__": layer has slave, cannot clone\n");
return(NULL);
}
if (!(peer->extentions & EXT_INST_CLONE)) {
printk(KERN_WARNING __FUNCTION__": peer cannot clone\n");
return(NULL);
}
dl->slave = (hisaxinstance_t *)st;
if ((err = peer->obj->own_ctrl(peer, MGR_CLONELAYER | REQUEST,
&dl->slave))) {
dl->slave = NULL;
printk(KERN_WARNING __FUNCTION__": peer clone error %d\n", err);
return(NULL);
}
return(dl->slave);
}
static int
connect_if_req(hisaxdevice_t *dev, iframe_t *iff) {
devicelayer_t *dl;
interface_info_t *ifi = (interface_info_t *)&iff->data.p;
hisaxinstance_t *owner;
hisaxinstance_t *peer;
hisaxinstance_t *pp;
hisaxif_t *hifp;
int stat;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": addr:%x own(%x) peer(%x)\n",
iff->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 __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 (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 __FUNCTION__": peer if has no peer\n");
return(-EINVAL);
}
if (stat == IF_UP) {
memcpy(&owner->up, hifp, sizeof(hisaxif_t));
memcpy(&dl->s_up, hifp, sizeof(hisaxif_t));
owner->up.owner = owner;
hifp->peer = owner;
hifp->func = from_up_down;
hifp->fdata = dl;
hifp = &pp->down;
memcpy(&owner->down, hifp, sizeof(hisaxif_t));
memcpy(&dl->s_down, hifp, sizeof(hisaxif_t));
owner->down.owner = owner;
hifp->peer = owner;
hifp->func = from_up_down;
hifp->fdata = dl;
} else {
memcpy(&owner->down, hifp, sizeof(hisaxif_t));
memcpy(&dl->s_down, hifp, sizeof(hisaxif_t));
owner->up.owner = owner;
hifp->peer = owner;
hifp->func = from_up_down;
hifp->fdata = dl;
hifp = &pp->up;
memcpy(&owner->up, hifp, sizeof(hisaxif_t));
memcpy(&dl->s_up, hifp, sizeof(hisaxif_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 __FUNCTION__": cannot create new peer instance\n");
return(-EBUSY);
}
}
}
if (ifi->extentions & EXT_IF_EXCLUSIV) {
if (hifp->stat != IF_NOACTIV) {
printk(KERN_WARNING __FUNCTION__": peer if is in use\n");
return(-EBUSY);
}
}
return(ConnectIF(owner, peer));
}
static int
set_if_req(hisaxdevice_t *dev, iframe_t *iff) {
hisaxif_t *hif,*phif,*shif;
int stat;
interface_info_t *ifi = (interface_info_t *)&iff->data.p;
devicelayer_t *dl;
hisaxinstance_t *inst, *peer;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": addr:%x own(%x) peer(%x)\n",
iff->addr, ifi->owner, ifi->peer);
if (!(dl=get_devlayer(dev, iff->addr)))
return(-ENXIO);
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 (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 __FUNCTION__": if not UP/DOWN\n");
return(-EINVAL);
}
if (shif->stat != IF_NOACTIV) {
printk(KERN_WARNING __FUNCTION__": save if busy\n");
return(-EBUSY);
}
if (hif->stat != IF_NOACTIV) {
printk(KERN_WARNING __FUNCTION__": own if busy\n");
return(-EBUSY);
}
hif->stat = stat;
hif->owner = inst;
memcpy(shif, phif, sizeof(hisaxif_t));
memset(phif, 0, sizeof(hisaxif_t));
return(peer->obj->own_ctrl(peer, iff->prim, hif));
}
static int
add_if_req(hisaxdevice_t *dev, iframe_t *iff) {
hisaxif_t *hif;
interface_info_t *ifi = (interface_info_t *)&iff->data.p;
hisaxinstance_t *inst, *peer;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": addr:%x own(%x) peer(%x)\n",
iff->addr, ifi->owner, ifi->peer);
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 (ifi->stat == IF_DOWN) {
hif = &inst->up;
} else if (ifi->stat == IF_UP) {
hif = &inst->down;
} else {
printk(KERN_WARNING __FUNCTION__": if not UP/DOWN\n");
return(-EINVAL);
}
return(peer->obj->ctrl(peer, iff->prim, hif));
}
static int
del_if_req(hisaxdevice_t *dev, iframe_t *iff)
{
devicelayer_t *dl;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": addr:%x\n", iff->addr);
if (!(dl=get_devlayer(dev, iff->addr)))
return(-ENXIO);
return(remove_if(dl, iff->addr));
}
static void
dev_expire_timer(hisaxtimer_t *ht)
{
iframe_t off;
if (device_debug & DEBUG_DEV_TIMER)
printk(KERN_DEBUG __FUNCTION__": timer(%x)\n",
ht->id);
if (test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags)) {
off.dinfo = 0;
off.prim = MGR_TIMER | INDICATION;
off.addr = ht->id;
off.len = 0;
hisax_rdata(ht->dev, &off, 0);
} else
printk(KERN_WARNING __FUNCTION__": timer(%x) not active\n",
ht->id);
}
static int
dev_init_timer(hisaxdevice_t *dev, iframe_t *iff)
{
hisaxtimer_t *ht;
ht = get_devtimer(dev, iff->addr);
if (!ht) {
ht = kmalloc(sizeof(hisaxtimer_t), GFP_ATOMIC);
if (!ht)
return(-ENOMEM);
ht->prev = NULL;
ht->next = NULL;
ht->dev = dev;
ht->id = iff->addr;
ht->tl.data = (long) ht;
ht->tl.function = (void *) dev_expire_timer;
init_timer(&ht->tl);
APPEND_TO_LIST(ht, dev->timer);
if (device_debug & DEBUG_DEV_TIMER)
printk(KERN_DEBUG __FUNCTION__": new(%x)\n",
ht->id);
} else if (device_debug & DEBUG_DEV_TIMER)
printk(KERN_DEBUG __FUNCTION__": old(%x)\n",
ht->id);
if (timer_pending(&ht->tl)) {
printk(KERN_WARNING __FUNCTION__": timer(%x) pending\n",
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(hisaxdevice_t *dev, iframe_t *iff)
{
hisaxtimer_t *ht;
ht = get_devtimer(dev, iff->addr);
if (!ht) {
printk(KERN_WARNING __FUNCTION__": no timer(%x)\n", iff->addr);
return(-ENODEV);
}
if (timer_pending(&ht->tl)) {
printk(KERN_WARNING __FUNCTION__": timer(%x) pending\n",
ht->id);
return(-EBUSY);
}
if (iff->dinfo < 10) {
printk(KERN_WARNING __FUNCTION__": timer(%x): %d ms too short\n",
ht->id, iff->dinfo);
return(-EINVAL);
}
if (device_debug & DEBUG_DEV_TIMER)
printk(KERN_DEBUG __FUNCTION__": timer(%x) %d ms\n",
ht->id, iff->dinfo);
init_timer(&ht->tl);
ht->tl.expires = jiffies + (iff->dinfo * HZ) / 1000;
test_and_set_bit(FLG_MGR_TIMER_RUNING, &ht->Flags);
add_timer(&ht->tl);
return(0);
}
static int
dev_del_timer(hisaxdevice_t *dev, iframe_t *iff)
{
hisaxtimer_t *ht;
ht = get_devtimer(dev, iff->addr);
if (!ht) {
printk(KERN_WARNING __FUNCTION__": no timer(%x)\n", iff->addr);
return(-ENODEV);
}
if (device_debug & DEBUG_DEV_TIMER)
printk(KERN_DEBUG __FUNCTION__": timer(%x)\n",
ht->id);
del_timer(&ht->tl);
if (!test_and_clear_bit(FLG_MGR_TIMER_RUNING, &ht->Flags))
printk(KERN_WARNING __FUNCTION__": timer(%x) not running\n",
ht->id);
return(0);
}
static int
dev_remove_timer(hisaxdevice_t *dev, int id)
{
hisaxtimer_t *ht;
ht = get_devtimer(dev, id);
if (!ht) {
printk(KERN_WARNING __FUNCTION__": no timer(%x)\n", id);
return(-ENODEV);
}
if (device_debug & DEBUG_DEV_TIMER)
printk(KERN_DEBUG __FUNCTION__": timer(%x)\n",
ht->id);
del_timer(&ht->tl);
REMOVE_FROM_LISTBASE(ht, dev->timer);
kfree(ht);
return(0);
}
static int
get_status(iframe_t *off)
{
status_info_t *si = (status_info_t *)off->data.p;
hisaxinstance_t *inst;
int err;
if (!(inst = get_instance4id(off->addr & IF_ADDRMASK))) {
printk(KERN_WARNING __FUNCTION__": no instance\n");
err = -ENODEV;
} else {
err = inst->obj->own_ctrl(inst, MGR_STATUS | REQUEST, si);
}
if (err)
off->len = err;
else
off->len = si->len + 2*sizeof(int);
return(err);
}
static void
get_layer_info(iframe_t *frm)
{
hisaxinstance_t *inst;
layer_info_t *li = (layer_info_t *)frm->data.p;
if (!(inst = get_instance4id(frm->addr & IF_ADDRMASK))) {
printk(KERN_WARNING __FUNCTION__": no instance\n");
frm->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;
memcpy(&li->pid, &inst->pid, sizeof(hisax_pid_t));
frm->len = sizeof(layer_info_t);
}
static void
get_if_info(iframe_t *frm)
{
hisaxinstance_t *inst;
hisaxif_t *hif;
interface_info_t *ii = (interface_info_t *)frm->data.p;
if (!(inst = get_instance4id(frm->addr & IF_ADDRMASK))) {
printk(KERN_WARNING __FUNCTION__": no instance\n");
frm->len = -ENODEV;
return;
}
if (frm->dinfo == IF_DOWN)
hif = &inst->down;
else if (frm->dinfo == IF_UP)
hif = &inst->up;
else {
printk(KERN_WARNING __FUNCTION__": wrong interface %x\n",
frm->dinfo);
frm->len = -EINVAL;
return;
}
frm->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;
frm->len = sizeof(interface_info_t);
}
static int
wdata_frame(hisaxdevice_t *dev, iframe_t *iff) {
hisaxif_t *hif = NULL;
devicelayer_t *dl;
int err=-ENXIO;
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) {
printk(KERN_WARNING __FUNCTION__": inst.up no down\n");
hif = NULL;
}
} else if (iff->addr & IF_DOWN) {
hif = &dl->inst.down;
if (IF_TYPE(hif) != IF_UP) {
printk(KERN_WARNING __FUNCTION__": inst.down no up\n");
hif = NULL;
}
}
if (hif) {
if (device_debug & DEBUG_WDATA)
printk(KERN_DEBUG __FUNCTION__": pr(%x) di(%x) l(%d)\n",
iff->prim, iff->dinfo, iff->len);
if (iff->len < 0) {
printk(KERN_WARNING
__FUNCTION__":data negativ(%d)\n",
iff->len);
return(-EINVAL);
}
err = if_link(hif, iff->prim, iff->dinfo, iff->len,
&iff->data.b[0], UPLINK_HEADER_SPACE);
if (device_debug & DEBUG_WDATA && err)
printk(KERN_DEBUG __FUNCTION__": if_link ret(%x)\n",
err);
} else {
if (device_debug & DEBUG_WDATA)
printk(KERN_DEBUG "hisax: no matching interface\n");
}
return(err);
}
static int
hisax_wdata_if(hisaxdevice_t *dev, iframe_t *iff, int len) {
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);
if (len < head) {
printk(KERN_WARNING __FUNCTION__": 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.dinfo = get_stack_cnt();
off.len = 0;
} else {
off.data.p = stbuf;
get_stack_info(&off);
}
hisax_rdata(dev, &off, 0);
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_NEWSTACK | REQUEST):
used = head + iff->len;
if (len<used)
return(len);
off.addr = iff->addr;
off.dinfo = 0;
off.prim = MGR_NEWSTACK | CONFIRM;
off.len = 0;
err = new_devstack(dev, (stack_info_t *)&iff->data.p);
if (err<0)
off.len = err;
else
off.dinfo = err;
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;
case (MGR_SELCHANNEL | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_SELCHANNEL | CONFIRM;
st = sel_channel(iff->addr, iff->dinfo);
if (st) {
off.len = 0;
off.dinfo = st->id;
} else {
off.dinfo = 0;
off.len = -ENODEV;
}
hisax_rdata(dev, &off, 1);
break;
case (MGR_GETLAYERID | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_GETLAYERID | CONFIRM;
off.dinfo = 0;
lay = iff->dinfo;
off.len = 0;
if (LAYER_OUTRANGE(lay)) {
off.len = -EINVAL;
hisax_rdata(dev, &off, 1);
break;
} else
lay = ISDN_LAYER(lay);
if ((st = get_stack4id(iff->addr))) {
if ((layer = getlayer4lay(st, lay))) {
if (layer->inst)
off.dinfo = layer->inst->id;
}
}
hisax_rdata(dev, &off, 0);
break;
case (MGR_GETLAYER | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_GETLAYER | CONFIRM;
off.dinfo = 0;
off.len = 0;
off.data.p = stbuf;
get_layer_info(&off);
hisax_rdata(dev, &off, 0);
break;
case (MGR_NEWLAYER | REQUEST):
used = head + sizeof(layer_info_t);
if (len<used)
return(len);
off.addr = iff->addr;
off.dinfo = 0;
off.prim = MGR_NEWLAYER | CONFIRM;
off.data.p = stbuf;
off.len = create_layer(dev, (layer_info_t *)&iff->data.i,
(int *)stbuf);
hisax_rdata(dev, &off, 0);
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;
off.addr = iff->addr;
off.prim = MGR_GETIF | CONFIRM;
off.dinfo = iff->dinfo;
off.len = 0;
off.data.p = stbuf;
get_if_info(&off);
hisax_rdata(dev, &off, 0);
break;
case (MGR_CONNECT | REQUEST):
used = head + sizeof(interface_info_t);
if (len<used)
return(len);
off.addr = iff->addr;
off.prim = MGR_CONNECT | CONFIRM;
off.dinfo = 0;
off.len = connect_if_req(dev, iff);
hisax_rdata(dev, &off, 1);
break;
case (MGR_SETIF | REQUEST):
used = head + iff->len;
if (len<used)
return(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_ADDIF | REQUEST):
used = head + iff->len;
if (len<used)
return(len);
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_DISCONNECT | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_DISCONNECT | CONFIRM;
off.dinfo = 0;
off.len = del_if_req(dev, iff);
hisax_rdata(dev, &off, 1);
break;
case (MGR_INITTIMER | REQUEST):
used = head;
off.len = dev_init_timer(dev, iff);
off.addr = iff->addr;
off.prim = MGR_INITTIMER | CONFIRM;
off.dinfo = iff->dinfo;
hisax_rdata(dev, &off, 0);
break;
case (MGR_ADDTIMER | REQUEST):
used = head;
off.len = dev_add_timer(dev, iff);
off.addr = iff->addr;
off.prim = MGR_ADDTIMER | CONFIRM;
off.dinfo = 0;
hisax_rdata(dev, &off, 0);
break;
case (MGR_DELTIMER | REQUEST):
used = head;
off.len = dev_del_timer(dev, iff);
off.addr = iff->addr;
off.prim = MGR_DELTIMER | CONFIRM;
off.dinfo = iff->dinfo;
hisax_rdata(dev, &off, 0);
break;
case (MGR_REMOVETIMER | REQUEST):
used = head;
off.len = dev_remove_timer(dev, iff->addr);
off.addr = iff->addr;
off.prim = MGR_REMOVETIMER | CONFIRM;
off.dinfo = 0;
hisax_rdata(dev, &off, 0);
break;
case (MGR_TIMER | RESPONSE):
used = head;
break;
case (MGR_STATUS | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_STATUS | CONFIRM;
off.dinfo = 0;
off.data.p = stbuf;
if (get_status(&off))
hisax_rdata(dev, &off, 1);
else
hisax_rdata(dev, &off, 0);
break;
case (MGR_SETDEVOPT | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_SETDEVOPT | CONFIRM;
off.dinfo = 0;
off.len = 0;
if (iff->dinfo == FLG_HISAXPORT_ONEFRAME) {
test_and_set_bit(FLG_HISAXPORT_ONEFRAME,
&dev->rport.Flag);
} else if (!iff->dinfo) {
test_and_clear_bit(FLG_HISAXPORT_ONEFRAME,
&dev->rport.Flag);
} else {
off.len = -EINVAL;
}
hisax_rdata(dev, &off, 0);
break;
case (MGR_GETDEVOPT | REQUEST):
used = head;
off.addr = iff->addr;
off.prim = MGR_GETDEVOPT | CONFIRM;
off.len = 0;
if (test_bit(FLG_HISAXPORT_ONEFRAME, &dev->rport.Flag))
off.dinfo = FLG_HISAXPORT_ONEFRAME;
else
off.dinfo = 0;
hisax_rdata(dev, &off, 0);
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 & IF_TYPEMASK) {
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_wdata(hisaxdevice_t *dev) {
int used = 0;
u_long flags;
spin_lock_irqsave(&dev->wport.lock, flags);
if (test_and_set_bit(FLG_HISAXPORT_BUSY, &dev->wport.Flag)) {
spin_unlock_irqrestore(&dev->wport.lock, flags);
return(0);
}
while (1) {
size_t frag;
if (!dev->wport.cnt) {
wake_up(&dev->wport.procq);
break;
}
if (dev->minor == HISAX_CORE_DEVICE) {
iframe_t *iff;
iframe_t hlp;
int broken = 0;
frag = dev->wport.buf + dev->wport.size
- dev->wport.op;
if (dev->wport.cnt < IFRAME_HEAD_SIZE) {
printk(KERN_WARNING __FUNCTION__": frame(%d,%d) too short\n",
dev->wport.cnt, IFRAME_HEAD_SIZE);
p_pull_o(&dev->wport, dev->wport.cnt);
wake_up(&dev->wport.procq);
break;
}
if (frag < IFRAME_HEAD_SIZE) {
broken = 1;
p_memcpy_o(&dev->wport, &hlp, IFRAME_HEAD_SIZE);
if (hlp.len >0) {
if (hlp.len < dev->wport.cnt) {
printk(KERN_WARNING __FUNCTION__": framedata(%d/%d)too short\n",
dev->wport.cnt, hlp.len);
p_pull_o(&dev->wport, dev->wport.cnt);
wake_up(&dev->wport.procq);
break;
}
}
iff = &hlp;
} else {
iff = (iframe_t *)dev->wport.op;
if (iff->len > 0) {
if (dev->wport.cnt < (iff->len + IFRAME_HEAD_SIZE)) {
printk(KERN_WARNING __FUNCTION__": frame(%d,%d) too short\n",
dev->wport.cnt, IFRAME_HEAD_SIZE + iff->len);
p_pull_o(&dev->wport, dev->wport.cnt);
wake_up(&dev->wport.procq);
break;
}
if (frag < (iff->len + IFRAME_HEAD_SIZE)) {
broken = 1;
p_memcpy_o(&dev->wport, &hlp, IFRAME_HEAD_SIZE);
}
}
}
if (broken) {
if (hlp.len > 0) {
iff = vmalloc(IFRAME_HEAD_SIZE + hlp.len);
if (!iff) {
printk(KERN_WARNING __FUNCTION__": no %d vmem for iff\n",
IFRAME_HEAD_SIZE + hlp.len);
p_pull_o(&dev->wport, hlp.len);
wake_up(&dev->wport.procq);
continue;
}
memcpy(iff, &hlp, IFRAME_HEAD_SIZE);
p_memcpy_o(&dev->wport, &iff->data.p,
iff->len);
} else {
iff = &hlp;
}
}
used = IFRAME_HEAD_SIZE;
if (iff->len > 0)
used += iff->len;
spin_unlock_irqrestore(&dev->wport.lock, flags);
hisax_wdata_if(dev, iff, used);
if (broken) {
if (used>IFRAME_HEAD_SIZE)
vfree(iff);
spin_lock_irqsave(&dev->wport.lock, flags);
} else {
spin_lock_irqsave(&dev->wport.lock, flags);
p_pull_o(&dev->wport, used);
}
} else { /* RAW DEVICES */
printk(KERN_DEBUG __FUNCTION__": wflg(%x)\n",
dev->wport.Flag);
if (test_bit(FLG_HISAXPORT_BLOCK, &dev->wport.Flag))
break;
used = dev->wport.cnt;
if (used > MAX_DATA_SIZE)
used = MAX_DATA_SIZE;
printk(KERN_DEBUG __FUNCTION__": cnt %d/%d\n",
used, dev->wport.cnt);
if (test_bit(FLG_HISAXPORT_ENABLED, &dev->wport.Flag)) {
struct sk_buff *skb;
skb = alloc_skb(used + HISAX_HEAD_SIZE,
GFP_ATOMIC);
if (skb) {
skb_reserve(skb, HISAX_HEAD_SIZE);
p_memcpy_o(&dev->wport, skb_put(skb,
used), used);
test_and_set_bit(FLG_HISAXPORT_BLOCK,
&dev->wport.Flag);
spin_unlock_irqrestore(&dev->wport.lock, flags);
used = if_addhead(&dev->wport.pif,
PH_DATA | REQUEST, (int)skb, skb);
if (used) {
printk(KERN_WARNING __FUNCTION__
": dev(%d) down err(%d)\n",
dev->minor, used);
kfree_skb(skb);
}
spin_lock_irqsave(&dev->wport.lock, flags);
} else {
printk(KERN_WARNING __FUNCTION__
": dev(%d) no skb(%d)\n",
dev->minor, used + HISAX_HEAD_SIZE);
p_pull_o(&dev->wport, used);
}
} else {
printk(KERN_WARNING __FUNCTION__
": dev(%d) wport not enabled\n",
dev->minor);
p_pull_o(&dev->wport, used);
}
}
wake_up(&dev->wport.procq);
}
test_and_clear_bit(FLG_HISAXPORT_BUSY, &dev->wport.Flag);
spin_unlock_irqrestore(&dev->wport.lock, flags);
return(0);
}
static hisaxdevice_t *
init_device(u_int minor) {
hisaxdevice_t *dev;
u_long flags;
dev = kmalloc(sizeof(hisaxdevice_t), GFP_KERNEL);
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": dev(%d) %p\n",
minor, dev);
if (dev) {
memset(dev, 0, sizeof(hisaxdevice_t));
dev->minor = minor;
init_waitqueue_head(&dev->rport.procq);
init_waitqueue_head(&dev->wport.procq);
init_MUTEX(&dev->io_sema);
write_lock_irqsave(&hisax_device_lock, flags);
APPEND_TO_LIST(dev, hisax_devicelist);
write_unlock_irqrestore(&hisax_device_lock, flags);
}
return(dev);
}
hisaxdevice_t *
get_free_rawdevice(void)
{
hisaxdevice_t *dev;
u_int minor;
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__":\n");
for (minor=HISAX_MINOR_RAW_MIN; minor<=HISAX_MINOR_RAW_MAX; minor++) {
dev = get_hisaxdevice4minor(minor);
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": dev(%d) %p\n",
minor, dev);
if (!dev) {
dev = init_device(minor);
if (!dev)
return(NULL);
dev->rport.pif.func = hisax_rdata_raw;
dev->rport.pif.fdata = dev;
return(dev);
}
}
return(NULL);
}
int
free_device(hisaxdevice_t *dev)
{
u_long flags;
if (!dev)
return(-ENODEV);
if (device_debug & DEBUG_MGR_FUNC)
printk(KERN_DEBUG __FUNCTION__": dev(%d)\n",
dev->minor);
/* release related stuff */
while(dev->layer)
del_layer(dev->layer);
while(dev->stack)
del_stack(dev->stack);
while(dev->timer)
dev_remove_timer(dev, dev->timer->id);
if (dev->rport.buf)
vfree(dev->rport.buf);
if (dev->wport.buf)
vfree(dev->wport.buf);
write_lock_irqsave(&hisax_device_lock, flags);
REMOVE_FROM_LISTBASE(dev, hisax_devicelist);
write_unlock_irqrestore(&hisax_device_lock, flags);
kfree(dev);
return(0);
}
static int
hisax_open(struct inode *ino, struct file *filep)
{
u_int minor = MINOR(ino->i_rdev);
hisaxdevice_t *dev = NULL;
u_long flags;
int isnew = 0;
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_open in: minor(%d) %p %p mode(%x)\n",
minor, filep, filep->private_data, filep->f_mode);
if (minor) {
dev = get_hisaxdevice4minor(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){
if (!dev->rport.buf) {
dev->rport.buf = vmalloc(HISAX_DEVBUF_SIZE);
if (!dev->rport.buf) {
if (isnew) {
write_lock_irqsave(&hisax_device_lock, flags);
REMOVE_FROM_LISTBASE(dev, hisax_devicelist);
write_unlock_irqrestore(&hisax_device_lock, flags);
kfree(dev);
}
return(-ENOMEM);
}
dev->rport.lock = SPIN_LOCK_UNLOCKED;
dev->rport.size = HISAX_DEVBUF_SIZE;
}
test_and_set_bit(FLG_HISAXPORT_OPEN, &dev->rport.Flag);
dev->rport.ip = dev->rport.op = dev->rport.buf;
dev->rport.cnt = 0;
}
if (dev->open_mode & FMODE_WRITE) {
if (!dev->wport.buf) {
dev->wport.buf = vmalloc(HISAX_DEVBUF_SIZE);
if (!dev->wport.buf) {
if (isnew) {
if (dev->rport.buf)
vfree(dev->rport.buf);
write_lock_irqsave(&hisax_device_lock, flags);
REMOVE_FROM_LISTBASE(dev, hisax_devicelist);
write_unlock_irqrestore(&hisax_device_lock, flags);
kfree(dev);
}
return(-ENOMEM);
}
dev->wport.lock = SPIN_LOCK_UNLOCKED;
dev->wport.size = HISAX_DEVBUF_SIZE;
}
test_and_set_bit(FLG_HISAXPORT_OPEN, &dev->wport.Flag);
dev->wport.ip = dev->wport.op = dev->wport.buf;
dev->wport.cnt = 0;
}
hisaxlock_core();
filep->private_data = dev;
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;
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(%d) %p mode %x/%x\n",
dev->minor, dev, dev->open_mode, filep->f_mode);
dev->open_mode &= ~filep->f_mode;
read_unlock(&hisax_device_lock);
if (filep->f_mode & FMODE_READ) {
test_and_clear_bit(FLG_HISAXPORT_OPEN,
&dev->rport.Flag);
}
if (filep->f_mode & FMODE_WRITE) {
test_and_clear_bit(FLG_HISAXPORT_OPEN,
&dev->wport.Flag);
}
filep->private_data = NULL;
if (!dev->minor)
free_device(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 __inline__ ssize_t
do_hisax_read(struct file *file, char *buf, size_t count, loff_t * off)
{
hisaxdevice_t *dev = file->private_data;
size_t len, frag;
u_long flags;
if (off != &file->f_pos)
return(-ESPIPE);
if (!dev->rport.buf)
return -EINVAL;
if (!access_ok(VERIFY_WRITE, buf, count))
return(-EFAULT);
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_read: file(%d) %p max %d\n",
dev->minor, file, count);
while (!dev->rport.cnt) {
if (file->f_flags & O_NONBLOCK)
return(-EAGAIN);
interruptible_sleep_on(&(dev->rport.procq));
if (signal_pending(current))
return(-ERESTARTSYS);
}
spin_lock_irqsave(&dev->rport.lock, flags);
if (test_bit(FLG_HISAXPORT_ONEFRAME, &dev->rport.Flag)) {
len = next_frame_len(&dev->rport);
if (!len) {
spin_unlock_irqrestore(&dev->rport.lock, flags);
return(-EINVAL);
}
if (count < len) {
spin_unlock_irqrestore(&dev->rport.lock, flags);
return(-ENOSPC);
}
} else {
if (count < dev->rport.cnt)
len = count;
else
len = dev->rport.cnt;
}
frag = dev->rport.buf + dev->rport.size - dev->rport.op;
if (frag <= len) {
if (copy_to_user(buf, dev->rport.op, frag)) {
spin_unlock_irqrestore(&dev->rport.lock, flags);
return(-EFAULT);
}
len -= frag;
dev->rport.op = dev->rport.buf;
dev->rport.cnt -= frag;
} else
frag = 0;
if (len) {
if (copy_to_user(buf + frag, dev->rport.op, len)) {
spin_unlock_irqrestore(&dev->rport.lock, flags);
return(-EFAULT);
}
dev->rport.cnt -= len;
dev->rport.op += len;
}
*off += len + frag;
spin_unlock_irqrestore(&dev->rport.lock, flags);
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_read: file(%d) %d\n",
dev->minor, len + frag);
return(len + frag);
}
static ssize_t
hisax_read(struct file *file, char *buf, size_t count, loff_t * off)
{
hisaxdevice_t *dev = file->private_data;
ssize_t ret;
if (!dev)
return(-ENODEV);
down(&dev->io_sema);
ret = do_hisax_read(file, buf, count, off);
up(&dev->io_sema);
return(ret);
}
static loff_t
hisax_llseek(struct file *file, loff_t offset, int orig)
{
return -ESPIPE;
}
static __inline__ ssize_t
do_hisax_write(struct file *file, const char *buf, size_t count, loff_t * off)
{
hisaxdevice_t *dev = file->private_data;
size_t len, frag;
u_long flags;
if (off != &file->f_pos)
return(-ESPIPE);
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_write: file(%d) %p count %d/%d/%d\n",
dev->minor, file, count, dev->wport.cnt, dev->wport.size);
if (!dev->wport.buf)
return -EINVAL;
if (!access_ok(VERIFY_WRITE, buf, count))
return(-EFAULT);
if (count > dev->wport.size)
return(-ENOSPC);
spin_lock_irqsave(&dev->wport.lock, flags);
while ((dev->wport.size - dev->wport.cnt) < count) {
spin_unlock_irqrestore(&dev->wport.lock, flags);
if (file->f_flags & O_NONBLOCK)
return(-EAGAIN);
interruptible_sleep_on(&(dev->wport.procq));
if (signal_pending(current))
return(-ERESTARTSYS);
spin_lock_irqsave(&dev->wport.lock, flags);
}
len = count;
frag = dev->wport.buf + dev->wport.size - dev->wport.ip;
if (frag <= len) {
copy_from_user(dev->wport.ip, buf, frag);
dev->wport.ip = dev->wport.buf;
len -= frag;
dev->wport.cnt += frag;
} else
frag = 0;
if (len) {
copy_from_user(dev->wport.ip, buf + frag, len);
dev->wport.cnt += len;
dev->wport.ip += len;
}
spin_unlock_irqrestore(&dev->wport.lock, flags);
hisax_wdata(dev);
return(count);
}
static ssize_t
hisax_write(struct file *file, const char *buf, size_t count, loff_t * off)
{
hisaxdevice_t *dev = file->private_data;
ssize_t ret;
if (!dev)
return(-ENODEV);
down(&dev->io_sema);
ret = do_hisax_write(file, buf, count, off);
up(&dev->io_sema);
return(ret);
}
static unsigned int
hisax_poll(struct file *file, poll_table * wait)
{
unsigned int mask = POLLERR;
hisaxdevice_t *dev = file->private_data;
hisaxport_t *rport = (file->f_mode & FMODE_READ) ?
&dev->rport : NULL;
hisaxport_t *wport = (file->f_mode & FMODE_WRITE) ?
&dev->wport : NULL;
if (dev) {
if (device_debug & DEBUG_DEV_OP)
printk(KERN_DEBUG "hisax_poll in: file(%d) %p\n",
dev->minor, file);
if (rport) {
poll_wait(file, &rport->procq, wait);
mask = 0;
if (rport->cnt)
mask |= (POLLIN | POLLRDNORM);
}
if (wport) {
poll_wait(file, &wport->procq, wait);
if (mask == POLLERR)
mask = 0;
if (wport->cnt < wport->size)
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, struct sk_buff *skb) {
devicelayer_t *dl;
iframe_t off;
hisax_head_t *hh;
int retval = -EINVAL;
if (!hif || !hif->fdata || !skb)
return(-EINVAL);
dl = hif->fdata;
if (skb->len < HISAX_FRAME_MIN)
return(-EINVAL);
hh = (hisax_head_t *)skb->data;
off.data.p = skb_pull(skb, sizeof(hisax_head_t));
off.len = skb->len;
off.addr = dl->iaddr | IF_TYPE(hif);
off.prim = hh->prim;
off.dinfo = hh->dinfo;
if (device_debug & DEBUG_RDATA)
printk(KERN_DEBUG "from_up_down: %x(%x) dinfo:%x len:%d\n",
off.prim, off.addr, off.dinfo, off.len);
retval = hisax_rdata(dl->dev, &off, 0);
if (retval == (4*sizeof(u_int) + off.len)) {
dev_kfree_skb(skb);
retval = 0;
} else if (retval == 0)
retval = -ENOSPC;
return(retval);
}
static int
set_if(devicelayer_t *dl, u_int prim, hisaxif_t *hif)
{
int err = 0;
err = SetIF(&dl->inst, hif, prim, from_up_down, from_up_down, dl);
return(err);
}
static int
udev_manager(void *data, u_int prim, void *arg) {
hisaxinstance_t *inst = 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 == inst)
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_CONNECT | REQUEST:
err = 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 = DisConnectIF(inst, 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) {
free_device(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);
}