]> Pileus Git - ~andy/linux/blobdiff - net/bluetooth/hci_conn.c
mac80211: fix rx->key NULL pointer dereference in promiscuous mode
[~andy/linux] / net / bluetooth / hci_conn.c
index c1c597e3e198e9ffba26dd4dcf2e59e2ffc923c3..3db432473ad5214efc902f5bbaa36f631114defa 100644 (file)
@@ -123,7 +123,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn)
 
        BT_DBG("%p", conn);
 
-       if (conn->hdev->hci_ver < 2)
+       if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2)
                return;
 
        bacpy(&cp.bdaddr, &conn->dst);
@@ -275,9 +275,10 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
        }
 }
 
-static void hci_conn_timeout(unsigned long arg)
+static void hci_conn_timeout(struct work_struct *work)
 {
-       struct hci_conn *conn = (void *) arg;
+       struct hci_conn *conn = container_of(work, struct hci_conn,
+                                                       disc_work.work);
        struct hci_dev *hdev = conn->hdev;
        __u8 reason;
 
@@ -311,6 +312,42 @@ static void hci_conn_timeout(unsigned long arg)
        hci_dev_unlock(hdev);
 }
 
+/* Enter sniff mode */
+static void hci_conn_enter_sniff_mode(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+
+       BT_DBG("conn %p mode %d", conn, conn->mode);
+
+       if (test_bit(HCI_RAW, &hdev->flags))
+               return;
+
+       if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
+               return;
+
+       if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
+               return;
+
+       if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
+               struct hci_cp_sniff_subrate cp;
+               cp.handle             = cpu_to_le16(conn->handle);
+               cp.max_latency        = cpu_to_le16(0);
+               cp.min_remote_timeout = cpu_to_le16(0);
+               cp.min_local_timeout  = cpu_to_le16(0);
+               hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
+       }
+
+       if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
+               struct hci_cp_sniff_mode cp;
+               cp.handle       = cpu_to_le16(conn->handle);
+               cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
+               cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
+               cp.attempt      = cpu_to_le16(4);
+               cp.timeout      = cpu_to_le16(1);
+               hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp);
+       }
+}
+
 static void hci_conn_idle(unsigned long arg)
 {
        struct hci_conn *conn = (void *) arg;
@@ -325,12 +362,8 @@ static void hci_conn_auto_accept(unsigned long arg)
        struct hci_conn *conn = (void *) arg;
        struct hci_dev *hdev = conn->hdev;
 
-       hci_dev_lock(hdev);
-
        hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
                                                                &conn->dst);
-
-       hci_dev_unlock(hdev);
 }
 
 struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
@@ -374,7 +407,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
 
        skb_queue_head_init(&conn->data_q);
 
-       setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
+       INIT_LIST_HEAD(&conn->chan_list);;
+
+       INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout);
        setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
        setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
                                                        (unsigned long) conn);
@@ -383,8 +418,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
 
        hci_dev_hold(hdev);
 
-       tasklet_disable(&hdev->tx_task);
-
        hci_conn_hash_add(hdev, conn);
        if (hdev->notify)
                hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
@@ -393,8 +426,6 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
 
        hci_conn_init_sysfs(conn);
 
-       tasklet_enable(&hdev->tx_task);
-
        return conn;
 }
 
@@ -406,7 +437,7 @@ int hci_conn_del(struct hci_conn *conn)
 
        del_timer(&conn->idle_timer);
 
-       del_timer(&conn->disc_timer);
+       cancel_delayed_work_sync(&conn->disc_work);
 
        del_timer(&conn->auto_accept_timer);
 
@@ -430,14 +461,13 @@ int hci_conn_del(struct hci_conn *conn)
                }
        }
 
-       tasklet_disable(&hdev->tx_task);
+
+       hci_chan_list_flush(conn);
 
        hci_conn_hash_del(hdev, conn);
        if (hdev->notify)
                hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
 
-       tasklet_enable(&hdev->tx_task);
-
        skb_queue_purge(&conn->data_q);
 
        hci_conn_put_device(conn);
@@ -453,16 +483,13 @@ int hci_conn_del(struct hci_conn *conn)
 struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
 {
        int use_src = bacmp(src, BDADDR_ANY);
-       struct hci_dev *hdev = NULL;
-       struct list_head *p;
+       struct hci_dev *hdev = NULL, *d;
 
        BT_DBG("%s -> %s", batostr(src), batostr(dst));
 
-       read_lock_bh(&hci_dev_list_lock);
-
-       list_for_each(p, &hci_dev_list) {
-               struct hci_dev *d = list_entry(p, struct hci_dev, list);
+       read_lock(&hci_dev_list_lock);
 
+       list_for_each_entry(d, &hci_dev_list, list) {
                if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
                        continue;
 
@@ -485,7 +512,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
        if (hdev)
                hdev = hci_dev_hold(hdev);
 
-       read_unlock_bh(&hci_dev_list_lock);
+       read_unlock(&hci_dev_list_lock);
        return hdev;
 }
 EXPORT_SYMBOL(hci_get_route);
@@ -766,60 +793,18 @@ timer:
                        jiffies + msecs_to_jiffies(hdev->idle_timeout));
 }
 
-/* Enter sniff mode */
-void hci_conn_enter_sniff_mode(struct hci_conn *conn)
-{
-       struct hci_dev *hdev = conn->hdev;
-
-       BT_DBG("conn %p mode %d", conn, conn->mode);
-
-       if (test_bit(HCI_RAW, &hdev->flags))
-               return;
-
-       if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn))
-               return;
-
-       if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF))
-               return;
-
-       if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) {
-               struct hci_cp_sniff_subrate cp;
-               cp.handle             = cpu_to_le16(conn->handle);
-               cp.max_latency        = cpu_to_le16(0);
-               cp.min_remote_timeout = cpu_to_le16(0);
-               cp.min_local_timeout  = cpu_to_le16(0);
-               hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp);
-       }
-
-       if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
-               struct hci_cp_sniff_mode cp;
-               cp.handle       = cpu_to_le16(conn->handle);
-               cp.max_interval = cpu_to_le16(hdev->sniff_max_interval);
-               cp.min_interval = cpu_to_le16(hdev->sniff_min_interval);
-               cp.attempt      = cpu_to_le16(4);
-               cp.timeout      = cpu_to_le16(1);
-               hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp);
-       }
-}
-
 /* Drop all connection on the device */
 void hci_conn_hash_flush(struct hci_dev *hdev)
 {
        struct hci_conn_hash *h = &hdev->conn_hash;
-       struct list_head *p;
+       struct hci_conn *c;
 
        BT_DBG("hdev %s", hdev->name);
 
-       p = h->list.next;
-       while (p != &h->list) {
-               struct hci_conn *c;
-
-               c = list_entry(p, struct hci_conn, list);
-               p = p->next;
-
+       list_for_each_entry_rcu(c, &h->list, list) {
                c->state = BT_CLOSED;
 
-               hci_proto_disconn_cfm(c, 0x16);
+               hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
                hci_conn_del(c);
        }
 }
@@ -855,10 +840,10 @@ EXPORT_SYMBOL(hci_conn_put_device);
 
 int hci_get_conn_list(void __user *arg)
 {
+       register struct hci_conn *c;
        struct hci_conn_list_req req, *cl;
        struct hci_conn_info *ci;
        struct hci_dev *hdev;
-       struct list_head *p;
        int n = 0, size, err;
 
        if (copy_from_user(&req, arg, sizeof(req)))
@@ -881,11 +866,8 @@ int hci_get_conn_list(void __user *arg)
 
        ci = cl->conn_info;
 
-       hci_dev_lock_bh(hdev);
-       list_for_each(p, &hdev->conn_hash.list) {
-               register struct hci_conn *c;
-               c = list_entry(p, struct hci_conn, list);
-
+       hci_dev_lock(hdev);
+       list_for_each_entry(c, &hdev->conn_hash.list, list) {
                bacpy(&(ci + n)->bdaddr, &c->dst);
                (ci + n)->handle = c->handle;
                (ci + n)->type  = c->type;
@@ -895,7 +877,7 @@ int hci_get_conn_list(void __user *arg)
                if (++n >= req.conn_num)
                        break;
        }
-       hci_dev_unlock_bh(hdev);
+       hci_dev_unlock(hdev);
 
        cl->dev_id = hdev->id;
        cl->conn_num = n;
@@ -919,7 +901,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
        if (copy_from_user(&req, arg, sizeof(req)))
                return -EFAULT;
 
-       hci_dev_lock_bh(hdev);
+       hci_dev_lock(hdev);
        conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
        if (conn) {
                bacpy(&ci.bdaddr, &conn->dst);
@@ -929,7 +911,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg)
                ci.state = conn->state;
                ci.link_mode = conn->link_mode;
        }
-       hci_dev_unlock_bh(hdev);
+       hci_dev_unlock(hdev);
 
        if (!conn)
                return -ENOENT;
@@ -945,14 +927,60 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
        if (copy_from_user(&req, arg, sizeof(req)))
                return -EFAULT;
 
-       hci_dev_lock_bh(hdev);
+       hci_dev_lock(hdev);
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr);
        if (conn)
                req.type = conn->auth_type;
-       hci_dev_unlock_bh(hdev);
+       hci_dev_unlock(hdev);
 
        if (!conn)
                return -ENOENT;
 
        return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0;
 }
+
+struct hci_chan *hci_chan_create(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_chan *chan;
+
+       BT_DBG("%s conn %p", hdev->name, conn);
+
+       chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC);
+       if (!chan)
+               return NULL;
+
+       chan->conn = conn;
+       skb_queue_head_init(&chan->data_q);
+
+       list_add_rcu(&chan->list, &conn->chan_list);
+
+       return chan;
+}
+
+int hci_chan_del(struct hci_chan *chan)
+{
+       struct hci_conn *conn = chan->conn;
+       struct hci_dev *hdev = conn->hdev;
+
+       BT_DBG("%s conn %p chan %p", hdev->name, conn, chan);
+
+       list_del_rcu(&chan->list);
+
+       synchronize_rcu();
+
+       skb_queue_purge(&chan->data_q);
+       kfree(chan);
+
+       return 0;
+}
+
+void hci_chan_list_flush(struct hci_conn *conn)
+{
+       struct hci_chan *chan;
+
+       BT_DBG("conn %p", conn);
+
+       list_for_each_entry_rcu(chan, &conn->chan_list, list)
+               hci_chan_del(chan);
+}