aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@nokia.com>2010-12-15 06:53:18 -0500
committerGustavo F. Padovan <padovan@profusion.mobi>2011-02-07 22:40:04 -0500
commitab81cbf99c881ca2b9a83682a8722fc84b2483d2 (patch)
tree7e9478c23bfa7e794fe31be04f317f5776bc80b7
parent7990681c409e8a31eac122342e64da6c3b77a249 (diff)
Bluetooth: Implement automatic setup procedure for local adapters
This patch implements automatic initialization of basic information about newly registered Bluetooth adapters. E.g. the address and features are always needed so it makes sense for the kernel to automatically power on adapters and read this information. A new HCI_SETUP flag is added to track this state. In order to not consume unnecessary amounts of power if there isn't a user space available that could switch the adapter back off, a timer is added to do this automatically as long as no Bluetooth user space seems to be present. A new HCI_AUTO_OFF flag is added that user space needs to clear to avoid the automatic power off. Additionally, the management interface index_added event is moved to the end of the HCI_SETUP stage so a user space supporting the managment inteface has all the necessary information available for fetching when it gets notified of a new adapter. The HCI_DEV_REG event is kept in the same place as before since existing HCI raw socket based user space versions depend on seeing the kernels initialization sequence (hci_init_req) to determine when the adapter is ready for use. Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r--include/net/bluetooth/hci.h3
-rw-r--r--include/net/bluetooth/hci_core.h6
-rw-r--r--net/bluetooth/hci_core.c64
-rw-r--r--net/bluetooth/mgmt.c8
4 files changed, 79 insertions, 2 deletions
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 5d033dc9d43b..51c9df16e764 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -76,6 +76,9 @@ enum {
76 HCI_INQUIRY, 76 HCI_INQUIRY,
77 77
78 HCI_RAW, 78 HCI_RAW,
79
80 HCI_SETUP,
81 HCI_AUTO_OFF,
79}; 82};
80 83
81/* HCI ioctl defines */ 84/* HCI ioctl defines */
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4e14610baece..75c4f201c1c6 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -114,6 +114,10 @@ struct hci_dev {
114 114
115 struct workqueue_struct *workqueue; 115 struct workqueue_struct *workqueue;
116 116
117 struct work_struct power_on;
118 struct work_struct power_off;
119 struct timer_list off_timer;
120
117 struct tasklet_struct cmd_task; 121 struct tasklet_struct cmd_task;
118 struct tasklet_struct rx_task; 122 struct tasklet_struct rx_task;
119 struct tasklet_struct tx_task; 123 struct tasklet_struct tx_task;
@@ -437,6 +441,8 @@ int hci_inquiry(void __user *arg);
437struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); 441struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
438int hci_blacklist_clear(struct hci_dev *hdev); 442int hci_blacklist_clear(struct hci_dev *hdev);
439 443
444void hci_del_off_timer(struct hci_dev *hdev);
445
440void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); 446void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
441 447
442int hci_recv_frame(struct sk_buff *skb); 448int hci_recv_frame(struct sk_buff *skb);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 9ba92adaa9ad..b22ce9f8bf91 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -50,6 +50,8 @@
50#include <net/bluetooth/bluetooth.h> 50#include <net/bluetooth/bluetooth.h>
51#include <net/bluetooth/hci_core.h> 51#include <net/bluetooth/hci_core.h>
52 52
53#define AUTO_OFF_TIMEOUT 2000
54
53static void hci_cmd_task(unsigned long arg); 55static void hci_cmd_task(unsigned long arg);
54static void hci_rx_task(unsigned long arg); 56static void hci_rx_task(unsigned long arg);
55static void hci_tx_task(unsigned long arg); 57static void hci_tx_task(unsigned long arg);
@@ -794,6 +796,7 @@ int hci_get_dev_list(void __user *arg)
794 list_for_each(p, &hci_dev_list) { 796 list_for_each(p, &hci_dev_list) {
795 struct hci_dev *hdev; 797 struct hci_dev *hdev;
796 hdev = list_entry(p, struct hci_dev, list); 798 hdev = list_entry(p, struct hci_dev, list);
799 hci_del_off_timer(hdev);
797 (dr + n)->dev_id = hdev->id; 800 (dr + n)->dev_id = hdev->id;
798 (dr + n)->dev_opt = hdev->flags; 801 (dr + n)->dev_opt = hdev->flags;
799 if (++n >= dev_num) 802 if (++n >= dev_num)
@@ -823,6 +826,8 @@ int hci_get_dev_info(void __user *arg)
823 if (!hdev) 826 if (!hdev)
824 return -ENODEV; 827 return -ENODEV;
825 828
829 hci_del_off_timer(hdev);
830
826 strcpy(di.name, hdev->name); 831 strcpy(di.name, hdev->name);
827 di.bdaddr = hdev->bdaddr; 832 di.bdaddr = hdev->bdaddr;
828 di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); 833 di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
@@ -891,6 +896,51 @@ void hci_free_dev(struct hci_dev *hdev)
891} 896}
892EXPORT_SYMBOL(hci_free_dev); 897EXPORT_SYMBOL(hci_free_dev);
893 898
899static void hci_power_on(struct work_struct *work)
900{
901 struct hci_dev *hdev = container_of(work, struct hci_dev, power_on);
902
903 BT_DBG("%s", hdev->name);
904
905 if (hci_dev_open(hdev->id) < 0)
906 return;
907
908 if (test_bit(HCI_AUTO_OFF, &hdev->flags))
909 mod_timer(&hdev->off_timer,
910 jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT));
911
912 if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
913 mgmt_index_added(hdev->id);
914}
915
916static void hci_power_off(struct work_struct *work)
917{
918 struct hci_dev *hdev = container_of(work, struct hci_dev, power_off);
919
920 BT_DBG("%s", hdev->name);
921
922 hci_dev_close(hdev->id);
923}
924
925static void hci_auto_off(unsigned long data)
926{
927 struct hci_dev *hdev = (struct hci_dev *) data;
928
929 BT_DBG("%s", hdev->name);
930
931 clear_bit(HCI_AUTO_OFF, &hdev->flags);
932
933 queue_work(hdev->workqueue, &hdev->power_off);
934}
935
936void hci_del_off_timer(struct hci_dev *hdev)
937{
938 BT_DBG("%s", hdev->name);
939
940 clear_bit(HCI_AUTO_OFF, &hdev->flags);
941 del_timer(&hdev->off_timer);
942}
943
894/* Register HCI device */ 944/* Register HCI device */
895int hci_register_dev(struct hci_dev *hdev) 945int hci_register_dev(struct hci_dev *hdev)
896{ 946{
@@ -948,6 +998,10 @@ int hci_register_dev(struct hci_dev *hdev)
948 998
949 INIT_LIST_HEAD(&hdev->blacklist); 999 INIT_LIST_HEAD(&hdev->blacklist);
950 1000
1001 INIT_WORK(&hdev->power_on, hci_power_on);
1002 INIT_WORK(&hdev->power_off, hci_power_off);
1003 setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
1004
951 memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); 1005 memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
952 1006
953 atomic_set(&hdev->promisc, 0); 1007 atomic_set(&hdev->promisc, 0);
@@ -969,7 +1023,10 @@ int hci_register_dev(struct hci_dev *hdev)
969 } 1023 }
970 } 1024 }
971 1025
972 mgmt_index_added(hdev->id); 1026 set_bit(HCI_AUTO_OFF, &hdev->flags);
1027 set_bit(HCI_SETUP, &hdev->flags);
1028 queue_work(hdev->workqueue, &hdev->power_on);
1029
973 hci_notify(hdev, HCI_DEV_REG); 1030 hci_notify(hdev, HCI_DEV_REG);
974 1031
975 return id; 1032 return id;
@@ -999,7 +1056,10 @@ int hci_unregister_dev(struct hci_dev *hdev)
999 for (i = 0; i < NUM_REASSEMBLY; i++) 1056 for (i = 0; i < NUM_REASSEMBLY; i++)
1000 kfree_skb(hdev->reassembly[i]); 1057 kfree_skb(hdev->reassembly[i]);
1001 1058
1002 mgmt_index_removed(hdev->id); 1059 if (!test_bit(HCI_INIT, &hdev->flags) &&
1060 !test_bit(HCI_SETUP, &hdev->flags))
1061 mgmt_index_removed(hdev->id);
1062
1003 hci_notify(hdev, HCI_DEV_UNREG); 1063 hci_notify(hdev, HCI_DEV_UNREG);
1004 1064
1005 if (hdev->rfkill) { 1065 if (hdev->rfkill) {
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index ace872615c06..d479e241a9de 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -129,6 +129,12 @@ static int read_index_list(struct sock *sk)
129 i = 0; 129 i = 0;
130 list_for_each(p, &hci_dev_list) { 130 list_for_each(p, &hci_dev_list) {
131 struct hci_dev *d = list_entry(p, struct hci_dev, list); 131 struct hci_dev *d = list_entry(p, struct hci_dev, list);
132
133 hci_del_off_timer(d);
134
135 if (test_bit(HCI_SETUP, &d->flags))
136 continue;
137
132 put_unaligned_le16(d->id, &rp->index[i++]); 138 put_unaligned_le16(d->id, &rp->index[i++]);
133 BT_DBG("Added hci%u", d->id); 139 BT_DBG("Added hci%u", d->id);
134 } 140 }
@@ -180,6 +186,8 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
180 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); 186 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
181 } 187 }
182 188
189 hci_del_off_timer(hdev);
190
183 hci_dev_lock_bh(hdev); 191 hci_dev_lock_bh(hdev);
184 192
185 put_unaligned_le16(hdev->id, &rp->index); 193 put_unaligned_le16(hdev->id, &rp->index);