Bluetooth: Fix memory leak in L2CAP
[safe/jmp/linux-2.6] / net / bluetooth / hci_core.c
index fa7c537..94ba349 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/skbuff.h>
 #include <linux/interrupt.h>
 #include <linux/notifier.h>
+#include <linux/rfkill.h>
 #include <net/sock.h>
 
 #include <asm/system.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
-#ifndef CONFIG_BT_HCI_CORE_DEBUG
-#undef  BT_DBG
-#define BT_DBG(D...)
-#endif
-
 static void hci_cmd_task(unsigned long arg);
 static void hci_rx_task(unsigned long arg);
 static void hci_tx_task(unsigned long arg);
@@ -197,8 +193,9 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
        while ((skb = skb_dequeue(&hdev->driver_init))) {
                bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
                skb->dev = (void *) hdev;
+
                skb_queue_tail(&hdev->cmd_q, skb);
-               hci_sched_cmd(hdev);
+               tasklet_schedule(&hdev->cmd_task);
        }
        skb_queue_purge(&hdev->driver_init);
 
@@ -290,7 +287,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
 {
        __le16 policy = cpu_to_le16(opt);
 
-       BT_DBG("%s %x", hdev->name, opt);
+       BT_DBG("%s %x", hdev->name, policy);
 
        /* Default link policy */
        hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy);
@@ -481,6 +478,11 @@ int hci_dev_open(__u16 dev)
 
        hci_req_lock(hdev);
 
+       if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) {
+               ret = -ERFKILL;
+               goto done;
+       }
+
        if (test_bit(HCI_UP, &hdev->flags)) {
                ret = -EALREADY;
                goto done;
@@ -818,6 +820,24 @@ int hci_get_dev_info(void __user *arg)
 
 /* ---- Interface to HCI drivers ---- */
 
+static int hci_rfkill_set_block(void *data, bool blocked)
+{
+       struct hci_dev *hdev = data;
+
+       BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked);
+
+       if (!blocked)
+               return 0;
+
+       hci_dev_do_close(hdev);
+
+       return 0;
+}
+
+static const struct rfkill_ops hci_rfkill_ops = {
+       .set_block = hci_rfkill_set_block,
+};
+
 /* Alloc HCI device */
 struct hci_dev *hci_alloc_dev(void)
 {
@@ -849,7 +869,8 @@ int hci_register_dev(struct hci_dev *hdev)
        struct list_head *head = &hci_dev_list, *p;
        int i, id = 0;
 
-       BT_DBG("%p name %s type %d owner %p", hdev, hdev->name, hdev->type, hdev->owner);
+       BT_DBG("%p name %s type %d owner %p", hdev, hdev->name,
+                                               hdev->type, hdev->owner);
 
        if (!hdev->open || !hdev->close || !hdev->destruct)
                return -EINVAL;
@@ -891,7 +912,7 @@ int hci_register_dev(struct hci_dev *hdev)
                hdev->reassembly[i] = NULL;
 
        init_waitqueue_head(&hdev->req_wait_q);
-       init_MUTEX(&hdev->req_lock);
+       mutex_init(&hdev->req_lock);
 
        inquiry_cache_init(hdev);
 
@@ -905,6 +926,15 @@ int hci_register_dev(struct hci_dev *hdev)
 
        hci_register_sysfs(hdev);
 
+       hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
+                               RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev);
+       if (hdev->rfkill) {
+               if (rfkill_register(hdev->rfkill) < 0) {
+                       rfkill_destroy(hdev->rfkill);
+                       hdev->rfkill = NULL;
+               }
+       }
+
        hci_notify(hdev, HCI_DEV_REG);
 
        return id;
@@ -929,6 +959,11 @@ int hci_unregister_dev(struct hci_dev *hdev)
 
        hci_notify(hdev, HCI_DEV_UNREG);
 
+       if (hdev->rfkill) {
+               rfkill_unregister(hdev->rfkill);
+               rfkill_destroy(hdev->rfkill);
+       }
+
        hci_unregister_sysfs(hdev);
 
        __hci_dev_put(hdev);
@@ -953,6 +988,30 @@ int hci_resume_dev(struct hci_dev *hdev)
 }
 EXPORT_SYMBOL(hci_resume_dev);
 
+/* Receive frame from HCI drivers */
+int hci_recv_frame(struct sk_buff *skb)
+{
+       struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+       if (!hdev || (!test_bit(HCI_UP, &hdev->flags)
+                               && !test_bit(HCI_INIT, &hdev->flags))) {
+               kfree_skb(skb);
+               return -ENXIO;
+       }
+
+       /* Incomming skb */
+       bt_cb(skb)->incoming = 1;
+
+       /* Time stamp */
+       __net_timestamp(skb);
+
+       /* Queue frame for rx task */
+       skb_queue_tail(&hdev->rx_q, skb);
+       tasklet_schedule(&hdev->rx_task);
+
+       return 0;
+}
+EXPORT_SYMBOL(hci_recv_frame);
+
 /* Receive packet type fragment */
 #define __reassembly(hdev, type)  ((hdev)->reassembly[(type) - 2])
 
@@ -1159,8 +1218,9 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param)
 
        bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
        skb->dev = (void *) hdev;
+
        skb_queue_tail(&hdev->cmd_q, skb);
-       hci_sched_cmd(hdev);
+       tasklet_schedule(&hdev->cmd_task);
 
        return 0;
 }
@@ -1237,7 +1297,8 @@ int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
                spin_unlock_bh(&conn->data_q.lock);
        }
 
-       hci_sched_tx(hdev);
+       tasklet_schedule(&hdev->tx_task);
+
        return 0;
 }
 EXPORT_SYMBOL(hci_send_acl);
@@ -1264,8 +1325,10 @@ int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
 
        skb->dev = (void *) hdev;
        bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+
        skb_queue_tail(&conn->data_q, skb);
-       hci_sched_tx(hdev);
+       tasklet_schedule(&hdev->tx_task);
+
        return 0;
 }
 EXPORT_SYMBOL(hci_send_sco);
@@ -1570,8 +1633,7 @@ static void hci_cmd_task(unsigned long arg)
 
        /* Send queued commands */
        if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
-               if (hdev->sent_cmd)
-                       kfree_skb(hdev->sent_cmd);
+               kfree_skb(hdev->sent_cmd);
 
                if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
                        atomic_dec(&hdev->cmd_cnt);
@@ -1579,7 +1641,7 @@ static void hci_cmd_task(unsigned long arg)
                        hdev->cmd_last_tx = jiffies;
                } else {
                        skb_queue_head(&hdev->cmd_q, skb);
-                       hci_sched_cmd(hdev);
+                       tasklet_schedule(&hdev->cmd_task);
                }
        }
 }