diff options
Diffstat (limited to 'drivers/isdn/capi/capi.c')
-rw-r--r-- | drivers/isdn/capi/capi.c | 1203 |
1 files changed, 574 insertions, 629 deletions
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 65bf91e16a4..ee5837522f5 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -23,16 +23,13 @@ #include <linux/smp_lock.h> #include <linux/timer.h> #include <linux/wait.h> -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE #include <linux/tty.h> -#ifdef CONFIG_PPP #include <linux/netdevice.h> #include <linux/ppp_defs.h> #include <linux/if_ppp.h> -#endif /* CONFIG_PPP */ -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ #include <linux/skbuff.h> #include <linux/proc_fs.h> +#include <linux/seq_file.h> #include <linux/poll.h> #include <linux/capi.h> #include <linux/kernelcapi.h> @@ -41,35 +38,29 @@ #include <linux/moduleparam.h> #include <linux/isdn/capiutil.h> #include <linux/isdn/capicmd.h> -#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) -#include "capifs.h" -#endif -static char *revision = "$Revision: 1.1.2.7 $"; +#include "capifs.h" MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface"); MODULE_AUTHOR("Carsten Paeth"); MODULE_LICENSE("GPL"); -#undef _DEBUG_REFCOUNT /* alloc/free and open/close debug */ #undef _DEBUG_TTYFUNCS /* call to tty_driver */ #undef _DEBUG_DATAFLOW /* data flow */ /* -------- driver information -------------------------------------- */ static struct class *capi_class; - static int capi_major = 68; /* allocated */ + +module_param_named(major, capi_major, uint, 0); + #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -#define CAPINC_NR_PORTS 32 +#define CAPINC_NR_PORTS 32 #define CAPINC_MAX_PORTS 256 -static int capi_ttymajor = 191; + static int capi_ttyminors = CAPINC_NR_PORTS; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ -module_param_named(major, capi_major, uint, 0); -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -module_param_named(ttymajor, capi_ttymajor, uint, 0); module_param_named(ttyminors, capi_ttyminors, uint, 0); #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ @@ -83,53 +74,43 @@ module_param_named(ttyminors, capi_ttyminors, uint, 0); struct capidev; struct capincci; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE struct capiminor; -struct datahandle_queue { +struct ackqueue_entry { struct list_head list; u16 datahandle; }; struct capiminor { - struct list_head list; - struct capincci *nccip; + struct kref kref; + unsigned int minor; + struct dentry *capifs_dentry; - struct capi20_appl *ap; - u32 ncci; - u16 datahandle; - u16 msgid; + struct capi20_appl *ap; + u32 ncci; + atomic_t datahandle; + atomic_t msgid; - struct tty_struct *tty; + struct tty_port port; int ttyinstop; int ttyoutstop; - struct sk_buff *ttyskb; - atomic_t ttyopencount; - struct sk_buff_head inqueue; - int inbytes; - struct sk_buff_head outqueue; - int outbytes; + struct sk_buff_head inqueue; + + struct sk_buff_head outqueue; + int outbytes; + struct sk_buff *outskb; + spinlock_t outlock; /* transmit path */ struct list_head ackqueue; int nack; spinlock_t ackqlock; }; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - -/* FIXME: The following lock is a sledgehammer-workaround to a - * locking issue with the capiminor (and maybe other) data structure(s). - * Access to this data is done in a racy way and crashes the machine with - * a FritzCard DSL driver; sooner or later. This is a workaround - * which trades scalability vs stability, so it doesn't crash the kernel anymore. - * The correct (and scalable) fix for the issue seems to require - * an API change to the drivers... . */ -static DEFINE_SPINLOCK(workaround_lock); struct capincci { - struct capincci *next; + struct list_head list; u32 ncci; struct capidev *cdev; #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE @@ -146,28 +127,28 @@ struct capidev { struct sk_buff_head recvqueue; wait_queue_head_t recvwait; - struct capincci *nccis; + struct list_head nccis; - struct mutex ncci_list_mtx; + struct mutex lock; }; /* -------- global variables ---------------------------------------- */ -static DEFINE_RWLOCK(capidev_list_lock); +static DEFINE_MUTEX(capidev_list_lock); static LIST_HEAD(capidev_list); #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE -static DEFINE_RWLOCK(capiminor_list_lock); -static LIST_HEAD(capiminor_list); -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE +static DEFINE_SPINLOCK(capiminors_lock); +static struct capiminor **capiminors; + +static struct tty_driver *capinc_tty_driver; + /* -------- datahandles --------------------------------------------- */ -static int capincci_add_ack(struct capiminor *mp, u16 datahandle) +static int capiminor_add_ack(struct capiminor *mp, u16 datahandle) { - struct datahandle_queue *n; - unsigned long flags; + struct ackqueue_entry *n; n = kmalloc(sizeof(*n), GFP_ATOMIC); if (unlikely(!n)) { @@ -176,253 +157,246 @@ static int capincci_add_ack(struct capiminor *mp, u16 datahandle) } n->datahandle = datahandle; INIT_LIST_HEAD(&n->list); - spin_lock_irqsave(&mp->ackqlock, flags); + spin_lock_bh(&mp->ackqlock); list_add_tail(&n->list, &mp->ackqueue); mp->nack++; - spin_unlock_irqrestore(&mp->ackqlock, flags); + spin_unlock_bh(&mp->ackqlock); return 0; } static int capiminor_del_ack(struct capiminor *mp, u16 datahandle) { - struct datahandle_queue *p, *tmp; - unsigned long flags; + struct ackqueue_entry *p, *tmp; - spin_lock_irqsave(&mp->ackqlock, flags); + spin_lock_bh(&mp->ackqlock); list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { if (p->datahandle == datahandle) { list_del(&p->list); - kfree(p); mp->nack--; - spin_unlock_irqrestore(&mp->ackqlock, flags); + spin_unlock_bh(&mp->ackqlock); + kfree(p); return 0; } } - spin_unlock_irqrestore(&mp->ackqlock, flags); + spin_unlock_bh(&mp->ackqlock); return -1; } static void capiminor_del_all_ack(struct capiminor *mp) { - struct datahandle_queue *p, *tmp; - unsigned long flags; + struct ackqueue_entry *p, *tmp; - spin_lock_irqsave(&mp->ackqlock, flags); list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) { list_del(&p->list); kfree(p); mp->nack--; } - spin_unlock_irqrestore(&mp->ackqlock, flags); } /* -------- struct capiminor ---------------------------------------- */ +static const struct tty_port_operations capiminor_port_ops; /* we have none */ + static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci) { - struct capiminor *mp, *p; - unsigned int minor = 0; - unsigned long flags; + struct capiminor *mp; + struct device *dev; + unsigned int minor; - mp = kzalloc(sizeof(*mp), GFP_ATOMIC); + mp = kzalloc(sizeof(*mp), GFP_KERNEL); if (!mp) { printk(KERN_ERR "capi: can't alloc capiminor\n"); return NULL; } + kref_init(&mp->kref); + mp->ap = ap; mp->ncci = ncci; - mp->msgid = 0; - atomic_set(&mp->ttyopencount,0); INIT_LIST_HEAD(&mp->ackqueue); spin_lock_init(&mp->ackqlock); skb_queue_head_init(&mp->inqueue); skb_queue_head_init(&mp->outqueue); + spin_lock_init(&mp->outlock); - /* Allocate the least unused minor number. - */ - write_lock_irqsave(&capiminor_list_lock, flags); - if (list_empty(&capiminor_list)) - list_add(&mp->list, &capiminor_list); - else { - list_for_each_entry(p, &capiminor_list, list) { - if (p->minor > minor) - break; - minor++; - } - - if (minor < capi_ttyminors) { - mp->minor = minor; - list_add(&mp->list, p->list.prev); + tty_port_init(&mp->port); + mp->port.ops = &capiminor_port_ops; + + /* Allocate the least unused minor number. */ + spin_lock(&capiminors_lock); + for (minor = 0; minor < capi_ttyminors; minor++) + if (!capiminors[minor]) { + capiminors[minor] = mp; + break; } - } - write_unlock_irqrestore(&capiminor_list_lock, flags); + spin_unlock(&capiminors_lock); - if (!(minor < capi_ttyminors)) { + if (minor == capi_ttyminors) { printk(KERN_NOTICE "capi: out of minors\n"); - kfree(mp); - return NULL; + goto err_out1; } + mp->minor = minor; + + dev = tty_register_device(capinc_tty_driver, minor, NULL); + if (IS_ERR(dev)) + goto err_out2; + return mp; + +err_out2: + spin_lock(&capiminors_lock); + capiminors[minor] = NULL; + spin_unlock(&capiminors_lock); + +err_out1: + kfree(mp); + return NULL; } -static void capiminor_free(struct capiminor *mp) +static void capiminor_destroy(struct kref *kref) { - unsigned long flags; - - write_lock_irqsave(&capiminor_list_lock, flags); - list_del(&mp->list); - write_unlock_irqrestore(&capiminor_list_lock, flags); + struct capiminor *mp = container_of(kref, struct capiminor, kref); - kfree_skb(mp->ttyskb); - mp->ttyskb = NULL; + kfree_skb(mp->outskb); skb_queue_purge(&mp->inqueue); skb_queue_purge(&mp->outqueue); capiminor_del_all_ack(mp); kfree(mp); } -static struct capiminor *capiminor_find(unsigned int minor) +static struct capiminor *capiminor_get(unsigned int minor) { - struct list_head *l; - struct capiminor *p = NULL; + struct capiminor *mp; - read_lock(&capiminor_list_lock); - list_for_each(l, &capiminor_list) { - p = list_entry(l, struct capiminor, list); - if (p->minor == minor) - break; - } - read_unlock(&capiminor_list_lock); - if (l == &capiminor_list) - return NULL; + spin_lock(&capiminors_lock); + mp = capiminors[minor]; + if (mp) + kref_get(&mp->kref); + spin_unlock(&capiminors_lock); - return p; + return mp; +} + +static inline void capiminor_put(struct capiminor *mp) +{ + kref_put(&mp->kref, capiminor_destroy); +} + +static void capiminor_free(struct capiminor *mp) +{ + tty_unregister_device(capinc_tty_driver, mp->minor); + + spin_lock(&capiminors_lock); + capiminors[mp->minor] = NULL; + spin_unlock(&capiminors_lock); + + capiminor_put(mp); } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ /* -------- struct capincci ----------------------------------------- */ -static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci) +static void capincci_alloc_minor(struct capidev *cdev, struct capincci *np) { - struct capincci *np, **pp; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct capiminor *mp = NULL; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + struct capiminor *mp; + dev_t device; - np = kzalloc(sizeof(*np), GFP_ATOMIC); - if (!np) - return NULL; - np->ncci = ncci; - np->cdev = cdev; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - mp = NULL; - if (cdev->userflags & CAPIFLAG_HIGHJACKING) - mp = np->minorp = capiminor_alloc(&cdev->ap, ncci); + if (!(cdev->userflags & CAPIFLAG_HIGHJACKING)) + return; + + mp = np->minorp = capiminor_alloc(&cdev->ap, np->ncci); if (mp) { - mp->nccip = np; -#ifdef _DEBUG_REFCOUNT - printk(KERN_DEBUG "set mp->nccip\n"); -#endif -#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) - capifs_new_ncci(mp->minor, MKDEV(capi_ttymajor, mp->minor)); -#endif + device = MKDEV(capinc_tty_driver->major, mp->minor); + mp->capifs_dentry = capifs_new_ncci(mp->minor, device); } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - for (pp=&cdev->nccis; *pp; pp = &(*pp)->next) - ; - *pp = np; - return np; } -static void capincci_free(struct capidev *cdev, u32 ncci) +static void capincci_free_minor(struct capincci *np) { - struct capincci *np, **pp; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct capiminor *mp; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + struct capiminor *mp = np->minorp; + struct tty_struct *tty; - pp=&cdev->nccis; - while (*pp) { - np = *pp; - if (ncci == 0xffffffff || np->ncci == ncci) { - *pp = (*pp)->next; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - if ((mp = np->minorp) != NULL) { -#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) - capifs_free_ncci(mp->minor); -#endif - if (mp->tty) { - mp->nccip = NULL; -#ifdef _DEBUG_REFCOUNT - printk(KERN_DEBUG "reset mp->nccip\n"); -#endif - tty_hangup(mp->tty); - } else { - capiminor_free(mp); - } - } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - kfree(np); - if (*pp == NULL) return; - } else { - pp = &(*pp)->next; + if (mp) { + capifs_free_ncci(mp->capifs_dentry); + + tty = tty_port_tty_get(&mp->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); } + + capiminor_free(mp); } } -static struct capincci *capincci_find(struct capidev *cdev, u32 ncci) +static inline unsigned int capincci_minor_opencount(struct capincci *np) { - struct capincci *p; + struct capiminor *mp = np->minorp; + unsigned int count = 0; + struct tty_struct *tty; - for (p=cdev->nccis; p ; p = p->next) { - if (p->ncci == ncci) - break; + if (mp) { + tty = tty_port_tty_get(&mp->port); + if (tty) { + count = tty->count; + tty_kref_put(tty); + } } - return p; + return count; } -/* -------- struct capidev ------------------------------------------ */ +#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ + +static inline void +capincci_alloc_minor(struct capidev *cdev, struct capincci *np) { } +static inline void capincci_free_minor(struct capincci *np) { } -static struct capidev *capidev_alloc(void) +static inline unsigned int capincci_minor_opencount(struct capincci *np) { - struct capidev *cdev; - unsigned long flags; + return 0; +} - cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); - if (!cdev) +#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ + +static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci) +{ + struct capincci *np; + + np = kzalloc(sizeof(*np), GFP_KERNEL); + if (!np) return NULL; + np->ncci = ncci; + np->cdev = cdev; - mutex_init(&cdev->ncci_list_mtx); - skb_queue_head_init(&cdev->recvqueue); - init_waitqueue_head(&cdev->recvwait); - write_lock_irqsave(&capidev_list_lock, flags); - list_add_tail(&cdev->list, &capidev_list); - write_unlock_irqrestore(&capidev_list_lock, flags); - return cdev; + capincci_alloc_minor(cdev, np); + + list_add_tail(&np->list, &cdev->nccis); + + return np; } -static void capidev_free(struct capidev *cdev) +static void capincci_free(struct capidev *cdev, u32 ncci) { - unsigned long flags; + struct capincci *np, *tmp; - if (cdev->ap.applid) { - capi20_release(&cdev->ap); - cdev->ap.applid = 0; - } - skb_queue_purge(&cdev->recvqueue); + list_for_each_entry_safe(np, tmp, &cdev->nccis, list) + if (ncci == 0xffffffff || np->ncci == ncci) { + capincci_free_minor(np); + list_del(&np->list); + kfree(np); + } +} - mutex_lock(&cdev->ncci_list_mtx); - capincci_free(cdev, 0xffffffff); - mutex_unlock(&cdev->ncci_list_mtx); +static struct capincci *capincci_find(struct capidev *cdev, u32 ncci) +{ + struct capincci *np; - write_lock_irqsave(&capidev_list_lock, flags); - list_del(&cdev->list); - write_unlock_irqrestore(&capidev_list_lock, flags); - kfree(cdev); + list_for_each_entry(np, &cdev->nccis, list) + if (np->ncci == ncci) + return np; + return NULL; } #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE @@ -432,7 +406,7 @@ static struct sk_buff * gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) { struct sk_buff *nskb; - nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC); + nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_KERNEL); if (nskb) { u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2); unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN); @@ -440,7 +414,7 @@ gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) capimsg_setu16(s, 2, mp->ap->applid); capimsg_setu8 (s, 4, CAPI_DATA_B3); capimsg_setu8 (s, 5, CAPI_RESP); - capimsg_setu16(s, 6, mp->msgid++); + capimsg_setu16(s, 6, atomic_inc_return(&mp->msgid)); capimsg_setu32(s, 8, mp->ncci); capimsg_setu16(s, 12, datahandle); } @@ -449,122 +423,156 @@ gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb) static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb) { + unsigned int datalen = skb->len - CAPIMSG_LEN(skb->data); + struct tty_struct *tty; struct sk_buff *nskb; - int datalen; u16 errcode, datahandle; struct tty_ldisc *ld; - - datalen = skb->len - CAPIMSG_LEN(skb->data); - if (mp->tty == NULL) - { + int ret = -1; + + tty = tty_port_tty_get(&mp->port); + if (!tty) { #ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi: currently no receiver\n"); #endif return -1; } - ld = tty_ldisc_ref(mp->tty); - if (ld == NULL) - return -1; + ld = tty_ldisc_ref(tty); + if (!ld) { + /* fatal error, do not requeue */ + ret = 0; + kfree_skb(skb); + goto deref_tty; + } + if (ld->ops->receive_buf == NULL) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n"); #endif - goto bad; + /* fatal error, do not requeue */ + goto free_skb; } if (mp->ttyinstop) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: recv tty throttled\n"); #endif - goto bad; + goto deref_ldisc; } - if (mp->tty->receive_room < datalen) { + + if (tty->receive_room < datalen) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: no room in tty\n"); #endif - goto bad; + goto deref_ldisc; } - if ((nskb = gen_data_b3_resp_for(mp, skb)) == NULL) { + + nskb = gen_data_b3_resp_for(mp, skb); + if (!nskb) { printk(KERN_ERR "capi: gen_data_b3_resp failed\n"); - goto bad; + goto deref_ldisc; } - datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4); + + datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN + 4); + errcode = capi20_put_message(mp->ap, nskb); - if (errcode != CAPI_NOERROR) { + + if (errcode == CAPI_NOERROR) { + skb_pull(skb, CAPIMSG_LEN(skb->data)); +#ifdef _DEBUG_DATAFLOW + printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", + datahandle, skb->len); +#endif + ld->ops->receive_buf(tty, skb->data, NULL, skb->len); + } else { printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n", errcode); kfree_skb(nskb); - goto bad; + + if (errcode == CAPI_SENDQUEUEFULL) + goto deref_ldisc; } - (void)skb_pull(skb, CAPIMSG_LEN(skb->data)); -#ifdef _DEBUG_DATAFLOW - printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n", - datahandle, skb->len); -#endif - ld->ops->receive_buf(mp->tty, skb->data, NULL, skb->len); + +free_skb: + ret = 0; kfree_skb(skb); + +deref_ldisc: tty_ldisc_deref(ld); - return 0; -bad: - tty_ldisc_deref(ld); - return -1; + +deref_tty: + tty_kref_put(tty); + return ret; } static void handle_minor_recv(struct capiminor *mp) { struct sk_buff *skb; - while ((skb = skb_dequeue(&mp->inqueue)) != NULL) { - unsigned int len = skb->len; - mp->inbytes -= len; + + while ((skb = skb_dequeue(&mp->inqueue)) != NULL) if (handle_recv_skb(mp, skb) < 0) { skb_queue_head(&mp->inqueue, skb); - mp->inbytes += len; return; } - } } -static int handle_minor_send(struct capiminor *mp) +static void handle_minor_send(struct capiminor *mp) { + struct tty_struct *tty; struct sk_buff *skb; u16 len; - int count = 0; u16 errcode; u16 datahandle; - if (mp->tty && mp->ttyoutstop) { + tty = tty_port_tty_get(&mp->port); + if (!tty) + return; + + if (mp->ttyoutstop) { #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS) printk(KERN_DEBUG "capi: send: tty stopped\n"); #endif - return 0; + tty_kref_put(tty); + return; } - while ((skb = skb_dequeue(&mp->outqueue)) != NULL) { - datahandle = mp->datahandle; + while (1) { + spin_lock_bh(&mp->outlock); + skb = __skb_dequeue(&mp->outqueue); + if (!skb) { + spin_unlock_bh(&mp->outlock); + break; + } len = (u16)skb->len; + mp->outbytes -= len; + spin_unlock_bh(&mp->outlock); + + datahandle = atomic_inc_return(&mp->datahandle); skb_push(skb, CAPI_DATA_B3_REQ_LEN); memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN); capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN); capimsg_setu16(skb->data, 2, mp->ap->applid); capimsg_setu8 (skb->data, 4, CAPI_DATA_B3); capimsg_setu8 (skb->data, 5, CAPI_REQ); - capimsg_setu16(skb->data, 6, mp->msgid++); + capimsg_setu16(skb->data, 6, atomic_inc_return(&mp->msgid)); capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */ capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */ capimsg_setu16(skb->data, 16, len); /* Data length */ capimsg_setu16(skb->data, 18, datahandle); capimsg_setu16(skb->data, 20, 0); /* Flags */ - if (capincci_add_ack(mp, datahandle) < 0) { + if (capiminor_add_ack(mp, datahandle) < 0) { skb_pull(skb, CAPI_DATA_B3_REQ_LEN); - skb_queue_head(&mp->outqueue, skb); - return count; + + spin_lock_bh(&mp->outlock); + __skb_queue_head(&mp->outqueue, skb); + mp->outbytes += len; + spin_unlock_bh(&mp->outlock); + + break; } errcode = capi20_put_message(mp->ap, skb); if (errcode == CAPI_NOERROR) { - mp->datahandle++; - count++; - mp->outbytes -= len; #ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n", datahandle, len); @@ -575,16 +583,20 @@ static int handle_minor_send(struct capiminor *mp) if (errcode == CAPI_SENDQUEUEFULL) { skb_pull(skb, CAPI_DATA_B3_REQ_LEN); - skb_queue_head(&mp->outqueue, skb); + + spin_lock_bh(&mp->outlock); + __skb_queue_head(&mp->outqueue, skb); + mp->outbytes += len; + spin_unlock_bh(&mp->outlock); + break; } /* ups, drop packet */ printk(KERN_ERR "capi: put_message = %x\n", errcode); - mp->outbytes -= len; kfree_skb(skb); } - return count; + tty_kref_put(tty); } #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ @@ -594,65 +606,56 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) { struct capidev *cdev = ap->private; #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE + struct tty_struct *tty; struct capiminor *mp; u16 datahandle; #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ struct capincci *np; - u32 ncci; - unsigned long flags; + + mutex_lock(&cdev->lock); if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) { u16 info = CAPIMSG_U16(skb->data, 12); // Info field - if ((info & 0xff00) == 0) { - mutex_lock(&cdev->ncci_list_mtx); + if ((info & 0xff00) == 0) capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); - mutex_unlock(&cdev->ncci_list_mtx); - } } - if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) { - mutex_lock(&cdev->ncci_list_mtx); + if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) capincci_alloc(cdev, CAPIMSG_NCCI(skb->data)); - mutex_unlock(&cdev->ncci_list_mtx); - } - spin_lock_irqsave(&workaround_lock, flags); + if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) { skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); - spin_unlock_irqrestore(&workaround_lock, flags); - return; + goto unlock_out; } - ncci = CAPIMSG_CONTROL(skb->data); - for (np = cdev->nccis; np && np->ncci != ncci; np = np->next) - ; + + np = capincci_find(cdev, CAPIMSG_CONTROL(skb->data)); if (!np) { printk(KERN_ERR "BUG: capi_signal: ncci not found\n"); skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); - spin_unlock_irqrestore(&workaround_lock, flags); - return; + goto unlock_out; } + #ifndef CONFIG_ISDN_CAPI_MIDDLEWARE skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); + #else /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + mp = np->minorp; if (!mp) { skb_queue_tail(&cdev->recvqueue, skb); wake_up_interruptible(&cdev->recvwait); - spin_unlock_irqrestore(&workaround_lock, flags); - return; + goto unlock_out; } - - if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) { - datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2); #ifdef _DEBUG_DATAFLOW printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n", datahandle, skb->len-CAPIMSG_LEN(skb->data)); #endif skb_queue_tail(&mp->inqueue, skb); - mp->inbytes += skb->len; + handle_minor_recv(mp); } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) { @@ -664,10 +667,13 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2)); #endif kfree_skb(skb); - (void)capiminor_del_ack(mp, datahandle); - if (mp->tty) - tty_wakeup(mp->tty); - (void)handle_minor_send(mp); + capiminor_del_ack(mp, datahandle); + tty = tty_port_tty_get(&mp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + handle_minor_send(mp); } else { /* ups, let capi application handle it :-) */ @@ -675,7 +681,9 @@ static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb) wake_up_interruptible(&cdev->recvwait); } #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - spin_unlock_irqrestore(&workaround_lock, flags); + +unlock_out: + mutex_unlock(&cdev->lock); } /* -------- file_operations for capidev ----------------------------- */ @@ -686,24 +694,19 @@ capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) struct capidev *cdev = (struct capidev *)file->private_data; struct sk_buff *skb; size_t copied; + int err; if (!cdev->ap.applid) return -ENODEV; - if ((skb = skb_dequeue(&cdev->recvqueue)) == NULL) { - + skb = skb_dequeue(&cdev->recvqueue); + if (!skb) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; - - for (;;) { - interruptible_sleep_on(&cdev->recvwait); - if ((skb = skb_dequeue(&cdev->recvqueue)) != NULL) - break; - if (signal_pending(current)) - break; - } - if (skb == NULL) - return -ERESTARTNOHAND; + err = wait_event_interruptible(cdev->recvwait, + (skb = skb_dequeue(&cdev->recvqueue))); + if (err) + return err; } if (skb->len > count) { skb_queue_head(&cdev->recvqueue, skb); @@ -753,9 +756,9 @@ capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos CAPIMSG_SETAPPID(skb->data, cdev->ap.applid); if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) { - mutex_lock(&cdev->ncci_list_mtx); + mutex_lock(&cdev->lock); capincci_free(cdev, CAPIMSG_NCCI(skb->data)); - mutex_unlock(&cdev->ncci_list_mtx); + mutex_unlock(&cdev->lock); } cdev->errcode = capi20_put_message(&cdev->ap, skb); @@ -788,30 +791,35 @@ capi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct capidev *cdev = file->private_data; - struct capi20_appl *ap = &cdev->ap; capi_ioctl_struct data; int retval = -EINVAL; void __user *argp = (void __user *)arg; switch (cmd) { case CAPI_REGISTER: - { - if (ap->applid) - return -EEXIST; + mutex_lock(&cdev->lock); - if (copy_from_user(&cdev->ap.rparam, argp, - sizeof(struct capi_register_params))) - return -EFAULT; - - cdev->ap.private = cdev; - cdev->ap.recv_message = capi_recv_message; - cdev->errcode = capi20_register(ap); - if (cdev->errcode) { - ap->applid = 0; - return -EIO; - } + if (cdev->ap.applid) { + retval = -EEXIST; + goto register_out; + } + if (copy_from_user(&cdev->ap.rparam, argp, + sizeof(struct capi_register_params))) { + retval = -EFAULT; + goto register_out; + } + cdev->ap.private = cdev; + cdev->ap.recv_message = capi_recv_message; + cdev->errcode = capi20_register(&cdev->ap); + retval = (int)cdev->ap.applid; + if (cdev->errcode) { + cdev->ap.applid = 0; + retval = -EIO; } - return (int)ap->applid; + +register_out: + mutex_unlock(&cdev->lock); + return retval; case CAPI_GET_VERSION: { @@ -910,101 +918,104 @@ capi_ioctl(struct inode *inode, struct file *file, return 0; case CAPI_SET_FLAGS: - case CAPI_CLR_FLAGS: - { - unsigned userflags; - if (copy_from_user(&userflags, argp, - sizeof(userflags))) - return -EFAULT; - if (cmd == CAPI_SET_FLAGS) - cdev->userflags |= userflags; - else - cdev->userflags &= ~userflags; - } - return 0; + case CAPI_CLR_FLAGS: { + unsigned userflags; + + if (copy_from_user(&userflags, argp, sizeof(userflags))) + return -EFAULT; + mutex_lock(&cdev->lock); + if (cmd == CAPI_SET_FLAGS) + cdev->userflags |= userflags; + else + cdev->userflags &= ~userflags; + mutex_unlock(&cdev->lock); + return 0; + } case CAPI_GET_FLAGS: if (copy_to_user(argp, &cdev->userflags, sizeof(cdev->userflags))) return -EFAULT; return 0; - case CAPI_NCCI_OPENCOUNT: - { - struct capincci *nccip; -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - struct capiminor *mp; -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - unsigned ncci; - int count = 0; - if (copy_from_user(&ncci, argp, sizeof(ncci))) - return -EFAULT; + case CAPI_NCCI_OPENCOUNT: { + struct capincci *nccip; + unsigned ncci; + int count = 0; - mutex_lock(&cdev->ncci_list_mtx); - if ((nccip = capincci_find(cdev, (u32) ncci)) == NULL) { - mutex_unlock(&cdev->ncci_list_mtx); - return 0; - } -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - if ((mp = nccip->minorp) != NULL) { - count += atomic_read(&mp->ttyopencount); - } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ - mutex_unlock(&cdev->ncci_list_mtx); - return count; - } - return 0; + if (copy_from_user(&ncci, argp, sizeof(ncci))) + return -EFAULT; + + mutex_lock(&cdev->lock); + nccip = capincci_find(cdev, (u32)ncci); + if (nccip) + count = capincci_minor_opencount(nccip); + mutex_unlock(&cdev->lock); + return count; + } #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE - case CAPI_NCCI_GETUNIT: - { - struct capincci *nccip; - struct capiminor *mp; - unsigned ncci; - int unit = 0; - if (copy_from_user(&ncci, argp, - sizeof(ncci))) - return -EFAULT; - mutex_lock(&cdev->ncci_list_mtx); - nccip = capincci_find(cdev, (u32) ncci); - if (!nccip || (mp = nccip->minorp) == NULL) { - mutex_unlock(&cdev->ncci_list_mtx); - return -ESRCH; - } - unit = mp->minor; - mutex_unlock(&cdev->ncci_list_mtx); - return unit; + case CAPI_NCCI_GETUNIT: { + struct capincci *nccip; + struct capiminor *mp; + unsigned ncci; + int unit = -ESRCH; + + if (copy_from_user(&ncci, argp, sizeof(ncci))) + return -EFAULT; + + mutex_lock(&cdev->lock); + nccip = capincci_find(cdev, (u32)ncci); + if (nccip) { + mp = nccip->minorp; + if (mp) + unit = mp->minor; } - return 0; + mutex_unlock(&cdev->lock); + return unit; + } #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ + + default: + return -EINVAL; } - return -EINVAL; } -static int -capi_open(struct inode *inode, struct file *file) +static int capi_open(struct inode *inode, struct file *file) { - int ret; - - lock_kernel(); - if (file->private_data) - ret = -EEXIST; - else if ((file->private_data = capidev_alloc()) == NULL) - ret = -ENOMEM; - else - ret = nonseekable_open(inode, file); - unlock_kernel(); - return ret; + struct capidev *cdev; + + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + mutex_init(&cdev->lock); + skb_queue_head_init(&cdev->recvqueue); + init_waitqueue_head(&cdev->recvwait); + INIT_LIST_HEAD(&cdev->nccis); + file->private_data = cdev; + + mutex_lock(&capidev_list_lock); + list_add_tail(&cdev->list, &capidev_list); + mutex_unlock(&capidev_list_lock); + + return nonseekable_open(inode, file); } -static int -capi_release(struct inode *inode, struct file *file) +static int capi_release(struct inode *inode, struct file *file) { - struct capidev *cdev = (struct capidev *)file->private_data; + struct capidev *cdev = file->private_data; - capidev_free(cdev); - file->private_data = NULL; - + mutex_lock(&capidev_list_lock); + list_del(&cdev->list); + mutex_unlock(&capidev_list_lock); + + if (cdev->ap.applid) + capi20_release(&cdev->ap); + skb_queue_purge(&cdev->recvqueue); + capincci_free(cdev, 0xffffffff); + + kfree(cdev); return 0; } @@ -1023,182 +1034,159 @@ static const struct file_operations capi_fops = #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE /* -------- tty_operations for capincci ----------------------------- */ -static int capinc_tty_open(struct tty_struct * tty, struct file * file) +static int +capinc_tty_install(struct tty_driver *driver, struct tty_struct *tty) { - struct capiminor *mp; - unsigned long flags; + int idx = tty->index; + struct capiminor *mp = capiminor_get(idx); + int ret = tty_init_termios(tty); + + if (ret == 0) { + tty_driver_kref_get(driver); + tty->count++; + tty->driver_data = mp; + driver->ttys[idx] = tty; + } else + capiminor_put(mp); + return ret; +} - if ((mp = capiminor_find(iminor(file->f_path.dentry->d_inode))) == NULL) - return -ENXIO; - if (mp->nccip == NULL) - return -ENXIO; +static void capinc_tty_cleanup(struct tty_struct *tty) +{ + struct capiminor *mp = tty->driver_data; + tty->driver_data = NULL; + capiminor_put(mp); +} - tty->driver_data = (void *)mp; +static int capinc_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct capiminor *mp = tty->driver_data; + int err; + + err = tty_port_open(&mp->port, tty, filp); + if (err) + return err; - spin_lock_irqsave(&workaround_lock, flags); - if (atomic_read(&mp->ttyopencount) == 0) - mp->tty = tty; - atomic_inc(&mp->ttyopencount); -#ifdef _DEBUG_REFCOUNT - printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount)); -#endif handle_minor_recv(mp); - spin_unlock_irqrestore(&workaround_lock, flags); return 0; } -static void capinc_tty_close(struct tty_struct * tty, struct file * file) +static void capinc_tty_close(struct tty_struct *tty, struct file *filp) { - struct capiminor *mp; - - mp = (struct capiminor *)tty->driver_data; - if (mp) { - if (atomic_dec_and_test(&mp->ttyopencount)) { -#ifdef _DEBUG_REFCOUNT - printk(KERN_DEBUG "capinc_tty_close lastclose\n"); -#endif - tty->driver_data = NULL; - mp->tty = NULL; - } -#ifdef _DEBUG_REFCOUNT - printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount)); -#endif - if (mp->nccip == NULL) - capiminor_free(mp); - } + struct capiminor *mp = tty->driver_data; -#ifdef _DEBUG_REFCOUNT - printk(KERN_DEBUG "capinc_tty_close\n"); -#endif + tty_port_close(&mp->port, tty, filp); } -static int capinc_tty_write(struct tty_struct * tty, +static int capinc_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct capiminor *mp = tty->driver_data; struct sk_buff *skb; - unsigned long flags; #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count); #endif - if (!mp || !mp->nccip) { -#ifdef _DEBUG_TTYFUNCS - printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n"); -#endif - return 0; - } - - spin_lock_irqsave(&workaround_lock, flags); - skb = mp->ttyskb; + spin_lock_bh(&mp->outlock); + skb = mp->outskb; if (skb) { - mp->ttyskb = NULL; - skb_queue_tail(&mp->outqueue, skb); + mp->outskb = NULL; + __skb_queue_tail(&mp->outqueue, skb); mp->outbytes += skb->len; } skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC); if (!skb) { printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n"); - spin_unlock_irqrestore(&workaround_lock, flags); + spin_unlock_bh(&mp->outlock); return -ENOMEM; } skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); memcpy(skb_put(skb, count), buf, count); - skb_queue_tail(&mp->outqueue, skb); + __skb_queue_tail(&mp->outqueue, skb); mp->outbytes += skb->len; - (void)handle_minor_send(mp); - (void)handle_minor_recv(mp); - spin_unlock_irqrestore(&workaround_lock, flags); + spin_unlock_bh(&mp->outlock); + + handle_minor_send(mp); + return count; } static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct capiminor *mp = tty->driver_data; + bool invoke_send = false; struct sk_buff *skb; - unsigned long flags; int ret = 1; #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_put_char(%u)\n", ch); #endif - if (!mp || !mp->nccip) { -#ifdef _DEBUG_TTYFUNCS - printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n"); -#endif - return 0; - } - - spin_lock_irqsave(&workaround_lock, flags); - skb = mp->ttyskb; + spin_lock_bh(&mp->outlock); + skb = mp->outskb; if (skb) { if (skb_tailroom(skb) > 0) { *(skb_put(skb, 1)) = ch; - spin_unlock_irqrestore(&workaround_lock, flags); - return 1; + goto unlock_out; } - mp->ttyskb = NULL; - skb_queue_tail(&mp->outqueue, skb); + mp->outskb = NULL; + __skb_queue_tail(&mp->outqueue, skb); mp->outbytes += skb->len; - (void)handle_minor_send(mp); + invoke_send = true; } + skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC); if (skb) { skb_reserve(skb, CAPI_DATA_B3_REQ_LEN); *(skb_put(skb, 1)) = ch; - mp->ttyskb = skb; + mp->outskb = skb; } else { printk(KERN_ERR "capinc_put_char: char %u lost\n", ch); ret = 0; } - spin_unlock_irqrestore(&workaround_lock, flags); + +unlock_out: + spin_unlock_bh(&mp->outlock); + + if (invoke_send) + handle_minor_send(mp); + return ret; } static void capinc_tty_flush_chars(struct tty_struct *tty) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct capiminor *mp = tty->driver_data; struct sk_buff *skb; - unsigned long flags; #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_flush_chars\n"); #endif - if (!mp || !mp->nccip) { -#ifdef _DEBUG_TTYFUNCS - printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n"); -#endif - return; - } - - spin_lock_irqsave(&workaround_lock, flags); - skb = mp->ttyskb; + spin_lock_bh(&mp->outlock); + skb = mp->outskb; if (skb) { - mp->ttyskb = NULL; - skb_queue_tail(&mp->outqueue, skb); + mp->outskb = NULL; + __skb_queue_tail(&mp->outqueue, skb); mp->outbytes += skb->len; - (void)handle_minor_send(mp); - } - (void)handle_minor_recv(mp); - spin_unlock_irqrestore(&workaround_lock, flags); + spin_unlock_bh(&mp->outlock); + + handle_minor_send(mp); + } else + spin_unlock_bh(&mp->outlock); + + handle_minor_recv(mp); } static int capinc_tty_write_room(struct tty_struct *tty) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct capiminor *mp = tty->driver_data; int room; - if (!mp || !mp->nccip) { -#ifdef _DEBUG_TTYFUNCS - printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n"); -#endif - return 0; - } + room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue); room *= CAPI_MAX_BLKSIZE; #ifdef _DEBUG_TTYFUNCS @@ -1209,13 +1197,8 @@ static int capinc_tty_write_room(struct tty_struct *tty) static int capinc_tty_chars_in_buffer(struct tty_struct *tty) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; - if (!mp || !mp->nccip) { -#ifdef _DEBUG_TTYFUNCS - printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n"); -#endif - return 0; - } + struct capiminor *mp = tty->driver_data; + #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n", mp->outbytes, mp->nack, @@ -1244,62 +1227,55 @@ static void capinc_tty_set_termios(struct tty_struct *tty, struct ktermios * old #endif } -static void capinc_tty_throttle(struct tty_struct * tty) +static void capinc_tty_throttle(struct tty_struct *tty) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct capiminor *mp = tty->driver_data; #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_throttle\n"); #endif - if (mp) - mp->ttyinstop = 1; + mp->ttyinstop = 1; } -static void capinc_tty_unthrottle(struct tty_struct * tty) +static void capinc_tty_unthrottle(struct tty_struct *tty) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; - unsigned long flags; + struct capiminor *mp = tty->driver_data; + #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_unthrottle\n"); #endif - if (mp) { - spin_lock_irqsave(&workaround_lock, flags); - mp->ttyinstop = 0; - handle_minor_recv(mp); - spin_unlock_irqrestore(&workaround_lock, flags); - } + mp->ttyinstop = 0; + handle_minor_recv(mp); } static void capinc_tty_stop(struct tty_struct *tty) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; + struct capiminor *mp = tty->driver_data; + #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_stop\n"); #endif - if (mp) { - mp->ttyoutstop = 1; - } + mp->ttyoutstop = 1; } static void capinc_tty_start(struct tty_struct *tty) { - struct capiminor *mp = (struct capiminor *)tty->driver_data; - unsigned long flags; + struct capiminor *mp = tty->driver_data; + #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_start\n"); #endif - if (mp) { - spin_lock_irqsave(&workaround_lock, flags); - mp->ttyoutstop = 0; - (void)handle_minor_send(mp); - spin_unlock_irqrestore(&workaround_lock, flags); - } + mp->ttyoutstop = 0; + handle_minor_send(mp); } static void capinc_tty_hangup(struct tty_struct *tty) { + struct capiminor *mp = tty->driver_data; + #ifdef _DEBUG_TTYFUNCS printk(KERN_DEBUG "capinc_tty_hangup\n"); #endif + tty_port_hangup(&mp->port); } static int capinc_tty_break_ctl(struct tty_struct *tty, int state) @@ -1331,8 +1307,6 @@ static void capinc_tty_send_xchar(struct tty_struct *tty, char ch) #endif } -static struct tty_driver *capinc_tty_driver; - static const struct tty_operations capinc_ops = { .open = capinc_tty_open, .close = capinc_tty_close, @@ -1352,25 +1326,34 @@ static const struct tty_operations capinc_ops = { .flush_buffer = capinc_tty_flush_buffer, .set_ldisc = capinc_tty_set_ldisc, .send_xchar = capinc_tty_send_xchar, + .install = capinc_tty_install, + .cleanup = capinc_tty_cleanup, }; -static int capinc_tty_init(void) +static int __init capinc_tty_init(void) { struct tty_driver *drv; - + int err; + if (capi_ttyminors > CAPINC_MAX_PORTS) capi_ttyminors = CAPINC_MAX_PORTS; if (capi_ttyminors <= 0) capi_ttyminors = CAPINC_NR_PORTS; - drv = alloc_tty_driver(capi_ttyminors); - if (!drv) + capiminors = kzalloc(sizeof(struct capi_minor *) * capi_ttyminors, + GFP_KERNEL); + if (!capiminors) return -ENOMEM; + drv = alloc_tty_driver(capi_ttyminors); + if (!drv) { + kfree(capiminors); + return -ENOMEM; + } drv->owner = THIS_MODULE; drv->driver_name = "capi_nc"; drv->name = "capi"; - drv->major = capi_ttymajor; + drv->major = 0; drv->minor_start = 0; drv->type = TTY_DRIVER_TYPE_SERIAL; drv->subtype = SERIAL_TYPE_NORMAL; @@ -1379,27 +1362,39 @@ static int capinc_tty_init(void) drv->init_termios.c_oflag = OPOST | ONLCR; drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; drv->init_termios.c_lflag = 0; - drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS; + drv->flags = + TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(drv, &capinc_ops); - if (tty_register_driver(drv)) { + + err = tty_register_driver(drv); + if (err) { put_tty_driver(drv); + kfree(capiminors); printk(KERN_ERR "Couldn't register capi_nc driver\n"); - return -1; + return err; } capinc_tty_driver = drv; return 0; } -static void capinc_tty_exit(void) +static void __exit capinc_tty_exit(void) { - struct tty_driver *drv = capinc_tty_driver; - int retval; - if ((retval = tty_unregister_driver(drv))) - printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval); - put_tty_driver(drv); + tty_unregister_driver(capinc_tty_driver); + put_tty_driver(capinc_tty_driver); + kfree(capiminors); } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ +#else /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ + +static inline int capinc_tty_init(void) +{ + return 0; +} + +static inline void capinc_tty_exit(void) { } + +#endif /* !CONFIG_ISDN_CAPI_MIDDLEWARE */ /* -------- /proc functions ----------------------------------------- */ @@ -1407,134 +1402,91 @@ static void capinc_tty_exit(void) * /proc/capi/capi20: * minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt */ -static int proc_capidev_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int capi20_proc_show(struct seq_file *m, void *v) { struct capidev *cdev; struct list_head *l; - int len = 0; - read_lock(&capidev_list_lock); + mutex_lock(&capidev_list_lock); list_for_each(l, &capidev_list) { cdev = list_entry(l, struct capidev, list); - len += sprintf(page+len, "0 %d %lu %lu %lu %lu\n", + seq_printf(m, "0 %d %lu %lu %lu %lu\n", cdev->ap.applid, cdev->ap.nrecvctlpkt, cdev->ap.nrecvdatapkt, cdev->ap.nsentctlpkt, cdev->ap.nsentdatapkt); - if (len <= off) { - off -= len; - len = 0; - } else { - if (len-off > count) - goto endloop; - } } + mutex_unlock(&capidev_list_lock); + return 0; +} -endloop: - read_unlock(&capidev_list_lock); - if (len < count) - *eof = 1; - if (len > count) len = count; - if (len < 0) len = 0; - return len; +static int capi20_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, capi20_proc_show, NULL); } +static const struct file_operations capi20_proc_fops = { + .owner = THIS_MODULE, + .open = capi20_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * /proc/capi/capi20ncci: * applid ncci */ -static int proc_capincci_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int capi20ncci_proc_show(struct seq_file *m, void *v) { - struct capidev *cdev; - struct capincci *np; - struct list_head *l; - int len = 0; + struct capidev *cdev; + struct capincci *np; - read_lock(&capidev_list_lock); - list_for_each(l, &capidev_list) { - cdev = list_entry(l, struct capidev, list); - for (np=cdev->nccis; np; np = np->next) { - len += sprintf(page+len, "%d 0x%x\n", - cdev->ap.applid, - np->ncci); - if (len <= off) { - off -= len; - len = 0; - } else { - if (len-off > count) - goto endloop; - } - } + mutex_lock(&capidev_list_lock); + list_for_each_entry(cdev, &capidev_list, list) { + mutex_lock(&cdev->lock); + list_for_each_entry(np, &cdev->nccis, list) + seq_printf(m, "%d 0x%x\n", cdev->ap.applid, np->ncci); + mutex_unlock(&cdev->lock); } -endloop: - read_unlock(&capidev_list_lock); - *start = page+off; - if (len < count) - *eof = 1; - if (len>count) len = count; - if (len<0) len = 0; - return len; + mutex_unlock(&capidev_list_lock); + return 0; +} + +static int capi20ncci_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, capi20ncci_proc_show, NULL); } -static struct procfsentries { - char *name; - mode_t mode; - int (*read_proc)(char *page, char **start, off_t off, - int count, int *eof, void *data); - struct proc_dir_entry *procent; -} procfsentries[] = { - /* { "capi", S_IFDIR, 0 }, */ - { "capi/capi20", 0 , proc_capidev_read_proc }, - { "capi/capi20ncci", 0 , proc_capincci_read_proc }, +static const struct file_operations capi20ncci_proc_fops = { + .owner = THIS_MODULE, + .open = capi20ncci_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, }; static void __init proc_init(void) { - int nelem = ARRAY_SIZE(procfsentries); - int i; - - for (i=0; i < nelem; i++) { - struct procfsentries *p = procfsentries + i; - p->procent = create_proc_entry(p->name, p->mode, NULL); - if (p->procent) p->procent->read_proc = p->read_proc; - } + proc_create("capi/capi20", 0, NULL, &capi20_proc_fops); + proc_create("capi/capi20ncci", 0, NULL, &capi20ncci_proc_fops); } static void __exit proc_exit(void) { - int nelem = ARRAY_SIZE(procfsentries); - int i; - - for (i=nelem-1; i >= 0; i--) { - struct procfsentries *p = procfsentries + i; - if (p->procent) { - remove_proc_entry(p->name, NULL); - p->procent = NULL; - } - } + remove_proc_entry("capi/capi20", NULL); + remove_proc_entry("capi/capi20ncci", NULL); } /* -------- init function and module interface ---------------------- */ -static char rev[32]; - static int __init capi_init(void) { - char *p; - char *compileinfo; + const char *compileinfo; int major_ret; - if ((p = strchr(revision, ':')) != NULL && p[1]) { - strlcpy(rev, p + 2, sizeof(rev)); - if ((p = strchr(rev, '$')) != NULL && p > rev) - *(p-1) = 0; - } else - strcpy(rev, "1.0"); - major_ret = register_chrdev(capi_major, "capi20", &capi_fops); if (major_ret < 0) { printk(KERN_ERR "capi20: unable to get major %d\n", capi_major); @@ -1548,28 +1500,24 @@ static int __init capi_init(void) device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi"); -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE if (capinc_tty_init() < 0) { device_destroy(capi_class, MKDEV(capi_major, 0)); class_destroy(capi_class); unregister_chrdev(capi_major, "capi20"); return -ENOMEM; } -#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */ proc_init(); -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE #if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE) compileinfo = " (middleware+capifs)"; -#else +#elif defined(CONFIG_ISDN_CAPI_MIDDLEWARE) compileinfo = " (no capifs)"; -#endif #else compileinfo = " (no middleware)"; #endif - printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n", - rev, capi_major, compileinfo); + printk(KERN_NOTICE "CAPI 2.0 started up with major %d%s\n", + capi_major, compileinfo); return 0; } @@ -1582,10 +1530,7 @@ static void __exit capi_exit(void) class_destroy(capi_class); unregister_chrdev(capi_major, "capi20"); -#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE capinc_tty_exit(); -#endif - printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev); } module_init(capi_init); |