diff options
Diffstat (limited to 'net/bluetooth')
-rw-r--r-- | net/bluetooth/rfcomm/tty.c | 114 |
1 files changed, 72 insertions, 42 deletions
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 9c0e142041bd..73dd61530130 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -633,49 +633,61 @@ static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev) 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) +{ + struct rfcomm_dev *dev = tty->driver_data; + + if (dev->tty_dev->parent) + device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); + + /* Close DLC and dettach TTY */ + rfcomm_dlc_close(dev->dlc, 0); + + clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); + + rfcomm_dlc_lock(dev->dlc); + tty->driver_data = NULL; + dev->port.tty = NULL; + rfcomm_dlc_unlock(dev->dlc); + + tty_port_put(&dev->port); +} + +/* 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) { DECLARE_WAITQUEUE(wait, current); struct rfcomm_dev *dev; struct rfcomm_dlc *dlc; - unsigned long flags; - int err, id; - - id = tty->index; + int err; - BT_DBG("tty %p id %d", tty, id); - - /* 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); + dev = rfcomm_dev_get(tty->index); if (!dev) return -ENODEV; - BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst, - dev->channel, dev->port.count); - - spin_lock_irqsave(&dev->port.lock, flags); - if (++dev->port.count > 1) { - spin_unlock_irqrestore(&dev->port.lock, flags); - return 0; - } - spin_unlock_irqrestore(&dev->port.lock, flags); - dlc = dev->dlc; /* Attach TTY and open DLC */ - rfcomm_dlc_lock(dlc); tty->driver_data = dev; dev->port.tty = tty; rfcomm_dlc_unlock(dlc); set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); + /* install the tty_port */ + err = tty_port_install(&dev->port, driver, tty); + if (err < 0) + goto error_no_dlc; + err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); if (err < 0) - return err; + goto error_no_dlc; /* Wait for DLC to connect */ add_wait_queue(&dev->wait, &wait); @@ -702,15 +714,45 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) set_current_state(TASK_RUNNING); remove_wait_queue(&dev->wait, &wait); - if (err == 0) - device_move(dev->tty_dev, rfcomm_get_device(dev), - DPM_ORDER_DEV_AFTER_PARENT); + if (err < 0) + goto error_no_connection; + + device_move(dev->tty_dev, rfcomm_get_device(dev), + DPM_ORDER_DEV_AFTER_PARENT); + return 0; + +error_no_connection: + rfcomm_dlc_close(dlc, err); +error_no_dlc: + clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); + tty_port_put(&dev->port); + return err; +} + +static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct rfcomm_dev *dev = tty->driver_data; + unsigned long flags; + + BT_DBG("tty %p id %d", tty, tty->index); + BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst, + dev->channel, dev->port.count); + + spin_lock_irqsave(&dev->port.lock, flags); + dev->port.count++; + spin_unlock_irqrestore(&dev->port.lock, flags); + + /* + * 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 err; + return 0; } static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) @@ -727,25 +769,11 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) spin_lock_irqsave(&dev->port.lock, flags); if (!--dev->port.count) { spin_unlock_irqrestore(&dev->port.lock, flags); - if (dev->tty_dev->parent) - device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); - - /* Close DLC and dettach TTY */ - rfcomm_dlc_close(dev->dlc, 0); - - clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - - rfcomm_dlc_lock(dev->dlc); - tty->driver_data = NULL; - dev->port.tty = NULL; - rfcomm_dlc_unlock(dev->dlc); if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) tty_port_put(&dev->port); } else spin_unlock_irqrestore(&dev->port.lock, flags); - - tty_port_put(&dev->port); } static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) @@ -1118,6 +1146,8 @@ static const struct tty_operations rfcomm_ops = { .wait_until_sent = rfcomm_tty_wait_until_sent, .tiocmget = rfcomm_tty_tiocmget, .tiocmset = rfcomm_tty_tiocmset, + .install = rfcomm_tty_install, + .cleanup = rfcomm_tty_cleanup, }; int __init rfcomm_init_ttys(void) |