📄 capi.c
字号:
/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $ * * CAPI 2.0 Interface for Linux * * Copyright 1996 by Carsten Paeth <calle@calle.de> * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */#include <linux/config.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/fcntl.h>#include <linux/fs.h>#include <linux/signal.h>#include <linux/mm.h>#include <linux/smp_lock.h>#include <linux/timer.h>#include <linux/wait.h>#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE#include <linux/tty.h>#ifdef CONFIG_PPP#include <linux/netdevice.h>#include <linux/ppp_defs.h>#include <linux/if_ppp.h>#endif /* CONFIG_PPP */#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */#include <linux/skbuff.h>#include <linux/proc_fs.h>#include <linux/poll.h>#include <linux/capi.h>#include <linux/kernelcapi.h>#include <linux/init.h>#include <linux/device.h>#include <linux/moduleparam.h>#include <linux/devfs_fs_kernel.h>#include <linux/isdn/capiutil.h>#include <linux/isdn/capicmd.h>#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)#include "capifs.h"#endifstatic char *revision = "$Revision: 1.1.2.7 $";MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface");MODULE_AUTHOR("Carsten Paeth");MODULE_LICENSE("GPL");#undef _DEBUG_REFCOUNT /* alloc/free and open/close debug */#undef _DEBUG_TTYFUNCS /* call to tty_driver */#undef _DEBUG_DATAFLOW /* data flow *//* -------- driver information -------------------------------------- */static struct class *capi_class;static int capi_major = 68; /* allocated */#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE#define CAPINC_NR_PORTS 32#define CAPINC_MAX_PORTS 256static int capi_ttymajor = 191;static int capi_ttyminors = CAPINC_NR_PORTS;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */module_param_named(major, capi_major, uint, 0);#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREmodule_param_named(ttymajor, capi_ttymajor, uint, 0);module_param_named(ttyminors, capi_ttyminors, uint, 0);#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE *//* -------- defines ------------------------------------------------- */#define CAPINC_MAX_RECVQUEUE 10#define CAPINC_MAX_SENDQUEUE 10#define CAPI_MAX_BLKSIZE 2048/* -------- data structures ----------------------------------------- */struct capidev;struct capincci;#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREstruct capiminor;struct capiminor { struct list_head list; struct capincci *nccip; unsigned int minor; struct capi20_appl *ap; u32 ncci; u16 datahandle; u16 msgid; struct tty_struct *tty; int ttyinstop; int ttyoutstop; struct sk_buff *ttyskb; atomic_t ttyopencount; struct sk_buff_head inqueue; int inbytes; struct sk_buff_head outqueue; int outbytes; /* transmit path */ struct datahandle_queue { struct datahandle_queue *next; u16 datahandle; } *ackqueue; int nack;};#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */struct capincci { struct capincci *next; u32 ncci; struct capidev *cdev;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE struct capiminor *minorp;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */};struct capidev { struct list_head list; struct capi20_appl ap; u16 errcode; unsigned userflags; struct sk_buff_head recvqueue; wait_queue_head_t recvwait; struct capincci *nccis; struct semaphore ncci_list_sem;};/* -------- global variables ---------------------------------------- */static DEFINE_RWLOCK(capidev_list_lock);static LIST_HEAD(capidev_list);#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREstatic DEFINE_RWLOCK(capiminor_list_lock);static LIST_HEAD(capiminor_list);#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE/* -------- datahandles --------------------------------------------- */static int capincci_add_ack(struct capiminor *mp, u16 datahandle){ struct datahandle_queue *n, **pp; n = kmalloc(sizeof(*n), GFP_ATOMIC); if (!n) { printk(KERN_ERR "capi: alloc datahandle failed\n"); return -1; } n->next = NULL; n->datahandle = datahandle; for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) ; *pp = n; mp->nack++; return 0;}static int capiminor_del_ack(struct capiminor *mp, u16 datahandle){ struct datahandle_queue **pp, *p; for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) { if ((*pp)->datahandle == datahandle) { p = *pp; *pp = (*pp)->next; kfree(p); mp->nack--; return 0; } } return -1;}static void capiminor_del_all_ack(struct capiminor *mp){ struct datahandle_queue **pp, *p; pp = &mp->ackqueue; while (*pp) { p = *pp; *pp = (*pp)->next; kfree(p); mp->nack--; }}/* -------- struct capiminor ---------------------------------------- */static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci){ struct capiminor *mp, *p; unsigned int minor = 0; unsigned long flags; mp = kmalloc(sizeof(*mp), GFP_ATOMIC); if (!mp) { printk(KERN_ERR "capi: can't alloc capiminor\n"); return NULL; } memset(mp, 0, sizeof(struct capiminor)); mp->ap = ap; mp->ncci = ncci; mp->msgid = 0; atomic_set(&mp->ttyopencount,0); skb_queue_head_init(&mp->inqueue); skb_queue_head_init(&mp->outqueue); /* Allocate the least unused minor number. */ write_lock_irqsave(&capiminor_list_lock, flags); if (list_empty(&capiminor_list)) list_add(&mp->list, &capiminor_list); else { list_for_each_entry(p, &capiminor_list, list) { if (p->minor > minor) break; minor++; } if (minor < capi_ttyminors) { mp->minor = minor; list_add(&mp->list, p->list.prev); } } write_unlock_irqrestore(&capiminor_list_lock, flags); if (!(minor < capi_ttyminors)) { printk(KERN_NOTICE "capi: out of minors\n"); kfree(mp); return NULL; } return mp;}static void capiminor_free(struct capiminor *mp){ unsigned long flags; write_lock_irqsave(&capiminor_list_lock, flags); list_del(&mp->list); write_unlock_irqrestore(&capiminor_list_lock, flags); if (mp->ttyskb) kfree_skb(mp->ttyskb); mp->ttyskb = NULL; skb_queue_purge(&mp->inqueue); skb_queue_purge(&mp->outqueue); capiminor_del_all_ack(mp); kfree(mp);}static struct capiminor *capiminor_find(unsigned int minor){ struct list_head *l; struct capiminor *p = NULL; read_lock(&capiminor_list_lock); list_for_each(l, &capiminor_list) { p = list_entry(l, struct capiminor, list); if (p->minor == minor) break; } read_unlock(&capiminor_list_lock); if (l == &capiminor_list) return NULL; return p;}#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE *//* -------- struct capincci ----------------------------------------- */static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci){ struct capincci *np, **pp;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE struct capiminor *mp = NULL;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ np = kmalloc(sizeof(*np), GFP_ATOMIC); if (!np) return NULL; memset(np, 0, sizeof(struct capincci)); np->ncci = ncci; np->cdev = cdev;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE mp = NULL; if (cdev->userflags & CAPIFLAG_HIGHJACKING) mp = np->minorp = capiminor_alloc(&cdev->ap, ncci); if (mp) { mp->nccip = np;#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "set mp->nccip\n");#endif#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) capifs_new_ncci(mp->minor, MKDEV(capi_ttymajor, mp->minor));#endif }#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ for (pp=&cdev->nccis; *pp; pp = &(*pp)->next) ; *pp = np; return np;}static void capincci_free(struct capidev *cdev, u32 ncci){ struct capincci *np, **pp;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE struct capiminor *mp;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ pp=&cdev->nccis; while (*pp) { np = *pp; if (ncci == 0xffffffff || np->ncci == ncci) { *pp = (*pp)->next;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE if ((mp = np->minorp) != 0) {#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) capifs_free_ncci(mp->minor);#endif if (mp->tty) { mp->nccip = NULL;#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "reset mp->nccip\n");#endif tty_hangup(mp->tty); } else { capiminor_free(mp); } }#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ kfree(np); if (*pp == 0) return; } else { pp = &(*pp)->next; } }}static struct capincci *capincci_find(struct capidev *cdev, u32 ncci){ struct capincci *p; for (p=cdev->nccis; p ; p = p->next) { if (p->ncci == ncci) break; } return p;}/* -------- struct capidev ------------------------------------------ */static struct capidev *capidev_alloc(void){ struct capidev *cdev; unsigned long flags; cdev = kmalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return NULL; memset(cdev, 0, sizeof(struct capidev)); init_MUTEX(&cdev->ncci_list_sem); skb_queue_head_init(&cdev->recvqueue); init_waitqueue_head(&cdev->recvwait); write_lock_irqsave(&capidev_list_lock, flags); list_add_tail(&cdev->list, &capidev_list); write_unlock_irqrestore(&capidev_list_lock, flags); return cdev;}static void capidev_free(struct capidev *cdev){ unsigned long flags; if (cdev->ap.applid) { capi20_release(&cdev->ap); cdev->ap.applid = 0; } skb_queue_purge(&cdev->recvqueue); down(&cdev->ncci_list_sem); capincci_free(cdev, 0xffffffff); up(&cdev->ncci_list_sem); write_lock_irqsave(&capidev_list_lock, flags); list_del(&cdev->list); write_unlock_irqrestore(&capidev_list_lock, flags); kfree(cdev);}#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE/* -------- handle data queue --------------------------------------- */static struct sk_buff *gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb){ struct sk_buff *nskb; nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC); if (nskb) { u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2); unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN); capimsg_setu16(s, 2, mp->ap->applid); capimsg_setu8 (s, 4, CAPI_DATA_B3); capimsg_setu8 (s, 5, CAPI_RESP); capimsg_setu16(s, 6, mp->msgid++); capimsg_setu32(s, 8, mp->ncci); capimsg_setu16(s, 12, datahandle); } return nskb;}static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb){ struct sk_buff *nskb; int datalen; u16 errcode, datahandle; struct tty_ldisc *ld; datalen = skb->len - CAPIMSG_LEN(skb->data); if (mp->tty == NULL) {#ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi: currently no receiver\n");#endif return -1; } ld = tty_ldisc_ref(mp->tty); if (ld == NULL) return -1; if (ld->receive_buf == NULL) {#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n");#endif goto bad; } if (mp->ttyinstop) {#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: recv tty throttled\n");#endif goto bad; } if (ld->receive_room && ld->receive_room(mp->tty) < datalen) {#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: no room in tty\n");#endif goto bad; } if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) { printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); goto bad; } datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); errcode = capi20_put_message(mp->ap, nskb); if (errcode != CAPI_NOERROR) { printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", errcode); kfree_skb(nskb); goto bad; } (void)skb_pull(skb, CAPIMSG_LEN(skb->data));#ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", datahandle, skb->len);#endif ld->receive_buf(mp->tty, skb->data, NULL, skb->len); kfree_skb(skb); tty_ldisc_deref(ld); return 0;bad: tty_ldisc_deref(ld); return -1;}static void handle_minor_recv(struct capiminor *mp){ struct sk_buff *skb; while ((skb = skb_dequeue(&mp->inqueue)) != 0) { unsigned int len = skb->len; mp->inbytes -= len; if (handle_recv_skb(mp, skb) < 0) { skb_queue_head(&mp->inqueue, skb); mp->inbytes += len; return; } }}static int handle_minor_send(struct capiminor *mp){ struct sk_buff *skb; u16 len; int count = 0; u16 errcode; u16 datahandle;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -