⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 capi.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* $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 + -