diff options
Diffstat (limited to 'net/bluetooth/rfcomm/tty.c')
| -rw-r--r-- | net/bluetooth/rfcomm/tty.c | 701 |
1 files changed, 414 insertions, 287 deletions
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index bd8d671a0ba..8e385a0ae60 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -1,4 +1,4 @@ -/* +/* RFCOMM implementation for Linux Bluetooth stack (BlueZ). Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> @@ -11,20 +11,18 @@ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS + ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, + COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* * RFCOMM TTY. - * - * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $ */ #include <linux/module.h> @@ -33,61 +31,56 @@ #include <linux/tty_driver.h> #include <linux/tty_flip.h> -#include <linux/capability.h> -#include <linux/slab.h> -#include <linux/skbuff.h> - #include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> #include <net/bluetooth/rfcomm.h> -#ifndef CONFIG_BT_RFCOMM_DEBUG -#undef BT_DBG -#define BT_DBG(D...) -#endif - #define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */ #define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */ #define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */ #define RFCOMM_TTY_MINOR 0 +static DEFINE_MUTEX(rfcomm_ioctl_mutex); static struct tty_driver *rfcomm_tty_driver; struct rfcomm_dev { + struct tty_port port; struct list_head list; - atomic_t refcnt; char name[12]; int id; unsigned long flags; - int opened; int err; + unsigned long status; /* don't export to userspace */ + bdaddr_t src; bdaddr_t dst; - u8 channel; + u8 channel; - uint modem_status; + uint modem_status; struct rfcomm_dlc *dlc; - struct tty_struct *tty; - wait_queue_head_t wait; - struct tasklet_struct wakeup_task; - atomic_t wmem_alloc; + struct device *tty_dev; + + atomic_t wmem_alloc; + + struct sk_buff_head pending; }; static LIST_HEAD(rfcomm_dev_list); -static DEFINE_RWLOCK(rfcomm_dev_lock); +static DEFINE_MUTEX(rfcomm_dev_lock); static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); -static void rfcomm_tty_wakeup(unsigned long arg); - /* ---- Device functions ---- */ -static void rfcomm_dev_destruct(struct rfcomm_dev *dev) + +static void rfcomm_dev_destruct(struct tty_port *port) { + struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port); struct rfcomm_dlc *dlc = dev->dlc; BT_DBG("dev %p dlc %p", dev, dlc); @@ -100,97 +93,153 @@ static void rfcomm_dev_destruct(struct rfcomm_dev *dev) rfcomm_dlc_put(dlc); - tty_unregister_device(rfcomm_tty_driver, dev->id); + if (dev->tty_dev) + tty_unregister_device(rfcomm_tty_driver, dev->id); - /* Refcount should only hit zero when called from rfcomm_dev_del() - which will have taken us off the list. Everything else are - refcounting bugs. */ - BUG_ON(!list_empty(&dev->list)); + mutex_lock(&rfcomm_dev_lock); + list_del(&dev->list); + mutex_unlock(&rfcomm_dev_lock); kfree(dev); - /* It's safe to call module_put() here because socket still + /* It's safe to call module_put() here because socket still holds reference to this module. */ module_put(THIS_MODULE); } -static inline void rfcomm_dev_hold(struct rfcomm_dev *dev) +/* device-specific initialization: open the dlc */ +static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port); + int err; + + err = rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel); + if (err) + set_bit(TTY_IO_ERROR, &tty->flags); + return err; +} + +/* we block the open until the dlc->state becomes BT_CONNECTED */ +static int rfcomm_dev_carrier_raised(struct tty_port *port) { - atomic_inc(&dev->refcnt); + struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port); + + return (dev->dlc->state == BT_CONNECTED); } -static inline void rfcomm_dev_put(struct rfcomm_dev *dev) +/* device-specific cleanup: close the dlc */ +static void rfcomm_dev_shutdown(struct tty_port *port) { - /* The reason this isn't actually a race, as you no - doubt have a little voice screaming at you in your - head, is that the refcount should never actually - reach zero unless the device has already been taken - off the list, in rfcomm_dev_del(). And if that's not - true, we'll hit the BUG() in rfcomm_dev_destruct() - anyway. */ - if (atomic_dec_and_test(&dev->refcnt)) - rfcomm_dev_destruct(dev); + struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port); + + if (dev->tty_dev->parent) + device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); + + /* close the dlc */ + rfcomm_dlc_close(dev->dlc, 0); } -static struct rfcomm_dev *__rfcomm_dev_get(int id) +static const struct tty_port_operations rfcomm_port_ops = { + .destruct = rfcomm_dev_destruct, + .activate = rfcomm_dev_activate, + .shutdown = rfcomm_dev_shutdown, + .carrier_raised = rfcomm_dev_carrier_raised, +}; + +static struct rfcomm_dev *__rfcomm_dev_lookup(int id) { struct rfcomm_dev *dev; - struct list_head *p; - list_for_each(p, &rfcomm_dev_list) { - dev = list_entry(p, struct rfcomm_dev, list); + list_for_each_entry(dev, &rfcomm_dev_list, list) if (dev->id == id) return dev; - } return NULL; } -static inline struct rfcomm_dev *rfcomm_dev_get(int id) +static struct rfcomm_dev *rfcomm_dev_get(int id) { struct rfcomm_dev *dev; - read_lock(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); + + dev = __rfcomm_dev_lookup(id); - dev = __rfcomm_dev_get(id); - if (dev) - rfcomm_dev_hold(dev); + if (dev && !tty_port_get(&dev->port)) + dev = NULL; - read_unlock(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); return dev; } -static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) +static void rfcomm_reparent_device(struct rfcomm_dev *dev) { - struct rfcomm_dev *dev; - struct list_head *head = &rfcomm_dev_list, *p; + struct hci_dev *hdev; + struct hci_conn *conn; + + hdev = hci_get_route(&dev->dst, &dev->src); + if (!hdev) + return; + + /* The lookup results are unsafe to access without the + * hci device lock (FIXME: why is this not documented?) + */ + hci_dev_lock(hdev); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst); + + /* Just because the acl link is in the hash table is no + * guarantee the sysfs device has been added ... + */ + if (conn && device_is_registered(&conn->dev)) + device_move(dev->tty_dev, &conn->dev, DPM_ORDER_DEV_AFTER_PARENT); + + hci_dev_unlock(hdev); + hci_dev_put(hdev); +} + +static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf) +{ + struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); + return sprintf(buf, "%pMR\n", &dev->dst); +} + +static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf) +{ + struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); + return sprintf(buf, "%d\n", dev->channel); +} + +static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); +static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); + +static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req, + struct rfcomm_dlc *dlc) +{ + struct rfcomm_dev *dev, *entry; + struct list_head *head = &rfcomm_dev_list; int err = 0; - BT_DBG("id %d channel %d", req->dev_id, req->channel); - dev = kzalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); if (!dev) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - write_lock_bh(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); if (req->dev_id < 0) { dev->id = 0; - list_for_each(p, &rfcomm_dev_list) { - if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) + list_for_each_entry(entry, &rfcomm_dev_list, list) { + if (entry->id != dev->id) break; dev->id++; - head = p; + head = &entry->list; } } else { dev->id = req->dev_id; - list_for_each(p, &rfcomm_dev_list) { - struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list); - + list_for_each_entry(entry, &rfcomm_dev_list, list) { if (entry->id == dev->id) { err = -EADDRINUSE; goto out; @@ -199,7 +248,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) if (entry->id > dev->id - 1) break; - head = p; + head = &entry->list; } } @@ -211,97 +260,134 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) sprintf(dev->name, "rfcomm%d", dev->id); list_add(&dev->list, head); - atomic_set(&dev->refcnt, 1); bacpy(&dev->src, &req->src); bacpy(&dev->dst, &req->dst); dev->channel = req->channel; - dev->flags = req->flags & + dev->flags = req->flags & ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); - init_waitqueue_head(&dev->wait); - tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); + tty_port_init(&dev->port); + dev->port.ops = &rfcomm_port_ops; + + skb_queue_head_init(&dev->pending); rfcomm_dlc_lock(dlc); + + if (req->flags & (1 << RFCOMM_REUSE_DLC)) { + struct sock *sk = dlc->owner; + struct sk_buff *skb; + + BUG_ON(!sk); + + rfcomm_dlc_throttle(dlc); + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) { + skb_orphan(skb); + skb_queue_tail(&dev->pending, skb); + atomic_sub(skb->len, &sk->sk_rmem_alloc); + } + } + dlc->data_ready = rfcomm_dev_data_ready; dlc->state_change = rfcomm_dev_state_change; dlc->modem_status = rfcomm_dev_modem_status; dlc->owner = dev; dev->dlc = dlc; + + rfcomm_dev_modem_status(dlc, dlc->remote_v24_sig); + rfcomm_dlc_unlock(dlc); - /* It's safe to call __module_get() here because socket already + /* It's safe to call __module_get() here because socket already holds reference to this module. */ __module_get(THIS_MODULE); + mutex_unlock(&rfcomm_dev_lock); + return dev; + out: - write_unlock_bh(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); + kfree(dev); + return ERR_PTR(err); +} - if (err) { - kfree(dev); - return err; +static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) +{ + struct rfcomm_dev *dev; + struct device *tty; + + BT_DBG("id %d channel %d", req->dev_id, req->channel); + + dev = __rfcomm_dev_add(req, dlc); + if (IS_ERR(dev)) { + rfcomm_dlc_put(dlc); + return PTR_ERR(dev); } - tty_register_device(rfcomm_tty_driver, dev->id, NULL); + tty = tty_port_register_device(&dev->port, rfcomm_tty_driver, + dev->id, NULL); + if (IS_ERR(tty)) { + tty_port_put(&dev->port); + return PTR_ERR(tty); + } - return dev->id; -} + dev->tty_dev = tty; + rfcomm_reparent_device(dev); + dev_set_drvdata(dev->tty_dev, dev); -static void rfcomm_dev_del(struct rfcomm_dev *dev) -{ - BT_DBG("dev %p", dev); + if (device_create_file(dev->tty_dev, &dev_attr_address) < 0) + BT_ERR("Failed to create address attribute"); - write_lock_bh(&rfcomm_dev_lock); - list_del_init(&dev->list); - write_unlock_bh(&rfcomm_dev_lock); + if (device_create_file(dev->tty_dev, &dev_attr_channel) < 0) + BT_ERR("Failed to create channel attribute"); - rfcomm_dev_put(dev); + return dev->id; } /* ---- Send buffer ---- */ -static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) +static inline unsigned int rfcomm_room(struct rfcomm_dev *dev) { - /* We can't let it be zero, because we don't get a callback - when tx_credits becomes nonzero, hence we'd never wake up */ - return dlc->mtu * (dlc->tx_credits?:1); + struct rfcomm_dlc *dlc = dev->dlc; + + /* Limit the outstanding number of packets not yet sent to 40 */ + int pending = 40 - atomic_read(&dev->wmem_alloc); + + return max(0, pending) * dlc->mtu; } static void rfcomm_wfree(struct sk_buff *skb) { struct rfcomm_dev *dev = (void *) skb->sk; - atomic_sub(skb->truesize, &dev->wmem_alloc); + atomic_dec(&dev->wmem_alloc); if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) - tasklet_schedule(&dev->wakeup_task); - rfcomm_dev_put(dev); + tty_port_tty_wakeup(&dev->port); + tty_port_put(&dev->port); } -static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) +static void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) { - rfcomm_dev_hold(dev); - atomic_add(skb->truesize, &dev->wmem_alloc); + tty_port_get(&dev->port); + atomic_inc(&dev->wmem_alloc); skb->sk = (void *) dev; skb->destructor = rfcomm_wfree; } static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority) { - if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { - struct sk_buff *skb = alloc_skb(size, priority); - if (skb) { - rfcomm_set_owner_w(skb, dev); - return skb; - } - } - return NULL; + struct sk_buff *skb = alloc_skb(size, priority); + if (skb) + rfcomm_set_owner_w(skb, dev); + return skb; } /* ---- Device IOCTLs ---- */ #define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP)) -static int rfcomm_create_dev(struct sock *sk, void __user *arg) +static int __rfcomm_create_dev(struct sock *sk, void __user *arg) { struct rfcomm_dev_req req; struct rfcomm_dlc *dlc; @@ -310,7 +396,7 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg) if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; - BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags); + BT_DBG("sk %p dev_id %d flags 0x%x", sk, req.dev_id, req.flags); if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) return -EPERM; @@ -323,16 +409,22 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg) dlc = rfcomm_pi(sk)->dlc; rfcomm_dlc_hold(dlc); } else { + /* Validate the channel is unused */ + dlc = rfcomm_dlc_exists(&req.src, &req.dst, req.channel); + if (IS_ERR(dlc)) + return PTR_ERR(dlc); + else if (dlc) { + rfcomm_dlc_put(dlc); + return -EBUSY; + } dlc = rfcomm_dlc_alloc(GFP_KERNEL); if (!dlc) return -ENOMEM; } id = rfcomm_dev_add(&req, dlc); - if (id < 0) { - rfcomm_dlc_put(dlc); + if (id < 0) return id; - } if (req.flags & (1 << RFCOMM_REUSE_DLC)) { /* DLC is now used by device. @@ -343,37 +435,76 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg) return id; } -static int rfcomm_release_dev(void __user *arg) +static int __rfcomm_release_dev(void __user *arg) { struct rfcomm_dev_req req; struct rfcomm_dev *dev; + struct tty_struct *tty; if (copy_from_user(&req, arg, sizeof(req))) return -EFAULT; - BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags); + BT_DBG("dev_id %d flags 0x%x", req.dev_id, req.flags); - if (!(dev = rfcomm_dev_get(req.dev_id))) + dev = rfcomm_dev_get(req.dev_id); + if (!dev) return -ENODEV; if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) { - rfcomm_dev_put(dev); + tty_port_put(&dev->port); return -EPERM; } + /* only release once */ + if (test_and_set_bit(RFCOMM_DEV_RELEASED, &dev->status)) { + tty_port_put(&dev->port); + return -EALREADY; + } + if (req.flags & (1 << RFCOMM_HANGUP_NOW)) rfcomm_dlc_close(dev->dlc, 0); - rfcomm_dev_del(dev); - rfcomm_dev_put(dev); + /* Shut down TTY synchronously before freeing rfcomm_dev */ + tty = tty_port_tty_get(&dev->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + + if (!test_bit(RFCOMM_TTY_OWNED, &dev->status)) + tty_port_put(&dev->port); + + tty_port_put(&dev->port); return 0; } +static int rfcomm_create_dev(struct sock *sk, void __user *arg) +{ + int ret; + + mutex_lock(&rfcomm_ioctl_mutex); + ret = __rfcomm_create_dev(sk, arg); + mutex_unlock(&rfcomm_ioctl_mutex); + + return ret; +} + +static int rfcomm_release_dev(void __user *arg) +{ + int ret; + + mutex_lock(&rfcomm_ioctl_mutex); + ret = __rfcomm_release_dev(arg); + mutex_unlock(&rfcomm_ioctl_mutex); + + return ret; +} + static int rfcomm_get_dev_list(void __user *arg) { + struct rfcomm_dev *dev; struct rfcomm_dev_list_req *dl; struct rfcomm_dev_info *di; - struct list_head *p; int n = 0, size, err; u16 dev_num; @@ -387,26 +518,29 @@ static int rfcomm_get_dev_list(void __user *arg) size = sizeof(*dl) + dev_num * sizeof(*di); - if (!(dl = kmalloc(size, GFP_KERNEL))) + dl = kzalloc(size, GFP_KERNEL); + if (!dl) return -ENOMEM; di = dl->dev_info; - read_lock_bh(&rfcomm_dev_lock); + mutex_lock(&rfcomm_dev_lock); - list_for_each(p, &rfcomm_dev_list) { - struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list); + list_for_each_entry(dev, &rfcomm_dev_list, list) { + if (!tty_port_get(&dev->port)) + continue; (di + n)->id = dev->id; (di + n)->flags = dev->flags; (di + n)->state = dev->dlc->state; (di + n)->channel = dev->channel; bacpy(&(di + n)->src, &dev->src); bacpy(&(di + n)->dst, &dev->dst); + tty_port_put(&dev->port); if (++n >= dev_num) break; } - read_unlock_bh(&rfcomm_dev_lock); + mutex_unlock(&rfcomm_dev_lock); dl->dev_num = n; size = sizeof(*dl) + n * sizeof(*di); @@ -428,7 +562,8 @@ static int rfcomm_get_dev_info(void __user *arg) if (copy_from_user(&di, arg, sizeof(di))) return -EFAULT; - if (!(dev = rfcomm_dev_get(di.id))) + dev = rfcomm_dev_get(di.id); + if (!dev) return -ENODEV; di.flags = dev->flags; @@ -440,7 +575,7 @@ static int rfcomm_get_dev_info(void __user *arg) if (copy_to_user(arg, &di, sizeof(di))) err = -EFAULT; - rfcomm_dev_put(dev); + tty_port_put(&dev->port); return err; } @@ -469,17 +604,21 @@ int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) { struct rfcomm_dev *dev = dlc->owner; - struct tty_struct *tty; - - if (!dev || !(tty = dev->tty)) { + + if (!dev) { kfree_skb(skb); return; } - BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); + if (!skb_queue_empty(&dev->pending)) { + skb_queue_tail(&dev->pending, skb); + return; + } + + BT_DBG("dlc %p len %d", dlc, skb->len); - tty_insert_flip_string(tty, skb->data, skb->len); - tty_flip_buffer_push(tty); + tty_insert_flip_string(&dev->port, skb->data, skb->len); + tty_flip_buffer_push(&dev->port); kfree_skb(skb); } @@ -489,28 +628,16 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) struct rfcomm_dev *dev = dlc->owner; if (!dev) return; - + BT_DBG("dlc %p dev %p err %d", dlc, dev, err); dev->err = err; - wake_up_interruptible(&dev->wait); - - if (dlc->state == BT_CLOSED) { - if (!dev->tty) { - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { - rfcomm_dev_hold(dev); - rfcomm_dev_del(dev); - - /* We have to drop DLC lock here, otherwise - rfcomm_dev_put() will dead lock if it's - the last reference. */ - rfcomm_dlc_unlock(dlc); - rfcomm_dev_put(dev); - rfcomm_dlc_lock(dlc); - } - } else - tty_hangup(dev->tty); - } + if (dlc->state == BT_CONNECTED) { + rfcomm_reparent_device(dev); + + wake_up_interruptible(&dev->port.open_wait); + } else if (dlc->state == BT_CLOSED) + tty_port_tty_hangup(&dev->port, false); } static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) @@ -521,12 +648,10 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); - if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) { - if (dev->tty && !C_CLOCAL(dev->tty)) - tty_hangup(dev->tty); - } + if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) + tty_port_tty_hangup(&dev->port, true); - dev->modem_status = + dev->modem_status = ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) | ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) | @@ -534,110 +659,125 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) } /* ---- TTY functions ---- */ -static void rfcomm_tty_wakeup(unsigned long arg) +static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev) { - struct rfcomm_dev *dev = (void *) arg; - struct tty_struct *tty = dev->tty; - if (!tty) - return; + struct sk_buff *skb; + int inserted = 0; + + BT_DBG("dev %p", dev); + + rfcomm_dlc_lock(dev->dlc); - BT_DBG("dev %p tty %p", dev, tty); + while ((skb = skb_dequeue(&dev->pending))) { + inserted += tty_insert_flip_string(&dev->port, skb->data, + skb->len); + kfree_skb(skb); + } - if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup)(tty); + rfcomm_dlc_unlock(dev->dlc); - wake_up_interruptible(&tty->write_wait); -#ifdef SERIAL_HAVE_POLL_WAIT - wake_up_interruptible(&tty->poll_wait); -#endif + if (inserted > 0) + tty_flip_buffer_push(&dev->port); } -static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) +/* do the reverse of install, clearing the tty fields and releasing the + * reference to tty_port + */ +static void rfcomm_tty_cleanup(struct tty_struct *tty) { - DECLARE_WAITQUEUE(wait, current); - struct rfcomm_dev *dev; - struct rfcomm_dlc *dlc; - int err, id; + struct rfcomm_dev *dev = tty->driver_data; - id = tty->index; + clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - BT_DBG("tty %p id %d", tty, id); + rfcomm_dlc_lock(dev->dlc); + tty->driver_data = NULL; + rfcomm_dlc_unlock(dev->dlc); - /* We don't leak this refcount. For reasons which are not entirely - clear, the TTY layer will call our ->close() method even if the - open fails. We decrease the refcount there, and decreasing it - here too would cause breakage. */ - dev = rfcomm_dev_get(id); - if (!dev) - return -ENODEV; + /* + * purge the dlc->tx_queue to avoid circular dependencies + * between dev and dlc + */ + skb_queue_purge(&dev->dlc->tx_queue); + + tty_port_put(&dev->port); +} - BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened); +/* we acquire the tty_port reference since it's here the tty is first used + * by setting the termios. We also populate the driver_data field and install + * the tty port + */ +static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct rfcomm_dev *dev; + struct rfcomm_dlc *dlc; + int err; - if (dev->opened++ != 0) - return 0; + dev = rfcomm_dev_get(tty->index); + if (!dev) + return -ENODEV; dlc = dev->dlc; /* Attach TTY and open DLC */ - rfcomm_dlc_lock(dlc); tty->driver_data = dev; - dev->tty = tty; rfcomm_dlc_unlock(dlc); set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); - if (err < 0) + /* install the tty_port */ + err = tty_port_install(&dev->port, driver, tty); + if (err) { + rfcomm_tty_cleanup(tty); return err; + } - /* Wait for DLC to connect */ - add_wait_queue(&dev->wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); + /* take over the tty_port reference if the port was created with the + * flag RFCOMM_RELEASE_ONHUP. This will force the release of the port + * when the last process closes the tty. The behaviour is expected by + * userspace. + */ + if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { + set_bit(RFCOMM_TTY_OWNED, &dev->status); + tty_port_put(&dev->port); + } - if (dlc->state == BT_CLOSED) { - err = -dev->err; - break; - } + return 0; +} - if (dlc->state == BT_CONNECTED) - break; +static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct rfcomm_dev *dev = tty->driver_data; + int err; - if (signal_pending(current)) { - err = -EINTR; - break; - } + BT_DBG("tty %p id %d", tty, tty->index); - schedule(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dev->wait, &wait); + BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst, + dev->channel, dev->port.count); - return err; + err = tty_port_open(&dev->port, tty, filp); + if (err) + return err; + + /* + * FIXME: rfcomm should use proper flow control for + * received data. This hack will be unnecessary and can + * be removed when that's implemented + */ + rfcomm_tty_copy_pending(dev); + + rfcomm_dlc_unthrottle(dev->dlc); + + return 0; } static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - if (!dev) - return; - - BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened); - - if (--dev->opened == 0) { - /* Close DLC and dettach TTY */ - rfcomm_dlc_close(dev->dlc, 0); - - clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - tasklet_kill(&dev->wakeup_task); - rfcomm_dlc_lock(dev->dlc); - tty->driver_data = NULL; - dev->tty = NULL; - rfcomm_dlc_unlock(dev->dlc); - } + BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, + dev->port.count); - rfcomm_dev_put(dev); + tty_port_close(&dev->port, tty, filp); } static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) @@ -645,7 +785,7 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; struct sk_buff *skb; - int err = 0, sent = 0, size; + int sent = 0, size; BT_DBG("tty %p count %d", tty, count); @@ -653,7 +793,6 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in size = min_t(uint, count, dlc->mtu); skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); - if (!skb) break; @@ -661,32 +800,29 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in memcpy(skb_put(skb, size), buf + sent, size); - if ((err = rfcomm_dlc_send(dlc, skb)) < 0) { - kfree_skb(skb); - break; - } + rfcomm_dlc_send_noerror(dlc, skb); sent += size; count -= size; } - return sent ? sent : err; + return sent; } static int rfcomm_tty_write_room(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - int room; + int room = 0; + + if (dev && dev->dlc) + room = rfcomm_room(dev); - BT_DBG("tty %p", tty); + BT_DBG("tty %p room %d", tty, room); - room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc); - if (room < 0) - room = 0; return room; } -static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) +static int rfcomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { BT_DBG("tty %p cmd 0x%02x", tty, cmd); @@ -703,10 +839,6 @@ static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned BT_DBG("TIOCMIWAIT"); break; - case TIOCGICOUNT: - BT_DBG("TIOCGICOUNT"); - break; - case TIOCGSERIAL: BT_ERR("TIOCGSERIAL is not supported"); return -ENOIOCTLCMD; @@ -735,9 +867,9 @@ static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned return -ENOIOCTLCMD; } -static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) +static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { - struct termios *new = (struct termios *) tty->termios; + struct ktermios *new = &tty->termios; int old_baud_rate = tty_termios_baud_rate(old); int new_baud_rate = tty_termios_baud_rate(new); @@ -748,13 +880,16 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) BT_DBG("tty %p termios %p", tty, old); + if (!dev || !dev->dlc || !dev->dlc->session) + return; + /* Handle turning off CRTSCTS */ - if ((old->c_cflag & CRTSCTS) && !(new->c_cflag & CRTSCTS)) + if ((old->c_cflag & CRTSCTS) && !(new->c_cflag & CRTSCTS)) BT_DBG("Turning off CRTSCTS unsupported"); /* Parity on/off and when on, odd/even */ if (((old->c_cflag & PARENB) != (new->c_cflag & PARENB)) || - ((old->c_cflag & PARODD) != (new->c_cflag & PARODD)) ) { + ((old->c_cflag & PARODD) != (new->c_cflag & PARODD))) { changes |= RFCOMM_RPN_PM_PARITY; BT_DBG("Parity change detected."); } @@ -799,14 +934,13 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) /* POSIX does not support 1.5 stop bits and RFCOMM does not * support 2 stop bits. So a request for 2 stop bits gets * translated to 1.5 stop bits */ - if (new->c_cflag & CSTOPB) { + if (new->c_cflag & CSTOPB) stop_bits = RFCOMM_RPN_STOP_15; - } else { + else stop_bits = RFCOMM_RPN_STOP_1; - } /* Handle number of data bits [5-8] */ - if ((old->c_cflag & CSIZE) != (new->c_cflag & CSIZE)) + if ((old->c_cflag & CSIZE) != (new->c_cflag & CSIZE)) changes |= RFCOMM_RPN_PM_DATA; switch (new->c_cflag & CSIZE) { @@ -844,7 +978,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) case 9600: baud = RFCOMM_RPN_BR_9600; break; - case 19200: + case 19200: baud = RFCOMM_RPN_BR_19200; break; case 38400: @@ -863,15 +997,13 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old) /* 9600 is standard accordinag to the RFCOMM specification */ baud = RFCOMM_RPN_BR_9600; break; - + } if (changes) rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud, data_bits, stop_bits, parity, RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); - - return; } static void rfcomm_tty_throttle(struct tty_struct *tty) @@ -895,12 +1027,14 @@ static void rfcomm_tty_unthrottle(struct tty_struct *tty) static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - struct rfcomm_dlc *dlc = dev->dlc; BT_DBG("tty %p dev %p", tty, dev); - if (!skb_queue_empty(&dlc->tx_queue)) - return dlc->mtu; + if (!dev || !dev->dlc) + return 0; + + if (!skb_queue_empty(&dev->dlc->tx_queue)) + return dev->dlc->mtu; return 0; } @@ -908,15 +1042,14 @@ static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) static void rfcomm_tty_flush_buffer(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - if (!dev) - return; BT_DBG("tty %p dev %p", tty, dev); - skb_queue_purge(&dev->dlc->tx_queue); + if (!dev || !dev->dlc) + return; - if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup) - tty->ldisc.write_wakeup(tty); + skb_queue_purge(&dev->dlc->tx_queue); + tty_wakeup(tty); } static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) @@ -932,32 +1065,22 @@ static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) static void rfcomm_tty_hangup(struct tty_struct *tty) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - if (!dev) - return; BT_DBG("tty %p dev %p", tty, dev); - rfcomm_tty_flush_buffer(tty); - - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) - rfcomm_dev_del(dev); + tty_port_hangup(&dev->port); } -static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused) +static int rfcomm_tty_tiocmget(struct tty_struct *tty) { - return 0; -} - -static int rfcomm_tty_tiocmget(struct tty_struct *tty, struct file *filp) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; + struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; BT_DBG("tty %p dev %p", tty, dev); - return dev->modem_status; + return dev->modem_status; } -static int rfcomm_tty_tiocmset(struct tty_struct *tty, struct file *filp, unsigned int set, unsigned int clear) +static int rfcomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; struct rfcomm_dlc *dlc = dev->dlc; @@ -992,7 +1115,7 @@ static int rfcomm_tty_tiocmset(struct tty_struct *tty, struct file *filp, unsign /* ---- TTY structure ---- */ -static struct tty_operations rfcomm_ops = { +static const struct tty_operations rfcomm_ops = { .open = rfcomm_tty_open, .close = rfcomm_tty_close, .write = rfcomm_tty_write, @@ -1006,18 +1129,20 @@ static struct tty_operations rfcomm_ops = { .send_xchar = rfcomm_tty_send_xchar, .hangup = rfcomm_tty_hangup, .wait_until_sent = rfcomm_tty_wait_until_sent, - .read_proc = rfcomm_tty_read_proc, .tiocmget = rfcomm_tty_tiocmget, .tiocmset = rfcomm_tty_tiocmset, + .install = rfcomm_tty_install, + .cleanup = rfcomm_tty_cleanup, }; -int rfcomm_init_ttys(void) +int __init rfcomm_init_ttys(void) { + int error; + rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); if (!rfcomm_tty_driver) - return -1; + return -ENOMEM; - rfcomm_tty_driver->owner = THIS_MODULE; rfcomm_tty_driver->driver_name = "rfcomm"; rfcomm_tty_driver->name = "rfcomm"; rfcomm_tty_driver->major = RFCOMM_TTY_MAJOR; @@ -1026,13 +1151,15 @@ int rfcomm_init_ttys(void) rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL; rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; rfcomm_tty_driver->init_termios = tty_std_termios; - rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; + rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON; tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); - if (tty_register_driver(rfcomm_tty_driver)) { + error = tty_register_driver(rfcomm_tty_driver); + if (error) { BT_ERR("Can't register RFCOMM TTY driver"); put_tty_driver(rfcomm_tty_driver); - return -1; + return error; } BT_INFO("RFCOMM TTY layer initialized"); |
