aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVille Tervo <ville.tervo@nokia.com>2011-02-10 20:38:47 -0500
committerGustavo F. Padovan <padovan@profusion.mobi>2011-02-16 14:32:45 -0500
commitfcd89c09a59a054fb986861e0862aa2fff7d7c40 (patch)
tree115d525a9789e974b0a118d9cc22b792370f40f7
parent63185f64ef06464706b32c9a301f71f68cd93e52 (diff)
Bluetooth: Add LE connect support
Bluetooth V4.0 adds support for Low Energy (LE) connections. Specification introduces new set of hci commands to control LE connection. This patch adds logic to create, cancel and disconnect LE connections. Signed-off-by: Ville Tervo <ville.tervo@nokia.com> Acked-by: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
-rw-r--r--include/net/bluetooth/hci.h2
-rw-r--r--include/net/bluetooth/hci_core.h25
-rw-r--r--net/bluetooth/hci_conn.c51
-rw-r--r--net/bluetooth/hci_event.c93
4 files changed, 164 insertions, 7 deletions
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index 802d2505f138..e756f82a29e5 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -168,6 +168,8 @@ enum {
168#define SCO_LINK 0x00 168#define SCO_LINK 0x00
169#define ACL_LINK 0x01 169#define ACL_LINK 0x01
170#define ESCO_LINK 0x02 170#define ESCO_LINK 0x02
171/* Low Energy links do not have defined link type. Use invented one */
172#define LE_LINK 0x80
171 173
172/* LMP features */ 174/* LMP features */
173#define LMP_3SLOT 0x01 175#define LMP_3SLOT 0x01
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 6163bff6fa91..f434e96ce020 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -60,6 +60,7 @@ struct hci_conn_hash {
60 spinlock_t lock; 60 spinlock_t lock;
61 unsigned int acl_num; 61 unsigned int acl_num;
62 unsigned int sco_num; 62 unsigned int sco_num;
63 unsigned int le_num;
63}; 64};
64 65
65struct bdaddr_list { 66struct bdaddr_list {
@@ -309,20 +310,36 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
309{ 310{
310 struct hci_conn_hash *h = &hdev->conn_hash; 311 struct hci_conn_hash *h = &hdev->conn_hash;
311 list_add(&c->list, &h->list); 312 list_add(&c->list, &h->list);
312 if (c->type == ACL_LINK) 313 switch (c->type) {
314 case ACL_LINK:
313 h->acl_num++; 315 h->acl_num++;
314 else 316 break;
317 case LE_LINK:
318 h->le_num++;
319 break;
320 case SCO_LINK:
321 case ESCO_LINK:
315 h->sco_num++; 322 h->sco_num++;
323 break;
324 }
316} 325}
317 326
318static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) 327static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
319{ 328{
320 struct hci_conn_hash *h = &hdev->conn_hash; 329 struct hci_conn_hash *h = &hdev->conn_hash;
321 list_del(&c->list); 330 list_del(&c->list);
322 if (c->type == ACL_LINK) 331 switch (c->type) {
332 case ACL_LINK:
323 h->acl_num--; 333 h->acl_num--;
324 else 334 break;
335 case LE_LINK:
336 h->le_num--;
337 break;
338 case SCO_LINK:
339 case ESCO_LINK:
325 h->sco_num--; 340 h->sco_num--;
341 break;
342 }
326} 343}
327 344
328static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, 345static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 42dc39f25b72..d0c470c18f9d 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -45,6 +45,32 @@
45#include <net/bluetooth/bluetooth.h> 45#include <net/bluetooth/bluetooth.h>
46#include <net/bluetooth/hci_core.h> 46#include <net/bluetooth/hci_core.h>
47 47
48static void hci_le_connect(struct hci_conn *conn)
49{
50 struct hci_dev *hdev = conn->hdev;
51 struct hci_cp_le_create_conn cp;
52
53 conn->state = BT_CONNECT;
54 conn->out = 1;
55
56 memset(&cp, 0, sizeof(cp));
57 cp.scan_interval = cpu_to_le16(0x0004);
58 cp.scan_window = cpu_to_le16(0x0004);
59 bacpy(&cp.peer_addr, &conn->dst);
60 cp.conn_interval_min = cpu_to_le16(0x0008);
61 cp.conn_interval_max = cpu_to_le16(0x0100);
62 cp.supervision_timeout = cpu_to_le16(0x0064);
63 cp.min_ce_len = cpu_to_le16(0x0001);
64 cp.max_ce_len = cpu_to_le16(0x0001);
65
66 hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
67}
68
69static void hci_le_connect_cancel(struct hci_conn *conn)
70{
71 hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
72}
73
48void hci_acl_connect(struct hci_conn *conn) 74void hci_acl_connect(struct hci_conn *conn)
49{ 75{
50 struct hci_dev *hdev = conn->hdev; 76 struct hci_dev *hdev = conn->hdev;
@@ -193,8 +219,12 @@ static void hci_conn_timeout(unsigned long arg)
193 switch (conn->state) { 219 switch (conn->state) {
194 case BT_CONNECT: 220 case BT_CONNECT:
195 case BT_CONNECT2: 221 case BT_CONNECT2:
196 if (conn->type == ACL_LINK && conn->out) 222 if (conn->out) {
197 hci_acl_connect_cancel(conn); 223 if (conn->type == ACL_LINK)
224 hci_acl_connect_cancel(conn);
225 else if (conn->type == LE_LINK)
226 hci_le_connect_cancel(conn);
227 }
198 break; 228 break;
199 case BT_CONFIG: 229 case BT_CONFIG:
200 case BT_CONNECTED: 230 case BT_CONNECTED:
@@ -361,15 +391,30 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
361} 391}
362EXPORT_SYMBOL(hci_get_route); 392EXPORT_SYMBOL(hci_get_route);
363 393
364/* Create SCO or ACL connection. 394/* Create SCO, ACL or LE connection.
365 * Device _must_ be locked */ 395 * Device _must_ be locked */
366struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) 396struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
367{ 397{
368 struct hci_conn *acl; 398 struct hci_conn *acl;
369 struct hci_conn *sco; 399 struct hci_conn *sco;
400 struct hci_conn *le;
370 401
371 BT_DBG("%s dst %s", hdev->name, batostr(dst)); 402 BT_DBG("%s dst %s", hdev->name, batostr(dst));
372 403
404 if (type == LE_LINK) {
405 le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
406 if (!le)
407 le = hci_conn_add(hdev, LE_LINK, dst);
408 if (!le)
409 return NULL;
410 if (le->state == BT_OPEN)
411 hci_le_connect(le);
412
413 hci_conn_hold(le);
414
415 return le;
416 }
417
373 acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); 418 acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
374 if (!acl) { 419 if (!acl) {
375 acl = hci_conn_add(hdev, ACL_LINK, dst); 420 acl = hci_conn_add(hdev, ACL_LINK, dst);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index cee46cbe7aeb..47c6e9316ce8 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1107,6 +1107,43 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
1107 hci_dev_unlock(hdev); 1107 hci_dev_unlock(hdev);
1108} 1108}
1109 1109
1110static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
1111{
1112 struct hci_cp_le_create_conn *cp;
1113 struct hci_conn *conn;
1114
1115 BT_DBG("%s status 0x%x", hdev->name, status);
1116
1117 cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
1118 if (!cp)
1119 return;
1120
1121 hci_dev_lock(hdev);
1122
1123 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
1124
1125 BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr),
1126 conn);
1127
1128 if (status) {