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