📄 drv.c
字号:
/* * PCBIT-D interface with isdn4linux * * 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 General Public License, incorporated herein by reference. *//* * Fixes: * * Nuno Grilo <l38486@alfa.ist.utl.pt> * fixed msn_list NULL pointer dereference. * */#include <linux/module.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/string.h>#include <linux/skbuff.h>#include <linux/isdnif.h>#include <asm/string.h>#include <asm/io.h>#include <linux/ioport.h>#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 */static int pcbit_command(isdn_ctrl* ctl);static int pcbit_stat(u_char __user * buf, int len, int, int);static int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb);static int pcbit_writecmd(const u_char __user *, 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)); init_waitqueue_head(&dev->set_running_wq); spin_lock_init(&dev->lock); if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) { dev->ph_mem = mem_base; if (!request_mem_region(dev->ph_mem, 4096, "PCBIT mem")) { printk(KERN_WARNING "PCBIT: memory region %lx-%lx already in use\n", dev->ph_mem, dev->ph_mem + 4096); kfree(dev); dev_pcbit[board] = NULL; return -EACCES; } dev->sh_mem = ioremap(dev->ph_mem, 4096); } 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"); iounmap(dev->sh_mem); release_mem_region(dev->ph_mem, 4096); 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); iounmap(dev->sh_mem); release_mem_region(dev->ph_mem, 4096); kfree(dev); return -ENOMEM; } memset(dev->b1, 0, sizeof(struct pcbit_chan)); memset(dev->b2, 0, sizeof(struct pcbit_chan)); dev->b2->id = 1; INIT_WORK(&dev->qdelivery, pcbit_deliver, dev); /* * interrupts */ if (request_irq(irq, &pcbit_irq_handler, 0, pcbit_devname[board], dev) != 0) { kfree(dev->b1); kfree(dev->b2); iounmap(dev->sh_mem); release_mem_region(dev->ph_mem, 4096); 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 = 16; dev_if = kmalloc(sizeof(isdn_if), GFP_KERNEL); if (!dev_if) { free_irq(irq, dev); kfree(dev->b1); kfree(dev->b2); iounmap(dev->sh_mem); release_mem_region(dev->ph_mem, 4096); kfree(dev); dev_pcbit[board] = NULL; return -EIO; } dev->dev_if = dev_if; dev_if->owner = THIS_MODULE; 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->hl_hdrlen = 16; 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); iounmap(dev->sh_mem); release_mem_region(dev->ph_mem, 4096); 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 MODULEvoid 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); iounmap(dev->sh_mem); release_mem_region(dev->ph_mem, 4096); kfree(dev); }}#endifstatic 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_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; 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_TIMERstatic 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); }#endifstatic int pcbit_xmit(int driver, int chnum, int ack, 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;}static int pcbit_writecmd(const u_char __user *buf, int len, int driver, int channel){ struct pcbit_dev * dev; int i, j; const u_char * loadbuf; u_char * ptr = NULL; u_char *cbuf; 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 < 0 || len > BANK4 + 1 || len > 1024) { printk("pcbit_writecmd: invalid length %d\n", len); return -EINVAL; } cbuf = kmalloc(len, GFP_KERNEL); if (!cbuf) return -ENOMEM; if (copy_from_user(cbuf, buf, len)) { kfree(cbuf); return -EFAULT; } memcpy_toio(dev->sh_mem, cbuf, len); kfree(cbuf); return len; case L2_FWMODE: /* this is the hard part */ /* dumb board */ /* get it into kernel space */ if ((ptr = kmalloc(len, GFP_KERNEL))==NULL) return -ENOMEM; if (copy_from_user(ptr, buf, len)) { kfree(ptr); return -EFAULT; } loadbuf = ptr; 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; } kfree(ptr); return errstat ? errstat : len; default: return -EBUSY; }}/* * 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; 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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -