]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/bluetooth/rfcomm/tty.c
Driver core: move the driver specific module code into the driver core
[linux-2.6-omap-h63xx.git] / net / bluetooth / rfcomm / tty.c
index b2b1cceb102a1961c9d1e9c7161228c232281ec7..788c70321858f39576867de05e4ec2c885d9445e 100644 (file)
@@ -95,6 +95,11 @@ static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
 
        BT_DBG("dev %p dlc %p", dev, dlc);
 
+       /* 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));
+
        rfcomm_dlc_lock(dlc);
        /* Detach DLC if it's owned by this dev */
        if (dlc->owner == dev)
@@ -105,11 +110,6 @@ static void rfcomm_dev_destruct(struct rfcomm_dev *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));
-
        kfree(dev);
 
        /* It's safe to call module_put() here because socket still
@@ -156,8 +156,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);
 
@@ -180,6 +185,23 @@ static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
        return conn ? &conn->dev : NULL;
 }
 
+static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf)
+{
+       struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
+       bdaddr_t bdaddr;
+       baswap(&bdaddr, &dev->dst);
+       return sprintf(buf, "%s\n", batostr(&bdaddr));
+}
+
+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 int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 {
        struct rfcomm_dev *dev;
@@ -258,13 +280,28 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 out:
        write_unlock_bh(&rfcomm_dev_lock);
 
-       if (err) {
+       if (err < 0) {
                kfree(dev);
                return err;
        }
 
        dev->tty_dev = tty_register_device(rfcomm_tty_driver, dev->id, NULL);
 
+       if (IS_ERR(dev->tty_dev)) {
+               err = PTR_ERR(dev->tty_dev);
+               list_del(&dev->list);
+               kfree(dev);
+               return err;
+       }
+
+       dev_set_drvdata(dev->tty_dev, dev);
+
+       if (device_create_file(dev->tty_dev, &dev_attr_address) < 0)
+               BT_ERR("Failed to create address attribute");
+
+       if (device_create_file(dev->tty_dev, &dev_attr_channel) < 0)
+               BT_ERR("Failed to create channel attribute");
+
        return dev->id;
 }
 
@@ -272,6 +309,11 @@ static void rfcomm_dev_del(struct rfcomm_dev *dev)
 {
        BT_DBG("dev %p", dev);
 
+       if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+               BUG_ON(1);
+       else
+               set_bit(RFCOMM_TTY_RELEASED, &dev->flags);
+
        write_lock_bh(&rfcomm_dev_lock);
        list_del_init(&dev->list);
        write_unlock_bh(&rfcomm_dev_lock);
@@ -329,7 +371,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;
@@ -370,7 +412,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;
@@ -383,6 +425,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;
@@ -415,6 +461,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;
@@ -648,7 +696,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);
+               if (dev->tty_dev->parent)
+                       device_move(dev->tty_dev, NULL);
 
                /* Close DLC and dettach TTY */
                rfcomm_dlc_close(dev->dlc, 0);