162306a36Sopenharmony_ci/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $ 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * CAPI 2.0 Interface for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 1996 by Carsten Paeth <calle@calle.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This software may be used and distributed according to the terms 862306a36Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/compiler.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/ethtool.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/major.h> 1862306a36Sopenharmony_ci#include <linux/sched.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/fcntl.h> 2162306a36Sopenharmony_ci#include <linux/fs.h> 2262306a36Sopenharmony_ci#include <linux/signal.h> 2362306a36Sopenharmony_ci#include <linux/mutex.h> 2462306a36Sopenharmony_ci#include <linux/mm.h> 2562306a36Sopenharmony_ci#include <linux/timer.h> 2662306a36Sopenharmony_ci#include <linux/wait.h> 2762306a36Sopenharmony_ci#include <linux/tty.h> 2862306a36Sopenharmony_ci#include <linux/netdevice.h> 2962306a36Sopenharmony_ci#include <linux/ppp_defs.h> 3062306a36Sopenharmony_ci#include <linux/ppp-ioctl.h> 3162306a36Sopenharmony_ci#include <linux/skbuff.h> 3262306a36Sopenharmony_ci#include <linux/proc_fs.h> 3362306a36Sopenharmony_ci#include <linux/seq_file.h> 3462306a36Sopenharmony_ci#include <linux/poll.h> 3562306a36Sopenharmony_ci#include <linux/capi.h> 3662306a36Sopenharmony_ci#include <linux/kernelcapi.h> 3762306a36Sopenharmony_ci#include <linux/init.h> 3862306a36Sopenharmony_ci#include <linux/device.h> 3962306a36Sopenharmony_ci#include <linux/moduleparam.h> 4062306a36Sopenharmony_ci#include <linux/isdn/capiutil.h> 4162306a36Sopenharmony_ci#include <linux/isdn/capicmd.h> 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include "kcapi.h" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ciMODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer and /dev/capi20 interface"); 4662306a36Sopenharmony_ciMODULE_AUTHOR("Carsten Paeth"); 4762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* -------- driver information -------------------------------------- */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic DEFINE_MUTEX(capi_mutex); 5262306a36Sopenharmony_cistatic struct class *capi_class; 5362306a36Sopenharmony_cistatic int capi_major = 68; /* allocated */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cimodule_param_named(major, capi_major, uint, 0); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE 5862306a36Sopenharmony_ci#define CAPINC_NR_PORTS 32 5962306a36Sopenharmony_ci#define CAPINC_MAX_PORTS 256 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int capi_ttyminors = CAPINC_NR_PORTS; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cimodule_param_named(ttyminors, capi_ttyminors, uint, 0); 6462306a36Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* -------- defines ------------------------------------------------- */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define CAPINC_MAX_RECVQUEUE 10 6962306a36Sopenharmony_ci#define CAPINC_MAX_SENDQUEUE 10 7062306a36Sopenharmony_ci#define CAPI_MAX_BLKSIZE 2048 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* -------- data structures ----------------------------------------- */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct capidev; 7562306a36Sopenharmony_cistruct capincci; 7662306a36Sopenharmony_cistruct capiminor; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistruct ackqueue_entry { 7962306a36Sopenharmony_ci struct list_head list; 8062306a36Sopenharmony_ci u16 datahandle; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct capiminor { 8462306a36Sopenharmony_ci unsigned int minor; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci struct capi20_appl *ap; 8762306a36Sopenharmony_ci u32 ncci; 8862306a36Sopenharmony_ci atomic_t datahandle; 8962306a36Sopenharmony_ci atomic_t msgid; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci struct tty_port port; 9262306a36Sopenharmony_ci int ttyinstop; 9362306a36Sopenharmony_ci int ttyoutstop; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci struct sk_buff_head inqueue; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci struct sk_buff_head outqueue; 9862306a36Sopenharmony_ci int outbytes; 9962306a36Sopenharmony_ci struct sk_buff *outskb; 10062306a36Sopenharmony_ci spinlock_t outlock; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* transmit path */ 10362306a36Sopenharmony_ci struct list_head ackqueue; 10462306a36Sopenharmony_ci int nack; 10562306a36Sopenharmony_ci spinlock_t ackqlock; 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistruct capincci { 10962306a36Sopenharmony_ci struct list_head list; 11062306a36Sopenharmony_ci u32 ncci; 11162306a36Sopenharmony_ci struct capidev *cdev; 11262306a36Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE 11362306a36Sopenharmony_ci struct capiminor *minorp; 11462306a36Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 11562306a36Sopenharmony_ci}; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistruct capidev { 11862306a36Sopenharmony_ci struct list_head list; 11962306a36Sopenharmony_ci struct capi20_appl ap; 12062306a36Sopenharmony_ci u16 errcode; 12162306a36Sopenharmony_ci unsigned userflags; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci struct sk_buff_head recvqueue; 12462306a36Sopenharmony_ci wait_queue_head_t recvwait; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci struct list_head nccis; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci struct mutex lock; 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* -------- global variables ---------------------------------------- */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic DEFINE_MUTEX(capidev_list_lock); 13462306a36Sopenharmony_cistatic LIST_HEAD(capidev_list); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(capiminors_lock); 13962306a36Sopenharmony_cistatic struct capiminor **capiminors; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic struct tty_driver *capinc_tty_driver; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* -------- datahandles --------------------------------------------- */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int capiminor_add_ack(struct capiminor *mp, u16 datahandle) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct ackqueue_entry *n; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci n = kmalloc(sizeof(*n), GFP_ATOMIC); 15062306a36Sopenharmony_ci if (unlikely(!n)) { 15162306a36Sopenharmony_ci printk(KERN_ERR "capi: alloc datahandle failed\n"); 15262306a36Sopenharmony_ci return -1; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci n->datahandle = datahandle; 15562306a36Sopenharmony_ci INIT_LIST_HEAD(&n->list); 15662306a36Sopenharmony_ci spin_lock_bh(&mp->ackqlock); 15762306a36Sopenharmony_ci list_add_tail(&n->list, &mp->ackqueue); 15862306a36Sopenharmony_ci mp->nack++; 15962306a36Sopenharmony_ci spin_unlock_bh(&mp->ackqlock); 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int capiminor_del_ack(struct capiminor *mp, u16 datahandle) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct ackqueue_entry *p, *tmp; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci spin_lock_bh(&mp->ackqlock); 16862306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { 16962306a36Sopenharmony_ci if (p->datahandle == datahandle) { 17062306a36Sopenharmony_ci list_del(&p->list); 17162306a36Sopenharmony_ci mp->nack--; 17262306a36Sopenharmony_ci spin_unlock_bh(&mp->ackqlock); 17362306a36Sopenharmony_ci kfree(p); 17462306a36Sopenharmony_ci return 0; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci spin_unlock_bh(&mp->ackqlock); 17862306a36Sopenharmony_ci return -1; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void capiminor_del_all_ack(struct capiminor *mp) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct ackqueue_entry *p, *tmp; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { 18662306a36Sopenharmony_ci list_del(&p->list); 18762306a36Sopenharmony_ci kfree(p); 18862306a36Sopenharmony_ci mp->nack--; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci/* -------- struct capiminor ---------------------------------------- */ 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void capiminor_destroy(struct tty_port *port) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct capiminor *mp = container_of(port, struct capiminor, port); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci kfree_skb(mp->outskb); 20062306a36Sopenharmony_ci skb_queue_purge(&mp->inqueue); 20162306a36Sopenharmony_ci skb_queue_purge(&mp->outqueue); 20262306a36Sopenharmony_ci capiminor_del_all_ack(mp); 20362306a36Sopenharmony_ci kfree(mp); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct tty_port_operations capiminor_port_ops = { 20762306a36Sopenharmony_ci .destruct = capiminor_destroy, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct capiminor *mp; 21362306a36Sopenharmony_ci struct device *dev; 21462306a36Sopenharmony_ci unsigned int minor; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci mp = kzalloc(sizeof(*mp), GFP_KERNEL); 21762306a36Sopenharmony_ci if (!mp) { 21862306a36Sopenharmony_ci printk(KERN_ERR "capi: can't alloc capiminor\n"); 21962306a36Sopenharmony_ci return NULL; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci mp->ap = ap; 22362306a36Sopenharmony_ci mp->ncci = ncci; 22462306a36Sopenharmony_ci INIT_LIST_HEAD(&mp->ackqueue); 22562306a36Sopenharmony_ci spin_lock_init(&mp->ackqlock); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci skb_queue_head_init(&mp->inqueue); 22862306a36Sopenharmony_ci skb_queue_head_init(&mp->outqueue); 22962306a36Sopenharmony_ci spin_lock_init(&mp->outlock); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci tty_port_init(&mp->port); 23262306a36Sopenharmony_ci mp->port.ops = &capiminor_port_ops; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Allocate the least unused minor number. */ 23562306a36Sopenharmony_ci spin_lock(&capiminors_lock); 23662306a36Sopenharmony_ci for (minor = 0; minor < capi_ttyminors; minor++) 23762306a36Sopenharmony_ci if (!capiminors[minor]) { 23862306a36Sopenharmony_ci capiminors[minor] = mp; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci spin_unlock(&capiminors_lock); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (minor == capi_ttyminors) { 24462306a36Sopenharmony_ci printk(KERN_NOTICE "capi: out of minors\n"); 24562306a36Sopenharmony_ci goto err_out1; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci mp->minor = minor; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci dev = tty_port_register_device(&mp->port, capinc_tty_driver, minor, 25162306a36Sopenharmony_ci NULL); 25262306a36Sopenharmony_ci if (IS_ERR(dev)) 25362306a36Sopenharmony_ci goto err_out2; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return mp; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cierr_out2: 25862306a36Sopenharmony_ci spin_lock(&capiminors_lock); 25962306a36Sopenharmony_ci capiminors[minor] = NULL; 26062306a36Sopenharmony_ci spin_unlock(&capiminors_lock); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cierr_out1: 26362306a36Sopenharmony_ci tty_port_put(&mp->port); 26462306a36Sopenharmony_ci return NULL; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic struct capiminor *capiminor_get(unsigned int minor) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct capiminor *mp; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci spin_lock(&capiminors_lock); 27262306a36Sopenharmony_ci mp = capiminors[minor]; 27362306a36Sopenharmony_ci if (mp) 27462306a36Sopenharmony_ci tty_port_get(&mp->port); 27562306a36Sopenharmony_ci spin_unlock(&capiminors_lock); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return mp; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic inline void capiminor_put(struct capiminor *mp) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci tty_port_put(&mp->port); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic void capiminor_free(struct capiminor *mp) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci tty_unregister_device(capinc_tty_driver, mp->minor); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci spin_lock(&capiminors_lock); 29062306a36Sopenharmony_ci capiminors[mp->minor] = NULL; 29162306a36Sopenharmony_ci spin_unlock(&capiminors_lock); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci capiminor_put(mp); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* -------- struct capincci ----------------------------------------- */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void capincci_alloc_minor(struct capidev *cdev, struct capincci *np) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci if (cdev->userflags & CAPIFLAG_HIGHJACKING) 30162306a36Sopenharmony_ci np->minorp = capiminor_alloc(&cdev->ap, np->ncci); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void capincci_free_minor(struct capincci *np) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct capiminor *mp = np->minorp; 30762306a36Sopenharmony_ci struct tty_struct *tty; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (mp) { 31062306a36Sopenharmony_ci tty = tty_port_tty_get(&mp->port); 31162306a36Sopenharmony_ci if (tty) { 31262306a36Sopenharmony_ci tty_vhangup(tty); 31362306a36Sopenharmony_ci tty_kref_put(tty); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci capiminor_free(mp); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic inline unsigned int capincci_minor_opencount(struct capincci *np) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct capiminor *mp = np->minorp; 32362306a36Sopenharmony_ci unsigned int count = 0; 32462306a36Sopenharmony_ci struct tty_struct *tty; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (mp) { 32762306a36Sopenharmony_ci tty = tty_port_tty_get(&mp->port); 32862306a36Sopenharmony_ci if (tty) { 32962306a36Sopenharmony_ci count = tty->count; 33062306a36Sopenharmony_ci tty_kref_put(tty); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci return count; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic inline void 33962306a36Sopenharmony_cicapincci_alloc_minor(struct capidev *cdev, struct capincci *np) { } 34062306a36Sopenharmony_cistatic inline void capincci_free_minor(struct capincci *np) { } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct capincci *np; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci np = kzalloc(sizeof(*np), GFP_KERNEL); 34962306a36Sopenharmony_ci if (!np) 35062306a36Sopenharmony_ci return NULL; 35162306a36Sopenharmony_ci np->ncci = ncci; 35262306a36Sopenharmony_ci np->cdev = cdev; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci capincci_alloc_minor(cdev, np); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci list_add_tail(&np->list, &cdev->nccis); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return np; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void capincci_free(struct capidev *cdev, u32 ncci) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct capincci *np, *tmp; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci list_for_each_entry_safe(np, tmp, &cdev->nccis, list) 36662306a36Sopenharmony_ci if (ncci == 0xffffffff || np->ncci == ncci) { 36762306a36Sopenharmony_ci capincci_free_minor(np); 36862306a36Sopenharmony_ci list_del(&np->list); 36962306a36Sopenharmony_ci kfree(np); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE 37462306a36Sopenharmony_cistatic struct capincci *capincci_find(struct capidev *cdev, u32 ncci) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct capincci *np; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci list_for_each_entry(np, &cdev->nccis, list) 37962306a36Sopenharmony_ci if (np->ncci == ncci) 38062306a36Sopenharmony_ci return np; 38162306a36Sopenharmony_ci return NULL; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* -------- handle data queue --------------------------------------- */ 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic struct sk_buff * 38762306a36Sopenharmony_cigen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct sk_buff *nskb; 39062306a36Sopenharmony_ci nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_KERNEL); 39162306a36Sopenharmony_ci if (nskb) { 39262306a36Sopenharmony_ci u16 datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2); 39362306a36Sopenharmony_ci unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); 39462306a36Sopenharmony_ci capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN); 39562306a36Sopenharmony_ci capimsg_setu16(s, 2, mp->ap->applid); 39662306a36Sopenharmony_ci capimsg_setu8 (s, 4, CAPI_DATA_B3); 39762306a36Sopenharmony_ci capimsg_setu8 (s, 5, CAPI_RESP); 39862306a36Sopenharmony_ci capimsg_setu16(s, 6, atomic_inc_return(&mp->msgid)); 39962306a36Sopenharmony_ci capimsg_setu32(s, 8, mp->ncci); 40062306a36Sopenharmony_ci capimsg_setu16(s, 12, datahandle); 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci return nskb; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data); 40862306a36Sopenharmony_ci struct tty_struct *tty; 40962306a36Sopenharmony_ci struct sk_buff *nskb; 41062306a36Sopenharmony_ci u16 errcode, datahandle; 41162306a36Sopenharmony_ci struct tty_ldisc *ld; 41262306a36Sopenharmony_ci int ret = -1; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci tty = tty_port_tty_get(&mp->port); 41562306a36Sopenharmony_ci if (!tty) { 41662306a36Sopenharmony_ci pr_debug("capi: currently no receiver\n"); 41762306a36Sopenharmony_ci return -1; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ld = tty_ldisc_ref(tty); 42162306a36Sopenharmony_ci if (!ld) { 42262306a36Sopenharmony_ci /* fatal error, do not requeue */ 42362306a36Sopenharmony_ci ret = 0; 42462306a36Sopenharmony_ci kfree_skb(skb); 42562306a36Sopenharmony_ci goto deref_tty; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (ld->ops->receive_buf == NULL) { 42962306a36Sopenharmony_ci pr_debug("capi: ldisc has no receive_buf function\n"); 43062306a36Sopenharmony_ci /* fatal error, do not requeue */ 43162306a36Sopenharmony_ci goto free_skb; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci if (mp->ttyinstop) { 43462306a36Sopenharmony_ci pr_debug("capi: recv tty throttled\n"); 43562306a36Sopenharmony_ci goto deref_ldisc; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (tty->receive_room < datalen) { 43962306a36Sopenharmony_ci pr_debug("capi: no room in tty\n"); 44062306a36Sopenharmony_ci goto deref_ldisc; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci nskb = gen_data_b3_resp_for(mp, skb); 44462306a36Sopenharmony_ci if (!nskb) { 44562306a36Sopenharmony_ci printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); 44662306a36Sopenharmony_ci goto deref_ldisc; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci errcode = capi20_put_message(mp->ap, nskb); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (errcode == CAPI_NOERROR) { 45462306a36Sopenharmony_ci skb_pull(skb, CAPIMSG_LEN(skb->data)); 45562306a36Sopenharmony_ci pr_debug("capi: DATA_B3_RESP %u len=%d => ldisc\n", 45662306a36Sopenharmony_ci datahandle, skb->len); 45762306a36Sopenharmony_ci ld->ops->receive_buf(tty, skb->data, NULL, skb->len); 45862306a36Sopenharmony_ci } else { 45962306a36Sopenharmony_ci printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", 46062306a36Sopenharmony_ci errcode); 46162306a36Sopenharmony_ci kfree_skb(nskb); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (errcode == CAPI_SENDQUEUEFULL) 46462306a36Sopenharmony_ci goto deref_ldisc; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cifree_skb: 46862306a36Sopenharmony_ci ret = 0; 46962306a36Sopenharmony_ci kfree_skb(skb); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cideref_ldisc: 47262306a36Sopenharmony_ci tty_ldisc_deref(ld); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cideref_tty: 47562306a36Sopenharmony_ci tty_kref_put(tty); 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void handle_minor_recv(struct capiminor *mp) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct sk_buff *skb; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci while ((skb = skb_dequeue(&mp->inqueue)) != NULL) 48462306a36Sopenharmony_ci if (handle_recv_skb(mp, skb) < 0) { 48562306a36Sopenharmony_ci skb_queue_head(&mp->inqueue, skb); 48662306a36Sopenharmony_ci return; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic void handle_minor_send(struct capiminor *mp) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct tty_struct *tty; 49362306a36Sopenharmony_ci struct sk_buff *skb; 49462306a36Sopenharmony_ci u16 len; 49562306a36Sopenharmony_ci u16 errcode; 49662306a36Sopenharmony_ci u16 datahandle; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci tty = tty_port_tty_get(&mp->port); 49962306a36Sopenharmony_ci if (!tty) 50062306a36Sopenharmony_ci return; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (mp->ttyoutstop) { 50362306a36Sopenharmony_ci pr_debug("capi: send: tty stopped\n"); 50462306a36Sopenharmony_ci tty_kref_put(tty); 50562306a36Sopenharmony_ci return; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci while (1) { 50962306a36Sopenharmony_ci spin_lock_bh(&mp->outlock); 51062306a36Sopenharmony_ci skb = __skb_dequeue(&mp->outqueue); 51162306a36Sopenharmony_ci if (!skb) { 51262306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci len = (u16)skb->len; 51662306a36Sopenharmony_ci mp->outbytes -= len; 51762306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci datahandle = atomic_inc_return(&mp->datahandle); 52062306a36Sopenharmony_ci skb_push(skb, CAPI_DATA_B3_REQ_LEN); 52162306a36Sopenharmony_ci memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); 52262306a36Sopenharmony_ci capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); 52362306a36Sopenharmony_ci capimsg_setu16(skb->data, 2, mp->ap->applid); 52462306a36Sopenharmony_ci capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); 52562306a36Sopenharmony_ci capimsg_setu8 (skb->data, 5, CAPI_REQ); 52662306a36Sopenharmony_ci capimsg_setu16(skb->data, 6, atomic_inc_return(&mp->msgid)); 52762306a36Sopenharmony_ci capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ 52862306a36Sopenharmony_ci capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */ 52962306a36Sopenharmony_ci capimsg_setu16(skb->data, 16, len); /* Data length */ 53062306a36Sopenharmony_ci capimsg_setu16(skb->data, 18, datahandle); 53162306a36Sopenharmony_ci capimsg_setu16(skb->data, 20, 0); /* Flags */ 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (capiminor_add_ack(mp, datahandle) < 0) { 53462306a36Sopenharmony_ci skb_pull(skb, CAPI_DATA_B3_REQ_LEN); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci spin_lock_bh(&mp->outlock); 53762306a36Sopenharmony_ci __skb_queue_head(&mp->outqueue, skb); 53862306a36Sopenharmony_ci mp->outbytes += len; 53962306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci break; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci errcode = capi20_put_message(mp->ap, skb); 54462306a36Sopenharmony_ci if (errcode == CAPI_NOERROR) { 54562306a36Sopenharmony_ci pr_debug("capi: DATA_B3_REQ %u len=%u\n", 54662306a36Sopenharmony_ci datahandle, len); 54762306a36Sopenharmony_ci continue; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci capiminor_del_ack(mp, datahandle); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (errcode == CAPI_SENDQUEUEFULL) { 55262306a36Sopenharmony_ci skb_pull(skb, CAPI_DATA_B3_REQ_LEN); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci spin_lock_bh(&mp->outlock); 55562306a36Sopenharmony_ci __skb_queue_head(&mp->outqueue, skb); 55662306a36Sopenharmony_ci mp->outbytes += len; 55762306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* ups, drop packet */ 56362306a36Sopenharmony_ci printk(KERN_ERR "capi: put_message = %x\n", errcode); 56462306a36Sopenharmony_ci kfree_skb(skb); 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci tty_kref_put(tty); 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 57062306a36Sopenharmony_ci/* -------- function called by lower level -------------------------- */ 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci struct capidev *cdev = ap->private; 57562306a36Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE 57662306a36Sopenharmony_ci struct capiminor *mp; 57762306a36Sopenharmony_ci u16 datahandle; 57862306a36Sopenharmony_ci struct capincci *np; 57962306a36Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci mutex_lock(&cdev->lock); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { 58462306a36Sopenharmony_ci u16 info = CAPIMSG_U16(skb->data, 12); // Info field 58562306a36Sopenharmony_ci if ((info & 0xff00) == 0) 58662306a36Sopenharmony_ci capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) 58962306a36Sopenharmony_ci capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { 59262306a36Sopenharmony_ci skb_queue_tail(&cdev->recvqueue, skb); 59362306a36Sopenharmony_ci wake_up_interruptible(&cdev->recvwait); 59462306a36Sopenharmony_ci goto unlock_out; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE 59862306a36Sopenharmony_ci skb_queue_tail(&cdev->recvqueue, skb); 59962306a36Sopenharmony_ci wake_up_interruptible(&cdev->recvwait); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci np = capincci_find(cdev, CAPIMSG_CONTROL(skb->data)); 60462306a36Sopenharmony_ci if (!np) { 60562306a36Sopenharmony_ci printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); 60662306a36Sopenharmony_ci skb_queue_tail(&cdev->recvqueue, skb); 60762306a36Sopenharmony_ci wake_up_interruptible(&cdev->recvwait); 60862306a36Sopenharmony_ci goto unlock_out; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci mp = np->minorp; 61262306a36Sopenharmony_ci if (!mp) { 61362306a36Sopenharmony_ci skb_queue_tail(&cdev->recvqueue, skb); 61462306a36Sopenharmony_ci wake_up_interruptible(&cdev->recvwait); 61562306a36Sopenharmony_ci goto unlock_out; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { 61862306a36Sopenharmony_ci datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 4 + 2); 61962306a36Sopenharmony_ci pr_debug("capi_signal: DATA_B3_IND %u len=%d\n", 62062306a36Sopenharmony_ci datahandle, skb->len-CAPIMSG_LEN(skb->data)); 62162306a36Sopenharmony_ci skb_queue_tail(&mp->inqueue, skb); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci handle_minor_recv(mp); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) { 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); 62862306a36Sopenharmony_ci pr_debug("capi_signal: DATA_B3_CONF %u 0x%x\n", 62962306a36Sopenharmony_ci datahandle, 63062306a36Sopenharmony_ci CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4 + 2)); 63162306a36Sopenharmony_ci kfree_skb(skb); 63262306a36Sopenharmony_ci capiminor_del_ack(mp, datahandle); 63362306a36Sopenharmony_ci tty_port_tty_wakeup(&mp->port); 63462306a36Sopenharmony_ci handle_minor_send(mp); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci /* ups, let capi application handle it :-) */ 63862306a36Sopenharmony_ci skb_queue_tail(&cdev->recvqueue, skb); 63962306a36Sopenharmony_ci wake_up_interruptible(&cdev->recvwait); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ciunlock_out: 64462306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/* -------- file_operations for capidev ----------------------------- */ 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic ssize_t 65062306a36Sopenharmony_cicapi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct capidev *cdev = file->private_data; 65362306a36Sopenharmony_ci struct sk_buff *skb; 65462306a36Sopenharmony_ci size_t copied; 65562306a36Sopenharmony_ci int err; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (!cdev->ap.applid) 65862306a36Sopenharmony_ci return -ENODEV; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci skb = skb_dequeue(&cdev->recvqueue); 66162306a36Sopenharmony_ci if (!skb) { 66262306a36Sopenharmony_ci if (file->f_flags & O_NONBLOCK) 66362306a36Sopenharmony_ci return -EAGAIN; 66462306a36Sopenharmony_ci err = wait_event_interruptible(cdev->recvwait, 66562306a36Sopenharmony_ci (skb = skb_dequeue(&cdev->recvqueue))); 66662306a36Sopenharmony_ci if (err) 66762306a36Sopenharmony_ci return err; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci if (skb->len > count) { 67062306a36Sopenharmony_ci skb_queue_head(&cdev->recvqueue, skb); 67162306a36Sopenharmony_ci return -EMSGSIZE; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci if (copy_to_user(buf, skb->data, skb->len)) { 67462306a36Sopenharmony_ci skb_queue_head(&cdev->recvqueue, skb); 67562306a36Sopenharmony_ci return -EFAULT; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci copied = skb->len; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci kfree_skb(skb); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return copied; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic ssize_t 68562306a36Sopenharmony_cicapi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct capidev *cdev = file->private_data; 68862306a36Sopenharmony_ci struct sk_buff *skb; 68962306a36Sopenharmony_ci u16 mlen; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (!cdev->ap.applid) 69262306a36Sopenharmony_ci return -ENODEV; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (count < CAPIMSG_BASELEN) 69562306a36Sopenharmony_ci return -EINVAL; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci skb = alloc_skb(count, GFP_USER); 69862306a36Sopenharmony_ci if (!skb) 69962306a36Sopenharmony_ci return -ENOMEM; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (copy_from_user(skb_put(skb, count), buf, count)) { 70262306a36Sopenharmony_ci kfree_skb(skb); 70362306a36Sopenharmony_ci return -EFAULT; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci mlen = CAPIMSG_LEN(skb->data); 70662306a36Sopenharmony_ci if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) { 70762306a36Sopenharmony_ci if (count < CAPI_DATA_B3_REQ_LEN || 70862306a36Sopenharmony_ci (size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) { 70962306a36Sopenharmony_ci kfree_skb(skb); 71062306a36Sopenharmony_ci return -EINVAL; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci } else { 71362306a36Sopenharmony_ci if (mlen != count) { 71462306a36Sopenharmony_ci kfree_skb(skb); 71562306a36Sopenharmony_ci return -EINVAL; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci CAPIMSG_SETAPPID(skb->data, cdev->ap.applid); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) { 72162306a36Sopenharmony_ci if (count < CAPI_DISCONNECT_B3_RESP_LEN) { 72262306a36Sopenharmony_ci kfree_skb(skb); 72362306a36Sopenharmony_ci return -EINVAL; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci mutex_lock(&cdev->lock); 72662306a36Sopenharmony_ci capincci_free(cdev, CAPIMSG_NCCI(skb->data)); 72762306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci cdev->errcode = capi20_put_message(&cdev->ap, skb); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (cdev->errcode) { 73362306a36Sopenharmony_ci kfree_skb(skb); 73462306a36Sopenharmony_ci return -EIO; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci return count; 73762306a36Sopenharmony_ci} 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_cistatic __poll_t 74062306a36Sopenharmony_cicapi_poll(struct file *file, poll_table *wait) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct capidev *cdev = file->private_data; 74362306a36Sopenharmony_ci __poll_t mask = 0; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (!cdev->ap.applid) 74662306a36Sopenharmony_ci return EPOLLERR; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci poll_wait(file, &(cdev->recvwait), wait); 74962306a36Sopenharmony_ci mask = EPOLLOUT | EPOLLWRNORM; 75062306a36Sopenharmony_ci if (!skb_queue_empty_lockless(&cdev->recvqueue)) 75162306a36Sopenharmony_ci mask |= EPOLLIN | EPOLLRDNORM; 75262306a36Sopenharmony_ci return mask; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int 75662306a36Sopenharmony_cicapi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct capidev *cdev = file->private_data; 75962306a36Sopenharmony_ci capi_ioctl_struct data; 76062306a36Sopenharmony_ci int retval = -EINVAL; 76162306a36Sopenharmony_ci void __user *argp = (void __user *)arg; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci switch (cmd) { 76462306a36Sopenharmony_ci case CAPI_REGISTER: 76562306a36Sopenharmony_ci mutex_lock(&cdev->lock); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (cdev->ap.applid) { 76862306a36Sopenharmony_ci retval = -EEXIST; 76962306a36Sopenharmony_ci goto register_out; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci if (copy_from_user(&cdev->ap.rparam, argp, 77262306a36Sopenharmony_ci sizeof(struct capi_register_params))) { 77362306a36Sopenharmony_ci retval = -EFAULT; 77462306a36Sopenharmony_ci goto register_out; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci cdev->ap.private = cdev; 77762306a36Sopenharmony_ci cdev->ap.recv_message = capi_recv_message; 77862306a36Sopenharmony_ci cdev->errcode = capi20_register(&cdev->ap); 77962306a36Sopenharmony_ci retval = (int)cdev->ap.applid; 78062306a36Sopenharmony_ci if (cdev->errcode) { 78162306a36Sopenharmony_ci cdev->ap.applid = 0; 78262306a36Sopenharmony_ci retval = -EIO; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ciregister_out: 78662306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 78762306a36Sopenharmony_ci return retval; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci case CAPI_GET_VERSION: 79062306a36Sopenharmony_ci if (copy_from_user(&data.contr, argp, 79162306a36Sopenharmony_ci sizeof(data.contr))) 79262306a36Sopenharmony_ci return -EFAULT; 79362306a36Sopenharmony_ci cdev->errcode = capi20_get_version(data.contr, &data.version); 79462306a36Sopenharmony_ci if (cdev->errcode) 79562306a36Sopenharmony_ci return -EIO; 79662306a36Sopenharmony_ci if (copy_to_user(argp, &data.version, 79762306a36Sopenharmony_ci sizeof(data.version))) 79862306a36Sopenharmony_ci return -EFAULT; 79962306a36Sopenharmony_ci return 0; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci case CAPI_GET_SERIAL: 80262306a36Sopenharmony_ci if (copy_from_user(&data.contr, argp, 80362306a36Sopenharmony_ci sizeof(data.contr))) 80462306a36Sopenharmony_ci return -EFAULT; 80562306a36Sopenharmony_ci cdev->errcode = capi20_get_serial(data.contr, data.serial); 80662306a36Sopenharmony_ci if (cdev->errcode) 80762306a36Sopenharmony_ci return -EIO; 80862306a36Sopenharmony_ci if (copy_to_user(argp, data.serial, 80962306a36Sopenharmony_ci sizeof(data.serial))) 81062306a36Sopenharmony_ci return -EFAULT; 81162306a36Sopenharmony_ci return 0; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci case CAPI_GET_PROFILE: 81462306a36Sopenharmony_ci if (copy_from_user(&data.contr, argp, 81562306a36Sopenharmony_ci sizeof(data.contr))) 81662306a36Sopenharmony_ci return -EFAULT; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (data.contr == 0) { 81962306a36Sopenharmony_ci cdev->errcode = capi20_get_profile(data.contr, &data.profile); 82062306a36Sopenharmony_ci if (cdev->errcode) 82162306a36Sopenharmony_ci return -EIO; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci retval = copy_to_user(argp, 82462306a36Sopenharmony_ci &data.profile.ncontroller, 82562306a36Sopenharmony_ci sizeof(data.profile.ncontroller)); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci } else { 82862306a36Sopenharmony_ci cdev->errcode = capi20_get_profile(data.contr, &data.profile); 82962306a36Sopenharmony_ci if (cdev->errcode) 83062306a36Sopenharmony_ci return -EIO; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci retval = copy_to_user(argp, &data.profile, 83362306a36Sopenharmony_ci sizeof(data.profile)); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci if (retval) 83662306a36Sopenharmony_ci return -EFAULT; 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci case CAPI_GET_MANUFACTURER: 84062306a36Sopenharmony_ci if (copy_from_user(&data.contr, argp, 84162306a36Sopenharmony_ci sizeof(data.contr))) 84262306a36Sopenharmony_ci return -EFAULT; 84362306a36Sopenharmony_ci cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer); 84462306a36Sopenharmony_ci if (cdev->errcode) 84562306a36Sopenharmony_ci return -EIO; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (copy_to_user(argp, data.manufacturer, 84862306a36Sopenharmony_ci sizeof(data.manufacturer))) 84962306a36Sopenharmony_ci return -EFAULT; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci case CAPI_GET_ERRCODE: 85462306a36Sopenharmony_ci data.errcode = cdev->errcode; 85562306a36Sopenharmony_ci cdev->errcode = CAPI_NOERROR; 85662306a36Sopenharmony_ci if (arg) { 85762306a36Sopenharmony_ci if (copy_to_user(argp, &data.errcode, 85862306a36Sopenharmony_ci sizeof(data.errcode))) 85962306a36Sopenharmony_ci return -EFAULT; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci return data.errcode; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci case CAPI_INSTALLED: 86462306a36Sopenharmony_ci if (capi20_isinstalled() == CAPI_NOERROR) 86562306a36Sopenharmony_ci return 0; 86662306a36Sopenharmony_ci return -ENXIO; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci case CAPI_MANUFACTURER_CMD: { 86962306a36Sopenharmony_ci struct capi_manufacturer_cmd mcmd; 87062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 87162306a36Sopenharmony_ci return -EPERM; 87262306a36Sopenharmony_ci if (copy_from_user(&mcmd, argp, sizeof(mcmd))) 87362306a36Sopenharmony_ci return -EFAULT; 87462306a36Sopenharmony_ci return capi20_manufacturer(mcmd.cmd, mcmd.data); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci case CAPI_SET_FLAGS: 87762306a36Sopenharmony_ci case CAPI_CLR_FLAGS: { 87862306a36Sopenharmony_ci unsigned userflags; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (copy_from_user(&userflags, argp, sizeof(userflags))) 88162306a36Sopenharmony_ci return -EFAULT; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci mutex_lock(&cdev->lock); 88462306a36Sopenharmony_ci if (cmd == CAPI_SET_FLAGS) 88562306a36Sopenharmony_ci cdev->userflags |= userflags; 88662306a36Sopenharmony_ci else 88762306a36Sopenharmony_ci cdev->userflags &= ~userflags; 88862306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 88962306a36Sopenharmony_ci return 0; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci case CAPI_GET_FLAGS: 89262306a36Sopenharmony_ci if (copy_to_user(argp, &cdev->userflags, 89362306a36Sopenharmony_ci sizeof(cdev->userflags))) 89462306a36Sopenharmony_ci return -EFAULT; 89562306a36Sopenharmony_ci return 0; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE 89862306a36Sopenharmony_ci case CAPI_NCCI_OPENCOUNT: 89962306a36Sopenharmony_ci return 0; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 90262306a36Sopenharmony_ci case CAPI_NCCI_OPENCOUNT: { 90362306a36Sopenharmony_ci struct capincci *nccip; 90462306a36Sopenharmony_ci unsigned ncci; 90562306a36Sopenharmony_ci int count = 0; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (copy_from_user(&ncci, argp, sizeof(ncci))) 90862306a36Sopenharmony_ci return -EFAULT; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci mutex_lock(&cdev->lock); 91162306a36Sopenharmony_ci nccip = capincci_find(cdev, (u32)ncci); 91262306a36Sopenharmony_ci if (nccip) 91362306a36Sopenharmony_ci count = capincci_minor_opencount(nccip); 91462306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 91562306a36Sopenharmony_ci return count; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci case CAPI_NCCI_GETUNIT: { 91962306a36Sopenharmony_ci struct capincci *nccip; 92062306a36Sopenharmony_ci struct capiminor *mp; 92162306a36Sopenharmony_ci unsigned ncci; 92262306a36Sopenharmony_ci int unit = -ESRCH; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (copy_from_user(&ncci, argp, sizeof(ncci))) 92562306a36Sopenharmony_ci return -EFAULT; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci mutex_lock(&cdev->lock); 92862306a36Sopenharmony_ci nccip = capincci_find(cdev, (u32)ncci); 92962306a36Sopenharmony_ci if (nccip) { 93062306a36Sopenharmony_ci mp = nccip->minorp; 93162306a36Sopenharmony_ci if (mp) 93262306a36Sopenharmony_ci unit = mp->minor; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 93562306a36Sopenharmony_ci return unit; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci default: 94062306a36Sopenharmony_ci return -EINVAL; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic long 94562306a36Sopenharmony_cicapi_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci int ret; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci mutex_lock(&capi_mutex); 95062306a36Sopenharmony_ci ret = capi_ioctl(file, cmd, arg); 95162306a36Sopenharmony_ci mutex_unlock(&capi_mutex); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci return ret; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 95762306a36Sopenharmony_cistatic long 95862306a36Sopenharmony_cicapi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 95962306a36Sopenharmony_ci{ 96062306a36Sopenharmony_ci int ret; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (cmd == CAPI_MANUFACTURER_CMD) { 96362306a36Sopenharmony_ci struct { 96462306a36Sopenharmony_ci compat_ulong_t cmd; 96562306a36Sopenharmony_ci compat_uptr_t data; 96662306a36Sopenharmony_ci } mcmd32; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 96962306a36Sopenharmony_ci return -EPERM; 97062306a36Sopenharmony_ci if (copy_from_user(&mcmd32, compat_ptr(arg), sizeof(mcmd32))) 97162306a36Sopenharmony_ci return -EFAULT; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci mutex_lock(&capi_mutex); 97462306a36Sopenharmony_ci ret = capi20_manufacturer(mcmd32.cmd, compat_ptr(mcmd32.data)); 97562306a36Sopenharmony_ci mutex_unlock(&capi_mutex); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return ret; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return capi_unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci#endif 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_cistatic int capi_open(struct inode *inode, struct file *file) 98562306a36Sopenharmony_ci{ 98662306a36Sopenharmony_ci struct capidev *cdev; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); 98962306a36Sopenharmony_ci if (!cdev) 99062306a36Sopenharmony_ci return -ENOMEM; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci mutex_init(&cdev->lock); 99362306a36Sopenharmony_ci skb_queue_head_init(&cdev->recvqueue); 99462306a36Sopenharmony_ci init_waitqueue_head(&cdev->recvwait); 99562306a36Sopenharmony_ci INIT_LIST_HEAD(&cdev->nccis); 99662306a36Sopenharmony_ci file->private_data = cdev; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci mutex_lock(&capidev_list_lock); 99962306a36Sopenharmony_ci list_add_tail(&cdev->list, &capidev_list); 100062306a36Sopenharmony_ci mutex_unlock(&capidev_list_lock); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci return stream_open(inode, file); 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic int capi_release(struct inode *inode, struct file *file) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct capidev *cdev = file->private_data; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci mutex_lock(&capidev_list_lock); 101062306a36Sopenharmony_ci list_del(&cdev->list); 101162306a36Sopenharmony_ci mutex_unlock(&capidev_list_lock); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (cdev->ap.applid) 101462306a36Sopenharmony_ci capi20_release(&cdev->ap); 101562306a36Sopenharmony_ci skb_queue_purge(&cdev->recvqueue); 101662306a36Sopenharmony_ci capincci_free(cdev, 0xffffffff); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci kfree(cdev); 101962306a36Sopenharmony_ci return 0; 102062306a36Sopenharmony_ci} 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_cistatic const struct file_operations capi_fops = 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci .owner = THIS_MODULE, 102562306a36Sopenharmony_ci .llseek = no_llseek, 102662306a36Sopenharmony_ci .read = capi_read, 102762306a36Sopenharmony_ci .write = capi_write, 102862306a36Sopenharmony_ci .poll = capi_poll, 102962306a36Sopenharmony_ci .unlocked_ioctl = capi_unlocked_ioctl, 103062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 103162306a36Sopenharmony_ci .compat_ioctl = capi_compat_ioctl, 103262306a36Sopenharmony_ci#endif 103362306a36Sopenharmony_ci .open = capi_open, 103462306a36Sopenharmony_ci .release = capi_release, 103562306a36Sopenharmony_ci}; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE 103862306a36Sopenharmony_ci/* -------- tty_operations for capincci ----------------------------- */ 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic int 104162306a36Sopenharmony_cicapinc_tty_install(struct tty_driver *driver, struct tty_struct *tty) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci struct capiminor *mp = capiminor_get(tty->index); 104462306a36Sopenharmony_ci int ret = tty_standard_install(driver, tty); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (ret == 0) 104762306a36Sopenharmony_ci tty->driver_data = mp; 104862306a36Sopenharmony_ci else 104962306a36Sopenharmony_ci capiminor_put(mp); 105062306a36Sopenharmony_ci return ret; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic void capinc_tty_cleanup(struct tty_struct *tty) 105462306a36Sopenharmony_ci{ 105562306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 105662306a36Sopenharmony_ci tty->driver_data = NULL; 105762306a36Sopenharmony_ci capiminor_put(mp); 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_cistatic int capinc_tty_open(struct tty_struct *tty, struct file *filp) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 106362306a36Sopenharmony_ci int err; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci err = tty_port_open(&mp->port, tty, filp); 106662306a36Sopenharmony_ci if (err) 106762306a36Sopenharmony_ci return err; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci handle_minor_recv(mp); 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic void capinc_tty_close(struct tty_struct *tty, struct file *filp) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci tty_port_close(&mp->port, tty, filp); 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic ssize_t capinc_tty_write(struct tty_struct *tty, const u8 *buf, 108162306a36Sopenharmony_ci size_t count) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 108462306a36Sopenharmony_ci struct sk_buff *skb; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci pr_debug("capinc_tty_write(count=%zu)\n", count); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci spin_lock_bh(&mp->outlock); 108962306a36Sopenharmony_ci skb = mp->outskb; 109062306a36Sopenharmony_ci if (skb) { 109162306a36Sopenharmony_ci mp->outskb = NULL; 109262306a36Sopenharmony_ci __skb_queue_tail(&mp->outqueue, skb); 109362306a36Sopenharmony_ci mp->outbytes += skb->len; 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + count, GFP_ATOMIC); 109762306a36Sopenharmony_ci if (!skb) { 109862306a36Sopenharmony_ci printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); 109962306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 110062306a36Sopenharmony_ci return -ENOMEM; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); 110462306a36Sopenharmony_ci skb_put_data(skb, buf, count); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci __skb_queue_tail(&mp->outqueue, skb); 110762306a36Sopenharmony_ci mp->outbytes += skb->len; 110862306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci handle_minor_send(mp); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci return count; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic int capinc_tty_put_char(struct tty_struct *tty, u8 ch) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 111862306a36Sopenharmony_ci bool invoke_send = false; 111962306a36Sopenharmony_ci struct sk_buff *skb; 112062306a36Sopenharmony_ci int ret = 1; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci pr_debug("capinc_put_char(%u)\n", ch); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci spin_lock_bh(&mp->outlock); 112562306a36Sopenharmony_ci skb = mp->outskb; 112662306a36Sopenharmony_ci if (skb) { 112762306a36Sopenharmony_ci if (skb_tailroom(skb) > 0) { 112862306a36Sopenharmony_ci skb_put_u8(skb, ch); 112962306a36Sopenharmony_ci goto unlock_out; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci mp->outskb = NULL; 113262306a36Sopenharmony_ci __skb_queue_tail(&mp->outqueue, skb); 113362306a36Sopenharmony_ci mp->outbytes += skb->len; 113462306a36Sopenharmony_ci invoke_send = true; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci skb = alloc_skb(CAPI_DATA_B3_REQ_LEN + CAPI_MAX_BLKSIZE, GFP_ATOMIC); 113862306a36Sopenharmony_ci if (skb) { 113962306a36Sopenharmony_ci skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); 114062306a36Sopenharmony_ci skb_put_u8(skb, ch); 114162306a36Sopenharmony_ci mp->outskb = skb; 114262306a36Sopenharmony_ci } else { 114362306a36Sopenharmony_ci printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); 114462306a36Sopenharmony_ci ret = 0; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ciunlock_out: 114862306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (invoke_send) 115162306a36Sopenharmony_ci handle_minor_send(mp); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci return ret; 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_cistatic void capinc_tty_flush_chars(struct tty_struct *tty) 115762306a36Sopenharmony_ci{ 115862306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 115962306a36Sopenharmony_ci struct sk_buff *skb; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci spin_lock_bh(&mp->outlock); 116262306a36Sopenharmony_ci skb = mp->outskb; 116362306a36Sopenharmony_ci if (skb) { 116462306a36Sopenharmony_ci mp->outskb = NULL; 116562306a36Sopenharmony_ci __skb_queue_tail(&mp->outqueue, skb); 116662306a36Sopenharmony_ci mp->outbytes += skb->len; 116762306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci handle_minor_send(mp); 117062306a36Sopenharmony_ci } else 117162306a36Sopenharmony_ci spin_unlock_bh(&mp->outlock); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci handle_minor_recv(mp); 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic unsigned int capinc_tty_write_room(struct tty_struct *tty) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 117962306a36Sopenharmony_ci unsigned int room; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue); 118262306a36Sopenharmony_ci room *= CAPI_MAX_BLKSIZE; 118362306a36Sopenharmony_ci pr_debug("capinc_tty_write_room = %u\n", room); 118462306a36Sopenharmony_ci return room; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic unsigned int capinc_tty_chars_in_buffer(struct tty_struct *tty) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci pr_debug("capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n", 119262306a36Sopenharmony_ci mp->outbytes, mp->nack, 119362306a36Sopenharmony_ci skb_queue_len(&mp->outqueue), 119462306a36Sopenharmony_ci skb_queue_len(&mp->inqueue)); 119562306a36Sopenharmony_ci return mp->outbytes; 119662306a36Sopenharmony_ci} 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_cistatic void capinc_tty_throttle(struct tty_struct *tty) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 120162306a36Sopenharmony_ci mp->ttyinstop = 1; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic void capinc_tty_unthrottle(struct tty_struct *tty) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci mp->ttyinstop = 0; 120962306a36Sopenharmony_ci handle_minor_recv(mp); 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic void capinc_tty_stop(struct tty_struct *tty) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci mp->ttyoutstop = 1; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic void capinc_tty_start(struct tty_struct *tty) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci mp->ttyoutstop = 0; 122462306a36Sopenharmony_ci handle_minor_send(mp); 122562306a36Sopenharmony_ci} 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_cistatic void capinc_tty_hangup(struct tty_struct *tty) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci struct capiminor *mp = tty->driver_data; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci tty_port_hangup(&mp->port); 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cistatic void capinc_tty_send_xchar(struct tty_struct *tty, char ch) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci pr_debug("capinc_tty_send_xchar(%d)\n", ch); 123762306a36Sopenharmony_ci} 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic const struct tty_operations capinc_ops = { 124062306a36Sopenharmony_ci .open = capinc_tty_open, 124162306a36Sopenharmony_ci .close = capinc_tty_close, 124262306a36Sopenharmony_ci .write = capinc_tty_write, 124362306a36Sopenharmony_ci .put_char = capinc_tty_put_char, 124462306a36Sopenharmony_ci .flush_chars = capinc_tty_flush_chars, 124562306a36Sopenharmony_ci .write_room = capinc_tty_write_room, 124662306a36Sopenharmony_ci .chars_in_buffer = capinc_tty_chars_in_buffer, 124762306a36Sopenharmony_ci .throttle = capinc_tty_throttle, 124862306a36Sopenharmony_ci .unthrottle = capinc_tty_unthrottle, 124962306a36Sopenharmony_ci .stop = capinc_tty_stop, 125062306a36Sopenharmony_ci .start = capinc_tty_start, 125162306a36Sopenharmony_ci .hangup = capinc_tty_hangup, 125262306a36Sopenharmony_ci .send_xchar = capinc_tty_send_xchar, 125362306a36Sopenharmony_ci .install = capinc_tty_install, 125462306a36Sopenharmony_ci .cleanup = capinc_tty_cleanup, 125562306a36Sopenharmony_ci}; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_cistatic int __init capinc_tty_init(void) 125862306a36Sopenharmony_ci{ 125962306a36Sopenharmony_ci struct tty_driver *drv; 126062306a36Sopenharmony_ci int err; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (capi_ttyminors > CAPINC_MAX_PORTS) 126362306a36Sopenharmony_ci capi_ttyminors = CAPINC_MAX_PORTS; 126462306a36Sopenharmony_ci if (capi_ttyminors <= 0) 126562306a36Sopenharmony_ci capi_ttyminors = CAPINC_NR_PORTS; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci capiminors = kcalloc(capi_ttyminors, sizeof(struct capiminor *), 126862306a36Sopenharmony_ci GFP_KERNEL); 126962306a36Sopenharmony_ci if (!capiminors) 127062306a36Sopenharmony_ci return -ENOMEM; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci drv = tty_alloc_driver(capi_ttyminors, TTY_DRIVER_REAL_RAW | 127362306a36Sopenharmony_ci TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_DYNAMIC_DEV); 127462306a36Sopenharmony_ci if (IS_ERR(drv)) { 127562306a36Sopenharmony_ci kfree(capiminors); 127662306a36Sopenharmony_ci return PTR_ERR(drv); 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci drv->driver_name = "capi_nc"; 127962306a36Sopenharmony_ci drv->name = "capi!"; 128062306a36Sopenharmony_ci drv->major = 0; 128162306a36Sopenharmony_ci drv->minor_start = 0; 128262306a36Sopenharmony_ci drv->type = TTY_DRIVER_TYPE_SERIAL; 128362306a36Sopenharmony_ci drv->subtype = SERIAL_TYPE_NORMAL; 128462306a36Sopenharmony_ci drv->init_termios = tty_std_termios; 128562306a36Sopenharmony_ci drv->init_termios.c_iflag = ICRNL; 128662306a36Sopenharmony_ci drv->init_termios.c_oflag = OPOST | ONLCR; 128762306a36Sopenharmony_ci drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; 128862306a36Sopenharmony_ci drv->init_termios.c_lflag = 0; 128962306a36Sopenharmony_ci tty_set_operations(drv, &capinc_ops); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci err = tty_register_driver(drv); 129262306a36Sopenharmony_ci if (err) { 129362306a36Sopenharmony_ci tty_driver_kref_put(drv); 129462306a36Sopenharmony_ci kfree(capiminors); 129562306a36Sopenharmony_ci printk(KERN_ERR "Couldn't register capi_nc driver\n"); 129662306a36Sopenharmony_ci return err; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci capinc_tty_driver = drv; 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic void __exit capinc_tty_exit(void) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci tty_unregister_driver(capinc_tty_driver); 130562306a36Sopenharmony_ci tty_driver_kref_put(capinc_tty_driver); 130662306a36Sopenharmony_ci kfree(capiminors); 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_cistatic inline int capinc_tty_init(void) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci return 0; 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic inline void capinc_tty_exit(void) { } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci/* -------- /proc functions ----------------------------------------- */ 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci/* 132362306a36Sopenharmony_ci * /proc/capi/capi20: 132462306a36Sopenharmony_ci * minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt 132562306a36Sopenharmony_ci */ 132662306a36Sopenharmony_cistatic int __maybe_unused capi20_proc_show(struct seq_file *m, void *v) 132762306a36Sopenharmony_ci{ 132862306a36Sopenharmony_ci struct capidev *cdev; 132962306a36Sopenharmony_ci struct list_head *l; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci mutex_lock(&capidev_list_lock); 133262306a36Sopenharmony_ci list_for_each(l, &capidev_list) { 133362306a36Sopenharmony_ci cdev = list_entry(l, struct capidev, list); 133462306a36Sopenharmony_ci seq_printf(m, "0 %d %lu %lu %lu %lu\n", 133562306a36Sopenharmony_ci cdev->ap.applid, 133662306a36Sopenharmony_ci cdev->ap.nrecvctlpkt, 133762306a36Sopenharmony_ci cdev->ap.nrecvdatapkt, 133862306a36Sopenharmony_ci cdev->ap.nsentctlpkt, 133962306a36Sopenharmony_ci cdev->ap.nsentdatapkt); 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci mutex_unlock(&capidev_list_lock); 134262306a36Sopenharmony_ci return 0; 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci/* 134662306a36Sopenharmony_ci * /proc/capi/capi20ncci: 134762306a36Sopenharmony_ci * applid ncci 134862306a36Sopenharmony_ci */ 134962306a36Sopenharmony_cistatic int __maybe_unused capi20ncci_proc_show(struct seq_file *m, void *v) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci struct capidev *cdev; 135262306a36Sopenharmony_ci struct capincci *np; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci mutex_lock(&capidev_list_lock); 135562306a36Sopenharmony_ci list_for_each_entry(cdev, &capidev_list, list) { 135662306a36Sopenharmony_ci mutex_lock(&cdev->lock); 135762306a36Sopenharmony_ci list_for_each_entry(np, &cdev->nccis, list) 135862306a36Sopenharmony_ci seq_printf(m, "%d 0x%x\n", cdev->ap.applid, np->ncci); 135962306a36Sopenharmony_ci mutex_unlock(&cdev->lock); 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci mutex_unlock(&capidev_list_lock); 136262306a36Sopenharmony_ci return 0; 136362306a36Sopenharmony_ci} 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_cistatic void __init proc_init(void) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci proc_create_single("capi/capi20", 0, NULL, capi20_proc_show); 136862306a36Sopenharmony_ci proc_create_single("capi/capi20ncci", 0, NULL, capi20ncci_proc_show); 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic void __exit proc_exit(void) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci remove_proc_entry("capi/capi20", NULL); 137462306a36Sopenharmony_ci remove_proc_entry("capi/capi20ncci", NULL); 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci/* -------- init function and module interface ---------------------- */ 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic int __init capi_init(void) 138162306a36Sopenharmony_ci{ 138262306a36Sopenharmony_ci const char *compileinfo; 138362306a36Sopenharmony_ci int major_ret; 138462306a36Sopenharmony_ci int ret; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci ret = kcapi_init(); 138762306a36Sopenharmony_ci if (ret) 138862306a36Sopenharmony_ci return ret; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci major_ret = register_chrdev(capi_major, "capi20", &capi_fops); 139162306a36Sopenharmony_ci if (major_ret < 0) { 139262306a36Sopenharmony_ci printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); 139362306a36Sopenharmony_ci kcapi_exit(); 139462306a36Sopenharmony_ci return major_ret; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci capi_class = class_create("capi"); 139762306a36Sopenharmony_ci if (IS_ERR(capi_class)) { 139862306a36Sopenharmony_ci unregister_chrdev(capi_major, "capi20"); 139962306a36Sopenharmony_ci kcapi_exit(); 140062306a36Sopenharmony_ci return PTR_ERR(capi_class); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi20"); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (capinc_tty_init() < 0) { 140662306a36Sopenharmony_ci device_destroy(capi_class, MKDEV(capi_major, 0)); 140762306a36Sopenharmony_ci class_destroy(capi_class); 140862306a36Sopenharmony_ci unregister_chrdev(capi_major, "capi20"); 140962306a36Sopenharmony_ci kcapi_exit(); 141062306a36Sopenharmony_ci return -ENOMEM; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci proc_init(); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE 141662306a36Sopenharmony_ci compileinfo = " (middleware)"; 141762306a36Sopenharmony_ci#else 141862306a36Sopenharmony_ci compileinfo = " (no middleware)"; 141962306a36Sopenharmony_ci#endif 142062306a36Sopenharmony_ci printk(KERN_NOTICE "CAPI 2.0 started up with major %d%s\n", 142162306a36Sopenharmony_ci capi_major, compileinfo); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci return 0; 142462306a36Sopenharmony_ci} 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_cistatic void __exit capi_exit(void) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci proc_exit(); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci device_destroy(capi_class, MKDEV(capi_major, 0)); 143162306a36Sopenharmony_ci class_destroy(capi_class); 143262306a36Sopenharmony_ci unregister_chrdev(capi_major, "capi20"); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci capinc_tty_exit(); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci kcapi_exit(); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cimodule_init(capi_init); 144062306a36Sopenharmony_cimodule_exit(capi_exit); 1441