📄 capi.c
字号:
/* $Id: capi.c,v 1.1.4.2 2001/12/09 18:45:13 kai 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>#undef CAPI_PPP_ON_RAW_DEVICE#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/devfs_fs_kernel.h>#include "capiutil.h"#include "capicmd.h"#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)#include "capifs.h"#endifstatic char *revision = "$Revision: 1.1.4.2 $";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 -------------------------------------- */int capi_major = 68; /* allocated */#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREint capi_rawmajor = 190;int capi_ttymajor = 191;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */MODULE_PARM(capi_major, "i");#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREMODULE_PARM(capi_rawmajor, "i");MODULE_PARM(capi_ttymajor, "i");#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 capiminor *next; struct capincci *nccip; unsigned int minor; u16 applid; u32 ncci; u16 datahandle; u16 msgid; struct file *file; 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; /* for raw device */ struct sk_buff_head recvqueue; wait_queue_head_t recvwait; wait_queue_head_t sendwait; /* 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 capidev *next; struct file *file; u16 applid; u16 errcode; unsigned int minor; unsigned userflags; struct sk_buff_head recvqueue; wait_queue_head_t recvwait; /* Statistic */ unsigned long nrecvctlpkt; unsigned long nrecvdatapkt; unsigned long nsentctlpkt; unsigned long nsentdatapkt; struct capincci *nccis;};/* -------- global variables ---------------------------------------- */static struct capi_interface *capifuncs = 0;static struct capidev *capidev_openlist = 0;#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREstatic struct capiminor *minors = 0;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */static kmem_cache_t *capidev_cachep = 0;static kmem_cache_t *capincci_cachep = 0;#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREstatic kmem_cache_t *capiminor_cachep = 0;static kmem_cache_t *capidh_cachep = 0;#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 = (struct datahandle_queue *) kmem_cache_alloc(capidh_cachep, GFP_ATOMIC); if (!n) { printk(KERN_ERR "capi: alloc datahandle failed\n"); return -1; } n->next = 0; 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; kmem_cache_free(capidh_cachep, 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; kmem_cache_free(capidh_cachep, p); mp->nack--; }}/* -------- struct capiminor ---------------------------------------- */static struct capiminor *capiminor_alloc(u16 applid, u32 ncci){ struct capiminor *mp, **pp; unsigned int minor = 0; MOD_INC_USE_COUNT; mp = (struct capiminor *)kmem_cache_alloc(capiminor_cachep, GFP_ATOMIC); if (!mp) { MOD_DEC_USE_COUNT; printk(KERN_ERR "capi: can't alloc capiminor\n"); return 0; }#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "capiminor_alloc %d\n", GET_USE_COUNT(THIS_MODULE));#endif memset(mp, 0, sizeof(struct capiminor)); mp->applid = applid; mp->ncci = ncci; mp->msgid = 0; atomic_set(&mp->ttyopencount,0); skb_queue_head_init(&mp->inqueue); skb_queue_head_init(&mp->outqueue); skb_queue_head_init(&mp->recvqueue); init_waitqueue_head(&mp->recvwait); init_waitqueue_head(&mp->sendwait); for (pp = &minors; *pp; pp = &(*pp)->next) { if ((*pp)->minor < minor) continue; if ((*pp)->minor > minor) break; minor++; } mp->minor = minor; mp->next = *pp; *pp = mp; return mp;}static void capiminor_free(struct capiminor *mp){ struct capiminor **pp; pp = &minors; while (*pp) { if (*pp == mp) { *pp = (*pp)->next; if (mp->ttyskb) kfree_skb(mp->ttyskb); mp->ttyskb = 0; skb_queue_purge(&mp->recvqueue); skb_queue_purge(&mp->inqueue); skb_queue_purge(&mp->outqueue); capiminor_del_all_ack(mp); kmem_cache_free(capiminor_cachep, mp); MOD_DEC_USE_COUNT;#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "capiminor_free %d\n", GET_USE_COUNT(THIS_MODULE));#endif return; } else { pp = &(*pp)->next; } }}static struct capiminor *capiminor_find(unsigned int minor){ struct capiminor *p; for (p = minors; p && p->minor != minor; p = p->next) ; 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 = 0; kdev_t kdev;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ np = (struct capincci *)kmem_cache_alloc(capincci_cachep, GFP_ATOMIC); if (!np) return 0; memset(np, 0, sizeof(struct capincci)); np->ncci = ncci; np->cdev = cdev;#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE mp = 0; if (cdev->userflags & CAPIFLAG_HIGHJACKING) mp = np->minorp = capiminor_alloc(cdev->applid, 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) kdev = MKDEV(capi_rawmajor, mp->minor); capifs_new_ncci('r', mp->minor, kdev); kdev = MKDEV(capi_ttymajor, mp->minor); capifs_new_ncci(0, mp->minor, kdev);#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('r', mp->minor); capifs_free_ncci(0, mp->minor);#endif if (mp->tty) { mp->nccip = 0;#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "reset mp->nccip\n");#endif tty_hangup(mp->tty); } else if (mp->file) { mp->nccip = 0;#ifdef _DEBUG_REFCOUNT printk(KERN_DEBUG "reset mp->nccip\n");#endif wake_up_interruptible(&mp->recvwait); wake_up_interruptible(&mp->sendwait); } else { capiminor_free(mp); } }#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ kmem_cache_free(capincci_cachep, 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(struct file *file){ struct capidev *cdev; struct capidev **pp; cdev = (struct capidev *)kmem_cache_alloc(capidev_cachep, GFP_KERNEL); if (!cdev) return 0; memset(cdev, 0, sizeof(struct capidev)); cdev->file = file; cdev->minor = MINOR(file->f_dentry->d_inode->i_rdev); skb_queue_head_init(&cdev->recvqueue); init_waitqueue_head(&cdev->recvwait); pp=&capidev_openlist; while (*pp) pp = &(*pp)->next; *pp = cdev; return cdev;}static void capidev_free(struct capidev *cdev){ struct capidev **pp; if (cdev->applid) (*capifuncs->capi_release) (cdev->applid); cdev->applid = 0; skb_queue_purge(&cdev->recvqueue); pp=&capidev_openlist; while (*pp && *pp != cdev) pp = &(*pp)->next; if (*pp) *pp = cdev->next; kmem_cache_free(capidev_cachep, cdev);}static struct capidev *capidev_find(u16 applid){ struct capidev *p; for (p=capidev_openlist; p; p = p->next) { if (p->applid == applid) break; } return p;}#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->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; unsigned int datalen; u16 errcode, datahandle; datalen = skb->len - CAPIMSG_LEN(skb->data); if (mp->tty) { if (mp->tty->ldisc.receive_buf == 0) { printk(KERN_ERR "capi: ldisc has no receive_buf function\n"); return -1; } if (mp->ttyinstop) {#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: recv tty throttled\n");#endif return -1; } if (mp->tty->ldisc.receive_room && mp->tty->ldisc.receive_room(mp->tty) < datalen) {#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: no room in tty\n");#endif return -1; } if ((nskb = gen_data_b3_resp_for(mp, skb)) == 0) { printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); return -1; } datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); errcode = (*capifuncs->capi_put_message)(mp->applid, nskb); if (errcode != CAPI_NOERROR) { printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", errcode); kfree_skb(nskb); return -1; } (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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -