18c2ecf20Sopenharmony_ci/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * CAPI 2.0 Interface for Linux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 1996 by Carsten Paeth <calle@calle.de>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * This software may be used and distributed according to the terms
88c2ecf20Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/compiler.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/major.h>
178c2ecf20Sopenharmony_ci#include <linux/sched.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
208c2ecf20Sopenharmony_ci#include <linux/fs.h>
218c2ecf20Sopenharmony_ci#include <linux/signal.h>
228c2ecf20Sopenharmony_ci#include <linux/mutex.h>
238c2ecf20Sopenharmony_ci#include <linux/mm.h>
248c2ecf20Sopenharmony_ci#include <linux/timer.h>
258c2ecf20Sopenharmony_ci#include <linux/wait.h>
268c2ecf20Sopenharmony_ci#include <linux/tty.h>
278c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
288c2ecf20Sopenharmony_ci#include <linux/ppp_defs.h>
298c2ecf20Sopenharmony_ci#include <linux/ppp-ioctl.h>
308c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
318c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
328c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
338c2ecf20Sopenharmony_ci#include <linux/poll.h>
348c2ecf20Sopenharmony_ci#include <linux/capi.h>
358c2ecf20Sopenharmony_ci#include <linux/kernelcapi.h>
368c2ecf20Sopenharmony_ci#include <linux/init.h>
378c2ecf20Sopenharmony_ci#include <linux/device.h>
388c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
398c2ecf20Sopenharmony_ci#include <linux/isdn/capiutil.h>
408c2ecf20Sopenharmony_ci#include <linux/isdn/capicmd.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include "kcapi.h"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer and /dev/capi20 interface");
458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Carsten Paeth");
468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* -------- driver information -------------------------------------- */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(capi_mutex);
518c2ecf20Sopenharmony_cistatic struct class *capi_class;
528c2ecf20Sopenharmony_cistatic int capi_major = 68;		/* allocated */
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cimodule_param_named(major, capi_major, uint, 0);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
578c2ecf20Sopenharmony_ci#define CAPINC_NR_PORTS		32
588c2ecf20Sopenharmony_ci#define CAPINC_MAX_PORTS	256
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int capi_ttyminors = CAPINC_NR_PORTS;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cimodule_param_named(ttyminors, capi_ttyminors, uint, 0);
638c2ecf20Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* -------- defines ------------------------------------------------- */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define CAPINC_MAX_RECVQUEUE	10
688c2ecf20Sopenharmony_ci#define CAPINC_MAX_SENDQUEUE	10
698c2ecf20Sopenharmony_ci#define CAPI_MAX_BLKSIZE	2048
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* -------- data structures ----------------------------------------- */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistruct capidev;
748c2ecf20Sopenharmony_cistruct capincci;
758c2ecf20Sopenharmony_cistruct capiminor;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistruct ackqueue_entry {
788c2ecf20Sopenharmony_ci	struct list_head	list;
798c2ecf20Sopenharmony_ci	u16			datahandle;
808c2ecf20Sopenharmony_ci};
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistruct capiminor {
838c2ecf20Sopenharmony_ci	unsigned int      minor;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	struct capi20_appl	*ap;
868c2ecf20Sopenharmony_ci	u32			ncci;
878c2ecf20Sopenharmony_ci	atomic_t		datahandle;
888c2ecf20Sopenharmony_ci	atomic_t		msgid;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	struct tty_port port;
918c2ecf20Sopenharmony_ci	int                ttyinstop;
928c2ecf20Sopenharmony_ci	int                ttyoutstop;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	struct sk_buff_head	inqueue;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	struct sk_buff_head	outqueue;
978c2ecf20Sopenharmony_ci	int			outbytes;
988c2ecf20Sopenharmony_ci	struct sk_buff		*outskb;
998c2ecf20Sopenharmony_ci	spinlock_t		outlock;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* transmit path */
1028c2ecf20Sopenharmony_ci	struct list_head ackqueue;
1038c2ecf20Sopenharmony_ci	int nack;
1048c2ecf20Sopenharmony_ci	spinlock_t ackqlock;
1058c2ecf20Sopenharmony_ci};
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistruct capincci {
1088c2ecf20Sopenharmony_ci	struct list_head list;
1098c2ecf20Sopenharmony_ci	u32		 ncci;
1108c2ecf20Sopenharmony_ci	struct capidev	*cdev;
1118c2ecf20Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
1128c2ecf20Sopenharmony_ci	struct capiminor *minorp;
1138c2ecf20Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistruct capidev {
1178c2ecf20Sopenharmony_ci	struct list_head list;
1188c2ecf20Sopenharmony_ci	struct capi20_appl ap;
1198c2ecf20Sopenharmony_ci	u16		errcode;
1208c2ecf20Sopenharmony_ci	unsigned        userflags;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	struct sk_buff_head recvqueue;
1238c2ecf20Sopenharmony_ci	wait_queue_head_t recvwait;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	struct list_head nccis;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	struct mutex lock;
1288c2ecf20Sopenharmony_ci};
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* -------- global variables ---------------------------------------- */
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(capidev_list_lock);
1338c2ecf20Sopenharmony_cistatic LIST_HEAD(capidev_list);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(capiminors_lock);
1388c2ecf20Sopenharmony_cistatic struct capiminor **capiminors;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic struct tty_driver *capinc_tty_driver;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/* -------- datahandles --------------------------------------------- */
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int capiminor_add_ack(struct capiminor *mp, u16 datahandle)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct ackqueue_entry *n;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	n = kmalloc(sizeof(*n), GFP_ATOMIC);
1498c2ecf20Sopenharmony_ci	if (unlikely(!n)) {
1508c2ecf20Sopenharmony_ci		printk(KERN_ERR "capi: alloc datahandle failed\n");
1518c2ecf20Sopenharmony_ci		return -1;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	n->datahandle = datahandle;
1548c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&n->list);
1558c2ecf20Sopenharmony_ci	spin_lock_bh(&mp->ackqlock);
1568c2ecf20Sopenharmony_ci	list_add_tail(&n->list, &mp->ackqueue);
1578c2ecf20Sopenharmony_ci	mp->nack++;
1588c2ecf20Sopenharmony_ci	spin_unlock_bh(&mp->ackqlock);
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct ackqueue_entry *p, *tmp;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	spin_lock_bh(&mp->ackqlock);
1678c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
1688c2ecf20Sopenharmony_ci		if (p->datahandle == datahandle) {
1698c2ecf20Sopenharmony_ci			list_del(&p->list);
1708c2ecf20Sopenharmony_ci			mp->nack--;
1718c2ecf20Sopenharmony_ci			spin_unlock_bh(&mp->ackqlock);
1728c2ecf20Sopenharmony_ci			kfree(p);
1738c2ecf20Sopenharmony_ci			return 0;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci	spin_unlock_bh(&mp->ackqlock);
1778c2ecf20Sopenharmony_ci	return -1;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic void capiminor_del_all_ack(struct capiminor *mp)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct ackqueue_entry *p, *tmp;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
1858c2ecf20Sopenharmony_ci		list_del(&p->list);
1868c2ecf20Sopenharmony_ci		kfree(p);
1878c2ecf20Sopenharmony_ci		mp->nack--;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* -------- struct capiminor ---------------------------------------- */
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void capiminor_destroy(struct tty_port *port)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct capiminor *mp = container_of(port, struct capiminor, port);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	kfree_skb(mp->outskb);
1998c2ecf20Sopenharmony_ci	skb_queue_purge(&mp->inqueue);
2008c2ecf20Sopenharmony_ci	skb_queue_purge(&mp->outqueue);
2018c2ecf20Sopenharmony_ci	capiminor_del_all_ack(mp);
2028c2ecf20Sopenharmony_ci	kfree(mp);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic const struct tty_port_operations capiminor_port_ops = {
2068c2ecf20Sopenharmony_ci	.destruct = capiminor_destroy,
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct capiminor *mp;
2128c2ecf20Sopenharmony_ci	struct device *dev;
2138c2ecf20Sopenharmony_ci	unsigned int minor;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	mp = kzalloc(sizeof(*mp), GFP_KERNEL);
2168c2ecf20Sopenharmony_ci	if (!mp) {
2178c2ecf20Sopenharmony_ci		printk(KERN_ERR "capi: can't alloc capiminor\n");
2188c2ecf20Sopenharmony_ci		return NULL;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	mp->ap = ap;
2228c2ecf20Sopenharmony_ci	mp->ncci = ncci;
2238c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mp->ackqueue);
2248c2ecf20Sopenharmony_ci	spin_lock_init(&mp->ackqlock);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	skb_queue_head_init(&mp->inqueue);
2278c2ecf20Sopenharmony_ci	skb_queue_head_init(&mp->outqueue);
2288c2ecf20Sopenharmony_ci	spin_lock_init(&mp->outlock);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	tty_port_init(&mp->port);
2318c2ecf20Sopenharmony_ci	mp->port.ops = &capiminor_port_ops;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Allocate the least unused minor number. */
2348c2ecf20Sopenharmony_ci	spin_lock(&capiminors_lock);
2358c2ecf20Sopenharmony_ci	for (minor = 0; minor < capi_ttyminors; minor++)
2368c2ecf20Sopenharmony_ci		if (!capiminors[minor]) {
2378c2ecf20Sopenharmony_ci			capiminors[minor] = mp;
2388c2ecf20Sopenharmony_ci			break;
2398c2ecf20Sopenharmony_ci		}
2408c2ecf20Sopenharmony_ci	spin_unlock(&capiminors_lock);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	if (minor == capi_ttyminors) {
2438c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "capi: out of minors\n");
2448c2ecf20Sopenharmony_ci		goto err_out1;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	mp->minor = minor;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	dev = tty_port_register_device(&mp->port, capinc_tty_driver, minor,
2508c2ecf20Sopenharmony_ci			NULL);
2518c2ecf20Sopenharmony_ci	if (IS_ERR(dev))
2528c2ecf20Sopenharmony_ci		goto err_out2;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return mp;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cierr_out2:
2578c2ecf20Sopenharmony_ci	spin_lock(&capiminors_lock);
2588c2ecf20Sopenharmony_ci	capiminors[minor] = NULL;
2598c2ecf20Sopenharmony_ci	spin_unlock(&capiminors_lock);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cierr_out1:
2628c2ecf20Sopenharmony_ci	tty_port_put(&mp->port);
2638c2ecf20Sopenharmony_ci	return NULL;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic struct capiminor *capiminor_get(unsigned int minor)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct capiminor *mp;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	spin_lock(&capiminors_lock);
2718c2ecf20Sopenharmony_ci	mp = capiminors[minor];
2728c2ecf20Sopenharmony_ci	if (mp)
2738c2ecf20Sopenharmony_ci		tty_port_get(&mp->port);
2748c2ecf20Sopenharmony_ci	spin_unlock(&capiminors_lock);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	return mp;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic inline void capiminor_put(struct capiminor *mp)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	tty_port_put(&mp->port);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic void capiminor_free(struct capiminor *mp)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	tty_unregister_device(capinc_tty_driver, mp->minor);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	spin_lock(&capiminors_lock);
2898c2ecf20Sopenharmony_ci	capiminors[mp->minor] = NULL;
2908c2ecf20Sopenharmony_ci	spin_unlock(&capiminors_lock);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	capiminor_put(mp);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/* -------- struct capincci ----------------------------------------- */
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic void capincci_alloc_minor(struct capidev *cdev, struct capincci *np)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	if (cdev->userflags & CAPIFLAG_HIGHJACKING)
3008c2ecf20Sopenharmony_ci		np->minorp = capiminor_alloc(&cdev->ap, np->ncci);
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic void capincci_free_minor(struct capincci *np)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	struct capiminor *mp = np->minorp;
3068c2ecf20Sopenharmony_ci	struct tty_struct *tty;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (mp) {
3098c2ecf20Sopenharmony_ci		tty = tty_port_tty_get(&mp->port);
3108c2ecf20Sopenharmony_ci		if (tty) {
3118c2ecf20Sopenharmony_ci			tty_vhangup(tty);
3128c2ecf20Sopenharmony_ci			tty_kref_put(tty);
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		capiminor_free(mp);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic inline unsigned int capincci_minor_opencount(struct capincci *np)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct capiminor *mp = np->minorp;
3228c2ecf20Sopenharmony_ci	unsigned int count = 0;
3238c2ecf20Sopenharmony_ci	struct tty_struct *tty;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (mp) {
3268c2ecf20Sopenharmony_ci		tty = tty_port_tty_get(&mp->port);
3278c2ecf20Sopenharmony_ci		if (tty) {
3288c2ecf20Sopenharmony_ci			count = tty->count;
3298c2ecf20Sopenharmony_ci			tty_kref_put(tty);
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci	return count;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic inline void
3388c2ecf20Sopenharmony_cicapincci_alloc_minor(struct capidev *cdev, struct capincci *np) { }
3398c2ecf20Sopenharmony_cistatic inline void capincci_free_minor(struct capincci *np) { }
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct capincci *np;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	np = kzalloc(sizeof(*np), GFP_KERNEL);
3488c2ecf20Sopenharmony_ci	if (!np)
3498c2ecf20Sopenharmony_ci		return NULL;
3508c2ecf20Sopenharmony_ci	np->ncci = ncci;
3518c2ecf20Sopenharmony_ci	np->cdev = cdev;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	capincci_alloc_minor(cdev, np);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	list_add_tail(&np->list, &cdev->nccis);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	return np;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic void capincci_free(struct capidev *cdev, u32 ncci)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct capincci *np, *tmp;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	list_for_each_entry_safe(np, tmp, &cdev->nccis, list)
3658c2ecf20Sopenharmony_ci		if (ncci == 0xffffffff || np->ncci == ncci) {
3668c2ecf20Sopenharmony_ci			capincci_free_minor(np);
3678c2ecf20Sopenharmony_ci			list_del(&np->list);
3688c2ecf20Sopenharmony_ci			kfree(np);
3698c2ecf20Sopenharmony_ci		}
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
3738c2ecf20Sopenharmony_cistatic struct capincci *capincci_find(struct capidev *cdev, u32 ncci)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct capincci *np;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	list_for_each_entry(np, &cdev->nccis, list)
3788c2ecf20Sopenharmony_ci		if (np->ncci == ncci)
3798c2ecf20Sopenharmony_ci			return np;
3808c2ecf20Sopenharmony_ci	return NULL;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci/* -------- handle data queue --------------------------------------- */
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic struct sk_buff *
3868c2ecf20Sopenharmony_cigen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
3898c2ecf20Sopenharmony_ci	nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_KERNEL);
3908c2ecf20Sopenharmony_ci	if (nskb) {
3918c2ecf20Sopenharmony_ci		u16 datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2);
3928c2ecf20Sopenharmony_ci		unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
3938c2ecf20Sopenharmony_ci		capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
3948c2ecf20Sopenharmony_ci		capimsg_setu16(s, 2, mp->ap->applid);
3958c2ecf20Sopenharmony_ci		capimsg_setu8 (s, 4, CAPI_DATA_B3);
3968c2ecf20Sopenharmony_ci		capimsg_setu8 (s, 5, CAPI_RESP);
3978c2ecf20Sopenharmony_ci		capimsg_setu16(s, 6, atomic_inc_return(&mp->msgid));
3988c2ecf20Sopenharmony_ci		capimsg_setu32(s, 8, mp->ncci);
3998c2ecf20Sopenharmony_ci		capimsg_setu16(s, 12, datahandle);
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci	return nskb;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data);
4078c2ecf20Sopenharmony_ci	struct tty_struct *tty;
4088c2ecf20Sopenharmony_ci	struct sk_buff *nskb;
4098c2ecf20Sopenharmony_ci	u16 errcode, datahandle;
4108c2ecf20Sopenharmony_ci	struct tty_ldisc *ld;
4118c2ecf20Sopenharmony_ci	int ret = -1;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&mp->port);
4148c2ecf20Sopenharmony_ci	if (!tty) {
4158c2ecf20Sopenharmony_ci		pr_debug("capi: currently no receiver\n");
4168c2ecf20Sopenharmony_ci		return -1;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	ld = tty_ldisc_ref(tty);
4208c2ecf20Sopenharmony_ci	if (!ld) {
4218c2ecf20Sopenharmony_ci		/* fatal error, do not requeue */
4228c2ecf20Sopenharmony_ci		ret = 0;
4238c2ecf20Sopenharmony_ci		kfree_skb(skb);
4248c2ecf20Sopenharmony_ci		goto deref_tty;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (ld->ops->receive_buf == NULL) {
4288c2ecf20Sopenharmony_ci		pr_debug("capi: ldisc has no receive_buf function\n");
4298c2ecf20Sopenharmony_ci		/* fatal error, do not requeue */
4308c2ecf20Sopenharmony_ci		goto free_skb;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci	if (mp->ttyinstop) {
4338c2ecf20Sopenharmony_ci		pr_debug("capi: recv tty throttled\n");
4348c2ecf20Sopenharmony_ci		goto deref_ldisc;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if (tty->receive_room < datalen) {
4388c2ecf20Sopenharmony_ci		pr_debug("capi: no room in tty\n");
4398c2ecf20Sopenharmony_ci		goto deref_ldisc;
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	nskb = gen_data_b3_resp_for(mp, skb);
4438c2ecf20Sopenharmony_ci	if (!nskb) {
4448c2ecf20Sopenharmony_ci		printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
4458c2ecf20Sopenharmony_ci		goto deref_ldisc;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	errcode = capi20_put_message(mp->ap, nskb);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (errcode == CAPI_NOERROR) {
4538c2ecf20Sopenharmony_ci		skb_pull(skb, CAPIMSG_LEN(skb->data));
4548c2ecf20Sopenharmony_ci		pr_debug("capi: DATA_B3_RESP %u len=%d => ldisc\n",
4558c2ecf20Sopenharmony_ci			 datahandle, skb->len);
4568c2ecf20Sopenharmony_ci		ld->ops->receive_buf(tty, skb->data, NULL, skb->len);
4578c2ecf20Sopenharmony_ci	} else {
4588c2ecf20Sopenharmony_ci		printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
4598c2ecf20Sopenharmony_ci		       errcode);
4608c2ecf20Sopenharmony_ci		kfree_skb(nskb);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		if (errcode == CAPI_SENDQUEUEFULL)
4638c2ecf20Sopenharmony_ci			goto deref_ldisc;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cifree_skb:
4678c2ecf20Sopenharmony_ci	ret = 0;
4688c2ecf20Sopenharmony_ci	kfree_skb(skb);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cideref_ldisc:
4718c2ecf20Sopenharmony_ci	tty_ldisc_deref(ld);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cideref_tty:
4748c2ecf20Sopenharmony_ci	tty_kref_put(tty);
4758c2ecf20Sopenharmony_ci	return ret;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic void handle_minor_recv(struct capiminor *mp)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&mp->inqueue)) != NULL)
4838c2ecf20Sopenharmony_ci		if (handle_recv_skb(mp, skb) < 0) {
4848c2ecf20Sopenharmony_ci			skb_queue_head(&mp->inqueue, skb);
4858c2ecf20Sopenharmony_ci			return;
4868c2ecf20Sopenharmony_ci		}
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void handle_minor_send(struct capiminor *mp)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct tty_struct *tty;
4928c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4938c2ecf20Sopenharmony_ci	u16 len;
4948c2ecf20Sopenharmony_ci	u16 errcode;
4958c2ecf20Sopenharmony_ci	u16 datahandle;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	tty = tty_port_tty_get(&mp->port);
4988c2ecf20Sopenharmony_ci	if (!tty)
4998c2ecf20Sopenharmony_ci		return;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	if (mp->ttyoutstop) {
5028c2ecf20Sopenharmony_ci		pr_debug("capi: send: tty stopped\n");
5038c2ecf20Sopenharmony_ci		tty_kref_put(tty);
5048c2ecf20Sopenharmony_ci		return;
5058c2ecf20Sopenharmony_ci	}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	while (1) {
5088c2ecf20Sopenharmony_ci		spin_lock_bh(&mp->outlock);
5098c2ecf20Sopenharmony_ci		skb = __skb_dequeue(&mp->outqueue);
5108c2ecf20Sopenharmony_ci		if (!skb) {
5118c2ecf20Sopenharmony_ci			spin_unlock_bh(&mp->outlock);
5128c2ecf20Sopenharmony_ci			break;
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci		len = (u16)skb->len;
5158c2ecf20Sopenharmony_ci		mp->outbytes -= len;
5168c2ecf20Sopenharmony_ci		spin_unlock_bh(&mp->outlock);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		datahandle = atomic_inc_return(&mp->datahandle);
5198c2ecf20Sopenharmony_ci		skb_push(skb, CAPI_DATA_B3_REQ_LEN);
5208c2ecf20Sopenharmony_ci		memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
5218c2ecf20Sopenharmony_ci		capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
5228c2ecf20Sopenharmony_ci		capimsg_setu16(skb->data, 2, mp->ap->applid);
5238c2ecf20Sopenharmony_ci		capimsg_setu8 (skb->data, 4, CAPI_DATA_B3);
5248c2ecf20Sopenharmony_ci		capimsg_setu8 (skb->data, 5, CAPI_REQ);
5258c2ecf20Sopenharmony_ci		capimsg_setu16(skb->data, 6, atomic_inc_return(&mp->msgid));
5268c2ecf20Sopenharmony_ci		capimsg_setu32(skb->data, 8, mp->ncci);	/* NCCI */
5278c2ecf20Sopenharmony_ci		capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */
5288c2ecf20Sopenharmony_ci		capimsg_setu16(skb->data, 16, len);	/* Data length */
5298c2ecf20Sopenharmony_ci		capimsg_setu16(skb->data, 18, datahandle);
5308c2ecf20Sopenharmony_ci		capimsg_setu16(skb->data, 20, 0);	/* Flags */
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if (capiminor_add_ack(mp, datahandle) < 0) {
5338c2ecf20Sopenharmony_ci			skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci			spin_lock_bh(&mp->outlock);
5368c2ecf20Sopenharmony_ci			__skb_queue_head(&mp->outqueue, skb);
5378c2ecf20Sopenharmony_ci			mp->outbytes += len;
5388c2ecf20Sopenharmony_ci			spin_unlock_bh(&mp->outlock);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci			break;
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci		errcode = capi20_put_message(mp->ap, skb);
5438c2ecf20Sopenharmony_ci		if (errcode == CAPI_NOERROR) {
5448c2ecf20Sopenharmony_ci			pr_debug("capi: DATA_B3_REQ %u len=%u\n",
5458c2ecf20Sopenharmony_ci				 datahandle, len);
5468c2ecf20Sopenharmony_ci			continue;
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci		capiminor_del_ack(mp, datahandle);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci		if (errcode == CAPI_SENDQUEUEFULL) {
5518c2ecf20Sopenharmony_ci			skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci			spin_lock_bh(&mp->outlock);
5548c2ecf20Sopenharmony_ci			__skb_queue_head(&mp->outqueue, skb);
5558c2ecf20Sopenharmony_ci			mp->outbytes += len;
5568c2ecf20Sopenharmony_ci			spin_unlock_bh(&mp->outlock);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci			break;
5598c2ecf20Sopenharmony_ci		}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		/* ups, drop packet */
5628c2ecf20Sopenharmony_ci		printk(KERN_ERR "capi: put_message = %x\n", errcode);
5638c2ecf20Sopenharmony_ci		kfree_skb(skb);
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci	tty_kref_put(tty);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
5698c2ecf20Sopenharmony_ci/* -------- function called by lower level -------------------------- */
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct capidev *cdev = ap->private;
5748c2ecf20Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
5758c2ecf20Sopenharmony_ci	struct capiminor *mp;
5768c2ecf20Sopenharmony_ci	u16 datahandle;
5778c2ecf20Sopenharmony_ci	struct capincci *np;
5788c2ecf20Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	mutex_lock(&cdev->lock);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) {
5838c2ecf20Sopenharmony_ci		u16 info = CAPIMSG_U16(skb->data, 12); // Info field
5848c2ecf20Sopenharmony_ci		if ((info & 0xff00) == 0)
5858c2ecf20Sopenharmony_ci			capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND)
5888c2ecf20Sopenharmony_ci		capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
5918c2ecf20Sopenharmony_ci		skb_queue_tail(&cdev->recvqueue, skb);
5928c2ecf20Sopenharmony_ci		wake_up_interruptible(&cdev->recvwait);
5938c2ecf20Sopenharmony_ci		goto unlock_out;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
5978c2ecf20Sopenharmony_ci	skb_queue_tail(&cdev->recvqueue, skb);
5988c2ecf20Sopenharmony_ci	wake_up_interruptible(&cdev->recvwait);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	np = capincci_find(cdev, CAPIMSG_CONTROL(skb->data));
6038c2ecf20Sopenharmony_ci	if (!np) {
6048c2ecf20Sopenharmony_ci		printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
6058c2ecf20Sopenharmony_ci		skb_queue_tail(&cdev->recvqueue, skb);
6068c2ecf20Sopenharmony_ci		wake_up_interruptible(&cdev->recvwait);
6078c2ecf20Sopenharmony_ci		goto unlock_out;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	mp = np->minorp;
6118c2ecf20Sopenharmony_ci	if (!mp) {
6128c2ecf20Sopenharmony_ci		skb_queue_tail(&cdev->recvqueue, skb);
6138c2ecf20Sopenharmony_ci		wake_up_interruptible(&cdev->recvwait);
6148c2ecf20Sopenharmony_ci		goto unlock_out;
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci	if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
6178c2ecf20Sopenharmony_ci		datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2);
6188c2ecf20Sopenharmony_ci		pr_debug("capi_signal: DATA_B3_IND %u len=%d\n",
6198c2ecf20Sopenharmony_ci			 datahandle, skb->len-CAPIMSG_LEN(skb->data));
6208c2ecf20Sopenharmony_ci		skb_queue_tail(&mp->inqueue, skb);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		handle_minor_recv(mp);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	} else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4);
6278c2ecf20Sopenharmony_ci		pr_debug("capi_signal: DATA_B3_CONF %u 0x%x\n",
6288c2ecf20Sopenharmony_ci			 datahandle,
6298c2ecf20Sopenharmony_ci			 CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 2));
6308c2ecf20Sopenharmony_ci		kfree_skb(skb);
6318c2ecf20Sopenharmony_ci		capiminor_del_ack(mp, datahandle);
6328c2ecf20Sopenharmony_ci		tty_port_tty_wakeup(&mp->port);
6338c2ecf20Sopenharmony_ci		handle_minor_send(mp);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	} else {
6368c2ecf20Sopenharmony_ci		/* ups, let capi application handle it :-) */
6378c2ecf20Sopenharmony_ci		skb_queue_tail(&cdev->recvqueue, skb);
6388c2ecf20Sopenharmony_ci		wake_up_interruptible(&cdev->recvwait);
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ciunlock_out:
6438c2ecf20Sopenharmony_ci	mutex_unlock(&cdev->lock);
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci/* -------- file_operations for capidev ----------------------------- */
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic ssize_t
6498c2ecf20Sopenharmony_cicapi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct capidev *cdev = file->private_data;
6528c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6538c2ecf20Sopenharmony_ci	size_t copied;
6548c2ecf20Sopenharmony_ci	int err;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (!cdev->ap.applid)
6578c2ecf20Sopenharmony_ci		return -ENODEV;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	skb = skb_dequeue(&cdev->recvqueue);
6608c2ecf20Sopenharmony_ci	if (!skb) {
6618c2ecf20Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
6628c2ecf20Sopenharmony_ci			return -EAGAIN;
6638c2ecf20Sopenharmony_ci		err = wait_event_interruptible(cdev->recvwait,
6648c2ecf20Sopenharmony_ci					       (skb = skb_dequeue(&cdev->recvqueue)));
6658c2ecf20Sopenharmony_ci		if (err)
6668c2ecf20Sopenharmony_ci			return err;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci	if (skb->len > count) {
6698c2ecf20Sopenharmony_ci		skb_queue_head(&cdev->recvqueue, skb);
6708c2ecf20Sopenharmony_ci		return -EMSGSIZE;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci	if (copy_to_user(buf, skb->data, skb->len)) {
6738c2ecf20Sopenharmony_ci		skb_queue_head(&cdev->recvqueue, skb);
6748c2ecf20Sopenharmony_ci		return -EFAULT;
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci	copied = skb->len;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	kfree_skb(skb);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return copied;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic ssize_t
6848c2ecf20Sopenharmony_cicapi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	struct capidev *cdev = file->private_data;
6878c2ecf20Sopenharmony_ci	struct sk_buff *skb;
6888c2ecf20Sopenharmony_ci	u16 mlen;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (!cdev->ap.applid)
6918c2ecf20Sopenharmony_ci		return -ENODEV;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	if (count < CAPIMSG_BASELEN)
6948c2ecf20Sopenharmony_ci		return -EINVAL;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	skb = alloc_skb(count, GFP_USER);
6978c2ecf20Sopenharmony_ci	if (!skb)
6988c2ecf20Sopenharmony_ci		return -ENOMEM;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	if (copy_from_user(skb_put(skb, count), buf, count)) {
7018c2ecf20Sopenharmony_ci		kfree_skb(skb);
7028c2ecf20Sopenharmony_ci		return -EFAULT;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci	mlen = CAPIMSG_LEN(skb->data);
7058c2ecf20Sopenharmony_ci	if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
7068c2ecf20Sopenharmony_ci		if (count < CAPI_DATA_B3_REQ_LEN ||
7078c2ecf20Sopenharmony_ci		    (size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) {
7088c2ecf20Sopenharmony_ci			kfree_skb(skb);
7098c2ecf20Sopenharmony_ci			return -EINVAL;
7108c2ecf20Sopenharmony_ci		}
7118c2ecf20Sopenharmony_ci	} else {
7128c2ecf20Sopenharmony_ci		if (mlen != count) {
7138c2ecf20Sopenharmony_ci			kfree_skb(skb);
7148c2ecf20Sopenharmony_ci			return -EINVAL;
7158c2ecf20Sopenharmony_ci		}
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci	CAPIMSG_SETAPPID(skb->data, cdev->ap.applid);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) {
7208c2ecf20Sopenharmony_ci		if (count < CAPI_DISCONNECT_B3_RESP_LEN) {
7218c2ecf20Sopenharmony_ci			kfree_skb(skb);
7228c2ecf20Sopenharmony_ci			return -EINVAL;
7238c2ecf20Sopenharmony_ci		}
7248c2ecf20Sopenharmony_ci		mutex_lock(&cdev->lock);
7258c2ecf20Sopenharmony_ci		capincci_free(cdev, CAPIMSG_NCCI(skb->data));
7268c2ecf20Sopenharmony_ci		mutex_unlock(&cdev->lock);
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	cdev->errcode = capi20_put_message(&cdev->ap, skb);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (cdev->errcode) {
7328c2ecf20Sopenharmony_ci		kfree_skb(skb);
7338c2ecf20Sopenharmony_ci		return -EIO;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci	return count;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic __poll_t
7398c2ecf20Sopenharmony_cicapi_poll(struct file *file, poll_table *wait)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct capidev *cdev = file->private_data;
7428c2ecf20Sopenharmony_ci	__poll_t mask = 0;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	if (!cdev->ap.applid)
7458c2ecf20Sopenharmony_ci		return EPOLLERR;
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	poll_wait(file, &(cdev->recvwait), wait);
7488c2ecf20Sopenharmony_ci	mask = EPOLLOUT | EPOLLWRNORM;
7498c2ecf20Sopenharmony_ci	if (!skb_queue_empty_lockless(&cdev->recvqueue))
7508c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
7518c2ecf20Sopenharmony_ci	return mask;
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic int
7558c2ecf20Sopenharmony_cicapi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	struct capidev *cdev = file->private_data;
7588c2ecf20Sopenharmony_ci	capi_ioctl_struct data;
7598c2ecf20Sopenharmony_ci	int retval = -EINVAL;
7608c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)arg;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	switch (cmd) {
7638c2ecf20Sopenharmony_ci	case CAPI_REGISTER:
7648c2ecf20Sopenharmony_ci		mutex_lock(&cdev->lock);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		if (cdev->ap.applid) {
7678c2ecf20Sopenharmony_ci			retval = -EEXIST;
7688c2ecf20Sopenharmony_ci			goto register_out;
7698c2ecf20Sopenharmony_ci		}
7708c2ecf20Sopenharmony_ci		if (copy_from_user(&cdev->ap.rparam, argp,
7718c2ecf20Sopenharmony_ci				   sizeof(struct capi_register_params))) {
7728c2ecf20Sopenharmony_ci			retval = -EFAULT;
7738c2ecf20Sopenharmony_ci			goto register_out;
7748c2ecf20Sopenharmony_ci		}
7758c2ecf20Sopenharmony_ci		cdev->ap.private = cdev;
7768c2ecf20Sopenharmony_ci		cdev->ap.recv_message = capi_recv_message;
7778c2ecf20Sopenharmony_ci		cdev->errcode = capi20_register(&cdev->ap);
7788c2ecf20Sopenharmony_ci		retval = (int)cdev->ap.applid;
7798c2ecf20Sopenharmony_ci		if (cdev->errcode) {
7808c2ecf20Sopenharmony_ci			cdev->ap.applid = 0;
7818c2ecf20Sopenharmony_ci			retval = -EIO;
7828c2ecf20Sopenharmony_ci		}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ciregister_out:
7858c2ecf20Sopenharmony_ci		mutex_unlock(&cdev->lock);
7868c2ecf20Sopenharmony_ci		return retval;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	case CAPI_GET_VERSION:
7898c2ecf20Sopenharmony_ci		if (copy_from_user(&data.contr, argp,
7908c2ecf20Sopenharmony_ci				   sizeof(data.contr)))
7918c2ecf20Sopenharmony_ci			return -EFAULT;
7928c2ecf20Sopenharmony_ci		cdev->errcode = capi20_get_version(data.contr, &data.version);
7938c2ecf20Sopenharmony_ci		if (cdev->errcode)
7948c2ecf20Sopenharmony_ci			return -EIO;
7958c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &data.version,
7968c2ecf20Sopenharmony_ci				 sizeof(data.version)))
7978c2ecf20Sopenharmony_ci			return -EFAULT;
7988c2ecf20Sopenharmony_ci		return 0;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	case CAPI_GET_SERIAL:
8018c2ecf20Sopenharmony_ci		if (copy_from_user(&data.contr, argp,
8028c2ecf20Sopenharmony_ci				   sizeof(data.contr)))
8038c2ecf20Sopenharmony_ci			return -EFAULT;
8048c2ecf20Sopenharmony_ci		cdev->errcode = capi20_get_serial(data.contr, data.serial);
8058c2ecf20Sopenharmony_ci		if (cdev->errcode)
8068c2ecf20Sopenharmony_ci			return -EIO;
8078c2ecf20Sopenharmony_ci		if (copy_to_user(argp, data.serial,
8088c2ecf20Sopenharmony_ci				 sizeof(data.serial)))
8098c2ecf20Sopenharmony_ci			return -EFAULT;
8108c2ecf20Sopenharmony_ci		return 0;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	case CAPI_GET_PROFILE:
8138c2ecf20Sopenharmony_ci		if (copy_from_user(&data.contr, argp,
8148c2ecf20Sopenharmony_ci				   sizeof(data.contr)))
8158c2ecf20Sopenharmony_ci			return -EFAULT;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci		if (data.contr == 0) {
8188c2ecf20Sopenharmony_ci			cdev->errcode = capi20_get_profile(data.contr, &data.profile);
8198c2ecf20Sopenharmony_ci			if (cdev->errcode)
8208c2ecf20Sopenharmony_ci				return -EIO;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci			retval = copy_to_user(argp,
8238c2ecf20Sopenharmony_ci					      &data.profile.ncontroller,
8248c2ecf20Sopenharmony_ci					      sizeof(data.profile.ncontroller));
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci		} else {
8278c2ecf20Sopenharmony_ci			cdev->errcode = capi20_get_profile(data.contr, &data.profile);
8288c2ecf20Sopenharmony_ci			if (cdev->errcode)
8298c2ecf20Sopenharmony_ci				return -EIO;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci			retval = copy_to_user(argp, &data.profile,
8328c2ecf20Sopenharmony_ci					      sizeof(data.profile));
8338c2ecf20Sopenharmony_ci		}
8348c2ecf20Sopenharmony_ci		if (retval)
8358c2ecf20Sopenharmony_ci			return -EFAULT;
8368c2ecf20Sopenharmony_ci		return 0;
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	case CAPI_GET_MANUFACTURER:
8398c2ecf20Sopenharmony_ci		if (copy_from_user(&data.contr, argp,
8408c2ecf20Sopenharmony_ci				   sizeof(data.contr)))
8418c2ecf20Sopenharmony_ci			return -EFAULT;
8428c2ecf20Sopenharmony_ci		cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer);
8438c2ecf20Sopenharmony_ci		if (cdev->errcode)
8448c2ecf20Sopenharmony_ci			return -EIO;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		if (copy_to_user(argp, data.manufacturer,
8478c2ecf20Sopenharmony_ci				 sizeof(data.manufacturer)))
8488c2ecf20Sopenharmony_ci			return -EFAULT;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci		return 0;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	case CAPI_GET_ERRCODE:
8538c2ecf20Sopenharmony_ci		data.errcode = cdev->errcode;
8548c2ecf20Sopenharmony_ci		cdev->errcode = CAPI_NOERROR;
8558c2ecf20Sopenharmony_ci		if (arg) {
8568c2ecf20Sopenharmony_ci			if (copy_to_user(argp, &data.errcode,
8578c2ecf20Sopenharmony_ci					 sizeof(data.errcode)))
8588c2ecf20Sopenharmony_ci				return -EFAULT;
8598c2ecf20Sopenharmony_ci		}
8608c2ecf20Sopenharmony_ci		return data.errcode;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	case CAPI_INSTALLED:
8638c2ecf20Sopenharmony_ci		if (capi20_isinstalled() == CAPI_NOERROR)
8648c2ecf20Sopenharmony_ci			return 0;
8658c2ecf20Sopenharmony_ci		return -ENXIO;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	case CAPI_MANUFACTURER_CMD: {
8688c2ecf20Sopenharmony_ci		struct capi_manufacturer_cmd mcmd;
8698c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
8708c2ecf20Sopenharmony_ci			return -EPERM;
8718c2ecf20Sopenharmony_ci		if (copy_from_user(&mcmd, argp, sizeof(mcmd)))
8728c2ecf20Sopenharmony_ci			return -EFAULT;
8738c2ecf20Sopenharmony_ci		return capi20_manufacturer(mcmd.cmd, mcmd.data);
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci	case CAPI_SET_FLAGS:
8768c2ecf20Sopenharmony_ci	case CAPI_CLR_FLAGS: {
8778c2ecf20Sopenharmony_ci		unsigned userflags;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci		if (copy_from_user(&userflags, argp, sizeof(userflags)))
8808c2ecf20Sopenharmony_ci			return -EFAULT;
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		mutex_lock(&cdev->lock);
8838c2ecf20Sopenharmony_ci		if (cmd == CAPI_SET_FLAGS)
8848c2ecf20Sopenharmony_ci			cdev->userflags |= userflags;
8858c2ecf20Sopenharmony_ci		else
8868c2ecf20Sopenharmony_ci			cdev->userflags &= ~userflags;
8878c2ecf20Sopenharmony_ci		mutex_unlock(&cdev->lock);
8888c2ecf20Sopenharmony_ci		return 0;
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci	case CAPI_GET_FLAGS:
8918c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &cdev->userflags,
8928c2ecf20Sopenharmony_ci				 sizeof(cdev->userflags)))
8938c2ecf20Sopenharmony_ci			return -EFAULT;
8948c2ecf20Sopenharmony_ci		return 0;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
8978c2ecf20Sopenharmony_ci	case CAPI_NCCI_OPENCOUNT:
8988c2ecf20Sopenharmony_ci		return 0;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
9018c2ecf20Sopenharmony_ci	case CAPI_NCCI_OPENCOUNT: {
9028c2ecf20Sopenharmony_ci		struct capincci *nccip;
9038c2ecf20Sopenharmony_ci		unsigned ncci;
9048c2ecf20Sopenharmony_ci		int count = 0;
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_ci		if (copy_from_user(&ncci, argp, sizeof(ncci)))
9078c2ecf20Sopenharmony_ci			return -EFAULT;
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci		mutex_lock(&cdev->lock);
9108c2ecf20Sopenharmony_ci		nccip = capincci_find(cdev, (u32)ncci);
9118c2ecf20Sopenharmony_ci		if (nccip)
9128c2ecf20Sopenharmony_ci			count = capincci_minor_opencount(nccip);
9138c2ecf20Sopenharmony_ci		mutex_unlock(&cdev->lock);
9148c2ecf20Sopenharmony_ci		return count;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	case CAPI_NCCI_GETUNIT: {
9188c2ecf20Sopenharmony_ci		struct capincci *nccip;
9198c2ecf20Sopenharmony_ci		struct capiminor *mp;
9208c2ecf20Sopenharmony_ci		unsigned ncci;
9218c2ecf20Sopenharmony_ci		int unit = -ESRCH;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci		if (copy_from_user(&ncci, argp, sizeof(ncci)))
9248c2ecf20Sopenharmony_ci			return -EFAULT;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		mutex_lock(&cdev->lock);
9278c2ecf20Sopenharmony_ci		nccip = capincci_find(cdev, (u32)ncci);
9288c2ecf20Sopenharmony_ci		if (nccip) {
9298c2ecf20Sopenharmony_ci			mp = nccip->minorp;
9308c2ecf20Sopenharmony_ci			if (mp)
9318c2ecf20Sopenharmony_ci				unit = mp->minor;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci		mutex_unlock(&cdev->lock);
9348c2ecf20Sopenharmony_ci		return unit;
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	default:
9398c2ecf20Sopenharmony_ci		return -EINVAL;
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cistatic long
9448c2ecf20Sopenharmony_cicapi_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci	int ret;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	mutex_lock(&capi_mutex);
9498c2ecf20Sopenharmony_ci	ret = capi_ioctl(file, cmd, arg);
9508c2ecf20Sopenharmony_ci	mutex_unlock(&capi_mutex);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	return ret;
9538c2ecf20Sopenharmony_ci}
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
9568c2ecf20Sopenharmony_cistatic long
9578c2ecf20Sopenharmony_cicapi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	int ret;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	if (cmd == CAPI_MANUFACTURER_CMD) {
9628c2ecf20Sopenharmony_ci		struct {
9638c2ecf20Sopenharmony_ci			compat_ulong_t cmd;
9648c2ecf20Sopenharmony_ci			compat_uptr_t data;
9658c2ecf20Sopenharmony_ci		} mcmd32;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci		if (!capable(CAP_SYS_ADMIN))
9688c2ecf20Sopenharmony_ci			return -EPERM;
9698c2ecf20Sopenharmony_ci		if (copy_from_user(&mcmd32, compat_ptr(arg), sizeof(mcmd32)))
9708c2ecf20Sopenharmony_ci			return -EFAULT;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		mutex_lock(&capi_mutex);
9738c2ecf20Sopenharmony_ci		ret = capi20_manufacturer(mcmd32.cmd, compat_ptr(mcmd32.data));
9748c2ecf20Sopenharmony_ci		mutex_unlock(&capi_mutex);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		return ret;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	return capi_unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
9808c2ecf20Sopenharmony_ci}
9818c2ecf20Sopenharmony_ci#endif
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic int capi_open(struct inode *inode, struct file *file)
9848c2ecf20Sopenharmony_ci{
9858c2ecf20Sopenharmony_ci	struct capidev *cdev;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
9888c2ecf20Sopenharmony_ci	if (!cdev)
9898c2ecf20Sopenharmony_ci		return -ENOMEM;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	mutex_init(&cdev->lock);
9928c2ecf20Sopenharmony_ci	skb_queue_head_init(&cdev->recvqueue);
9938c2ecf20Sopenharmony_ci	init_waitqueue_head(&cdev->recvwait);
9948c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cdev->nccis);
9958c2ecf20Sopenharmony_ci	file->private_data = cdev;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	mutex_lock(&capidev_list_lock);
9988c2ecf20Sopenharmony_ci	list_add_tail(&cdev->list, &capidev_list);
9998c2ecf20Sopenharmony_ci	mutex_unlock(&capidev_list_lock);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return stream_open(inode, file);
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic int capi_release(struct inode *inode, struct file *file)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	struct capidev *cdev = file->private_data;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	mutex_lock(&capidev_list_lock);
10098c2ecf20Sopenharmony_ci	list_del(&cdev->list);
10108c2ecf20Sopenharmony_ci	mutex_unlock(&capidev_list_lock);
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	if (cdev->ap.applid)
10138c2ecf20Sopenharmony_ci		capi20_release(&cdev->ap);
10148c2ecf20Sopenharmony_ci	skb_queue_purge(&cdev->recvqueue);
10158c2ecf20Sopenharmony_ci	capincci_free(cdev, 0xffffffff);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	kfree(cdev);
10188c2ecf20Sopenharmony_ci	return 0;
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_cistatic const struct file_operations capi_fops =
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
10248c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
10258c2ecf20Sopenharmony_ci	.read		= capi_read,
10268c2ecf20Sopenharmony_ci	.write		= capi_write,
10278c2ecf20Sopenharmony_ci	.poll		= capi_poll,
10288c2ecf20Sopenharmony_ci	.unlocked_ioctl	= capi_unlocked_ioctl,
10298c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT
10308c2ecf20Sopenharmony_ci	.compat_ioctl	= capi_compat_ioctl,
10318c2ecf20Sopenharmony_ci#endif
10328c2ecf20Sopenharmony_ci	.open		= capi_open,
10338c2ecf20Sopenharmony_ci	.release	= capi_release,
10348c2ecf20Sopenharmony_ci};
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
10378c2ecf20Sopenharmony_ci/* -------- tty_operations for capincci ----------------------------- */
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_cistatic int
10408c2ecf20Sopenharmony_cicapinc_tty_install(struct tty_driver *driver, struct tty_struct *tty)
10418c2ecf20Sopenharmony_ci{
10428c2ecf20Sopenharmony_ci	struct capiminor *mp = capiminor_get(tty->index);
10438c2ecf20Sopenharmony_ci	int ret = tty_standard_install(driver, tty);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	if (ret == 0)
10468c2ecf20Sopenharmony_ci		tty->driver_data = mp;
10478c2ecf20Sopenharmony_ci	else
10488c2ecf20Sopenharmony_ci		capiminor_put(mp);
10498c2ecf20Sopenharmony_ci	return ret;
10508c2ecf20Sopenharmony_ci}
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cistatic void capinc_tty_cleanup(struct tty_struct *tty)
10538c2ecf20Sopenharmony_ci{
10548c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
10558c2ecf20Sopenharmony_ci	tty->driver_data = NULL;
10568c2ecf20Sopenharmony_ci	capiminor_put(mp);
10578c2ecf20Sopenharmony_ci}
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_cistatic int capinc_tty_open(struct tty_struct *tty, struct file *filp)
10608c2ecf20Sopenharmony_ci{
10618c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
10628c2ecf20Sopenharmony_ci	int err;
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	err = tty_port_open(&mp->port, tty, filp);
10658c2ecf20Sopenharmony_ci	if (err)
10668c2ecf20Sopenharmony_ci		return err;
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	handle_minor_recv(mp);
10698c2ecf20Sopenharmony_ci	return 0;
10708c2ecf20Sopenharmony_ci}
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_cistatic void capinc_tty_close(struct tty_struct *tty, struct file *filp)
10738c2ecf20Sopenharmony_ci{
10748c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	tty_port_close(&mp->port, tty, filp);
10778c2ecf20Sopenharmony_ci}
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic int capinc_tty_write(struct tty_struct *tty,
10808c2ecf20Sopenharmony_ci			    const unsigned char *buf, int count)
10818c2ecf20Sopenharmony_ci{
10828c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
10838c2ecf20Sopenharmony_ci	struct sk_buff *skb;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_write(count=%d)\n", count);
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	spin_lock_bh(&mp->outlock);
10888c2ecf20Sopenharmony_ci	skb = mp->outskb;
10898c2ecf20Sopenharmony_ci	if (skb) {
10908c2ecf20Sopenharmony_ci		mp->outskb = NULL;
10918c2ecf20Sopenharmony_ci		__skb_queue_tail(&mp->outqueue, skb);
10928c2ecf20Sopenharmony_ci		mp->outbytes += skb->len;
10938c2ecf20Sopenharmony_ci	}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + count, GFP_ATOMIC);
10968c2ecf20Sopenharmony_ci	if (!skb) {
10978c2ecf20Sopenharmony_ci		printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
10988c2ecf20Sopenharmony_ci		spin_unlock_bh(&mp->outlock);
10998c2ecf20Sopenharmony_ci		return -ENOMEM;
11008c2ecf20Sopenharmony_ci	}
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
11038c2ecf20Sopenharmony_ci	skb_put_data(skb, buf, count);
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	__skb_queue_tail(&mp->outqueue, skb);
11068c2ecf20Sopenharmony_ci	mp->outbytes += skb->len;
11078c2ecf20Sopenharmony_ci	spin_unlock_bh(&mp->outlock);
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	handle_minor_send(mp);
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	return count;
11128c2ecf20Sopenharmony_ci}
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_cistatic int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
11158c2ecf20Sopenharmony_ci{
11168c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
11178c2ecf20Sopenharmony_ci	bool invoke_send = false;
11188c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11198c2ecf20Sopenharmony_ci	int ret = 1;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	pr_debug("capinc_put_char(%u)\n", ch);
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci	spin_lock_bh(&mp->outlock);
11248c2ecf20Sopenharmony_ci	skb = mp->outskb;
11258c2ecf20Sopenharmony_ci	if (skb) {
11268c2ecf20Sopenharmony_ci		if (skb_tailroom(skb) > 0) {
11278c2ecf20Sopenharmony_ci			skb_put_u8(skb, ch);
11288c2ecf20Sopenharmony_ci			goto unlock_out;
11298c2ecf20Sopenharmony_ci		}
11308c2ecf20Sopenharmony_ci		mp->outskb = NULL;
11318c2ecf20Sopenharmony_ci		__skb_queue_tail(&mp->outqueue, skb);
11328c2ecf20Sopenharmony_ci		mp->outbytes += skb->len;
11338c2ecf20Sopenharmony_ci		invoke_send = true;
11348c2ecf20Sopenharmony_ci	}
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + CAPI_MAX_BLKSIZE, GFP_ATOMIC);
11378c2ecf20Sopenharmony_ci	if (skb) {
11388c2ecf20Sopenharmony_ci		skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
11398c2ecf20Sopenharmony_ci		skb_put_u8(skb, ch);
11408c2ecf20Sopenharmony_ci		mp->outskb = skb;
11418c2ecf20Sopenharmony_ci	} else {
11428c2ecf20Sopenharmony_ci		printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
11438c2ecf20Sopenharmony_ci		ret = 0;
11448c2ecf20Sopenharmony_ci	}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ciunlock_out:
11478c2ecf20Sopenharmony_ci	spin_unlock_bh(&mp->outlock);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	if (invoke_send)
11508c2ecf20Sopenharmony_ci		handle_minor_send(mp);
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	return ret;
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic void capinc_tty_flush_chars(struct tty_struct *tty)
11568c2ecf20Sopenharmony_ci{
11578c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
11588c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_flush_chars\n");
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	spin_lock_bh(&mp->outlock);
11638c2ecf20Sopenharmony_ci	skb = mp->outskb;
11648c2ecf20Sopenharmony_ci	if (skb) {
11658c2ecf20Sopenharmony_ci		mp->outskb = NULL;
11668c2ecf20Sopenharmony_ci		__skb_queue_tail(&mp->outqueue, skb);
11678c2ecf20Sopenharmony_ci		mp->outbytes += skb->len;
11688c2ecf20Sopenharmony_ci		spin_unlock_bh(&mp->outlock);
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci		handle_minor_send(mp);
11718c2ecf20Sopenharmony_ci	} else
11728c2ecf20Sopenharmony_ci		spin_unlock_bh(&mp->outlock);
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci	handle_minor_recv(mp);
11758c2ecf20Sopenharmony_ci}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cistatic int capinc_tty_write_room(struct tty_struct *tty)
11788c2ecf20Sopenharmony_ci{
11798c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
11808c2ecf20Sopenharmony_ci	int room;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue);
11838c2ecf20Sopenharmony_ci	room *= CAPI_MAX_BLKSIZE;
11848c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_write_room = %d\n", room);
11858c2ecf20Sopenharmony_ci	return room;
11868c2ecf20Sopenharmony_ci}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_cistatic int capinc_tty_chars_in_buffer(struct tty_struct *tty)
11898c2ecf20Sopenharmony_ci{
11908c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n",
11938c2ecf20Sopenharmony_ci		 mp->outbytes, mp->nack,
11948c2ecf20Sopenharmony_ci		 skb_queue_len(&mp->outqueue),
11958c2ecf20Sopenharmony_ci		 skb_queue_len(&mp->inqueue));
11968c2ecf20Sopenharmony_ci	return mp->outbytes;
11978c2ecf20Sopenharmony_ci}
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_cistatic void capinc_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_set_termios\n");
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic void capinc_tty_throttle(struct tty_struct *tty)
12058c2ecf20Sopenharmony_ci{
12068c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
12078c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_throttle\n");
12088c2ecf20Sopenharmony_ci	mp->ttyinstop = 1;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_cistatic void capinc_tty_unthrottle(struct tty_struct *tty)
12128c2ecf20Sopenharmony_ci{
12138c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_unthrottle\n");
12168c2ecf20Sopenharmony_ci	mp->ttyinstop = 0;
12178c2ecf20Sopenharmony_ci	handle_minor_recv(mp);
12188c2ecf20Sopenharmony_ci}
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_cistatic void capinc_tty_stop(struct tty_struct *tty)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_stop\n");
12258c2ecf20Sopenharmony_ci	mp->ttyoutstop = 1;
12268c2ecf20Sopenharmony_ci}
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_cistatic void capinc_tty_start(struct tty_struct *tty)
12298c2ecf20Sopenharmony_ci{
12308c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_start\n");
12338c2ecf20Sopenharmony_ci	mp->ttyoutstop = 0;
12348c2ecf20Sopenharmony_ci	handle_minor_send(mp);
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic void capinc_tty_hangup(struct tty_struct *tty)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct capiminor *mp = tty->driver_data;
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_hangup\n");
12428c2ecf20Sopenharmony_ci	tty_port_hangup(&mp->port);
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_cistatic int capinc_tty_break_ctl(struct tty_struct *tty, int state)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_break_ctl(%d)\n", state);
12488c2ecf20Sopenharmony_ci	return 0;
12498c2ecf20Sopenharmony_ci}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_cistatic void capinc_tty_flush_buffer(struct tty_struct *tty)
12528c2ecf20Sopenharmony_ci{
12538c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_flush_buffer\n");
12548c2ecf20Sopenharmony_ci}
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_cistatic void capinc_tty_set_ldisc(struct tty_struct *tty)
12578c2ecf20Sopenharmony_ci{
12588c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_set_ldisc\n");
12598c2ecf20Sopenharmony_ci}
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_cistatic void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
12628c2ecf20Sopenharmony_ci{
12638c2ecf20Sopenharmony_ci	pr_debug("capinc_tty_send_xchar(%d)\n", ch);
12648c2ecf20Sopenharmony_ci}
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_cistatic const struct tty_operations capinc_ops = {
12678c2ecf20Sopenharmony_ci	.open = capinc_tty_open,
12688c2ecf20Sopenharmony_ci	.close = capinc_tty_close,
12698c2ecf20Sopenharmony_ci	.write = capinc_tty_write,
12708c2ecf20Sopenharmony_ci	.put_char = capinc_tty_put_char,
12718c2ecf20Sopenharmony_ci	.flush_chars = capinc_tty_flush_chars,
12728c2ecf20Sopenharmony_ci	.write_room = capinc_tty_write_room,
12738c2ecf20Sopenharmony_ci	.chars_in_buffer = capinc_tty_chars_in_buffer,
12748c2ecf20Sopenharmony_ci	.set_termios = capinc_tty_set_termios,
12758c2ecf20Sopenharmony_ci	.throttle = capinc_tty_throttle,
12768c2ecf20Sopenharmony_ci	.unthrottle = capinc_tty_unthrottle,
12778c2ecf20Sopenharmony_ci	.stop = capinc_tty_stop,
12788c2ecf20Sopenharmony_ci	.start = capinc_tty_start,
12798c2ecf20Sopenharmony_ci	.hangup = capinc_tty_hangup,
12808c2ecf20Sopenharmony_ci	.break_ctl = capinc_tty_break_ctl,
12818c2ecf20Sopenharmony_ci	.flush_buffer = capinc_tty_flush_buffer,
12828c2ecf20Sopenharmony_ci	.set_ldisc = capinc_tty_set_ldisc,
12838c2ecf20Sopenharmony_ci	.send_xchar = capinc_tty_send_xchar,
12848c2ecf20Sopenharmony_ci	.install = capinc_tty_install,
12858c2ecf20Sopenharmony_ci	.cleanup = capinc_tty_cleanup,
12868c2ecf20Sopenharmony_ci};
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_cistatic int __init capinc_tty_init(void)
12898c2ecf20Sopenharmony_ci{
12908c2ecf20Sopenharmony_ci	struct tty_driver *drv;
12918c2ecf20Sopenharmony_ci	int err;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	if (capi_ttyminors > CAPINC_MAX_PORTS)
12948c2ecf20Sopenharmony_ci		capi_ttyminors = CAPINC_MAX_PORTS;
12958c2ecf20Sopenharmony_ci	if (capi_ttyminors <= 0)
12968c2ecf20Sopenharmony_ci		capi_ttyminors = CAPINC_NR_PORTS;
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci	capiminors = kcalloc(capi_ttyminors, sizeof(struct capiminor *),
12998c2ecf20Sopenharmony_ci			     GFP_KERNEL);
13008c2ecf20Sopenharmony_ci	if (!capiminors)
13018c2ecf20Sopenharmony_ci		return -ENOMEM;
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	drv = alloc_tty_driver(capi_ttyminors);
13048c2ecf20Sopenharmony_ci	if (!drv) {
13058c2ecf20Sopenharmony_ci		kfree(capiminors);
13068c2ecf20Sopenharmony_ci		return -ENOMEM;
13078c2ecf20Sopenharmony_ci	}
13088c2ecf20Sopenharmony_ci	drv->driver_name = "capi_nc";
13098c2ecf20Sopenharmony_ci	drv->name = "capi!";
13108c2ecf20Sopenharmony_ci	drv->major = 0;
13118c2ecf20Sopenharmony_ci	drv->minor_start = 0;
13128c2ecf20Sopenharmony_ci	drv->type = TTY_DRIVER_TYPE_SERIAL;
13138c2ecf20Sopenharmony_ci	drv->subtype = SERIAL_TYPE_NORMAL;
13148c2ecf20Sopenharmony_ci	drv->init_termios = tty_std_termios;
13158c2ecf20Sopenharmony_ci	drv->init_termios.c_iflag = ICRNL;
13168c2ecf20Sopenharmony_ci	drv->init_termios.c_oflag = OPOST | ONLCR;
13178c2ecf20Sopenharmony_ci	drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
13188c2ecf20Sopenharmony_ci	drv->init_termios.c_lflag = 0;
13198c2ecf20Sopenharmony_ci	drv->flags =
13208c2ecf20Sopenharmony_ci		TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS |
13218c2ecf20Sopenharmony_ci		TTY_DRIVER_DYNAMIC_DEV;
13228c2ecf20Sopenharmony_ci	tty_set_operations(drv, &capinc_ops);
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	err = tty_register_driver(drv);
13258c2ecf20Sopenharmony_ci	if (err) {
13268c2ecf20Sopenharmony_ci		put_tty_driver(drv);
13278c2ecf20Sopenharmony_ci		kfree(capiminors);
13288c2ecf20Sopenharmony_ci		printk(KERN_ERR "Couldn't register capi_nc driver\n");
13298c2ecf20Sopenharmony_ci		return err;
13308c2ecf20Sopenharmony_ci	}
13318c2ecf20Sopenharmony_ci	capinc_tty_driver = drv;
13328c2ecf20Sopenharmony_ci	return 0;
13338c2ecf20Sopenharmony_ci}
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_cistatic void __exit capinc_tty_exit(void)
13368c2ecf20Sopenharmony_ci{
13378c2ecf20Sopenharmony_ci	tty_unregister_driver(capinc_tty_driver);
13388c2ecf20Sopenharmony_ci	put_tty_driver(capinc_tty_driver);
13398c2ecf20Sopenharmony_ci	kfree(capiminors);
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_cistatic inline int capinc_tty_init(void)
13458c2ecf20Sopenharmony_ci{
13468c2ecf20Sopenharmony_ci	return 0;
13478c2ecf20Sopenharmony_ci}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_cistatic inline void capinc_tty_exit(void) { }
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci/* -------- /proc functions ----------------------------------------- */
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci/*
13568c2ecf20Sopenharmony_ci * /proc/capi/capi20:
13578c2ecf20Sopenharmony_ci *  minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
13588c2ecf20Sopenharmony_ci */
13598c2ecf20Sopenharmony_cistatic int __maybe_unused capi20_proc_show(struct seq_file *m, void *v)
13608c2ecf20Sopenharmony_ci{
13618c2ecf20Sopenharmony_ci	struct capidev *cdev;
13628c2ecf20Sopenharmony_ci	struct list_head *l;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	mutex_lock(&capidev_list_lock);
13658c2ecf20Sopenharmony_ci	list_for_each(l, &capidev_list) {
13668c2ecf20Sopenharmony_ci		cdev = list_entry(l, struct capidev, list);
13678c2ecf20Sopenharmony_ci		seq_printf(m, "0 %d %lu %lu %lu %lu\n",
13688c2ecf20Sopenharmony_ci			   cdev->ap.applid,
13698c2ecf20Sopenharmony_ci			   cdev->ap.nrecvctlpkt,
13708c2ecf20Sopenharmony_ci			   cdev->ap.nrecvdatapkt,
13718c2ecf20Sopenharmony_ci			   cdev->ap.nsentctlpkt,
13728c2ecf20Sopenharmony_ci			   cdev->ap.nsentdatapkt);
13738c2ecf20Sopenharmony_ci	}
13748c2ecf20Sopenharmony_ci	mutex_unlock(&capidev_list_lock);
13758c2ecf20Sopenharmony_ci	return 0;
13768c2ecf20Sopenharmony_ci}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci/*
13798c2ecf20Sopenharmony_ci * /proc/capi/capi20ncci:
13808c2ecf20Sopenharmony_ci *  applid ncci
13818c2ecf20Sopenharmony_ci */
13828c2ecf20Sopenharmony_cistatic int __maybe_unused capi20ncci_proc_show(struct seq_file *m, void *v)
13838c2ecf20Sopenharmony_ci{
13848c2ecf20Sopenharmony_ci	struct capidev *cdev;
13858c2ecf20Sopenharmony_ci	struct capincci *np;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	mutex_lock(&capidev_list_lock);
13888c2ecf20Sopenharmony_ci	list_for_each_entry(cdev, &capidev_list, list) {
13898c2ecf20Sopenharmony_ci		mutex_lock(&cdev->lock);
13908c2ecf20Sopenharmony_ci		list_for_each_entry(np, &cdev->nccis, list)
13918c2ecf20Sopenharmony_ci			seq_printf(m, "%d 0x%x\n", cdev->ap.applid, np->ncci);
13928c2ecf20Sopenharmony_ci		mutex_unlock(&cdev->lock);
13938c2ecf20Sopenharmony_ci	}
13948c2ecf20Sopenharmony_ci	mutex_unlock(&capidev_list_lock);
13958c2ecf20Sopenharmony_ci	return 0;
13968c2ecf20Sopenharmony_ci}
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_cistatic void __init proc_init(void)
13998c2ecf20Sopenharmony_ci{
14008c2ecf20Sopenharmony_ci	proc_create_single("capi/capi20", 0, NULL, capi20_proc_show);
14018c2ecf20Sopenharmony_ci	proc_create_single("capi/capi20ncci", 0, NULL, capi20ncci_proc_show);
14028c2ecf20Sopenharmony_ci}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_cistatic void __exit proc_exit(void)
14058c2ecf20Sopenharmony_ci{
14068c2ecf20Sopenharmony_ci	remove_proc_entry("capi/capi20", NULL);
14078c2ecf20Sopenharmony_ci	remove_proc_entry("capi/capi20ncci", NULL);
14088c2ecf20Sopenharmony_ci}
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci/* -------- init function and module interface ---------------------- */
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic int __init capi_init(void)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci	const char *compileinfo;
14168c2ecf20Sopenharmony_ci	int major_ret;
14178c2ecf20Sopenharmony_ci	int ret;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	ret = kcapi_init();
14208c2ecf20Sopenharmony_ci	if (ret)
14218c2ecf20Sopenharmony_ci		return ret;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	major_ret = register_chrdev(capi_major, "capi20", &capi_fops);
14248c2ecf20Sopenharmony_ci	if (major_ret < 0) {
14258c2ecf20Sopenharmony_ci		printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
14268c2ecf20Sopenharmony_ci		kcapi_exit();
14278c2ecf20Sopenharmony_ci		return major_ret;
14288c2ecf20Sopenharmony_ci	}
14298c2ecf20Sopenharmony_ci	capi_class = class_create(THIS_MODULE, "capi");
14308c2ecf20Sopenharmony_ci	if (IS_ERR(capi_class)) {
14318c2ecf20Sopenharmony_ci		unregister_chrdev(capi_major, "capi20");
14328c2ecf20Sopenharmony_ci		kcapi_exit();
14338c2ecf20Sopenharmony_ci		return PTR_ERR(capi_class);
14348c2ecf20Sopenharmony_ci	}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi20");
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	if (capinc_tty_init() < 0) {
14398c2ecf20Sopenharmony_ci		device_destroy(capi_class, MKDEV(capi_major, 0));
14408c2ecf20Sopenharmony_ci		class_destroy(capi_class);
14418c2ecf20Sopenharmony_ci		unregister_chrdev(capi_major, "capi20");
14428c2ecf20Sopenharmony_ci		kcapi_exit();
14438c2ecf20Sopenharmony_ci		return -ENOMEM;
14448c2ecf20Sopenharmony_ci	}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	proc_init();
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
14498c2ecf20Sopenharmony_ci	compileinfo = " (middleware)";
14508c2ecf20Sopenharmony_ci#else
14518c2ecf20Sopenharmony_ci	compileinfo = " (no middleware)";
14528c2ecf20Sopenharmony_ci#endif
14538c2ecf20Sopenharmony_ci	printk(KERN_NOTICE "CAPI 2.0 started up with major %d%s\n",
14548c2ecf20Sopenharmony_ci	       capi_major, compileinfo);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	return 0;
14578c2ecf20Sopenharmony_ci}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_cistatic void __exit capi_exit(void)
14608c2ecf20Sopenharmony_ci{
14618c2ecf20Sopenharmony_ci	proc_exit();
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	device_destroy(capi_class, MKDEV(capi_major, 0));
14648c2ecf20Sopenharmony_ci	class_destroy(capi_class);
14658c2ecf20Sopenharmony_ci	unregister_chrdev(capi_major, "capi20");
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	capinc_tty_exit();
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	kcapi_exit();
14708c2ecf20Sopenharmony_ci}
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_cimodule_init(capi_init);
14738c2ecf20Sopenharmony_cimodule_exit(capi_exit);
1474