/* $Id$ * * Copyright 2000 by Karsten Keil * */ #include #include #include #include #include #include #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 (lenaddr; 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 (lenaddr; 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 (lenaddr; 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 (lenaddr; 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 (lenaddr; 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 (lenaddr; 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 (lenprim, 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); }