diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2006-07-03 04:02:33 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-07-03 22:53:58 -0400 |
commit | 04837f6447c7f3ef114cda1ad761822dedbff8cf (patch) | |
tree | 66dbb53e82550723191ffe54f0457eafc3a92d32 /net/bluetooth/hci_conn.c | |
parent | da1f519851d1c66331363253f364bdb5d924ea96 (diff) |
[Bluetooth] Add automatic sniff mode support
This patch introduces the automatic sniff mode feature. This allows
the host to switch idle connections into sniff mode to safe power.
Signed-off-by: Ulisses Furquim <ulissesf@gmail.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/hci_conn.c')
-rw-r--r-- | net/bluetooth/hci_conn.c | 100 |
1 files changed, 89 insertions, 11 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5c0c2b1ef34a..420ed4d7e57e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c | |||
@@ -115,8 +115,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) | |||
115 | 115 | ||
116 | static void hci_conn_timeout(unsigned long arg) | 116 | static void hci_conn_timeout(unsigned long arg) |
117 | { | 117 | { |
118 | struct hci_conn *conn = (void *)arg; | 118 | struct hci_conn *conn = (void *) arg; |
119 | struct hci_dev *hdev = conn->hdev; | 119 | struct hci_dev *hdev = conn->hdev; |
120 | 120 | ||
121 | BT_DBG("conn %p state %d", conn, conn->state); | 121 | BT_DBG("conn %p state %d", conn, conn->state); |
122 | 122 | ||
@@ -132,11 +132,13 @@ static void hci_conn_timeout(unsigned long arg) | |||
132 | return; | 132 | return; |
133 | } | 133 | } |
134 | 134 | ||
135 | static void hci_conn_init_timer(struct hci_conn *conn) | 135 | static void hci_conn_idle(unsigned long arg) |
136 | { | 136 | { |
137 | init_timer(&conn->timer); | 137 | struct hci_conn *conn = (void *) arg; |
138 | conn->timer.function = hci_conn_timeout; | 138 | |
139 | conn->timer.data = (unsigned long)conn; | 139 | BT_DBG("conn %p mode %d", conn, conn->mode); |
140 | |||
141 | hci_conn_enter_sniff_mode(conn); | ||
140 | } | 142 | } |
141 | 143 | ||
142 | struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) | 144 | struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) |
@@ -145,17 +147,27 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) | |||
145 | 147 | ||
146 | BT_DBG("%s dst %s", hdev->name, batostr(dst)); | 148 | BT_DBG("%s dst %s", hdev->name, batostr(dst)); |
147 | 149 | ||
148 | if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC))) | 150 | conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC); |
151 | if (!conn) | ||
149 | return NULL; | 152 | return NULL; |
150 | memset(conn, 0, sizeof(struct hci_conn)); | ||
151 | 153 | ||
152 | bacpy(&conn->dst, dst); | 154 | bacpy(&conn->dst, dst); |
153 | conn->type = type; | ||
154 | conn->hdev = hdev; | 155 | conn->hdev = hdev; |
156 | conn->type = type; | ||
157 | conn->mode = HCI_CM_ACTIVE; | ||
155 | conn->state = BT_OPEN; | 158 | conn->state = BT_OPEN; |
156 | 159 | ||
160 | conn->power_save = 1; | ||
161 | |||
157 | skb_queue_head_init(&conn->data_q); | 162 | skb_queue_head_init(&conn->data_q); |
158 | hci_conn_init_timer(conn); | 163 | |
164 | init_timer(&conn->disc_timer); | ||
165 | conn->disc_timer.function = hci_conn_timeout; | ||
166 | conn->disc_timer.data = (unsigned long) conn; | ||
167 | |||
168 | init_timer(&conn->idle_timer); | ||
169 | conn->idle_timer.function = hci_conn_idle; | ||
170 | conn->idle_timer.data = (unsigned long) conn; | ||
159 | 171 | ||
160 | atomic_set(&conn->refcnt, 0); | 172 | atomic_set(&conn->refcnt, 0); |
161 | 173 | ||
@@ -178,7 +190,9 @@ int hci_conn_del(struct hci_conn *conn) | |||
178 | 190 | ||
179 | BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); | 191 | BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); |
180 | 192 | ||
181 | hci_conn_del_timer(conn); | 193 | del_timer(&conn->idle_timer); |
194 | |||
195 | del_timer(&conn->disc_timer); | ||
182 | 196 | ||
183 | if (conn->type == SCO_LINK) { | 197 | if (conn->type == SCO_LINK) { |
184 | struct hci_conn *acl = conn->link; | 198 | struct hci_conn *acl = conn->link; |
@@ -364,6 +378,70 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) | |||
364 | } | 378 | } |
365 | EXPORT_SYMBOL(hci_conn_switch_role); | 379 | EXPORT_SYMBOL(hci_conn_switch_role); |
366 | 380 | ||
381 | /* Enter active mode */ | ||
382 | void hci_conn_enter_active_mode(struct hci_conn *conn) | ||
383 | { | ||
384 | struct hci_dev *hdev = conn->hdev; | ||
385 | |||
386 | BT_DBG("conn %p mode %d", conn, conn->mode); | ||
387 | |||
388 | if (test_bit(HCI_RAW, &hdev->flags)) | ||
389 | return; | ||
390 | |||
391 | if (conn->mode != HCI_CM_SNIFF || !conn->power_save) | ||
392 | goto timer; | ||
393 | |||
394 | if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { | ||
395 | struct hci_cp_exit_sniff_mode cp; | ||
396 | cp.handle = __cpu_to_le16(conn->handle); | ||
397 | hci_send_cmd(hdev, OGF_LINK_POLICY, | ||
398 | OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp); | ||
399 | } | ||
400 | |||
401 | timer: | ||
402 | if (hdev->idle_timeout > 0) | ||
403 | mod_timer(&conn->idle_timer, | ||
404 | jiffies + msecs_to_jiffies(hdev->idle_timeout)); | ||
405 | } | ||
406 | |||
407 | /* Enter sniff mode */ | ||
408 | void hci_conn_enter_sniff_mode(struct hci_conn *conn) | ||
409 | { | ||
410 | struct hci_dev *hdev = conn->hdev; | ||
411 | |||
412 | BT_DBG("conn %p mode %d", conn, conn->mode); | ||
413 | |||
414 | if (test_bit(HCI_RAW, &hdev->flags)) | ||
415 | return; | ||
416 | |||
417 | if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) | ||
418 | return; | ||
419 | |||
420 | if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF)) | ||
421 | return; | ||
422 | |||
423 | if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { | ||
424 | struct hci_cp_sniff_subrate cp; | ||
425 | cp.handle = __cpu_to_le16(conn->handle); | ||
426 | cp.max_latency = __constant_cpu_to_le16(0); | ||
427 | cp.min_remote_timeout = __constant_cpu_to_le16(0); | ||
428 | cp.min_local_timeout = __constant_cpu_to_le16(0); | ||
429 | hci_send_cmd(hdev, OGF_LINK_POLICY, | ||
430 | OCF_SNIFF_SUBRATE, sizeof(cp), &cp); | ||
431 | } | ||
432 | |||
433 | if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { | ||
434 | struct hci_cp_sniff_mode cp; | ||
435 | cp.handle = __cpu_to_le16(conn->handle); | ||
436 | cp.max_interval = __cpu_to_le16(hdev->sniff_max_interval); | ||
437 | cp.min_interval = __cpu_to_le16(hdev->sniff_min_interval); | ||
438 | cp.attempt = __constant_cpu_to_le16(4); | ||
439 | cp.timeout = __constant_cpu_to_le16(1); | ||
440 | hci_send_cmd(hdev, OGF_LINK_POLICY, | ||
441 | OCF_SNIFF_MODE, sizeof(cp), &cp); | ||
442 | } | ||
443 | } | ||
444 | |||
367 | /* Drop all connection on the device */ | 445 | /* Drop all connection on the device */ |
368 | void hci_conn_hash_flush(struct hci_dev *hdev) | 446 | void hci_conn_hash_flush(struct hci_dev *hdev) |
369 | { | 447 | { |