[Bluetooth] Keep rfcomm_dev on the list until it is freed
[pandora-kernel.git] / net / bluetooth / rfcomm / tty.c
index eb2b524..23ba61a 100644 (file)
@@ -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>
    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.
 */
 
@@ -74,6 +74,8 @@ struct rfcomm_dev {
        wait_queue_head_t       wait;
        struct tasklet_struct   wakeup_task;
 
+       struct device           *tty_dev;
+
        atomic_t                wmem_alloc;
 };
 
@@ -93,6 +95,10 @@ static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
 
        BT_DBG("dev %p dlc %p", dev, dlc);
 
+       write_lock_bh(&rfcomm_dev_lock);
+       list_del_init(&dev->list);
+       write_unlock_bh(&rfcomm_dev_lock);
+
        rfcomm_dlc_lock(dlc);
        /* Detach DLC if it's owned by this dev */
        if (dlc->owner == dev)
@@ -110,7 +116,7 @@ static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
 
        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);
 }
@@ -154,8 +160,13 @@ static inline struct rfcomm_dev *rfcomm_dev_get(int id)
        read_lock(&rfcomm_dev_lock);
 
        dev = __rfcomm_dev_get(id);
-       if (dev)
-               rfcomm_dev_hold(dev);
+
+       if (dev) {
+               if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+                       dev = NULL;
+               else
+                       rfcomm_dev_hold(dev);
+       }
 
        read_unlock(&rfcomm_dev_lock);
 
@@ -185,7 +196,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        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;
@@ -234,7 +245,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        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);
@@ -249,7 +260,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
        dev->dlc   = dlc;
        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);
 
@@ -261,7 +272,13 @@ out:
                return err;
        }
 
-       tty_register_device(rfcomm_tty_driver, dev->id, rfcomm_get_device(dev));
+       dev->tty_dev = tty_register_device(rfcomm_tty_driver, dev->id, NULL);
+
+       if (IS_ERR(dev->tty_dev)) {
+               list_del(&dev->list);
+               kfree(dev);
+               return PTR_ERR(dev->tty_dev);
+       }
 
        return dev->id;
 }
@@ -270,10 +287,7 @@ static void rfcomm_dev_del(struct rfcomm_dev *dev)
 {
        BT_DBG("dev %p", dev);
 
-       write_lock_bh(&rfcomm_dev_lock);
-       list_del_init(&dev->list);
-       write_unlock_bh(&rfcomm_dev_lock);
-
+       set_bit(RFCOMM_TTY_RELEASED, &dev->flags);
        rfcomm_dev_put(dev);
 }
 
@@ -327,7 +341,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;
@@ -368,7 +382,7 @@ static int rfcomm_release_dev(void __user *arg)
        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)))
                return -ENODEV;
@@ -381,6 +395,10 @@ static int rfcomm_release_dev(void __user *arg)
        if (req.flags & (1 << RFCOMM_HANGUP_NOW))
                rfcomm_dlc_close(dev->dlc, 0);
 
+       /* Shut down TTY synchronously before freeing rfcomm_dev */
+       if (dev->tty)
+               tty_vhangup(dev->tty);
+
        rfcomm_dev_del(dev);
        rfcomm_dev_put(dev);
        return 0;
@@ -413,6 +431,8 @@ static int rfcomm_get_dev_list(void __user *arg)
 
        list_for_each(p, &rfcomm_dev_list) {
                struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
+               if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+                       continue;
                (di + n)->id      = dev->id;
                (di + n)->flags   = dev->flags;
                (di + n)->state   = dev->dlc->state;
@@ -487,7 +507,7 @@ 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)) {
                kfree_skb(skb);
                return;
@@ -506,7 +526,7 @@ 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;
@@ -515,9 +535,10 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
        if (dlc->state == BT_CLOSED) {
                if (!dev->tty) {
                        if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
-                               rfcomm_dev_hold(dev);
-                               rfcomm_dev_del(dev);
+                               if (rfcomm_dev_get(dev->id) == NULL)
+                                       return;
 
+                               rfcomm_dev_del(dev);
                                /* We have to drop DLC lock here, otherwise
                                   rfcomm_dev_put() will dead lock if it's
                                   the last reference. */
@@ -525,7 +546,7 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
                                rfcomm_dev_put(dev);
                                rfcomm_dlc_lock(dlc);
                        }
-               } else 
+               } else
                        tty_hangup(dev->tty);
        }
 }
@@ -543,7 +564,7 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
                        tty_hangup(dev->tty);
        }
 
-       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) |
@@ -561,7 +582,7 @@ static void rfcomm_tty_wakeup(unsigned long arg)
        BT_DBG("dev %p tty %p", dev, tty);
 
        if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
-                (tty->ldisc.write_wakeup)(tty);
+               (tty->ldisc.write_wakeup)(tty);
 
        wake_up_interruptible(&tty->write_wait);
 #ifdef SERIAL_HAVE_POLL_WAIT
@@ -576,7 +597,7 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
        struct rfcomm_dlc *dlc;
        int err, id;
 
-        id = tty->index;
+       id = tty->index;
 
        BT_DBG("tty %p id %d", tty, id);
 
@@ -630,6 +651,9 @@ 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));
+
        return err;
 }
 
@@ -642,6 +666,8 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
        BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
 
        if (--dev->opened == 0) {
+               device_move(dev->tty_dev, NULL);
+
                /* Close DLC and dettach TTY */
                rfcomm_dlc_close(dev->dlc, 0);
 
@@ -670,7 +696,7 @@ 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;
 
@@ -773,7 +799,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
                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 */
@@ -830,7 +856,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
        }
 
        /* 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) {
@@ -868,7 +894,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
        case 9600:
                baud = RFCOMM_RPN_BR_9600;
                break;
-       case 19200: 
+       case 19200:
                baud = RFCOMM_RPN_BR_19200;
                break;
        case 38400:
@@ -887,7 +913,7 @@ static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
                /* 9600 is standard accordinag to the RFCOMM specification */
                baud = RFCOMM_RPN_BR_9600;
                break;
-       
+
        }
 
        if (changes)
@@ -967,8 +993,12 @@ static void rfcomm_tty_hangup(struct tty_struct *tty)
 
        rfcomm_tty_flush_buffer(tty);
 
-       if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+       if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
+               if (rfcomm_dev_get(dev->id) == NULL)
+                       return;
                rfcomm_dev_del(dev);
+               rfcomm_dev_put(dev);
+       }
 }
 
 static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)
@@ -978,11 +1008,11 @@ static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len,
 
 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)