/* * Copyright (C) 1996 Universidade de Lisboa * * Written by Pedro Roque Marques (roque@di.fc.ul.pt) * * This software may be used and distributed according to the terms of * the GNU Public License, incorporated herein by reference. */ /* * PCBIT-D interface with isdn4linux */ /* * Fixes: * * Nuno Grilo * fixed msn_list NULL pointer dereference. * */ #define __NO_VERSION__ #include #include #include #include #include #include #include #include #include #include #include #include #include "pcbit.h" #include "edss1.h" #include "layer2.h" #include "capi.h" extern ushort last_ref_num; static int pcbit_ioctl(isdn_ctrl* ctl); static char* pcbit_devname[MAX_PCBIT_CARDS] = { "pcbit0", "pcbit1", "pcbit2", "pcbit3" }; /* * prototypes */ int pcbit_command(isdn_ctrl* ctl); int pcbit_stat(u_char* buf, int len, int user, int, int); int pcbit_xmit(int driver, int chan, struct sk_buff *skb); int pcbit_writecmd(const u_char*, int, int, int, int); static int set_protocol_running(struct pcbit_dev * dev); static void pcbit_clear_msn(struct pcbit_dev *dev); static void pcbit_set_msn(struct pcbit_dev *dev, char *list); static int pcbit_check_msn(struct pcbit_dev *dev, char *msn); extern void pcbit_deliver(void * data); int pcbit_init_dev(int board, int mem_base, int irq) { struct pcbit_dev *dev; isdn_if *dev_if; if ((dev=kmalloc(sizeof(struct pcbit_dev), GFP_KERNEL)) == NULL) { printk("pcbit_init: couldn't malloc pcbit_dev struct\n"); return -ENOMEM; } dev_pcbit[board] = dev; memset(dev, 0, sizeof(struct pcbit_dev)); if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) dev->sh_mem = (unsigned char*) mem_base; else { printk("memory address invalid"); kfree(dev); dev_pcbit[board] = NULL; return -EACCES; } dev->b1 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL); if (!dev->b1) { printk("pcbit_init: couldn't malloc pcbit_chan struct\n"); kfree(dev); return -ENOMEM; } dev->b2 = kmalloc(sizeof(struct pcbit_chan), GFP_KERNEL); if (!dev->b2) { printk("pcbit_init: couldn't malloc pcbit_chan struct\n"); kfree(dev->b1); kfree(dev); return -ENOMEM; } memset(dev->b1, 0, sizeof(struct pcbit_chan)); memset(dev->b2, 0, sizeof(struct pcbit_chan)); dev->b2->id = 1; dev->qdelivery.next = NULL; dev->qdelivery.sync = 0; dev->qdelivery.routine = pcbit_deliver; dev->qdelivery.data = dev; /* * interrupts */ if (request_irq(irq, &pcbit_irq_handler, 0, pcbit_devname[board], dev) != 0) { kfree(dev->b1); kfree(dev->b2); kfree(dev); dev_pcbit[board] = NULL; return -EIO; } dev->irq = irq; /* next frame to be received */ dev->rcv_seq = 0; dev->send_seq = 0; dev->unack_seq = 0; dev->hl_hdrlen = 10; dev_if = kmalloc(sizeof(isdn_if), GFP_KERNEL); if (!dev_if) { free_irq(irq, dev); kfree(dev->b1); kfree(dev->b2); kfree(dev); dev_pcbit[board] = NULL; return -EIO; } dev->dev_if = dev_if; dev_if->channels = 2; dev_if->features = (ISDN_FEATURE_P_EURO | ISDN_FEATURE_L3_TRANS | ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS ); dev_if->writebuf_skb = pcbit_xmit; dev_if->writebuf = NULL; dev_if->hl_hdrlen = 10; dev_if->maxbufsize = MAXBUFSIZE; dev_if->command = pcbit_command; dev_if->writecmd = pcbit_writecmd; dev_if->readstat = pcbit_stat; strcpy(dev_if->id, pcbit_devname[board]); if (!register_isdn(dev_if)) { free_irq(irq, dev); kfree(dev->b1); kfree(dev->b2); kfree(dev); dev_pcbit[board] = NULL; return -EIO; } dev->id = dev_if->channels; dev->l2_state = L2_DOWN; dev->free = 511; /* * set_protocol_running(dev); */ return 0; } #ifdef MODULE void pcbit_terminate(int board) { struct pcbit_dev * dev; dev = dev_pcbit[board]; if (dev) { /* unregister_isdn(dev->dev_if); */ free_irq(dev->irq, dev); pcbit_clear_msn(dev); kfree(dev->dev_if); if (dev->b1->fsm_timer.function) del_timer(&dev->b1->fsm_timer); if (dev->b2->fsm_timer.function) del_timer(&dev->b2->fsm_timer); kfree(dev->b1); kfree(dev->b2); kfree(dev); } } #endif int pcbit_command(isdn_ctrl* ctl) { struct pcbit_dev *dev; struct pcbit_chan *chan; struct callb_data info; dev = finddev(ctl->driver); if (!dev) { printk("pcbit_command: unknown device\n"); return -1; } chan = (ctl->arg & 0x0F) ? dev->b2 : dev->b1; switch(ctl->command) { case ISDN_CMD_IOCTL: return pcbit_ioctl(ctl); break; case ISDN_CMD_DIAL: info.type = EV_USR_SETUP_REQ; info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone; pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info); break; case ISDN_CMD_ACCEPTD: pcbit_fsm_event(dev, chan, EV_USR_SETUP_RESP, NULL); break; case ISDN_CMD_ACCEPTB: printk("ISDN_CMD_ACCEPTB - not really needed\n"); break; case ISDN_CMD_HANGUP: pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL); break; case ISDN_CMD_SETL2: chan->proto = (ctl->arg >> 8); break; case ISDN_CMD_GETL2: return chan->proto; break; case ISDN_CMD_LOCK: MOD_INC_USE_COUNT; break; case ISDN_CMD_UNLOCK: MOD_DEC_USE_COUNT; break; case ISDN_CMD_CLREAZ: pcbit_clear_msn(dev); break; case ISDN_CMD_SETEAZ: pcbit_set_msn(dev, ctl->parm.num); break; case ISDN_CMD_SETL3: if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS) printk(KERN_DEBUG "L3 protocol unknown\n"); break; case ISDN_CMD_GETL3: return ISDN_PROTO_L3_TRANS; break; case ISDN_CMD_GETEAZ: case ISDN_CMD_SETSIL: case ISDN_CMD_GETSIL: printk(KERN_DEBUG "pcbit_command: code %d not implemented yet\n", ctl->command); break; default: printk(KERN_DEBUG "pcbit_command: unknown command\n"); break; }; return 0; } /* * Another Hack :-( * on some conditions the board stops sending TDATA_CONFs * let's see if we can turn around the problem */ #ifdef BLOCK_TIMER static void pcbit_block_timer(unsigned long data) { struct pcbit_chan *chan; struct pcbit_dev * dev; isdn_ctrl ictl; chan = (struct pcbit_chan *) data; dev = chan2dev(chan); if (dev == NULL) { printk(KERN_DEBUG "pcbit: chan2dev failed\n"); return; } del_timer(&chan->block_timer); chan->block_timer.function = NULL; #ifdef DEBUG printk(KERN_DEBUG "pcbit_block_timer\n"); #endif chan->queued = 0; ictl.driver = dev->id; ictl.command = ISDN_STAT_BSENT; ictl.arg = chan->id; dev->dev_if->statcallb(&ictl); } #endif int pcbit_xmit(int driver, int chnum, struct sk_buff *skb) { ushort hdrlen; int refnum, len; struct pcbit_chan * chan; struct pcbit_dev *dev; dev = finddev(driver); if (dev == NULL) { printk("finddev returned NULL"); return -1; } chan = chnum ? dev->b2 : dev->b1; if (chan->fsm_state != ST_ACTIVE) return -1; if (chan->queued >= MAX_QUEUED ) { #ifdef DEBUG_QUEUE printk(KERN_DEBUG "pcbit: %d packets already in queue - write fails\n", chan->queued); #endif /* * packet stays on the head of the device queue * since dev_start_xmit will fail * see net/core/dev.c */ #ifdef BLOCK_TIMER if (chan->block_timer.function == NULL) { init_timer(&chan->block_timer); chan->block_timer.function = &pcbit_block_timer; chan->block_timer.data = (long) chan; chan->block_timer.expires = jiffies + 1 * HZ; add_timer(&chan->block_timer); } #endif return 0; } chan->queued++; len = skb->len; hdrlen = capi_tdata_req(chan, skb); refnum = last_ref_num++ & 0x7fffU; chan->s_refnum = refnum; pcbit_l2_write(dev, MSG_TDATA_REQ, refnum, skb, hdrlen); return len; } int pcbit_writecmd(const u_char* buf, int len, int user, int driver, int channel) { struct pcbit_dev * dev; int i, j; const u_char * loadbuf; u_char * ptr = NULL; int errstat; dev = finddev(driver); if (!dev) { printk("pcbit_writecmd: couldn't find device"); return -ENODEV; } switch(dev->l2_state) { case L2_LWMODE: /* check (size <= rdp_size); write buf into board */ if (len > BANK4 + 1) { printk("pcbit_writecmd: invalid length %d\n", len); return -EFAULT; } if (user) { u_char cbuf[1024]; copy_from_user(cbuf, buf, len); for (i=0; ish_mem + i); } else memcpy_toio(dev->sh_mem, buf, len); return len; break; case L2_FWMODE: /* this is the hard part */ /* dumb board */ if (len < 0) return -EINVAL; if (user) { /* get it into kernel space */ if ((ptr = kmalloc(len, GFP_KERNEL))==NULL) return -ENOMEM; copy_from_user(ptr, buf, len); loadbuf = ptr; } else loadbuf = buf; errstat = 0; for (i=0; i < len; i++) { for(j=0; j < LOAD_RETRY; j++) if (!(readb(dev->sh_mem + dev->loadptr))) break; if (j == LOAD_RETRY) { errstat = -ETIME; printk("TIMEOUT i=%d\n", i); break; } writeb(loadbuf[i], dev->sh_mem + dev->loadptr + 1); writeb(0x01, dev->sh_mem + dev->loadptr); dev->loadptr += 2; if (dev->loadptr > LOAD_ZONE_END) dev->loadptr = LOAD_ZONE_START; } if (user) kfree(ptr); return errstat ? errstat : len; break; default: return -EBUSY; } return 0; } /* * demultiplexing of messages * */ void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, struct sk_buff * skb, ushort hdr_len, ushort refnum) { struct pcbit_chan *chan; struct sk_buff *skb2; unsigned short len; struct callb_data cbdata; int complete, err; isdn_ctrl ictl; #ifdef DEBUG struct msg_fmt * fmsg; #endif switch(msg) { case MSG_TDATA_IND: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } chan->r_refnum = skb->data[7]; skb_pull(skb, 8); dev->dev_if->rcvcallb_skb(dev->id, chan->id, skb); if (capi_tdata_resp(chan, &skb2) > 0) pcbit_l2_write(dev, MSG_TDATA_RESP, refnum, skb2, skb2->len); return; break; case MSG_TDATA_CONF: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } #ifdef DEBUG if ( (*((ushort *) (skb->data + 2) )) != 0) { printk(KERN_DEBUG "TDATA_CONF error\n"); } #endif #ifdef BLOCK_TIMER if (chan->queued == MAX_QUEUED) { del_timer(&chan->block_timer); chan->block_timer.function = NULL; } #endif chan->queued--; ictl.driver = dev->id; ictl.command = ISDN_STAT_BSENT; ictl.arg = chan->id; dev->dev_if->statcallb(&ictl); break; case MSG_CONN_IND: /* * channel: 1st not used will do * if both are used we're in trouble */ if (!dev->b1->fsm_state) chan = dev->b1; else if (!dev->b2->fsm_state) chan = dev->b2; else { printk(KERN_INFO "Incoming connection: no channels available"); if ((len = capi_disc_req(*(ushort*)(skb->data), &skb2, CAUSE_NOCHAN)) > 0) pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb2, len); break; } cbdata.data.setup.CalledPN = NULL; cbdata.data.setup.CallingPN = NULL; capi_decode_conn_ind(chan, skb, &cbdata); cbdata.type = EV_NET_SETUP; pcbit_fsm_event(dev, chan, EV_NET_SETUP, NULL); if (pcbit_check_msn(dev, cbdata.data.setup.CallingPN)) pcbit_fsm_event(dev, chan, EV_USR_PROCED_REQ, &cbdata); else pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL); if (cbdata.data.setup.CalledPN) kfree(cbdata.data.setup.CalledPN); if (cbdata.data.setup.CallingPN) kfree(cbdata.data.setup.CallingPN); break; case MSG_CONN_CONF: /* * We should be able to find the channel by the message * reference number. The current version of the firmware * doesn't sent the ref number correctly. */ #ifdef DEBUG printk(KERN_DEBUG "refnum=%04x b1=%04x b2=%04x\n", refnum, dev->b1->s_refnum, dev->b2->s_refnum); #endif #if 0 if (dev->b1->s_refnum == refnum) chan = dev->b1; else { if (dev->b2->s_refnum == refnum) chan = dev->b2; else { chan = NULL; printk(KERN_WARNING "Connection Confirm - refnum doesn't match chan\n"); break; } } #else /* We just try to find a channel in the right state */ if (dev->b1->fsm_state == ST_CALL_INIT) chan = dev->b1; else { if (dev->b2->s_refnum == ST_CALL_INIT) chan = dev->b2; else { chan = NULL; printk(KERN_WARNING "Connection Confirm - no channel in Call Init state\n"); break; } } #endif if (capi_decode_conn_conf(chan, skb, &complete)) { printk(KERN_DEBUG "conn_conf indicates error\n"); pcbit_fsm_event(dev, chan, EV_ERROR, NULL); } else if (complete) pcbit_fsm_event(dev, chan, EV_NET_CALL_PROC, NULL); else pcbit_fsm_event(dev, chan, EV_NET_SETUP_ACK, NULL); break; case MSG_CONN_ACTV_IND: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } if (capi_decode_conn_actv_ind(chan, skb)) { printk("error in capi_decode_conn_actv_ind\n"); /* pcbit_fsm_event(dev, chan, EV_ERROR, NULL); */ break; } chan->r_refnum = refnum; pcbit_fsm_event(dev, chan, EV_NET_CONN, NULL); break; case MSG_CONN_ACTV_CONF: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } if (capi_decode_conn_actv_conf(chan, skb) == 0) pcbit_fsm_event(dev, chan, EV_NET_CONN_ACK, NULL); else printk(KERN_DEBUG "decode_conn_actv_conf failed\n"); break; case MSG_SELP_CONF: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } if (!(err = capi_decode_sel_proto_conf(chan, skb))) pcbit_fsm_event(dev, chan, EV_NET_SELP_RESP, NULL); else { /* Error */ printk("error %d - capi_decode_sel_proto_conf\n", err); } break; case MSG_ACT_TRANSP_CONF: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } if (!capi_decode_actv_trans_conf(chan, skb)) pcbit_fsm_event(dev, chan, EV_NET_ACTV_RESP, NULL); break; case MSG_DISC_IND: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } if (!capi_decode_disc_ind(chan, skb)) pcbit_fsm_event(dev, chan, EV_NET_DISC, NULL); else printk(KERN_WARNING "capi_decode_disc_ind - error\n"); break; case MSG_DISC_CONF: if (!(chan = capi_channel(dev, skb))) { printk(KERN_WARNING "CAPI header: unknown channel id\n"); break; } if (!capi_decode_disc_ind(chan, skb)) pcbit_fsm_event(dev, chan, EV_NET_RELEASE, NULL); else printk(KERN_WARNING "capi_decode_disc_conf - error\n"); break; case MSG_INFO_IND: #ifdef DEBUG printk(KERN_DEBUG "received Info Indication - discarded\n"); #endif break; #ifdef DEBUG case MSG_DEBUG_188: capi_decode_debug_188(skb->data, skb->len); break; default: printk(KERN_DEBUG "pcbit_l3_receive: unknown message %08lx\n", msg); fmsg = (struct msg_fmt *) &msg; printk(KERN_DEBUG "cmd=%02x sub=%02x\n", fmsg->cmd, fmsg->scmd); break; #endif } SET_SKB_FREE(skb); kfree_skb(skb); } /* * Single statbuf * should be a statbuf per device */ static char statbuf[STATBUF_LEN]; static int stat_st = 0; static int stat_end = 0; static __inline void memcpy_to_COND(int flag, char *d, const char *s, int len) { if (flag) copy_to_user(d, s, len); else memcpy(d, s, len); } int pcbit_stat(u_char* buf, int len, int user, int driver, int channel) { int stat_count; stat_count = stat_end - stat_st; if (stat_count < 0) stat_count = STATBUF_LEN - stat_st + stat_end; /* FIXME: should we sleep and wait for more cookies ? */ if (len > stat_count) len = stat_count; if (stat_st < stat_end) { memcpy_to_COND(user, buf, statbuf + stat_st, len); stat_st += len; } else { if (len > STATBUF_LEN - stat_st) { memcpy_to_COND(user, buf, statbuf + stat_st, STATBUF_LEN - stat_st); memcpy_to_COND(user, buf, statbuf, len - (STATBUF_LEN - stat_st)); stat_st = len - (STATBUF_LEN - stat_st); } else { memcpy_to_COND(user, buf, statbuf + stat_st, len); stat_st += len; if (stat_st == STATBUF_LEN) stat_st = 0; } } if (stat_st == stat_end) stat_st = stat_end = 0; return len; } static void pcbit_logstat(struct pcbit_dev *dev, char *str) { int i; isdn_ctrl ictl; for (i=stat_end; iid; ictl.arg=strlen(str); dev->dev_if->statcallb(&ictl); } extern char * isdn_state_table[]; extern char * strisdnevent(unsigned short); void pcbit_state_change(struct pcbit_dev * dev, struct pcbit_chan * chan, unsigned short i, unsigned short ev, unsigned short f) { char buf[256]; sprintf(buf, "change on device: %d channel:%d\n%s -> %s -> %s\n", dev->id, chan->id, isdn_state_table[i], strisdnevent(ev), isdn_state_table[f] ); #ifdef DEBUG printk("%s", buf); #endif pcbit_logstat(dev, buf); } static void set_running_timeout(unsigned long ptr) { struct pcbit_dev * dev; #ifdef DEBUG printk(KERN_DEBUG "set_running_timeout\n"); #endif dev = (struct pcbit_dev *) ptr; wake_up_interruptible(&dev->set_running_wq); } static int set_protocol_running(struct pcbit_dev * dev) { isdn_ctrl ctl; init_timer(&dev->set_running_timer); dev->set_running_timer.function = &set_running_timeout; dev->set_running_timer.data = (ulong) dev; dev->set_running_timer.expires = jiffies + SET_RUN_TIMEOUT; /* kick it */ dev->l2_state = L2_STARTING; writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)), dev->sh_mem + BANK4); add_timer(&dev->set_running_timer); interruptible_sleep_on(&dev->set_running_wq); del_timer(&dev->set_running_timer); if (dev->l2_state == L2_RUNNING) { printk(KERN_DEBUG "pcbit: running\n"); dev->unack_seq = dev->send_seq; dev->writeptr = dev->sh_mem; dev->readptr = dev->sh_mem + BANK2; /* tell the good news to the upper layer */ ctl.driver = dev->id; ctl.command = ISDN_STAT_RUN; dev->dev_if->statcallb(&ctl); } else { printk(KERN_DEBUG "pcbit: initialization failed\n"); printk(KERN_DEBUG "pcbit: firmware not loaded\n"); dev->l2_state = L2_DOWN; #ifdef DEBUG printk(KERN_DEBUG "Bank3 = %02x\n", readb(dev->sh_mem + BANK3)); #endif *(dev->sh_mem + BANK4) = 0x40U; /* warn the upper layer */ ctl.driver = dev->id; ctl.command = ISDN_STAT_STOP; dev->dev_if->statcallb(&ctl); return -EL2HLT; /* Level 2 halted */ } return 0; } static int pcbit_ioctl(isdn_ctrl* ctl) { struct pcbit_dev * dev; struct pcbit_ioctl *cmd; dev = finddev(ctl->driver); if (!dev) { printk(KERN_DEBUG "pcbit_ioctl: unknown device\n"); return -ENODEV; } cmd = (struct pcbit_ioctl *) ctl->parm.num; switch(ctl->arg) { case PCBIT_IOCTL_GETSTAT: cmd->info.l2_status = dev->l2_state; break; case PCBIT_IOCTL_STRLOAD: if (dev->l2_state == L2_RUNNING) return -EBUSY; dev->unack_seq = dev->send_seq = dev->rcv_seq = 0; dev->writeptr = dev->sh_mem; dev->readptr = dev->sh_mem + BANK2; dev->l2_state = L2_LOADING; break; case PCBIT_IOCTL_LWMODE: if (dev->l2_state != L2_LOADING) return -EINVAL; dev->l2_state = L2_LWMODE; break; case PCBIT_IOCTL_FWMODE: if (dev->l2_state == L2_RUNNING) return -EBUSY; dev->loadptr = LOAD_ZONE_START; dev->l2_state = L2_FWMODE; break; case PCBIT_IOCTL_ENDLOAD: if (dev->l2_state == L2_RUNNING) return -EBUSY; dev->l2_state = L2_DOWN; break; case PCBIT_IOCTL_SETBYTE: if (dev->l2_state == L2_RUNNING) return -EBUSY; /* check addr */ if (cmd->info.rdp_byte.addr > BANK4) return -EFAULT; writeb(cmd->info.rdp_byte.value, dev->sh_mem + cmd->info.rdp_byte.addr); break; case PCBIT_IOCTL_GETBYTE: if (dev->l2_state == L2_RUNNING) return -EBUSY; /* check addr */ if (cmd->info.rdp_byte.addr > BANK4) { printk("getbyte: invalid addr %04x\n", cmd->info.rdp_byte.addr); return -EFAULT; } cmd->info.rdp_byte.value = readb(dev->sh_mem + cmd->info.rdp_byte.addr); break; case PCBIT_IOCTL_RUNNING: if (dev->l2_state == L2_RUNNING) return -EBUSY; return set_protocol_running(dev); break; case PCBIT_IOCTL_WATCH188: if (dev->l2_state != L2_LOADING) return -EINVAL; pcbit_l2_write(dev, MSG_WATCH188, 0x0001, NULL, 0); break; case PCBIT_IOCTL_PING188: if (dev->l2_state != L2_LOADING) return -EINVAL; pcbit_l2_write(dev, MSG_PING188_REQ, 0x0001, NULL, 0); break; case PCBIT_IOCTL_APION: if (dev->l2_state != L2_LOADING) return -EINVAL; pcbit_l2_write(dev, MSG_API_ON, 0x0001, NULL, 0); break; case PCBIT_IOCTL_STOP: dev->l2_state = L2_DOWN; writeb(0x40, dev->sh_mem + BANK4); dev->rcv_seq = 0; dev->send_seq = 0; dev->unack_seq = 0; break; default: printk("error: unknown ioctl\n"); break; }; return 0; } /* * MSN list handling * * if null reject all calls * if first entry has null MSN accept all calls */ static void pcbit_clear_msn(struct pcbit_dev *dev) { struct msn_entry *ptr, *back; for (ptr=dev->msn_list; ptr; ) { back = ptr->next; kfree(ptr); ptr = back; } dev->msn_list = NULL; } static void pcbit_set_msn(struct pcbit_dev *dev, char *list) { struct msn_entry *ptr; struct msn_entry *back = NULL; char *cp, *sp; int len; if (strlen(list) == 0) { ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC); if (!ptr) { printk(KERN_WARNING "kmalloc failed\n"); return; } ptr->msn = NULL; ptr->next = dev->msn_list; dev->msn_list = ptr; return; } if (dev->msn_list) for (back=dev->msn_list; back->next; back=back->next); sp = list; do { cp=strchr(sp, ','); if (cp) len = cp - sp; else len = strlen(sp); ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC); if (!ptr) { printk(KERN_WARNING "kmalloc failed\n"); return; } ptr->next = NULL; ptr->msn = kmalloc(len, GFP_ATOMIC); if (!ptr->msn) { printk(KERN_WARNING "kmalloc failed\n"); return; } memcpy(ptr->msn, sp, len - 1); ptr->msn[len] = 0; #ifdef DEBUG printk(KERN_DEBUG "msn: %s\n", ptr->msn); #endif if (dev->msn_list == NULL) dev->msn_list = ptr; else back->next = ptr; back = ptr; sp += len; } while(cp); } /* * check if we do signal or reject an incoming call */ static int pcbit_check_msn(struct pcbit_dev *dev, char *msn) { struct msn_entry *ptr; for (ptr=dev->msn_list; ptr; ptr=ptr->next) { if (ptr->msn == NULL) return 1; if (strcmp(ptr->msn, msn) == 0) return 1; } return 0; }