diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-01-04 14:25:28 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-01-04 14:25:28 -0500 |
commit | 782a9e31e81956a271375c0ba8d46fbb0480983e (patch) | |
tree | 0db1e951862111d3ea44fdcedeaee6ea55821165 /net | |
parent | 5c4bc1ce917d93ce8f7dd498fbec6881b3d7743a (diff) | |
parent | 17f9cc3124c97f50a19a7597e5f29f915b5b835c (diff) |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
Diffstat (limited to 'net')
-rw-r--r-- | net/bluetooth/Makefile | 2 | ||||
-rw-r--r-- | net/bluetooth/hci_core.c | 17 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 33 | ||||
-rw-r--r-- | net/bluetooth/hci_sock.c | 52 | ||||
-rw-r--r-- | net/bluetooth/l2cap.c | 8 | ||||
-rw-r--r-- | net/bluetooth/mgmt.c | 308 |
6 files changed, 398 insertions, 22 deletions
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index d1e433f7d673..250f954f0213 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile | |||
@@ -10,4 +10,4 @@ obj-$(CONFIG_BT_BNEP) += bnep/ | |||
10 | obj-$(CONFIG_BT_CMTP) += cmtp/ | 10 | obj-$(CONFIG_BT_CMTP) += cmtp/ |
11 | obj-$(CONFIG_BT_HIDP) += hidp/ | 11 | obj-$(CONFIG_BT_HIDP) += hidp/ |
12 | 12 | ||
13 | bluetooth-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o hci_sysfs.o lib.o | 13 | bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o |
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 51c61f75a797..8b602d881fd7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c | |||
@@ -91,9 +91,16 @@ static void hci_notify(struct hci_dev *hdev, int event) | |||
91 | 91 | ||
92 | /* ---- HCI requests ---- */ | 92 | /* ---- HCI requests ---- */ |
93 | 93 | ||
94 | void hci_req_complete(struct hci_dev *hdev, int result) | 94 | void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) |
95 | { | 95 | { |
96 | BT_DBG("%s result 0x%2.2x", hdev->name, result); | 96 | BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result); |
97 | |||
98 | /* If the request has set req_last_cmd (typical for multi-HCI | ||
99 | * command requests) check if the completed command matches | ||
100 | * this, and if not just return. Single HCI command requests | ||
101 | * typically leave req_last_cmd as 0 */ | ||
102 | if (hdev->req_last_cmd && cmd != hdev->req_last_cmd) | ||
103 | return; | ||
97 | 104 | ||
98 | if (hdev->req_status == HCI_REQ_PEND) { | 105 | if (hdev->req_status == HCI_REQ_PEND) { |
99 | hdev->req_result = result; | 106 | hdev->req_result = result; |
@@ -149,7 +156,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, | |||
149 | break; | 156 | break; |
150 | } | 157 | } |
151 | 158 | ||
152 | hdev->req_status = hdev->req_result = 0; | 159 | hdev->req_last_cmd = hdev->req_status = hdev->req_result = 0; |
153 | 160 | ||
154 | BT_DBG("%s end: err %d", hdev->name, err); | 161 | BT_DBG("%s end: err %d", hdev->name, err); |
155 | 162 | ||
@@ -252,6 +259,8 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) | |||
252 | /* Connection accept timeout ~20 secs */ | 259 | /* Connection accept timeout ~20 secs */ |
253 | param = cpu_to_le16(0x7d00); | 260 | param = cpu_to_le16(0x7d00); |
254 | hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); | 261 | hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); |
262 | |||
263 | hdev->req_last_cmd = HCI_OP_WRITE_CA_TIMEOUT; | ||
255 | } | 264 | } |
256 | 265 | ||
257 | static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) | 266 | static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) |
@@ -960,6 +969,7 @@ int hci_register_dev(struct hci_dev *hdev) | |||
960 | } | 969 | } |
961 | } | 970 | } |
962 | 971 | ||
972 | mgmt_index_added(hdev->id); | ||
963 | hci_notify(hdev, HCI_DEV_REG); | 973 | hci_notify(hdev, HCI_DEV_REG); |
964 | 974 | ||
965 | return id; | 975 | return id; |
@@ -989,6 +999,7 @@ int hci_unregister_dev(struct hci_dev *hdev) | |||
989 | for (i = 0; i < NUM_REASSEMBLY; i++) | 999 | for (i = 0; i < NUM_REASSEMBLY; i++) |
990 | kfree_skb(hdev->reassembly[i]); | 1000 | kfree_skb(hdev->reassembly[i]); |
991 | 1001 | ||
1002 | mgmt_index_removed(hdev->id); | ||
992 | hci_notify(hdev, HCI_DEV_UNREG); | 1003 | hci_notify(hdev, HCI_DEV_UNREG); |
993 | 1004 | ||
994 | if (hdev->rfkill) { | 1005 | if (hdev->rfkill) { |
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8923b36a67a2..38100170d380 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c | |||
@@ -58,7 +58,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) | |||
58 | 58 | ||
59 | clear_bit(HCI_INQUIRY, &hdev->flags); | 59 | clear_bit(HCI_INQUIRY, &hdev->flags); |
60 | 60 | ||
61 | hci_req_complete(hdev, status); | 61 | hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); |
62 | 62 | ||
63 | hci_conn_check_pending(hdev); | 63 | hci_conn_check_pending(hdev); |
64 | } | 64 | } |
@@ -174,7 +174,7 @@ static void hci_cc_write_def_link_policy(struct hci_dev *hdev, struct sk_buff *s | |||
174 | if (!status) | 174 | if (!status) |
175 | hdev->link_policy = get_unaligned_le16(sent); | 175 | hdev->link_policy = get_unaligned_le16(sent); |
176 | 176 | ||
177 | hci_req_complete(hdev, status); | 177 | hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status); |
178 | } | 178 | } |
179 | 179 | ||
180 | static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) | 180 | static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) |
@@ -183,7 +183,7 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) | |||
183 | 183 | ||
184 | BT_DBG("%s status 0x%x", hdev->name, status); | 184 | BT_DBG("%s status 0x%x", hdev->name, status); |
185 | 185 | ||
186 | hci_req_complete(hdev, status); | 186 | hci_req_complete(hdev, HCI_OP_RESET, status); |
187 | } | 187 | } |
188 | 188 | ||
189 | static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) | 189 | static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) |
@@ -235,7 +235,7 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) | |||
235 | clear_bit(HCI_AUTH, &hdev->flags); | 235 | clear_bit(HCI_AUTH, &hdev->flags); |
236 | } | 236 | } |
237 | 237 | ||
238 | hci_req_complete(hdev, status); | 238 | hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status); |
239 | } | 239 | } |
240 | 240 | ||
241 | static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) | 241 | static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) |
@@ -258,7 +258,7 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) | |||
258 | clear_bit(HCI_ENCRYPT, &hdev->flags); | 258 | clear_bit(HCI_ENCRYPT, &hdev->flags); |
259 | } | 259 | } |
260 | 260 | ||
261 | hci_req_complete(hdev, status); | 261 | hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status); |
262 | } | 262 | } |
263 | 263 | ||
264 | static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) | 264 | static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) |
@@ -285,7 +285,7 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) | |||
285 | set_bit(HCI_PSCAN, &hdev->flags); | 285 | set_bit(HCI_PSCAN, &hdev->flags); |
286 | } | 286 | } |
287 | 287 | ||
288 | hci_req_complete(hdev, status); | 288 | hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); |
289 | } | 289 | } |
290 | 290 | ||
291 | static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) | 291 | static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) |
@@ -383,7 +383,7 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) | |||
383 | 383 | ||
384 | BT_DBG("%s status 0x%x", hdev->name, status); | 384 | BT_DBG("%s status 0x%x", hdev->name, status); |
385 | 385 | ||
386 | hci_req_complete(hdev, status); | 386 | hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status); |
387 | } | 387 | } |
388 | 388 | ||
389 | static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) | 389 | static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) |
@@ -536,7 +536,16 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) | |||
536 | if (!rp->status) | 536 | if (!rp->status) |
537 | bacpy(&hdev->bdaddr, &rp->bdaddr); | 537 | bacpy(&hdev->bdaddr, &rp->bdaddr); |
538 | 538 | ||
539 | hci_req_complete(hdev, rp->status); | 539 | hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status); |
540 | } | ||
541 | |||
542 | static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) | ||
543 | { | ||
544 | __u8 status = *((__u8 *) skb->data); | ||
545 | |||
546 | BT_DBG("%s status 0x%x", hdev->name, status); | ||
547 | |||
548 | hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); | ||
540 | } | 549 | } |
541 | 550 | ||
542 | static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) | 551 | static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) |
@@ -544,7 +553,7 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) | |||
544 | BT_DBG("%s status 0x%x", hdev->name, status); | 553 | BT_DBG("%s status 0x%x", hdev->name, status); |
545 | 554 | ||
546 | if (status) { | 555 | if (status) { |
547 | hci_req_complete(hdev, status); | 556 | hci_req_complete(hdev, HCI_OP_INQUIRY, status); |
548 | 557 | ||
549 | hci_conn_check_pending(hdev); | 558 | hci_conn_check_pending(hdev); |
550 | } else | 559 | } else |
@@ -871,7 +880,7 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff | |||
871 | 880 | ||
872 | clear_bit(HCI_INQUIRY, &hdev->flags); | 881 | clear_bit(HCI_INQUIRY, &hdev->flags); |
873 | 882 | ||
874 | hci_req_complete(hdev, status); | 883 | hci_req_complete(hdev, HCI_OP_INQUIRY, status); |
875 | 884 | ||
876 | hci_conn_check_pending(hdev); | 885 | hci_conn_check_pending(hdev); |
877 | } | 886 | } |
@@ -1379,6 +1388,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk | |||
1379 | hci_cc_read_bd_addr(hdev, skb); | 1388 | hci_cc_read_bd_addr(hdev, skb); |
1380 | break; | 1389 | break; |
1381 | 1390 | ||
1391 | case HCI_OP_WRITE_CA_TIMEOUT: | ||
1392 | hci_cc_write_ca_timeout(hdev, skb); | ||
1393 | break; | ||
1394 | |||
1382 | default: | 1395 | default: |
1383 | BT_DBG("%s opcode 0x%x", hdev->name, opcode); | 1396 | BT_DBG("%s opcode 0x%x", hdev->name, opcode); |
1384 | break; | 1397 | break; |
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index b3753bad2a55..29827c77f6ce 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c | |||
@@ -49,6 +49,8 @@ | |||
49 | #include <net/bluetooth/bluetooth.h> | 49 | #include <net/bluetooth/bluetooth.h> |
50 | #include <net/bluetooth/hci_core.h> | 50 | #include <net/bluetooth/hci_core.h> |
51 | 51 | ||
52 | static int enable_mgmt; | ||
53 | |||
52 | /* ----- HCI socket interface ----- */ | 54 | /* ----- HCI socket interface ----- */ |
53 | 55 | ||
54 | static inline int hci_test_bit(int nr, void *addr) | 56 | static inline int hci_test_bit(int nr, void *addr) |
@@ -102,6 +104,12 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) | |||
102 | if (skb->sk == sk) | 104 | if (skb->sk == sk) |
103 | continue; | 105 | continue; |
104 | 106 | ||
107 | if (bt_cb(skb)->channel != hci_pi(sk)->channel) | ||
108 | continue; | ||
109 | |||
110 | if (bt_cb(skb)->channel == HCI_CHANNEL_CONTROL) | ||
111 | goto clone; | ||
112 | |||
105 | /* Apply filter */ | 113 | /* Apply filter */ |
106 | flt = &hci_pi(sk)->filter; | 114 | flt = &hci_pi(sk)->filter; |
107 | 115 | ||
@@ -125,12 +133,14 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) | |||
125 | continue; | 133 | continue; |
126 | } | 134 | } |
127 | 135 | ||
136 | clone: | ||
128 | nskb = skb_clone(skb, GFP_ATOMIC); | 137 | nskb = skb_clone(skb, GFP_ATOMIC); |
129 | if (!nskb) | 138 | if (!nskb) |
130 | continue; | 139 | continue; |
131 | 140 | ||
132 | /* Put type byte before the data */ | 141 | /* Put type byte before the data */ |
133 | memcpy(skb_push(nskb, 1), &bt_cb(nskb)->pkt_type, 1); | 142 | if (bt_cb(skb)->channel == HCI_CHANNEL_RAW) |
143 | memcpy(skb_push(nskb, 1), &bt_cb(nskb)->pkt_type, 1); | ||
134 | 144 | ||
135 | if (sock_queue_rcv_skb(sk, nskb)) | 145 | if (sock_queue_rcv_skb(sk, nskb)) |
136 | kfree_skb(nskb); | 146 | kfree_skb(nskb); |
@@ -353,25 +363,38 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long a | |||
353 | 363 | ||
354 | static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) | 364 | static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) |
355 | { | 365 | { |
356 | struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; | 366 | struct sockaddr_hci haddr; |
357 | struct sock *sk = sock->sk; | 367 | struct sock *sk = sock->sk; |
358 | struct hci_dev *hdev = NULL; | 368 | struct hci_dev *hdev = NULL; |
359 | int err = 0; | 369 | int len, err = 0; |
360 | 370 | ||
361 | BT_DBG("sock %p sk %p", sock, sk); | 371 | BT_DBG("sock %p sk %p", sock, sk); |
362 | 372 | ||
363 | if (!haddr || haddr->hci_family != AF_BLUETOOTH) | 373 | if (!addr) |
374 | return -EINVAL; | ||
375 | |||
376 | memset(&haddr, 0, sizeof(haddr)); | ||
377 | len = min_t(unsigned int, sizeof(haddr), addr_len); | ||
378 | memcpy(&haddr, addr, len); | ||
379 | |||
380 | if (haddr.hci_family != AF_BLUETOOTH) | ||
381 | return -EINVAL; | ||
382 | |||
383 | if (haddr.hci_channel > HCI_CHANNEL_CONTROL) | ||
384 | return -EINVAL; | ||
385 | |||
386 | if (haddr.hci_channel == HCI_CHANNEL_CONTROL && !enable_mgmt) | ||
364 | return -EINVAL; | 387 | return -EINVAL; |
365 | 388 | ||
366 | lock_sock(sk); | 389 | lock_sock(sk); |
367 | 390 | ||
368 | if (hci_pi(sk)->hdev) { | 391 | if (sk->sk_state == BT_BOUND || hci_pi(sk)->hdev) { |
369 | err = -EALREADY; | 392 | err = -EALREADY; |
370 | goto done; | 393 | goto done; |
371 | } | 394 | } |
372 | 395 | ||
373 | if (haddr->hci_dev != HCI_DEV_NONE) { | 396 | if (haddr.hci_dev != HCI_DEV_NONE) { |
374 | hdev = hci_dev_get(haddr->hci_dev); | 397 | hdev = hci_dev_get(haddr.hci_dev); |
375 | if (!hdev) { | 398 | if (!hdev) { |
376 | err = -ENODEV; | 399 | err = -ENODEV; |
377 | goto done; | 400 | goto done; |
@@ -380,6 +403,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le | |||
380 | atomic_inc(&hdev->promisc); | 403 | atomic_inc(&hdev->promisc); |
381 | } | 404 | } |
382 | 405 | ||
406 | hci_pi(sk)->channel = haddr.hci_channel; | ||
383 | hci_pi(sk)->hdev = hdev; | 407 | hci_pi(sk)->hdev = hdev; |
384 | sk->sk_state = BT_BOUND; | 408 | sk->sk_state = BT_BOUND; |
385 | 409 | ||
@@ -502,6 +526,17 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, | |||
502 | 526 | ||
503 | lock_sock(sk); | 527 | lock_sock(sk); |
504 | 528 | ||
529 | switch (hci_pi(sk)->channel) { | ||
530 | case HCI_CHANNEL_RAW: | ||
531 | break; | ||
532 | case HCI_CHANNEL_CONTROL: | ||
533 | err = mgmt_control(sk, msg, len); | ||
534 | goto done; | ||
535 | default: | ||
536 | err = -EINVAL; | ||
537 | goto done; | ||
538 | } | ||
539 | |||
505 | hdev = hci_pi(sk)->hdev; | 540 | hdev = hci_pi(sk)->hdev; |
506 | if (!hdev) { | 541 | if (!hdev) { |
507 | err = -EBADFD; | 542 | err = -EBADFD; |
@@ -831,3 +866,6 @@ void __exit hci_sock_cleanup(void) | |||
831 | 866 | ||
832 | proto_unregister(&hci_sk_proto); | 867 | proto_unregister(&hci_sk_proto); |
833 | } | 868 | } |
869 | |||
870 | module_param(enable_mgmt, bool, 0644); | ||
871 | MODULE_PARM_DESC(enable_mgmt, "Enable Management interface"); | ||
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index c12eccfdfe01..c791fcda7b2d 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c | |||
@@ -3124,8 +3124,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr | |||
3124 | if (!sk) | 3124 | if (!sk) |
3125 | return -ENOENT; | 3125 | return -ENOENT; |
3126 | 3126 | ||
3127 | if (sk->sk_state == BT_DISCONN) | 3127 | if (sk->sk_state != BT_CONFIG) { |
3128 | struct l2cap_cmd_rej rej; | ||
3129 | |||
3130 | rej.reason = cpu_to_le16(0x0002); | ||
3131 | l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, | ||
3132 | sizeof(rej), &rej); | ||
3128 | goto unlock; | 3133 | goto unlock; |
3134 | } | ||
3129 | 3135 | ||
3130 | /* Reject if config buffer is too small. */ | 3136 | /* Reject if config buffer is too small. */ |
3131 | len = cmd_len - sizeof(*req); | 3137 | len = cmd_len - sizeof(*req); |
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c new file mode 100644 index 000000000000..f827fd908380 --- /dev/null +++ b/net/bluetooth/mgmt.c | |||
@@ -0,0 +1,308 @@ | |||
1 | /* | ||
2 | BlueZ - Bluetooth protocol stack for Linux | ||
3 | Copyright (C) 2010 Nokia Corporation | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License version 2 as | ||
7 | published by the Free Software Foundation; | ||
8 | |||
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
10 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
12 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
13 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
14 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | |||
18 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
19 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
20 | SOFTWARE IS DISCLAIMED. | ||
21 | */ | ||
22 | |||
23 | /* Bluetooth HCI Management interface */ | ||
24 | |||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/unaligned.h> | ||
27 | |||
28 | #include <net/bluetooth/bluetooth.h> | ||
29 | #include <net/bluetooth/hci_core.h> | ||
30 | #include <net/bluetooth/mgmt.h> | ||
31 | |||
32 | #define MGMT_VERSION 0 | ||
33 | #define MGMT_REVISION 1 | ||
34 | |||
35 | static int cmd_status(struct sock *sk, u16 cmd, u8 status) | ||
36 | { | ||
37 | struct sk_buff *skb; | ||
38 | struct mgmt_hdr *hdr; | ||
39 | struct mgmt_ev_cmd_status *ev; | ||
40 | |||
41 | BT_DBG("sock %p", sk); | ||
42 | |||
43 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC); | ||
44 | if (!skb) | ||
45 | return -ENOMEM; | ||
46 | |||
47 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
48 | |||
49 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); | ||
50 | hdr->len = cpu_to_le16(sizeof(*ev)); | ||
51 | |||
52 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
53 | ev->status = status; | ||
54 | put_unaligned_le16(cmd, &ev->opcode); | ||
55 | |||
56 | if (sock_queue_rcv_skb(sk, skb) < 0) | ||
57 | kfree_skb(skb); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int read_version(struct sock *sk) | ||
63 | { | ||
64 | struct sk_buff *skb; | ||
65 | struct mgmt_hdr *hdr; | ||
66 | struct mgmt_ev_cmd_complete *ev; | ||
67 | struct mgmt_rp_read_version *rp; | ||
68 | |||
69 | BT_DBG("sock %p", sk); | ||
70 | |||
71 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); | ||
72 | if (!skb) | ||
73 | return -ENOMEM; | ||
74 | |||
75 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
76 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | ||
77 | hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); | ||
78 | |||
79 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
80 | put_unaligned_le16(MGMT_OP_READ_VERSION, &ev->opcode); | ||
81 | |||
82 | rp = (void *) skb_put(skb, sizeof(*rp)); | ||
83 | rp->version = MGMT_VERSION; | ||
84 | put_unaligned_le16(MGMT_REVISION, &rp->revision); | ||
85 | |||
86 | if (sock_queue_rcv_skb(sk, skb) < 0) | ||
87 | kfree_skb(skb); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int read_index_list(struct sock *sk) | ||
93 | { | ||
94 | struct sk_buff *skb; | ||
95 | struct mgmt_hdr *hdr; | ||
96 | struct mgmt_ev_cmd_complete *ev; | ||
97 | struct mgmt_rp_read_index_list *rp; | ||
98 | struct list_head *p; | ||
99 | size_t body_len; | ||
100 | u16 count; | ||
101 | int i; | ||
102 | |||
103 | BT_DBG("sock %p", sk); | ||
104 | |||
105 | read_lock(&hci_dev_list_lock); | ||
106 | |||
107 | count = 0; | ||
108 | list_for_each(p, &hci_dev_list) { | ||
109 | count++; | ||
110 | } | ||
111 | |||
112 | body_len = sizeof(*ev) + sizeof(*rp) + (2 * count); | ||
113 | skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC); | ||
114 | if (!skb) | ||
115 | return -ENOMEM; | ||
116 | |||
117 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
118 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | ||
119 | hdr->len = cpu_to_le16(body_len); | ||
120 | |||
121 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
122 | put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode); | ||
123 | |||
124 | rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count)); | ||
125 | put_unaligned_le16(count, &rp->num_controllers); | ||
126 | |||
127 | i = 0; | ||
128 | list_for_each(p, &hci_dev_list) { | ||
129 | struct hci_dev *d = list_entry(p, struct hci_dev, list); | ||
130 | put_unaligned_le16(d->id, &rp->index[i++]); | ||
131 | BT_DBG("Added hci%u", d->id); | ||
132 | } | ||
133 | |||
134 | read_unlock(&hci_dev_list_lock); | ||
135 | |||
136 | if (sock_queue_rcv_skb(sk, skb) < 0) | ||
137 | kfree_skb(skb); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) | ||
143 | { | ||
144 | struct sk_buff *skb; | ||
145 | struct mgmt_hdr *hdr; | ||
146 | struct mgmt_ev_cmd_complete *ev; | ||
147 | struct mgmt_rp_read_info *rp; | ||
148 | struct mgmt_cp_read_info *cp; | ||
149 | struct hci_dev *hdev; | ||
150 | u16 dev_id; | ||
151 | |||
152 | BT_DBG("sock %p", sk); | ||
153 | |||
154 | if (len != 2) | ||
155 | return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL); | ||
156 | |||
157 | skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); | ||
158 | if (!skb) | ||
159 | return -ENOMEM; | ||
160 | |||
161 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
162 | hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); | ||
163 | hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); | ||
164 | |||
165 | ev = (void *) skb_put(skb, sizeof(*ev)); | ||
166 | put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode); | ||
167 | |||
168 | rp = (void *) skb_put(skb, sizeof(*rp)); | ||
169 | |||
170 | cp = (void *) data; | ||
171 | dev_id = get_unaligned_le16(&cp->index); | ||
172 | |||
173 | BT_DBG("request for hci%u", dev_id); | ||
174 | |||
175 | hdev = hci_dev_get(dev_id); | ||
176 | if (!hdev) { | ||
177 | kfree_skb(skb); | ||
178 | return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); | ||
179 | } | ||
180 | |||
181 | hci_dev_lock_bh(hdev); | ||
182 | |||
183 | put_unaligned_le16(hdev->id, &rp->index); | ||
184 | rp->type = hdev->dev_type; | ||
185 | |||
186 | rp->powered = test_bit(HCI_UP, &hdev->flags); | ||
187 | rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags); | ||
188 | rp->pairable = test_bit(HCI_PSCAN, &hdev->flags); | ||
189 | |||
190 | if (test_bit(HCI_AUTH, &hdev->flags)) | ||
191 | rp->sec_mode = 3; | ||
192 | else if (hdev->ssp_mode > 0) | ||
193 | rp->sec_mode = 4; | ||
194 | else | ||
195 | rp->sec_mode = 2; | ||
196 | |||
197 | bacpy(&rp->bdaddr, &hdev->bdaddr); | ||
198 | memcpy(rp->features, hdev->features, 8); | ||
199 | memcpy(rp->dev_class, hdev->dev_class, 3); | ||
200 | put_unaligned_le16(hdev->manufacturer, &rp->manufacturer); | ||
201 | rp->hci_ver = hdev->hci_ver; | ||
202 | put_unaligned_le16(hdev->hci_rev, &rp->hci_rev); | ||
203 | |||
204 | hci_dev_unlock_bh(hdev); | ||
205 | hci_dev_put(hdev); | ||
206 | |||
207 | if (sock_queue_rcv_skb(sk, skb) < 0) | ||
208 | kfree_skb(skb); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) | ||
214 | { | ||
215 | unsigned char *buf; | ||
216 | struct mgmt_hdr *hdr; | ||
217 | u16 opcode, len; | ||
218 | int err; | ||
219 | |||
220 | BT_DBG("got %zu bytes", msglen); | ||
221 | |||
222 | if (msglen < sizeof(*hdr)) | ||
223 | return -EINVAL; | ||
224 | |||
225 | buf = kmalloc(msglen, GFP_ATOMIC); | ||
226 | if (!buf) | ||
227 | return -ENOMEM; | ||
228 | |||
229 | if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { | ||
230 | err = -EFAULT; | ||
231 | goto done; | ||
232 | } | ||
233 | |||
234 | hdr = (struct mgmt_hdr *) buf; | ||
235 | opcode = get_unaligned_le16(&hdr->opcode); | ||
236 | len = get_unaligned_le16(&hdr->len); | ||
237 | |||
238 | if (len != msglen - sizeof(*hdr)) { | ||
239 | err = -EINVAL; | ||
240 | goto done; | ||
241 | } | ||
242 | |||
243 | switch (opcode) { | ||
244 | case MGMT_OP_READ_VERSION: | ||
245 | err = read_version(sk); | ||
246 | break; | ||
247 | case MGMT_OP_READ_INDEX_LIST: | ||
248 | err = read_index_list(sk); | ||
249 | break; | ||
250 | case MGMT_OP_READ_INFO: | ||
251 | err = read_controller_info(sk, buf + sizeof(*hdr), len); | ||
252 | break; | ||
253 | default: | ||
254 | BT_DBG("Unknown op %u", opcode); | ||
255 | err = cmd_status(sk, opcode, 0x01); | ||
256 | break; | ||
257 | } | ||
258 | |||
259 | if (err < 0) | ||
260 | goto done; | ||
261 | |||
262 | err = msglen; | ||
263 | |||
264 | done: | ||
265 | kfree(buf); | ||
266 | return err; | ||
267 | } | ||
268 | |||
269 | static int mgmt_event(u16 event, void *data, u16 data_len) | ||
270 | { | ||
271 | struct sk_buff *skb; | ||
272 | struct mgmt_hdr *hdr; | ||
273 | |||
274 | skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); | ||
275 | if (!skb) | ||
276 | return -ENOMEM; | ||
277 | |||
278 | bt_cb(skb)->channel = HCI_CHANNEL_CONTROL; | ||
279 | |||
280 | hdr = (void *) skb_put(skb, sizeof(*hdr)); | ||
281 | hdr->opcode = cpu_to_le16(event); | ||
282 | hdr->len = cpu_to_le16(data_len); | ||
283 | |||
284 | memcpy(skb_put(skb, data_len), data, data_len); | ||
285 | |||
286 | hci_send_to_sock(NULL, skb); | ||
287 | kfree_skb(skb); | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | int mgmt_index_added(u16 index) | ||
293 | { | ||
294 | struct mgmt_ev_index_added ev; | ||
295 | |||
296 | put_unaligned_le16(index, &ev.index); | ||
297 | |||
298 | return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev)); | ||
299 | } | ||
300 | |||
301 | int mgmt_index_removed(u16 index) | ||
302 | { | ||
303 | struct mgmt_ev_index_added ev; | ||
304 | |||
305 | put_unaligned_le16(index, &ev.index); | ||
306 | |||
307 | return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev)); | ||
308 | } | ||