/* $Id$ * * Author Karsten Keil (keil@isdn4linux.de) * * This file is released under the GPLv2 * */ #include "core.h" static LIST_HEAD(mISDN_stacklist); static rwlock_t stacklist_lock = RW_LOCK_UNLOCKED; static LIST_HEAD(mISDN_instlist); static rwlock_t instlist_lock = RW_LOCK_UNLOCKED; int get_stack_cnt(void) { int cnt = 0; mISDNstack_t *st; read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) cnt++; read_unlock(&stacklist_lock); return(cnt); } void get_stack_info(struct sk_buff *skb) { mISDN_head_t *hp; mISDNstack_t *cst, *st; stack_info_t *si; int i; hp = mISDN_HEAD_P(skb); if (hp->addr == 0) { hp->dinfo = get_stack_cnt(); hp->len = 0; return; } else if (hp->addr <= 127 && hp->addr <= get_stack_cnt()) { /* stack nr */ i = 1; read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) { if (i == hp->addr) break; i++; } read_unlock(&stacklist_lock); } else st = get_stack4id(hp->addr); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: addr(%08x) st(%p) id(%08x)\n", __FUNCTION__, hp->addr, st, st ? st->id : 0); if (!st) { hp->len = -ENODEV; return; } else { si = (stack_info_t *)skb->data; memset(si, 0, sizeof(stack_info_t)); si->id = st->id; si->extentions = st->extentions; if (st->mgr) si->mgr = st->mgr->id; else si->mgr = 0; memcpy(&si->pid, &st->pid, sizeof(mISDN_pid_t)); memcpy(&si->para, &st->para, sizeof(mISDN_stPara_t)); if (st->clone) si->clone = st->clone->id; else si->clone = 0; if (st->master) si->master = st->master->id; else si->master = 0; si->instcnt = 0; for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i]) { si->inst[si->instcnt] = st->i_array[i]->id; si->instcnt++; } } si->childcnt = 0; list_for_each_entry(cst, &st->childlist, list) { si->child[si->childcnt] = cst->id; si->childcnt++; } hp->len = sizeof(stack_info_t); if (si->childcnt>2) hp->len += (si->childcnt-2)*sizeof(int); } skb_put(skb, hp->len); } static int get_free_stackid(mISDNstack_t *mst, int flag) { u_int id = 0, found; mISDNstack_t *st; if (!mst) { while(id < STACK_ID_MAX) { found = 0; id += STACK_ID_INC; read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) { if (st->id == id) { found++; break; } } read_unlock(&stacklist_lock); if (!found) return(id); } } else if (flag & FLG_CLONE_STACK) { id = mst->id | FLG_CLONE_STACK; while(id < CLONE_ID_MAX) { found = 0; id += CLONE_ID_INC; st = mst->clone; while (st) { if (st->id == id) { found++; break; } st = st->clone; } if (!found) return(id); } } else if (flag & FLG_CHILD_STACK) { id = mst->id | FLG_CHILD_STACK; while(id < CHILD_ID_MAX) { id += CHILD_ID_INC; found = 0; list_for_each_entry(st, &mst->childlist, list) { if (st->id == id) { found++; break; } } if (!found) return(id); } } return(0); } mISDNstack_t * get_stack4id(u_int id) { mISDNstack_t *cst, *st; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_stack4id(%x)\n", id); if (!id) /* 0 isn't a valid id */ return(NULL); read_lock(&stacklist_lock); list_for_each_entry(st, &mISDN_stacklist, list) { if (id == st->id) { read_unlock(&stacklist_lock); return(st); } if ((id & FLG_CHILD_STACK) && ((id & MASTER_ID_MASK) == st->id)) { list_for_each_entry(cst, &st->childlist, list) { if (cst->id == id) { read_unlock(&stacklist_lock); return(cst); } } } if ((id & FLG_CLONE_STACK) && ((id & MASTER_ID_MASK) == st->id)) { cst = st->clone; while (cst) { if (cst->id == id) { read_unlock(&stacklist_lock); return(cst); } cst = cst->clone; } } } read_unlock(&stacklist_lock); return(NULL); } mISDNinstance_t * getlayer4lay(mISDNstack_t *st, int layermask) { int i; if (!st) { int_error(); return(NULL); } for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i] && (st->i_array[i]->pid.layermask & layermask)) return(st->i_array[i]); } return(NULL); } static mISDNinstance_t * get_nextlayer(mISDNstack_t *st, u_int addr) { mISDNinstance_t *inst=NULL; int layer = addr & LAYER_ID_MASK; if (!(addr & FLG_MSG_TARGET)) { switch(addr & MSG_DIR_MASK) { case FLG_MSG_DOWN: if (addr & FLG_MSG_CLONED) { /* OK */ } else layer -= LAYER_ID_INC; break; case FLG_MSG_UP: if (addr & FLG_MSG_CLONED) { /* OK */ } else layer += LAYER_ID_INC; break; case MSG_TO_OWNER: break; default: /* broadcast */ int_errtxt("st(%08x) addr(%08x) wrong address", st->id, addr); return(NULL); } } if ((layer < 0) || (layer > MAX_LAYER_NR)) { int_errtxt("st(%08x) addr(%08x) layer %d out of range", st->id, addr, layer); return(NULL); } inst = st->i_array[layer]; if (core_debug & DEBUG_QUEUE_FUNC) printk(KERN_DEBUG "%s: st(%08x) addr(%08x) -> inst(%08x)\n", __FUNCTION__, st->id, addr, inst ? inst->id : 0); return(inst); } mISDNinstance_t * get_instance(mISDNstack_t *st, int layer_nr, int protocol) { mISDNinstance_t *inst=NULL; int i; if (!st) { int_error(); return(NULL); } if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_instance st(%08x) lnr(%d) prot(%x)\n", st->id, layer_nr, protocol); if ((layer_nr < 0) || (layer_nr > MAX_LAYER_NR)) { int_errtxt("lnr %d", layer_nr); return(NULL); } list_for_each_entry(inst, &st->prereg, list) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_instance prereg(%p, %x) lm %x/%x prot %x/%x\n", inst, inst->id, inst->pid.layermask, ISDN_LAYER(layer_nr), inst->pid.protocol[layer_nr], protocol); if ((inst->pid.layermask & ISDN_LAYER(layer_nr)) && (inst->pid.protocol[layer_nr] == protocol)) { i = register_layer(st, inst); if (i) { int_errtxt("error(%d) register preregistered inst(%08x) on st(%08x)", i, inst->id, st->id); return(NULL); } return(inst); } } for (i = 0; i <= MAX_LAYER_NR; i++) { if ((inst = st->i_array[i])) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "get_instance inst%d(%p, %x) lm %x/%x prot %x/%x\n", i,inst, inst->id, inst->pid.layermask, ISDN_LAYER(layer_nr), inst->pid.protocol[layer_nr], protocol); if ((inst->pid.layermask & ISDN_LAYER(layer_nr)) && (inst->pid.protocol[layer_nr] == protocol)) return(inst); } } return(NULL); } mISDNinstance_t * get_instance4id(u_int id) { mISDNinstance_t *inst; read_lock(&instlist_lock); list_for_each_entry(inst, &mISDN_instlist, list) if (inst->id == id) { read_unlock(&instlist_lock); return(inst); } read_unlock(&instlist_lock); return(NULL); } #ifdef OBSOLETE int get_layermask(mISDNlayer_t *layer) { int mask = 0; if (layer->inst) mask |= layer->inst->pid.layermask; return(mask); } int insertlayer(mISDNstack_t *st, mISDNlayer_t *layer, int layermask) { mISDNlayer_t *item; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s(%p, %p, %x)\n", __FUNCTION__, st, layer, layermask); if (!st || !layer) { int_error(); return(-EINVAL); } if (list_empty(&st->layerlist)) { list_add(&layer->list, &st->layerlist); } else { list_for_each_entry(item, &st->layerlist, list) { if (layermask < get_layermask(item)) { list_add_tail(&layer->list, &item->list); return(0); } } list_add_tail(&layer->list, &st->layerlist); } return(0); } #endif inline void _queue_message(mISDNstack_t *st, struct sk_buff *skb) { skb_queue_tail(&st->msgq, skb); if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) { test_and_set_bit(mISDN_STACK_WORK, &st->status); wake_up_interruptible(&st->workq); } } int mISDN_queue_message(mISDNinstance_t *inst, u_int aflag, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); mISDNstack_t *st = inst->st; u_int id; if (core_debug & DEBUG_QUEUE_FUNC) printk(KERN_DEBUG "%s(%08x, %x, prim(%x))\n", __FUNCTION__, inst->id, aflag, hh->prim); if (aflag & FLG_MSG_TARGET) { id = aflag; } else { id = (inst->id & INST_ID_MASK) | aflag; } if ((aflag & MSG_DIR_MASK) == FLG_MSG_DOWN) { if (inst->parent) { inst = inst->parent; st = inst->st; id = (inst->id & INST_ID_MASK) | FLG_MSG_TARGET | FLG_MSG_CLONED | FLG_MSG_DOWN; } } if (!st) return(-EINVAL); if (st->id == 0 || test_bit(mISDN_STACK_ABORT, &st->status)) return(-EBUSY); if (inst->id == 0) { /* instance is not initialised */ if (!(aflag & FLG_MSG_TARGET)) { id &= INST_ID_MASK; id |= (st->id & INST_ID_MASK) | aflag | FLG_INSTANCE; } } if (test_bit(mISDN_STACK_KILLED, &st->status)) return(-EBUSY); if ((st->id & STACK_ID_MASK) != (id & STACK_ID_MASK)) { int_errtxt("stack id not match st(%08x) id(%08x) inst(%08x) aflag(%08x) prim(%x)", st->id, id, inst->id, aflag, hh->prim); } hh->addr = id; _queue_message(st, skb); return(0); } static void do_broadcast(mISDNstack_t *st, struct sk_buff *skb) { mISDN_head_t *hh = mISDN_HEAD_P(skb); mISDNinstance_t *inst = NULL; struct sk_buff *c_skb = NULL; int i, err; for(i=0; i<=MAX_LAYER_NR; i++) { if (i == (hh->addr & LAYER_ID_MASK)) continue; // skip own layer inst = st->i_array[i]; if (!inst) continue; // maybe we have a gap if (!c_skb) c_skb = skb_copy(skb, GFP_KERNEL); // we need a new private copy if (!c_skb) break; // stop here when copy not possible if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg call addr(%08x) prim(%x)\n", __FUNCTION__, inst->id, hh->addr, hh->prim); if (inst->function) { err = inst->function(inst, c_skb); if (!err) c_skb = NULL; /* function consumed the skb */ if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg call return %d\n", __FUNCTION__, inst->id, err); } else { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: instance(%08x) no function\n", __FUNCTION__, inst->id); } } if (c_skb) dev_kfree_skb(c_skb); dev_kfree_skb(skb); } static void release_layers(mISDNstack_t *st, u_int prim) { int i; for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i]) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p) inst%d(%p):%x %s lm(%x)\n", __FUNCTION__, st, i, st->i_array[i], st->i_array[i]->id, st->i_array[i]->name, st->i_array[i]->pid.layermask); st->i_array[i]->obj->own_ctrl(st->i_array[i], prim, NULL); } } } static void do_clear_stack(mISDNstack_t *st) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%08x)\n", __FUNCTION__, st->id); kfree(st->pid.pbuf); memset(&st->pid, 0, sizeof(mISDN_pid_t)); memset(&st->para, 0, sizeof(mISDN_stPara_t)); release_layers(st, MGR_UNREGLAYER | REQUEST); } static int mISDNStackd(void *data) { mISDNstack_t *st = data; int err = 0; #ifdef CONFIG_SMP lock_kernel(); #endif MAKEDAEMON("mISDNStackd"); sigfillset(¤t->blocked); st->thread = current; #ifdef CONFIG_SMP unlock_kernel(); #endif if ( core_debug & DEBUG_THREADS) printk(KERN_DEBUG "mISDNStackd started for id(%08x)\n", st->id); for (;;) { struct sk_buff *skb, *c_skb; mISDN_head_t *hh; if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { test_and_clear_bit(mISDN_STACK_WORK, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); } else test_and_set_bit(mISDN_STACK_RUNNING, &st->status); while (test_bit(mISDN_STACK_WORK, &st->status)) { mISDNinstance_t *inst; skb = skb_dequeue(&st->msgq); if (!skb) { test_and_clear_bit(mISDN_STACK_WORK, &st->status); /* test if a race happens */ if (!(skb = skb_dequeue(&st->msgq))) continue; test_and_set_bit(mISDN_STACK_WORK, &st->status); } #ifdef MISDN_MSG_STATS st->msg_cnt++; #endif hh = mISDN_HEAD_P(skb); if (hh->prim == (MGR_CLEARSTACK | REQUEST)) { mISDN_headext_t *hhe = (mISDN_headext_t *)hh; if (test_and_set_bit(mISDN_STACK_CLEARING, &st->status)) { int_errtxt("double clearing"); } if (hhe->data[0]) { if (st->notify) { int_errtxt("notify already set"); up(st->notify); } st->notify = hhe->data[0]; } dev_kfree_skb(skb); continue; } if ((hh->addr & MSG_DIR_MASK) == MSG_BROADCAST) { do_broadcast(st, skb); continue; } inst = get_nextlayer(st, hh->addr); if (!inst) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: st(%08x) no instance for addr(%08x) prim(%x) dinfo(%x)\n", __FUNCTION__, st->id, hh->addr, hh->prim, hh->dinfo); dev_kfree_skb(skb); continue; } if (inst->clone && ((hh->addr & MSG_DIR_MASK) == FLG_MSG_UP)) { u_int id = (inst->clone->id & INST_ID_MASK) | FLG_MSG_TARGET | FLG_MSG_CLONED | FLG_MSG_UP; #ifdef MISDN_MSG_STATS st->clone_cnt++; #endif c_skb = skb_copy(skb, GFP_KERNEL); if (c_skb) { if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg clone msg to(%08x) caddr(%08x) prim(%x)\n", __FUNCTION__, inst->id, inst->clone->id, id, hh->prim); err = mISDN_queue_message(inst->clone, id, c_skb); if (err) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: clone instance(%08x) cannot queue msg(%08x) err(%d)\n", __FUNCTION__, inst->clone->id, id, err); dev_kfree_skb(c_skb); } } else { printk(KERN_WARNING "%s OOM on msg cloning inst(%08x) caddr(%08x) prim(%x) len(%d)\n", __FUNCTION__, inst->id, id, hh->prim, skb->len); } } if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: inst(%08x) msg call addr(%08x) prim(%x)\n", __FUNCTION__, inst->id, hh->addr, hh->prim); if (!inst->function) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: instance(%08x) no function\n", __FUNCTION__, inst->id); dev_kfree_skb(skb); continue; } err = inst->function(inst, skb); if (err) { if (core_debug & DEBUG_MSG_THREAD_ERR) printk(KERN_DEBUG "%s: instance(%08x)->function return(%d)\n", __FUNCTION__, inst->id, err); dev_kfree_skb(skb); continue; } if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) { test_and_clear_bit(mISDN_STACK_WORK, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); break; } } if (test_bit(mISDN_STACK_CLEARING, &st->status)) { test_and_set_bit(mISDN_STACK_STOPPED, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); do_clear_stack(st); test_and_clear_bit(mISDN_STACK_CLEARING, &st->status); test_and_set_bit(mISDN_STACK_RESTART, &st->status); } if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) { test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); test_and_set_bit(mISDN_STACK_RUNNING, &st->status); if (!skb_queue_empty(&st->msgq)) test_and_set_bit(mISDN_STACK_WORK, &st->status); } if (test_bit(mISDN_STACK_ABORT, &st->status)) break; if (st->notify != NULL) { up(st->notify); st->notify = NULL; } #ifdef MISDN_MSG_STATS st->sleep_cnt++; #endif test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); wait_event_interruptible(st->workq, (st->status & mISDN_STACK_ACTION_MASK)); if (core_debug & DEBUG_MSG_THREAD_INFO) printk(KERN_DEBUG "%s: %08x wake status %08lx\n", __FUNCTION__, st->id, st->status); test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); if (test_bit(mISDN_STACK_STOPPED, &st->status)) { test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); #ifdef MISDN_MSG_STATS st->stopped_cnt++; #endif } } #ifdef MISDN_MSG_STATS printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) proceed %d msg %d clone %d sleep %d stopped\n", st->id, st->msg_cnt, st->clone_cnt, st->sleep_cnt, st->stopped_cnt); printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) utime(%ld) stime(%ld)\n", st->id, st->thread->utime, st->thread->stime); printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) nvcsw(%ld) nivcsw(%ld)\n", st->id, st->thread->nvcsw, st->thread->nivcsw); printk(KERN_DEBUG "mISDNStackd daemon for id(%08x) killed now\n", st->id); #endif test_and_set_bit(mISDN_STACK_KILLED, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status); test_and_clear_bit(mISDN_STACK_ABORT, &st->status); discard_queue(&st->msgq); st->thread = NULL; if (st->notify != NULL) { up(st->notify); st->notify = NULL; } return(0); } int mISDN_start_stack_thread(mISDNstack_t *st) { int err = 0; if (st->thread == NULL && test_bit(mISDN_STACK_KILLED, &st->status)) { test_and_clear_bit(mISDN_STACK_KILLED, &st->status); kernel_thread(mISDNStackd, (void *)st, 0); } else err = -EBUSY; return(err); } mISDNstack_t * new_stack(mISDNstack_t *master, mISDNinstance_t *inst) { mISDNstack_t *newst; int err; u_long flags; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "create %s stack inst(%p)\n", master ? "child" : "master", inst); if (!(newst = kmalloc(sizeof(mISDNstack_t), GFP_ATOMIC))) { printk(KERN_ERR "kmalloc mISDN_stack failed\n"); return(NULL); } memset(newst, 0, sizeof(mISDNstack_t)); INIT_LIST_HEAD(&newst->list); INIT_LIST_HEAD(&newst->childlist); INIT_LIST_HEAD(&newst->prereg); init_waitqueue_head(&newst->workq); skb_queue_head_init(&newst->msgq); if (!master) { if (inst && inst->st) { master = inst->st; while(master->clone) master = master->clone; newst->id = get_free_stackid(inst->st, FLG_CLONE_STACK); newst->master = master; master->clone = newst; master = NULL; } else { newst->id = get_free_stackid(NULL, 0); } } else { newst->id = get_free_stackid(master, FLG_CHILD_STACK); } newst->mgr = inst; if (master) { list_add_tail(&newst->list, &master->childlist); newst->parent = master; } else if (!(newst->id & FLG_CLONE_STACK)) { write_lock_irqsave(&stacklist_lock, flags); list_add_tail(&newst->list, &mISDN_stacklist); write_unlock_irqrestore(&stacklist_lock, flags); } if (inst) { inst->st = newst; } err = mISDN_register_sysfs_stack(newst); if (err) { // FIXME error handling printk(KERN_ERR "Stack id %x not registered in sysfs\n", newst->id); } if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "Stack id %x added\n", newst->id); kernel_thread(mISDNStackd, (void *)newst, 0); return(newst); } int mISDN_start_stop(mISDNstack_t *st, int start) { int ret; if (start) { ret = test_and_clear_bit(mISDN_STACK_STOPPED, &st->status); test_and_set_bit(mISDN_STACK_WAKEUP, &st->status); if (!skb_queue_empty(&st->msgq)) test_and_set_bit(mISDN_STACK_WORK, &st->status); wake_up_interruptible(&st->workq); } else ret = test_and_set_bit(mISDN_STACK_STOPPED, &st->status); return(ret); } int do_for_all_layers(void *data, u_int prim, void *arg) { mISDNstack_t *st = data; int i; if (!st) { int_error(); return(-EINVAL); } for (i = 0; i <= MAX_LAYER_NR; i++) { if (st->i_array[i]) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p) inst%d(%p):%x %s prim(%x) arg(%p)\n", __FUNCTION__, st, i, st->i_array[i], st->i_array[i]->id, st->i_array[i]->name, prim, arg); st->i_array[i]->obj->own_ctrl(st->i_array[i], prim, arg); } } return(0); } int change_stack_para(mISDNstack_t *st, u_int prim, mISDN_stPara_t *stpara) { int changed = 0; if (!st) { int_error(); return(-EINVAL); } if (prim == (MGR_ADDSTPARA | REQUEST)) { if (!stpara) { int_error(); return(-EINVAL); } prim = MGR_ADDSTPARA | INDICATION; if (stpara->maxdatalen > 0 && stpara->maxdatalen < st->para.maxdatalen) { changed++; st->para.maxdatalen = stpara->maxdatalen; } if (stpara->up_headerlen > st->para.up_headerlen) { changed++; st->para.up_headerlen = stpara->up_headerlen; } if (stpara->down_headerlen > st->para.down_headerlen) { changed++; st->para.down_headerlen = stpara->down_headerlen; } if (!changed) return(0); stpara = &st->para; } else if (prim == (MGR_CLRSTPARA | REQUEST)) { prim = MGR_CLRSTPARA | INDICATION; memset(&st->para, 0, sizeof(mISDN_stPara_t)); stpara = NULL; } return(do_for_all_layers(st, prim, stpara)); } static int delete_stack(mISDNstack_t *st) { DECLARE_MUTEX_LOCKED(sem); u_long flags; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p:%08x)\n", __FUNCTION__, st, st->id); mISDN_unregister_sysfs_st(st); if (st->parent) st->parent = NULL; if (!list_empty(&st->prereg)) { mISDNinstance_t *inst, *ni; int_errtxt("st(%08x)->prereg not empty\n", st->id); list_for_each_entry_safe(inst, ni, &st->prereg, list) { int_errtxt("inst(%p:%08x) preregistered", inst, inst->id); list_del(&inst->list); } } if (st->thread) { if (st->thread != current) { if (st->notify) { int_error(); up(st->notify); } st->notify = &sem; } test_and_set_bit(mISDN_STACK_ABORT, &st->status); mISDN_start_stop(st, 1); if (st->thread != current) /* we cannot wait for us */ down(&sem); } release_layers(st, MGR_RELEASE | INDICATION); write_lock_irqsave(&stacklist_lock, flags); list_del(&st->list); write_unlock_irqrestore(&stacklist_lock, flags); kfree(st); return(0); } int release_stack(mISDNstack_t *st) { int err; mISDNstack_t *cst, *nst; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st); list_for_each_entry_safe(cst, nst, &st->childlist, list) { if ((err = delete_stack(cst))) { return(err); } } if (st->clone) { st->clone->master = st->master; } if (st->master) { st->master->clone = st->clone; } else if (st->clone) { /* no master left -> delete clone too */ delete_stack(st->clone); st->clone = NULL; } if ((err = delete_stack(st))) return(err); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: mISDN_stacklist(%p<-%p->%p)\n", __FUNCTION__, mISDN_stacklist.prev, &mISDN_stacklist, mISDN_stacklist.next); return(0); } void cleanup_object(mISDNobject_t *obj) { mISDNstack_t *st, *nst; mISDNinstance_t *inst; int i; read_lock(&stacklist_lock); list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) { for (i = 0; i < MAX_LAYER_NR; i++) { inst = st->i_array[i]; if (inst && inst->obj == obj) { read_unlock(&stacklist_lock); inst->obj->own_ctrl(st, MGR_RELEASE | INDICATION, inst); read_lock(&stacklist_lock); } } } read_unlock(&stacklist_lock); } void check_stacklist(void) { mISDNstack_t *st, *nst; read_lock(&stacklist_lock); if (!list_empty(&mISDN_stacklist)) { printk(KERN_WARNING "mISDNcore mISDN_stacklist not empty\n"); list_for_each_entry_safe(st, nst, &mISDN_stacklist, list) { printk(KERN_WARNING "mISDNcore st %x still in list\n", st->id); if (list_empty(&st->list)) { printk(KERN_WARNING "mISDNcore st == next\n"); break; } } } read_unlock(&stacklist_lock); } void release_stacks(mISDNobject_t *obj) { mISDNstack_t *st, *tmp; int rel, i; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: obj(%p) %s\n", __FUNCTION__, obj, obj->name); read_lock(&stacklist_lock); list_for_each_entry_safe(st, tmp, &mISDN_stacklist, list) { rel = 0; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p)\n", __FUNCTION__, st); for (i = 0; i <= MAX_LAYER_NR; i++) { if (!st->i_array[i]) continue; if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: inst%d(%p)\n", __FUNCTION__, i, st->i_array[i]); if (st->i_array[i]->obj == obj) rel++; } if (rel) { read_unlock(&stacklist_lock); release_stack(st); read_lock(&stacklist_lock); } } read_unlock(&stacklist_lock); if (obj->refcnt) printk(KERN_WARNING "release_stacks obj %s refcnt is %d\n", obj->name, obj->refcnt); } #ifdef OBSOLETE static void get_free_instid(mISDNstack_t *st, mISDNinstance_t *inst) { mISDNinstance_t *il; inst->id = mISDN_get_lowlayer(inst->pid.layermask)<<20; inst->id |= FLG_INSTANCE; if (st) { inst->id |= st->id; } else { list_for_each_entry(il, &mISDN_instlist, list) { if (il->id == inst->id) { if ((inst->id & IF_INSTMASK) >= INST_ID_MAX) { inst->id = 0; return; } inst->id += LAYER_ID_INC; il = list_entry(mISDN_instlist.next, mISDNinstance_t, list); } } } } #endif int register_layer(mISDNstack_t *st, mISDNinstance_t *inst) { int idx, err; mISDNinstance_t *dup; u_long flags; if (!inst || !st) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s:st(%p) inst(%p/%p) lmask(%x) id(%x)\n", __FUNCTION__, st, inst, inst->obj, inst->pid.layermask, inst->id); if (inst->id) { /* already registered */ // if (inst->st || !st) { int_errtxt("register duplicate %08x %p %p", inst->id, inst->st, st); return(-EBUSY); //} } /* * To simplify registration we assume that our stacks are * always build with monoton increasing layernumbers from * bottom (HW,L0) to highest number */ // if (st) { for (idx = 0; idx <= MAX_LAYER_NR; idx++) if (!st->i_array[idx]) break; if (idx > MAX_LAYER_NR) { int_errtxt("stack %08x overflow", st->id); return(-EXFULL); } inst->regcnt++; st->i_array[idx] = inst; inst->id = st->id | FLG_INSTANCE | idx; dup = get_instance4id(inst->id); if (dup) { int_errtxt("register duplicate %08x i1(%p) i2(%p) i1->st(%p) i2->st(%p) st(%p)", inst->id, inst, dup, inst->st, dup->st, st); inst->regcnt--; st->i_array[idx] = NULL; inst->id = 0; return(-EBUSY); } // } if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: inst(%p/%p) id(%x)\n", __FUNCTION__, inst, inst->obj, inst->id); if (!list_empty(&inst->list)) { if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: register preregistered instance st(%p/%p)\n", __FUNCTION__, st, inst->st); list_del_init(&inst->list); } inst->st = st; write_lock_irqsave(&instlist_lock, flags); list_add_tail(&inst->list, &mISDN_instlist); write_unlock_irqrestore(&instlist_lock, flags); err = mISDN_register_sysfs_inst(inst); if (err) { // FIXME error handling printk(KERN_ERR "%s: register_sysfs failed %d st(%08x) inst(%08x)\n", __FUNCTION__, err, st->id, inst->id); } return(0); } int preregister_layer(mISDNstack_t *st, mISDNinstance_t *inst) { if (!inst || !st) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s:st(%08x) inst(%p:%08x) lmask(%x)\n", __FUNCTION__, st->id, inst, inst->id, inst->pid.layermask); if (inst->id) { /* already registered */ int_errtxt("register duplicate %08x %p %p", inst->id, inst->st, st); return(-EBUSY); } inst->st = st; list_add_tail(&inst->list, &st->prereg); return(0); } int unregister_instance(mISDNinstance_t *inst) { int i; u_long flags; if (!inst) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%p) inst(%p):%x lay(%x)\n", __FUNCTION__, inst->st, inst, inst->id, inst->pid.layermask); mISDN_unregister_sysfs_inst(inst); if (inst->st && inst->id) { i = inst->id & LAYER_ID_MASK; if (i > MAX_LAYER_NR) { int_errtxt("unregister %08x st(%08x) wrong layer", inst->id, inst->st->id); return(-EINVAL); } if (inst->st->i_array[i] == inst) { inst->regcnt--; inst->st->i_array[i] = NULL; } else if (inst->st->i_array[i]) { int_errtxt("unregister %08x st(%08x) wrong instance %08x", inst->id, inst->st->id, inst->st->i_array[i]->id); return(-EINVAL); } else printk(KERN_WARNING "unregister %08x st(%08x) not in stack", inst->id, inst->st->id); if (inst->st && (inst->st->mgr != inst)) inst->st = NULL; } if (inst->parent) { /*we are cloned */ inst->parent->clone = inst->clone; if (inst->clone) inst->clone->parent = inst->parent; inst->clone = NULL; inst->parent = NULL; } else if (inst->clone) { /* deleting the top level master of a clone */ /* FIXME: should be handled somehow, maybe unregister the clone */ int_errtxt("removed master(%08x) of clone(%08x)", inst->id, inst->clone->id); inst->clone->parent = NULL; inst->clone = NULL; } write_lock_irqsave(&instlist_lock, flags); if (inst->list.prev && inst->list.next) list_del_init(&inst->list); else int_errtxt("uninitialized list inst(%08x)", inst->id); inst->id = 0; write_unlock_irqrestore(&instlist_lock, flags); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: mISDN_instlist(%p<-%p->%p)\n", __FUNCTION__, mISDN_instlist.prev, &mISDN_instlist, mISDN_instlist.next); return(0); } int copy_pid(mISDN_pid_t *dpid, mISDN_pid_t *spid, u_char *pbuf) { memcpy(dpid, spid, sizeof(mISDN_pid_t)); if (spid->pbuf && spid->maxplen) { if (!pbuf) { int_error(); return(-ENOMEM); } dpid->pbuf = pbuf; memcpy(dpid->pbuf, spid->pbuf, spid->maxplen); } return(0); } int set_stack(mISDNstack_t *st, mISDN_pid_t *pid) { int err, i; u_char *pbuf = NULL; mISDNinstance_t *inst; // mISDNlayer_t *hl, *hln; if (!st || !pid) { int_error(); return(-EINVAL); } if (!st->mgr || !st->mgr->obj) { int_error(); return(-EINVAL); } if (pid->pbuf) pbuf = kmalloc(pid->maxplen, GFP_ATOMIC); err = copy_pid(&st->pid, pid, pbuf); if (err) return(err); memcpy(&st->mgr->pid, &st->pid, sizeof(mISDN_pid_t)); if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) { int_error(); return(-ENOPROTOOPT); } else { mISDN_RemoveUsedPID(pid, &st->mgr->pid); } err = mISDN_ctrl(st, MGR_REGLAYER | REQUEST, st->mgr); if (err) { int_error(); return(err); } while (pid->layermask) { inst = get_next_instance(st, pid); if (!inst) { int_error(); mISDN_ctrl(st, MGR_CLEARSTACK| REQUEST, (void *)1); return(-ENOPROTOOPT); } mISDN_RemoveUsedPID(pid, &inst->pid); } if (!list_empty(&st->prereg)) int_errtxt("st(%08x)->prereg not empty\n", st->id); for (i = 0; i <= MAX_LAYER_NR; i++) { inst = st->i_array[i]; if (!inst) break; if (!inst->obj) { int_error(); continue; } if (!inst->obj->own_ctrl) { int_error(); continue; } inst->obj->own_ctrl(inst, MGR_SETSTACK | INDICATION, NULL); } return(0); } int clear_stack(mISDNstack_t *st, int wait) { struct sk_buff *skb; mISDN_headext_t *hhe; if (!st) return(-EINVAL); if (core_debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%08x)\n", __FUNCTION__, st->id); if (!(skb = alloc_skb(8, GFP_ATOMIC))) return(-ENOMEM); hhe = mISDN_HEADEXT_P(skb); hhe->prim = MGR_CLEARSTACK | REQUEST; hhe->addr = st->id; if (wait) { DECLARE_MUTEX_LOCKED(sem); hhe->data[0] = &sem; _queue_message(st, skb); if (st->thread == current) {/* we cannot wait for us */ int_error(); return(-EBUSY); } down(&sem); } else { hhe->data[0] = NULL; _queue_message(st, skb); } return(0); } static int test_stack_protocol(mISDNstack_t *st, u_int l1prot, u_int l2prot, u_int l3prot) { int cnt = MAX_LAYER_NR + 1, ret = 1; mISDN_pid_t pid; mISDNinstance_t *inst; clear_stack(st,1); memset(&pid, 0, sizeof(mISDN_pid_t)); pid.layermask = ISDN_LAYER(1); if (!(((l2prot == 2) || (l2prot == 0x40)) && (l3prot == 1))) pid.layermask |= ISDN_LAYER(2); if (!(l3prot == 1)) pid.layermask |= ISDN_LAYER(3); pid.protocol[1] = l1prot | ISDN_PID_LAYER(1) | ISDN_PID_BCHANNEL_BIT; if (pid.layermask & ISDN_LAYER(2)) pid.protocol[2] = l2prot | ISDN_PID_LAYER(2) | ISDN_PID_BCHANNEL_BIT; if (pid.layermask & ISDN_LAYER(3)) pid.protocol[3] = l3prot | ISDN_PID_LAYER(3) | ISDN_PID_BCHANNEL_BIT; copy_pid(&st->pid, &pid, NULL); memcpy(&st->mgr->pid, &pid, sizeof(mISDN_pid_t)); if (!mISDN_SetHandledPID(st->mgr->obj, &st->mgr->pid)) { memset(&st->pid, 0, sizeof(mISDN_pid_t)); return(-ENOPROTOOPT); } else { mISDN_RemoveUsedPID(&pid, &st->mgr->pid); } if (!pid.layermask) { memset(&st->pid, 0, sizeof(mISDN_pid_t)); return(0); } ret = mISDN_ctrl(st, MGR_REGLAYER | REQUEST, st->mgr); if (ret) { clear_stack(st, 1); return(ret); } while (pid.layermask && cnt--) { inst = get_next_instance(st, &pid); if (!inst) { ret = -ENOPROTOOPT; break; } mISDN_RemoveUsedPID(&pid, &inst->pid); } if (!cnt) ret = -ENOPROTOOPT; clear_stack(st, 1); return(ret); } static u_int validL1pid4L2[ISDN_PID_IDX_MAX + 1] = { 0x022d, 0x03ff, 0x0000, 0x0000, 0x0010, 0x022d, 0x03ff, 0x0380, 0x022d, 0x022d, 0x022d, 0x01c6, 0x0000, }; static u_int validL2pid4L3[ISDN_PID_IDX_MAX + 1] = { 0x1fff, 0x0000, 0x0101, 0x0101, 0x0010, 0x0010, 0x0000, 0x00c0, 0x0000, }; int evaluate_stack_pids(mISDNstack_t *st, mISDN_pid_t *pid) { int err; mISDN_pid_t pidmask; u_int l1bitm, l2bitm, l3bitm; u_int l1idx, l2idx, l3idx; if (!st || !pid) { int_error(); return(-EINVAL); } if (!st->mgr || !st->mgr->obj) { int_error(); return(-EINVAL); } copy_pid(&pidmask, pid, NULL); memset(pid, 0, sizeof(mISDN_pid_t)); for (l1idx=0; l1idx <= ISDN_PID_IDX_MAX; l1idx++) { l1bitm = 1 << l1idx; if (!(pidmask.protocol[1] & l1bitm)) continue; for (l2idx=0; l2idx <= ISDN_PID_IDX_MAX; l2idx++) { l2bitm = 1 << l2idx; if (!(pidmask.protocol[2] & l2bitm)) continue; if (!(validL1pid4L2[l2idx] & l1bitm)) continue; for (l3idx=0; l3idx <= ISDN_PID_IDX_MAX; l3idx++) { err = 1; l3bitm = 1 << l3idx; if (!(pidmask.protocol[3] & l3bitm)) continue; if (!(validL2pid4L3[l3idx] & l2bitm)) continue; err = test_stack_protocol(st, l1bitm, l2bitm, l3bitm); if (!err) { pid->protocol[3] |= l3bitm; pid->protocol[2] |= l2bitm; pid->protocol[1] |= l1bitm; } } } } clear_stack(st, 1); return(0); } EXPORT_SYMBOL(mISDN_queue_message);