mISDN/drivers/isdn/hardware/mISDN/helper.c

485 lines
9.5 KiB
C

/* $Id$
*
* Author Karsten Keil (keil@isdn4linux.de)
*
* This file is (c) under GNU PUBLIC LICENSE
*
*/
#define __NO_VERSION__
#include <linux/hisaxif.h>
#include "helper.h"
#include "hisax_hw.h"
int
discard_queue(struct sk_buff_head *q)
{
struct sk_buff *skb;
int ret=0;
while ((skb = skb_dequeue(q))) {
dev_kfree_skb(skb);
ret++;
}
return(ret);
}
struct sk_buff *
alloc_uplink_skb(size_t size)
{
struct sk_buff *skb;
if (!(skb = alloc_skb(size + UPLINK_HEADER_SPACE, GFP_ATOMIC)))
printk(KERN_WARNING __FUNCTION__"(%d): no skb size\n",
size);
else
skb_reserve(skb, UPLINK_HEADER_SPACE);
return(skb);
}
int
init_dchannel(dchannel_t *dch) {
if (!(dch->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for dlog\n");
return(-ENOMEM);
}
if (!(dch->rx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for dchannel rx_buf\n");
kfree(dch->dlog);
dch->dlog = NULL;
return(-ENOMEM);
}
dch->rx_idx = 0;
if (!(dch->tx_buf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for dchannel tx_buf\n");
kfree(dch->dlog);
dch->dlog = NULL;
kfree(dch->rx_buf);
dch->rx_buf = NULL;
return(-ENOMEM);
}
dch->tx_idx = 0;
dch->next_skb = NULL;
dch->event = 0;
dch->tqueue.data = dch;
skb_queue_head_init(&dch->rqueue);
return(0);
}
int
free_dchannel(dchannel_t *dch) {
if (dch->tqueue.sync)
printk(KERN_ERR"free_dchannel tqueue.sync\n");
discard_queue(&dch->rqueue);
if (dch->rx_buf) {
kfree(dch->rx_buf);
dch->rx_buf = NULL;
}
if (dch->tx_buf) {
kfree(dch->tx_buf);
dch->tx_buf = NULL;
}
if (dch->next_skb) {
dev_kfree_skb(dch->next_skb);
dch->next_skb = NULL;
}
if (dch->dlog) {
kfree(dch->dlog);
dch->dlog = NULL;
}
return(0);
}
int
init_bchannel(bchannel_t *bch) {
int devtyp = HISAX_RAW_DEVICE;
if (!(bch->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for blog\n");
return(-ENOMEM);
}
if (!(bch->rx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for bchannel rx_buf\n");
kfree(bch->blog);
bch->blog = NULL;
return (-ENOMEM);
}
if (!(bch->tx_buf = kmalloc(MAX_DATA_MEM, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax: No memory for bchannel tx_buf\n");
kfree(bch->blog);
bch->blog = NULL;
kfree(bch->rx_buf);
bch->rx_buf = NULL;
return (-ENOMEM);
}
skb_queue_head_init(&bch->rqueue);
bch->next_skb = NULL;
bch->Flag = 0;
bch->event = 0;
bch->rx_idx = 0;
bch->tx_len = 0;
bch->tx_idx = 0;
bch->tqueue.data = bch;
if (!bch->dev) {
if (bch->inst.obj->ctrl(&bch->dev, MGR_GETDEVICE | REQUEST,
&devtyp)) {
printk(KERN_WARNING
"HiSax: no raw device for bchannel\n");
}
}
return(0);
}
int
free_bchannel(bchannel_t *bch) {
if (bch->tqueue.sync)
printk(KERN_ERR"free_bchannel tqueue.sync\n");
discard_queue(&bch->rqueue);
if (bch->blog) {
kfree(bch->blog);
bch->blog = NULL;
}
if (bch->rx_buf) {
kfree(bch->rx_buf);
bch->rx_buf = NULL;
}
if (bch->tx_buf) {
kfree(bch->tx_buf);
bch->tx_buf = NULL;
}
if (bch->next_skb) {
dev_kfree_skb(bch->next_skb);
bch->next_skb = NULL;
}
if (bch->inst.obj->ctrl(bch->dev, MGR_DELDEVICE | REQUEST, NULL)) {
printk(KERN_WARNING
"HiSax: del raw device error\n");
} else
bch->dev = NULL;
return(0);
}
int bprotocol2pid(void *bp, hisax_pid_t *pid) {
__u8 *p = bp;
__u16 *w = bp;
int i;
p += 6;
for (i=1; i<=3; i++) {
if (*w > 23) {
int_errtxt("L%d pid %x\n",i,*w);
return(-EINVAL);
}
pid->protocol[i] = (1 <<*w) | ISDN_PID_LAYER(i) |
ISDN_PID_BCHANNEL_BIT;
if (*p)
pid->param[i] = p;
else
pid->param[i] = NULL;
w++;
p += *p;
p++;
}
pid->global = 0;
if (*p == 2) { // len of 1 word
p++;
w = (__u16 *)p;
pid->global = *w;
}
return(0);
}
void
set_dchannel_pid(hisax_pid_t *pid, int protocol, int layermask) {
if (!layermask)
layermask = ISDN_LAYER(0)| ISDN_LAYER(1) | ISDN_LAYER(2) |
ISDN_LAYER(3) | ISDN_LAYER(4);
memset(pid, 0, sizeof(hisax_pid_t));
pid->layermask = layermask;
if (layermask & ISDN_LAYER(0))
pid->protocol[0] = ISDN_PID_L0_TE_S0;
if (layermask & ISDN_LAYER(1))
pid->protocol[1] = ISDN_PID_L1_TE_S0;
if (layermask & ISDN_LAYER(2))
pid->protocol[2] = ISDN_PID_L2_LAPD;
if (layermask & ISDN_LAYER(3)) {
if (protocol == 2)
pid->protocol[3] = ISDN_PID_L3_DSS1USER;
}
if (layermask & ISDN_LAYER(4))
pid->protocol[4] = ISDN_PID_L4_CAPI20;
}
int
HasProtocol(hisaxobject_t *obj, int protocol)
{
int layer;
int pmask;
if (!obj) {
int_error();
return(0);
}
layer = (protocol & ISDN_PID_LAYER_MASK)>>24;
if (layer > MAX_LAYER_NR) {
int_errtxt("layer %d", layer);
return(0);
}
if (protocol & ISDN_PID_BCHANNEL_BIT)
pmask = obj->BPROTO.protocol[layer];
else
pmask = obj->DPROTO.protocol[layer];
if (pmask == ISDN_PID_ANY)
return(0);
if (protocol == (protocol & pmask))
return(1);
else
return(0);
}
int
SetHandledPID(hisaxobject_t *obj, hisax_pid_t *pid)
{
int layer;
int ret = 0;
hisax_pid_t sav;
if (!obj || !pid) {
int_error();
return(0);
}
printk(KERN_DEBUG __FUNCTION__": %s LM(%x)\n", obj->name,
pid->layermask);
memcpy(&sav, pid, sizeof(hisax_pid_t));
memset(pid, 0, sizeof(hisax_pid_t));
pid->global = sav.global;
if (!sav.layermask) {
printk(KERN_WARNING __FUNCTION__": no layermask in pid\n");
return(0);
}
for (layer=0; layer<=MAX_LAYER_NR; layer++) {
if (!(ISDN_LAYER(layer) & sav.layermask)) {
if (ret)
break;
else
continue;
}
if (HasProtocol(obj, sav.protocol[layer])) {
ret++;
pid->protocol[layer] = sav.protocol[layer];
pid->param[layer] = sav.param[layer];
pid->layermask |= ISDN_LAYER(layer);
} else
break;
}
return(ret);
}
void
RemoveUsedPID(hisax_pid_t *pid, hisax_pid_t *used)
{
int layer;
if (!used || !pid) {
int_error();
return;
}
if (!used->layermask)
return;
for (layer=0; layer<=MAX_LAYER_NR; layer++) {
if (!(ISDN_LAYER(layer) & used->layermask))
continue;
pid->protocol[layer] = ISDN_PID_NONE;
pid->param[layer] = NULL;
pid->layermask &= ~(ISDN_LAYER(layer));
}
}
int
layermask2layer(int layermask) {
switch(layermask) {
case ISDN_LAYER(0): return(0);
case ISDN_LAYER(1): return(1);
case ISDN_LAYER(2): return(2);
case ISDN_LAYER(3): return(3);
case ISDN_LAYER(4): return(4);
case ISDN_LAYER(5): return(5);
case ISDN_LAYER(6): return(6);
case ISDN_LAYER(7): return(7);
case 0: return(-1);
}
return(-2);
}
int
get_protocol(hisaxstack_t *st, int layer)
{
if (!st){
int_error();
return(-EINVAL);
}
if (LAYER_OUTRANGE(layer)) {
int_errtxt("L%d st(%x)", layer, st->id);
return(-EINVAL);
}
return(st->pid.protocol[layer]);
}
int
get_lowlayer(int layermask)
{
int layer;
if (!layermask)
return(0);
for (layer=0; layer <= MAX_LAYER_NR; layer++)
if (layermask & ISDN_LAYER(layer))
return(layer);
return(0);
}
int
get_down_layer(int layermask)
{
int downlayer = 1;
if (layermask>255 || (layermask & 1)) {
int_errtxt("lmask %x out of range", layermask);
return(0);
}
while(downlayer <= MAX_LAYER_NR) {
if (ISDN_LAYER(downlayer) & layermask)
break;
downlayer++;
}
downlayer--;
return(downlayer);
}
int get_up_layer(int layermask) {
int uplayer = MAX_LAYER_NR;
if (layermask>=128) {
int_errtxt("lmask %x out of range", layermask);
return(0);
}
while(uplayer>=0) {
if (ISDN_LAYER(uplayer) & layermask)
break;
uplayer--;
}
uplayer++;
return(uplayer);
}
int
SetIF(hisaxinstance_t *owner, hisaxif_t *hif, u_int prim, void *upfunc,
void *downfunc, void *data)
{
hisaxif_t *own_hif;
if (!owner) {
int_error();
return(-EINVAL);
}
if (!hif) {
int_error();
return(-EINVAL);
}
if (IF_TYPE(hif) == IF_UP) {
hif->func = upfunc;
own_hif = &owner->up;
printk(KERN_DEBUG __FUNCTION__": IF_UP: func:%p(%p)\n",
hif->func, owner->data);
} else if (IF_TYPE(hif) == IF_DOWN) {
hif->func = downfunc;
own_hif = &owner->down;
printk(KERN_DEBUG __FUNCTION__": IF_DOWN: func:%p(%p)\n",
hif->func, owner->data);
} else {
int_errtxt("stat(%x)", hif->stat);
return(-EINVAL);
}
hif->peer = owner;
hif->fdata = data;
if ((prim & SUBCOMMAND_MASK) == REQUEST) {
if (own_hif->stat == IF_NOACTIV) {
if (IF_TYPE(hif) == IF_UP)
own_hif->stat = IF_DOWN;
else
own_hif->stat = IF_UP;
own_hif->owner = owner;
return(hif->owner->obj->own_ctrl(hif->owner,
MGR_SETIF | INDICATION, own_hif));
} else {
int_errtxt("REQ own stat(%x)", own_hif->stat);
return(-EBUSY);
}
}
return(0);
}
int
ConnectIF(hisaxinstance_t *owner, hisaxinstance_t *peer)
{
hisaxif_t *hif;
if (!owner) {
int_error();
return(-EINVAL);
}
if (!peer) {
int_error();
return(-EINVAL);
}
if (owner->pid.layermask < peer->pid.layermask) {
hif = &owner->up;
hif->owner = owner;
hif->stat = IF_DOWN;
} else if (owner->pid.layermask > peer->pid.layermask) {
hif = &owner->down;
hif->owner = owner;
hif->stat = IF_UP;
} else {
int_errtxt("OLM == PLM: %x", owner->pid.layermask);
return(-EINVAL);
}
return(peer->obj->own_ctrl(peer, MGR_SETIF | REQUEST, hif));
}
int DisConnectIF(hisaxinstance_t *inst, hisaxif_t *hif) {
if (hif) {
if (hif->next && hif->next->owner) {
hif->next->owner->obj->ctrl(hif->next->owner,
MGR_DISCONNECT | REQUEST, hif->next);
}
if (inst->up.peer) {
if (inst->up.peer == hif->owner)
inst->up.peer->obj->ctrl(inst->up.peer,
MGR_DISCONNECT | INDICATION, &inst->up);
}
if (inst->down.peer) {
if (inst->down.peer == hif->owner)
inst->down.peer->obj->ctrl(inst->down.peer,
MGR_DISCONNECT | INDICATION, &inst->down);
}
}
return(0);
}