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