capi.c
来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 1,731 行 · 第 1/3 页
C
1,731 行
/* $Id: capi.c,v 1.1.4.1.2.2 2001/12/21 15:00:17 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>#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 <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.4.1.2.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_ttymajor = 191;#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */MODULE_PARM(capi_major, "i");#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREMODULE_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 list_head list; struct capincci *nccip; unsigned int minor; u16 applid; 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; u16 applid; u16 errcode; 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;static rwlock_t capidev_list_lock = RW_LOCK_UNLOCKED;static LIST_HEAD(capidev_list);#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREstatic rwlock_t capiminor_list_lock = RW_LOCK_UNLOCKED;static LIST_HEAD(capiminor_list);#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */static kmem_cache_t *capidev_cachep;static kmem_cache_t *capincci_cachep;#ifdef CONFIG_ISDN_CAPI_MIDDLEWAREstatic kmem_cache_t *capiminor_cachep;static kmem_cache_t *capidh_cachep;#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, *p; struct list_head *l; unsigned int minor = 0; unsigned long flags; MOD_INC_USE_COUNT; mp = 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); write_lock_irqsave(&capiminor_list_lock, flags); list_for_each(l, &capiminor_list) { p = list_entry(l, struct capiminor, list); if (p->minor > minor) { mp->minor = minor; list_add_tail(&mp->list, &p->list); break; } minor++; } write_unlock_irqrestore(&capiminor_list_lock, flags); if (l == &capiminor_list) { 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 = 0; 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}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 = 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 = mk_kdev(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 { 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(void){ struct capidev *cdev; unsigned long flags; cdev = kmem_cache_alloc(capidev_cachep, GFP_KERNEL); if (!cdev) return 0; memset(cdev, 0, sizeof(struct capidev)); 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->applid) (*capifuncs->capi_release) (cdev->applid); cdev->applid = 0; skb_queue_purge(&cdev->recvqueue); write_lock_irqsave(&capidev_list_lock, flags); list_del(&cdev->list); write_unlock_irqrestore(&capidev_list_lock, flags); kmem_cache_free(capidev_cachep, cdev);}static struct capidev *capidev_find(u16 applid){ // FIXME this doesn't guarantee that the device won't go away shortly struct list_head *l; struct capidev *p = NULL; read_lock(&capidev_list_lock); list_for_each(l, &capidev_list) { p = list_entry(l, struct capidev, list); if (p->applid == applid) break; } read_unlock(&capidev_list_lock); if (l == &capidev_list) return NULL; 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);#endif mp->tty->ldisc.receive_buf(mp->tty, skb->data, 0, skb->len); kfree_skb(skb); return 0; }#ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi: currently no receiver\n");#endif 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; if (mp->tty && mp->ttyoutstop) {#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: send: tty stopped\n");#endif return 0; } while ((skb = skb_dequeue(&mp->outqueue)) != 0) { datahandle = mp->datahandle; len = (u16)skb->len; skb_push(skb, CAPI_DATA_B3_REQ_LEN); memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); capimsg_setu16(skb->data, 2, mp->applid); capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); capimsg_setu8 (skb->data, 5, CAPI_REQ); capimsg_setu16(skb->data, 6, mp->msgid++); capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */ capimsg_setu16(skb->data, 16, len); /* Data length */ capimsg_setu16(skb->data, 18, datahandle); capimsg_setu16(skb->data, 20, 0); /* Flags */ if (capincci_add_ack(mp, datahandle) < 0) { skb_pull(skb, CAPI_DATA_B3_REQ_LEN); skb_queue_head(&mp->outqueue, skb); return count; } errcode = (*capifuncs->capi_put_message) (mp->applid, skb); if (errcode == CAPI_NOERROR) { mp->datahandle++; count++; mp->outbytes -= len;#ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n", datahandle, len);#endif continue; } capiminor_del_ack(mp, datahandle); if (errcode == CAPI_SENDQUEUEFULL) { skb_pull(skb, CAPI_DATA_B3_REQ_LEN); skb_queue_head(&mp->outqueue, skb); break;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?