blob: 86148b182891df5e6abcd7e434be63395c6b3001 [file] [log] [blame]
Johan Hedberg03811012010-12-08 00:21:06 +02001/*
2 BlueZ - Bluetooth protocol stack for Linux
Johan Hedbergea585ab2012-02-17 14:50:39 +02003
Johan Hedberg03811012010-12-08 00:21:06 +02004 Copyright (C) 2010 Nokia Corporation
Johan Hedbergea585ab2012-02-17 14:50:39 +02005 Copyright (C) 2011-2012 Intel Corporation
Johan Hedberg03811012010-12-08 00:21:06 +02006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation;
10
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
12 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
14 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
15 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
16 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
21 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
22 SOFTWARE IS DISCLAIMED.
23*/
24
25/* Bluetooth HCI Management interface */
26
Johan Hedbergca69b792011-11-11 18:10:00 +020027#include <linux/kernel.h>
Szymon Janc72359752011-02-17 14:16:32 +010028#include <linux/uaccess.h>
Paul Gortmaker3a9a2312011-05-27 09:12:25 -040029#include <linux/module.h>
Johan Hedberg03811012010-12-08 00:21:06 +020030#include <asm/unaligned.h>
31
32#include <net/bluetooth/bluetooth.h>
33#include <net/bluetooth/hci_core.h>
34#include <net/bluetooth/mgmt.h>
Brian Gix5fe57d92011-12-21 16:12:13 -080035#include <net/bluetooth/smp.h>
Johan Hedberg03811012010-12-08 00:21:06 +020036
Marcel Holtmannd7b7e792012-02-20 21:47:49 +010037bool enable_hs;
38bool enable_le;
39
Johan Hedberg2da9c552012-02-17 14:39:28 +020040#define MGMT_VERSION 1
41#define MGMT_REVISION 0
Johan Hedberg02d98122010-12-13 21:07:04 +020042
Johan Hedberge70bb2e2012-02-13 16:59:33 +020043static const u16 mgmt_commands[] = {
44 MGMT_OP_READ_INDEX_LIST,
45 MGMT_OP_READ_INFO,
46 MGMT_OP_SET_POWERED,
47 MGMT_OP_SET_DISCOVERABLE,
48 MGMT_OP_SET_CONNECTABLE,
49 MGMT_OP_SET_FAST_CONNECTABLE,
50 MGMT_OP_SET_PAIRABLE,
51 MGMT_OP_SET_LINK_SECURITY,
52 MGMT_OP_SET_SSP,
53 MGMT_OP_SET_HS,
54 MGMT_OP_SET_LE,
55 MGMT_OP_SET_DEV_CLASS,
56 MGMT_OP_SET_LOCAL_NAME,
57 MGMT_OP_ADD_UUID,
58 MGMT_OP_REMOVE_UUID,
59 MGMT_OP_LOAD_LINK_KEYS,
60 MGMT_OP_LOAD_LONG_TERM_KEYS,
61 MGMT_OP_DISCONNECT,
62 MGMT_OP_GET_CONNECTIONS,
63 MGMT_OP_PIN_CODE_REPLY,
64 MGMT_OP_PIN_CODE_NEG_REPLY,
65 MGMT_OP_SET_IO_CAPABILITY,
66 MGMT_OP_PAIR_DEVICE,
67 MGMT_OP_CANCEL_PAIR_DEVICE,
68 MGMT_OP_UNPAIR_DEVICE,
69 MGMT_OP_USER_CONFIRM_REPLY,
70 MGMT_OP_USER_CONFIRM_NEG_REPLY,
71 MGMT_OP_USER_PASSKEY_REPLY,
72 MGMT_OP_USER_PASSKEY_NEG_REPLY,
73 MGMT_OP_READ_LOCAL_OOB_DATA,
74 MGMT_OP_ADD_REMOTE_OOB_DATA,
75 MGMT_OP_REMOVE_REMOTE_OOB_DATA,
76 MGMT_OP_START_DISCOVERY,
77 MGMT_OP_STOP_DISCOVERY,
78 MGMT_OP_CONFIRM_NAME,
79 MGMT_OP_BLOCK_DEVICE,
80 MGMT_OP_UNBLOCK_DEVICE,
81};
82
83static const u16 mgmt_events[] = {
84 MGMT_EV_CONTROLLER_ERROR,
85 MGMT_EV_INDEX_ADDED,
86 MGMT_EV_INDEX_REMOVED,
87 MGMT_EV_NEW_SETTINGS,
88 MGMT_EV_CLASS_OF_DEV_CHANGED,
89 MGMT_EV_LOCAL_NAME_CHANGED,
90 MGMT_EV_NEW_LINK_KEY,
91 MGMT_EV_NEW_LONG_TERM_KEY,
92 MGMT_EV_DEVICE_CONNECTED,
93 MGMT_EV_DEVICE_DISCONNECTED,
94 MGMT_EV_CONNECT_FAILED,
95 MGMT_EV_PIN_CODE_REQUEST,
96 MGMT_EV_USER_CONFIRM_REQUEST,
97 MGMT_EV_USER_PASSKEY_REQUEST,
98 MGMT_EV_AUTH_FAILED,
99 MGMT_EV_DEVICE_FOUND,
100 MGMT_EV_DISCOVERING,
101 MGMT_EV_DEVICE_BLOCKED,
102 MGMT_EV_DEVICE_UNBLOCKED,
103 MGMT_EV_DEVICE_UNPAIRED,
104};
105
Andre Guedes3fd24152012-02-03 17:48:01 -0300106/*
107 * These LE scan and inquiry parameters were chosen according to LE General
108 * Discovery Procedure specification.
109 */
110#define LE_SCAN_TYPE 0x01
111#define LE_SCAN_WIN 0x12
112#define LE_SCAN_INT 0x12
113#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */
Andre Guedes5e0452c2012-02-17 20:39:38 -0300114#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */
Andre Guedes3fd24152012-02-03 17:48:01 -0300115
Andre Guedese8777522012-02-03 17:48:02 -0300116#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
Andre Guedes5e0452c2012-02-17 20:39:38 -0300117#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */
Andre Guedes2519a1f2011-11-07 11:45:24 -0300118
Johan Hedberg7d785252011-12-15 00:47:39 +0200119#define SERVICE_CACHE_TIMEOUT (5 * 1000)
120
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200121struct pending_cmd {
122 struct list_head list;
Johan Hedbergfc2f4b12011-11-09 13:58:56 +0200123 u16 opcode;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200124 int index;
Szymon Jancc68fb7f2011-03-22 13:12:19 +0100125 void *param;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200126 struct sock *sk;
Johan Hedberge9a416b2011-02-19 12:05:56 -0300127 void *user_data;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200128};
129
Johan Hedbergca69b792011-11-11 18:10:00 +0200130/* HCI to MGMT error code conversion table */
131static u8 mgmt_status_table[] = {
132 MGMT_STATUS_SUCCESS,
133 MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */
134 MGMT_STATUS_NOT_CONNECTED, /* No Connection */
135 MGMT_STATUS_FAILED, /* Hardware Failure */
136 MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */
137 MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */
138 MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */
139 MGMT_STATUS_NO_RESOURCES, /* Memory Full */
140 MGMT_STATUS_TIMEOUT, /* Connection Timeout */
141 MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */
142 MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */
143 MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */
144 MGMT_STATUS_BUSY, /* Command Disallowed */
145 MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */
146 MGMT_STATUS_REJECTED, /* Rejected Security */
147 MGMT_STATUS_REJECTED, /* Rejected Personal */
148 MGMT_STATUS_TIMEOUT, /* Host Timeout */
149 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */
150 MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */
151 MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */
152 MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */
153 MGMT_STATUS_DISCONNECTED, /* OE Power Off */
154 MGMT_STATUS_DISCONNECTED, /* Connection Terminated */
155 MGMT_STATUS_BUSY, /* Repeated Attempts */
156 MGMT_STATUS_REJECTED, /* Pairing Not Allowed */
157 MGMT_STATUS_FAILED, /* Unknown LMP PDU */
158 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */
159 MGMT_STATUS_REJECTED, /* SCO Offset Rejected */
160 MGMT_STATUS_REJECTED, /* SCO Interval Rejected */
161 MGMT_STATUS_REJECTED, /* Air Mode Rejected */
162 MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */
163 MGMT_STATUS_FAILED, /* Unspecified Error */
164 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */
165 MGMT_STATUS_FAILED, /* Role Change Not Allowed */
166 MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */
167 MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */
168 MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */
169 MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */
170 MGMT_STATUS_FAILED, /* Unit Link Key Used */
171 MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */
172 MGMT_STATUS_TIMEOUT, /* Instant Passed */
173 MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */
174 MGMT_STATUS_FAILED, /* Transaction Collision */
175 MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */
176 MGMT_STATUS_REJECTED, /* QoS Rejected */
177 MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */
178 MGMT_STATUS_REJECTED, /* Insufficient Security */
179 MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */
180 MGMT_STATUS_BUSY, /* Role Switch Pending */
181 MGMT_STATUS_FAILED, /* Slot Violation */
182 MGMT_STATUS_FAILED, /* Role Switch Failed */
183 MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */
184 MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */
185 MGMT_STATUS_BUSY, /* Host Busy Pairing */
186 MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */
187 MGMT_STATUS_BUSY, /* Controller Busy */
188 MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */
189 MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */
190 MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */
191 MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */
192 MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */
193};
194
195static u8 mgmt_status(u8 hci_status)
196{
197 if (hci_status < ARRAY_SIZE(mgmt_status_table))
198 return mgmt_status_table[hci_status];
199
200 return MGMT_STATUS_FAILED;
201}
202
Szymon Janc4e51eae2011-02-25 19:05:48 +0100203static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200204{
205 struct sk_buff *skb;
206 struct mgmt_hdr *hdr;
207 struct mgmt_ev_cmd_status *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300208 int err;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200209
Szymon Janc34eb5252011-02-28 14:10:08 +0100210 BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200211
212 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
213 if (!skb)
214 return -ENOMEM;
215
216 hdr = (void *) skb_put(skb, sizeof(*hdr));
217
218 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100219 hdr->index = cpu_to_le16(index);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200220 hdr->len = cpu_to_le16(sizeof(*ev));
221
222 ev = (void *) skb_put(skb, sizeof(*ev));
223 ev->status = status;
224 put_unaligned_le16(cmd, &ev->opcode);
225
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300226 err = sock_queue_rcv_skb(sk, skb);
227 if (err < 0)
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200228 kfree_skb(skb);
229
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300230 return err;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200231}
232
Johan Hedbergaee9b212012-02-18 15:07:59 +0200233static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
234 void *rp, size_t rp_len)
Johan Hedberg02d98122010-12-13 21:07:04 +0200235{
236 struct sk_buff *skb;
237 struct mgmt_hdr *hdr;
238 struct mgmt_ev_cmd_complete *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300239 int err;
Johan Hedberg02d98122010-12-13 21:07:04 +0200240
241 BT_DBG("sock %p", sk);
242
Johan Hedberga38528f2011-01-22 06:46:43 +0200243 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC);
Johan Hedberg02d98122010-12-13 21:07:04 +0200244 if (!skb)
245 return -ENOMEM;
246
247 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedberga38528f2011-01-22 06:46:43 +0200248
Johan Hedberg02d98122010-12-13 21:07:04 +0200249 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100250 hdr->index = cpu_to_le16(index);
Johan Hedberga38528f2011-01-22 06:46:43 +0200251 hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200252
Johan Hedberga38528f2011-01-22 06:46:43 +0200253 ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
254 put_unaligned_le16(cmd, &ev->opcode);
Johan Hedbergaee9b212012-02-18 15:07:59 +0200255 ev->status = status;
Szymon Janc8020c162011-02-28 14:09:50 +0100256
257 if (rp)
258 memcpy(ev->data, rp, rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200259
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300260 err = sock_queue_rcv_skb(sk, skb);
261 if (err < 0)
Johan Hedberg02d98122010-12-13 21:07:04 +0200262 kfree_skb(skb);
263
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300264 return err;;
Johan Hedberg02d98122010-12-13 21:07:04 +0200265}
266
Johan Hedberga38528f2011-01-22 06:46:43 +0200267static int read_version(struct sock *sk)
268{
269 struct mgmt_rp_read_version rp;
270
271 BT_DBG("sock %p", sk);
272
273 rp.version = MGMT_VERSION;
274 put_unaligned_le16(MGMT_REVISION, &rp.revision);
275
Johan Hedbergaee9b212012-02-18 15:07:59 +0200276 return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +0100277 sizeof(rp));
Johan Hedberga38528f2011-01-22 06:46:43 +0200278}
279
Johan Hedberge70bb2e2012-02-13 16:59:33 +0200280static int read_commands(struct sock *sk)
281{
282 struct mgmt_rp_read_commands *rp;
283 u16 num_commands = ARRAY_SIZE(mgmt_commands);
284 u16 num_events = ARRAY_SIZE(mgmt_events);
285 u16 *opcode;
286 size_t rp_size;
287 int i, err;
288
289 BT_DBG("sock %p", sk);
290
291 rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16));
292
293 rp = kmalloc(rp_size, GFP_KERNEL);
294 if (!rp)
295 return -ENOMEM;
296
297 put_unaligned_le16(num_commands, &rp->num_commands);
298 put_unaligned_le16(num_events, &rp->num_events);
299
300 for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++)
301 put_unaligned_le16(mgmt_commands[i], opcode);
302
303 for (i = 0; i < num_events; i++, opcode++)
304 put_unaligned_le16(mgmt_events[i], opcode);
305
Johan Hedbergaee9b212012-02-18 15:07:59 +0200306 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp,
Johan Hedberge70bb2e2012-02-13 16:59:33 +0200307 rp_size);
308 kfree(rp);
309
310 return err;
311}
312
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200313static int read_index_list(struct sock *sk)
314{
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200315 struct mgmt_rp_read_index_list *rp;
316 struct list_head *p;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200317 struct hci_dev *d;
Johan Hedberga38528f2011-01-22 06:46:43 +0200318 size_t rp_len;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200319 u16 count;
Johan Hedberga38528f2011-01-22 06:46:43 +0200320 int i, err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200321
322 BT_DBG("sock %p", sk);
323
324 read_lock(&hci_dev_list_lock);
325
326 count = 0;
327 list_for_each(p, &hci_dev_list) {
328 count++;
329 }
330
Johan Hedberga38528f2011-01-22 06:46:43 +0200331 rp_len = sizeof(*rp) + (2 * count);
332 rp = kmalloc(rp_len, GFP_ATOMIC);
333 if (!rp) {
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100334 read_unlock(&hci_dev_list_lock);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200335 return -ENOMEM;
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100336 }
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200337
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200338 put_unaligned_le16(count, &rp->num_controllers);
339
340 i = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200341 list_for_each_entry(d, &hci_dev_list, list) {
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200342 if (test_and_clear_bit(HCI_AUTO_OFF, &d->dev_flags))
Johan Hedberge0f93092011-11-09 01:44:22 +0200343 cancel_delayed_work(&d->power_off);
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200344
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200345 if (test_bit(HCI_SETUP, &d->dev_flags))
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200346 continue;
347
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200348 put_unaligned_le16(d->id, &rp->index[i++]);
349 BT_DBG("Added hci%u", d->id);
350 }
351
352 read_unlock(&hci_dev_list_lock);
353
Johan Hedbergaee9b212012-02-18 15:07:59 +0200354 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +0100355 rp_len);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200356
Johan Hedberga38528f2011-01-22 06:46:43 +0200357 kfree(rp);
358
359 return err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200360}
361
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200362static u32 get_supported_settings(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +0200363{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200364 u32 settings = 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200365
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200366 settings |= MGMT_SETTING_POWERED;
367 settings |= MGMT_SETTING_CONNECTABLE;
368 settings |= MGMT_SETTING_FAST_CONNECTABLE;
369 settings |= MGMT_SETTING_DISCOVERABLE;
370 settings |= MGMT_SETTING_PAIRABLE;
Johan Hedberg03811012010-12-08 00:21:06 +0200371
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200372 if (hdev->features[6] & LMP_SIMPLE_PAIR)
373 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200374
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200375 if (!(hdev->features[4] & LMP_NO_BREDR)) {
376 settings |= MGMT_SETTING_BREDR;
377 settings |= MGMT_SETTING_LINK_SECURITY;
378 }
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200379
Marcel Holtmannd7b7e792012-02-20 21:47:49 +0100380 if (enable_hs)
381 settings |= MGMT_SETTING_HS;
382
383 if (enable_le) {
384 if (hdev->features[4] & LMP_LE)
385 settings |= MGMT_SETTING_LE;
386 }
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200387
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200388 return settings;
389}
Johan Hedbergebc99fe2011-01-04 11:54:26 +0200390
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200391static u32 get_current_settings(struct hci_dev *hdev)
392{
393 u32 settings = 0;
Johan Hedbergdc4fe302011-03-16 14:29:36 +0200394
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200395 if (test_bit(HCI_UP, &hdev->flags))
396 settings |= MGMT_SETTING_POWERED;
397 else
398 return settings;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200399
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200400 if (test_bit(HCI_PSCAN, &hdev->flags))
401 settings |= MGMT_SETTING_CONNECTABLE;
402
403 if (test_bit(HCI_ISCAN, &hdev->flags))
404 settings |= MGMT_SETTING_DISCOVERABLE;
405
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200406 if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200407 settings |= MGMT_SETTING_PAIRABLE;
408
409 if (!(hdev->features[4] & LMP_NO_BREDR))
410 settings |= MGMT_SETTING_BREDR;
411
Andre Guedes59e29402011-12-30 10:34:03 -0300412 if (hdev->host_features[0] & LMP_HOST_LE)
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200413 settings |= MGMT_SETTING_LE;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200414
415 if (test_bit(HCI_AUTH, &hdev->flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200416 settings |= MGMT_SETTING_LINK_SECURITY;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200417
Johan Hedberg84bde9d2012-01-25 14:21:06 +0200418 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200419 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200420
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200421 return settings;
Johan Hedbergc542a062011-01-26 13:11:03 +0200422}
423
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300424#define PNP_INFO_SVCLASS_ID 0x1200
425
426static u8 bluetooth_base_uuid[] = {
427 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
428 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
429};
430
431static u16 get_uuid16(u8 *uuid128)
432{
433 u32 val;
434 int i;
435
436 for (i = 0; i < 12; i++) {
437 if (bluetooth_base_uuid[i] != uuid128[i])
438 return 0;
439 }
440
441 memcpy(&val, &uuid128[12], 4);
442
443 val = le32_to_cpu(val);
444 if (val > 0xffff)
445 return 0;
446
447 return (u16) val;
448}
449
450static void create_eir(struct hci_dev *hdev, u8 *data)
451{
452 u8 *ptr = data;
453 u16 eir_len = 0;
454 u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
455 int i, truncated = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200456 struct bt_uuid *uuid;
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300457 size_t name_len;
458
459 name_len = strlen(hdev->dev_name);
460
461 if (name_len > 0) {
462 /* EIR Data type */
463 if (name_len > 48) {
464 name_len = 48;
465 ptr[1] = EIR_NAME_SHORT;
466 } else
467 ptr[1] = EIR_NAME_COMPLETE;
468
469 /* EIR Data length */
470 ptr[0] = name_len + 1;
471
472 memcpy(ptr + 2, hdev->dev_name, name_len);
473
474 eir_len += (name_len + 2);
475 ptr += (name_len + 2);
476 }
477
478 memset(uuid16_list, 0, sizeof(uuid16_list));
479
480 /* Group all UUID16 types */
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200481 list_for_each_entry(uuid, &hdev->uuids, list) {
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300482 u16 uuid16;
483
484 uuid16 = get_uuid16(uuid->uuid);
485 if (uuid16 == 0)
486 return;
487
488 if (uuid16 < 0x1100)
489 continue;
490
491 if (uuid16 == PNP_INFO_SVCLASS_ID)
492 continue;
493
494 /* Stop if not enough space to put next UUID */
495 if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
496 truncated = 1;
497 break;
498 }
499
500 /* Check for duplicates */
501 for (i = 0; uuid16_list[i] != 0; i++)
502 if (uuid16_list[i] == uuid16)
503 break;
504
505 if (uuid16_list[i] == 0) {
506 uuid16_list[i] = uuid16;
507 eir_len += sizeof(u16);
508 }
509 }
510
511 if (uuid16_list[0] != 0) {
512 u8 *length = ptr;
513
514 /* EIR Data type */
515 ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
516
517 ptr += 2;
518 eir_len += 2;
519
520 for (i = 0; uuid16_list[i] != 0; i++) {
521 *ptr++ = (uuid16_list[i] & 0x00ff);
522 *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
523 }
524
525 /* EIR Data length */
526 *length = (i * sizeof(u16)) + 1;
527 }
528}
529
530static int update_eir(struct hci_dev *hdev)
531{
532 struct hci_cp_write_eir cp;
533
534 if (!(hdev->features[6] & LMP_EXT_INQ))
535 return 0;
536
Johan Hedberg84bde9d2012-01-25 14:21:06 +0200537 if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300538 return 0;
539
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200540 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300541 return 0;
542
543 memset(&cp, 0, sizeof(cp));
544
545 create_eir(hdev, cp.data);
546
547 if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
548 return 0;
549
550 memcpy(hdev->eir, cp.data, sizeof(cp.data));
551
552 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
553}
554
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200555static u8 get_service_classes(struct hci_dev *hdev)
556{
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300557 struct bt_uuid *uuid;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200558 u8 val = 0;
559
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300560 list_for_each_entry(uuid, &hdev->uuids, list)
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200561 val |= uuid->svc_hint;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200562
563 return val;
564}
565
566static int update_class(struct hci_dev *hdev)
567{
568 u8 cod[3];
569
570 BT_DBG("%s", hdev->name);
571
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200572 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200573 return 0;
574
575 cod[0] = hdev->minor_class;
576 cod[1] = hdev->major_class;
577 cod[2] = get_service_classes(hdev);
578
579 if (memcmp(cod, hdev->dev_class, 3) == 0)
580 return 0;
581
582 return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
583}
584
Johan Hedberg7d785252011-12-15 00:47:39 +0200585static void service_cache_off(struct work_struct *work)
586{
587 struct hci_dev *hdev = container_of(work, struct hci_dev,
588 service_cache.work);
589
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200590 if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200591 return;
592
593 hci_dev_lock(hdev);
594
595 update_eir(hdev);
596 update_class(hdev);
597
598 hci_dev_unlock(hdev);
599}
600
601static void mgmt_init_hdev(struct hci_dev *hdev)
602{
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200603 if (!test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200604 INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
605
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200606 if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200607 schedule_delayed_work(&hdev->service_cache,
608 msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
609}
610
Johan Hedberg03811012010-12-08 00:21:06 +0200611static int read_controller_info(struct sock *sk, u16 index)
612{
613 struct mgmt_rp_read_info rp;
614 struct hci_dev *hdev;
615
616 BT_DBG("sock %p hci%u", sk, index);
617
618 hdev = hci_dev_get(index);
619 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200620 return cmd_status(sk, index, MGMT_OP_READ_INFO,
621 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200622
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200623 if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
Johan Hedberg03811012010-12-08 00:21:06 +0200624 cancel_delayed_work_sync(&hdev->power_off);
625
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300626 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200627
Johan Hedberg7d785252011-12-15 00:47:39 +0200628 if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
629 mgmt_init_hdev(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200630
631 memset(&rp, 0, sizeof(rp));
632
Johan Hedberg03811012010-12-08 00:21:06 +0200633 bacpy(&rp.bdaddr, &hdev->bdaddr);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200634
635 rp.version = hdev->hci_ver;
636
Johan Hedberg03811012010-12-08 00:21:06 +0200637 put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200638
639 rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
640 rp.current_settings = cpu_to_le32(get_current_settings(hdev));
641
642 memcpy(rp.dev_class, hdev->dev_class, 3);
Johan Hedberg03811012010-12-08 00:21:06 +0200643
644 memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
645
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300646 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200647 hci_dev_put(hdev);
648
Johan Hedbergaee9b212012-02-18 15:07:59 +0200649 return cmd_complete(sk, index, MGMT_OP_READ_INFO, 0, &rp, sizeof(rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200650}
651
652static void mgmt_pending_free(struct pending_cmd *cmd)
653{
654 sock_put(cmd->sk);
655 kfree(cmd->param);
656 kfree(cmd);
657}
658
659static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
660 struct hci_dev *hdev,
661 void *data, u16 len)
662{
663 struct pending_cmd *cmd;
664
665 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
666 if (!cmd)
667 return NULL;
668
669 cmd->opcode = opcode;
670 cmd->index = hdev->id;
671
672 cmd->param = kmalloc(len, GFP_ATOMIC);
673 if (!cmd->param) {
674 kfree(cmd);
675 return NULL;
676 }
677
678 if (data)
679 memcpy(cmd->param, data, len);
680
681 cmd->sk = sk;
682 sock_hold(sk);
683
684 list_add(&cmd->list, &hdev->mgmt_pending);
685
686 return cmd;
687}
688
689static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
690 void (*cb)(struct pending_cmd *cmd, void *data),
691 void *data)
692{
693 struct list_head *p, *n;
694
695 list_for_each_safe(p, n, &hdev->mgmt_pending) {
696 struct pending_cmd *cmd;
697
698 cmd = list_entry(p, struct pending_cmd, list);
699
700 if (opcode > 0 && cmd->opcode != opcode)
701 continue;
702
703 cb(cmd, data);
704 }
705}
706
707static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
708{
709 struct pending_cmd *cmd;
710
711 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
712 if (cmd->opcode == opcode)
713 return cmd;
714 }
715
716 return NULL;
717}
718
719static void mgmt_pending_remove(struct pending_cmd *cmd)
720{
721 list_del(&cmd->list);
722 mgmt_pending_free(cmd);
723}
724
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200725static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
Johan Hedberg86805702011-11-11 16:18:52 +0200726{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200727 __le32 settings = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg86805702011-11-11 16:18:52 +0200728
Johan Hedbergaee9b212012-02-18 15:07:59 +0200729 return cmd_complete(sk, hdev->id, opcode, 0, &settings,
730 sizeof(settings));
Johan Hedberg86805702011-11-11 16:18:52 +0200731}
732
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300733static int set_powered(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200734{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300735 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200736 struct hci_dev *hdev;
737 struct pending_cmd *cmd;
738 int err, up;
739
Johan Hedberg03811012010-12-08 00:21:06 +0200740 BT_DBG("request for hci%u", index);
741
742 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200743 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
744 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200745
746 hdev = hci_dev_get(index);
747 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200748 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
749 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200750
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300751 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200752
753 up = test_bit(HCI_UP, &hdev->flags);
754 if ((cp->val && up) || (!cp->val && !up)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200755 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200756 goto failed;
757 }
758
759 if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200760 err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
761 MGMT_STATUS_BUSY);
Johan Hedberg03811012010-12-08 00:21:06 +0200762 goto failed;
763 }
764
765 cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
766 if (!cmd) {
767 err = -ENOMEM;
768 goto failed;
769 }
770
771 if (cp->val)
Gustavo F. Padovan7f971042011-12-18 12:40:32 -0200772 schedule_work(&hdev->power_on);
Johan Hedberg03811012010-12-08 00:21:06 +0200773 else
Gustavo F. Padovan80b7ab32011-12-17 14:52:27 -0200774 schedule_work(&hdev->power_off.work);
Johan Hedberg03811012010-12-08 00:21:06 +0200775
776 err = 0;
777
778failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300779 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200780 hci_dev_put(hdev);
781 return err;
782}
783
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300784static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200785{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300786 struct mgmt_cp_set_discoverable *cp = data;
Johan Hedberge41d8b42010-12-13 21:07:03 +0200787 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +0200788 struct pending_cmd *cmd;
789 u8 scan;
790 int err;
791
Johan Hedberg03811012010-12-08 00:21:06 +0200792 BT_DBG("request for hci%u", index);
793
794 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200795 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
796 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200797
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200798 hdev = hci_dev_get(index);
799 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200800 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
801 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200802
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300803 hci_dev_lock(hdev);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200804
805 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200806 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
807 MGMT_STATUS_NOT_POWERED);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200808 goto failed;
809 }
810
811 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
812 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200813 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
814 MGMT_STATUS_BUSY);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200815 goto failed;
816 }
817
818 if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
819 test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200820 err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200821 goto failed;
822 }
823
824 cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
825 if (!cmd) {
826 err = -ENOMEM;
827 goto failed;
828 }
829
830 scan = SCAN_PAGE;
831
832 if (cp->val)
833 scan |= SCAN_INQUIRY;
834 else
835 cancel_delayed_work(&hdev->discov_off);
836
837 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
838 if (err < 0)
839 mgmt_pending_remove(cmd);
840
Johan Hedberg03811012010-12-08 00:21:06 +0200841 if (cp->val)
842 hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
843
Johan Hedberge41d8b42010-12-13 21:07:03 +0200844failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300845 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200846 hci_dev_put(hdev);
847
848 return err;
849}
850
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300851static int set_connectable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200852{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300853 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200854 struct hci_dev *hdev;
855 struct pending_cmd *cmd;
856 u8 scan;
857 int err;
858
Johan Hedberge41d8b42010-12-13 21:07:03 +0200859 BT_DBG("request for hci%u", index);
860
Johan Hedberg03811012010-12-08 00:21:06 +0200861 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200862 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
863 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200864
865 hdev = hci_dev_get(index);
866 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200867 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
868 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200869
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300870 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200871
872 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200873 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
874 MGMT_STATUS_NOT_POWERED);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200875 goto failed;
876 }
877
878 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
879 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200880 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
881 MGMT_STATUS_BUSY);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200882 goto failed;
883 }
884
885 if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200886 err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200887 goto failed;
888 }
889
890 cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
891 if (!cmd) {
892 err = -ENOMEM;
893 goto failed;
894 }
895
896 if (cp->val)
897 scan = SCAN_PAGE;
898 else
899 scan = 0;
900
901 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
902 if (err < 0)
903 mgmt_pending_remove(cmd);
904
905failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300906 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200907 hci_dev_put(hdev);
908
909 return err;
910}
911
912static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
913 u16 data_len, struct sock *skip_sk)
914{
915 struct sk_buff *skb;
916 struct mgmt_hdr *hdr;
917
918 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
919 if (!skb)
920 return -ENOMEM;
921
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200922 hdr = (void *) skb_put(skb, sizeof(*hdr));
923 hdr->opcode = cpu_to_le16(event);
924 if (hdev)
925 hdr->index = cpu_to_le16(hdev->id);
926 else
927 hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
928 hdr->len = cpu_to_le16(data_len);
929
930 if (data)
931 memcpy(skb_put(skb, data_len), data, data_len);
932
Marcel Holtmann470fe1b2012-02-20 14:50:30 +0100933 hci_send_to_control(skb, skip_sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200934 kfree_skb(skb);
935
936 return 0;
937}
938
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300939static int set_pairable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg73f22f62010-12-29 16:00:25 +0200940{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300941 struct mgmt_mode *cp = data;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200942 struct hci_dev *hdev;
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200943 __le32 ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200944 int err;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200945
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200946 BT_DBG("request for hci%u", index);
947
948 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200949 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
950 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200951
952 hdev = hci_dev_get(index);
953 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200954 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
955 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200956
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300957 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200958
959 if (cp->val)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200960 set_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200961 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200962 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200963
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200964 err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200965 if (err < 0)
966 goto failed;
967
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200968 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200969
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200970 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200971
972failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300973 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200974 hci_dev_put(hdev);
975
976 return err;
977}
Johan Hedberg72a734e2010-12-30 00:38:22 +0200978
Johan Hedberg33ef95e2012-02-16 23:56:27 +0200979static int set_link_security(struct sock *sk, u16 index, void *data, u16 len)
980{
981 struct mgmt_mode *cp = data;
982 struct pending_cmd *cmd;
983 struct hci_dev *hdev;
984 uint8_t val;
985 int err;
986
987 BT_DBG("request for hci%u", index);
988
989 if (len != sizeof(*cp))
990 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
991 MGMT_STATUS_INVALID_PARAMS);
992
993 hdev = hci_dev_get(index);
994 if (!hdev)
995 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
996 MGMT_STATUS_INVALID_PARAMS);
997
998 hci_dev_lock(hdev);
999
1000 if (!test_bit(HCI_UP, &hdev->flags)) {
1001 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1002 MGMT_STATUS_NOT_POWERED);
1003 goto failed;
1004 }
1005
1006 if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
1007 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1008 MGMT_STATUS_BUSY);
1009 goto failed;
1010 }
1011
1012 val = !!cp->val;
1013
1014 if (test_bit(HCI_AUTH, &hdev->flags) == val) {
1015 err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
1016 goto failed;
1017 }
1018
1019 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len);
1020 if (!cmd) {
1021 err = -ENOMEM;
1022 goto failed;
1023 }
1024
1025 err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val);
1026 if (err < 0) {
1027 mgmt_pending_remove(cmd);
1028 goto failed;
1029 }
1030
1031failed:
1032 hci_dev_unlock(hdev);
1033 hci_dev_put(hdev);
1034
1035 return err;
1036}
1037
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001038static int set_ssp(struct sock *sk, u16 index, void *data, u16 len)
1039{
1040 struct mgmt_mode *cp = data;
1041 struct pending_cmd *cmd;
1042 struct hci_dev *hdev;
1043 uint8_t val;
1044 int err;
1045
1046 BT_DBG("request for hci%u", index);
1047
1048 if (len != sizeof(*cp))
1049 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1050 MGMT_STATUS_INVALID_PARAMS);
1051
1052 hdev = hci_dev_get(index);
1053 if (!hdev)
1054 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1055 MGMT_STATUS_INVALID_PARAMS);
1056
1057 hci_dev_lock(hdev);
1058
1059 if (!test_bit(HCI_UP, &hdev->flags)) {
1060 err = cmd_status(sk, index, MGMT_OP_SET_SSP,
1061 MGMT_STATUS_NOT_POWERED);
1062 goto failed;
1063 }
1064
1065 if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
1066 err = cmd_status(sk, index, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY);
1067 goto failed;
1068 }
1069
1070 val = !!cp->val;
1071
1072 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
1073 err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
1074 goto failed;
1075 }
1076
1077 cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len);
1078 if (!cmd) {
1079 err = -ENOMEM;
1080 goto failed;
1081 }
1082
1083 err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
1084 if (err < 0) {
1085 mgmt_pending_remove(cmd);
1086 goto failed;
1087 }
1088
1089failed:
1090 hci_dev_unlock(hdev);
1091 hci_dev_put(hdev);
1092
1093 return err;
1094}
1095
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001096static int add_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001097{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001098 struct mgmt_cp_add_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001099 struct hci_dev *hdev;
1100 struct bt_uuid *uuid;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001101 int err;
1102
Szymon Janc4e51eae2011-02-25 19:05:48 +01001103 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001104
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001105 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001106 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1107 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001108
Szymon Janc4e51eae2011-02-25 19:05:48 +01001109 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001110 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001111 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1112 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001113
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001114 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001115
1116 uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
1117 if (!uuid) {
1118 err = -ENOMEM;
1119 goto failed;
1120 }
1121
1122 memcpy(uuid->uuid, cp->uuid, 16);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001123 uuid->svc_hint = cp->svc_hint;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001124
1125 list_add(&uuid->list, &hdev->uuids);
1126
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001127 err = update_class(hdev);
1128 if (err < 0)
1129 goto failed;
1130
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001131 err = update_eir(hdev);
1132 if (err < 0)
1133 goto failed;
1134
Johan Hedbergaee9b212012-02-18 15:07:59 +02001135 err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, 0, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001136
1137failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001138 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001139 hci_dev_put(hdev);
1140
1141 return err;
1142}
1143
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001144static int remove_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001145{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001146 struct mgmt_cp_remove_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001147 struct list_head *p, *n;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001148 struct hci_dev *hdev;
1149 u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001150 int err, found;
1151
Szymon Janc4e51eae2011-02-25 19:05:48 +01001152 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001153
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001154 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001155 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1156 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001157
Szymon Janc4e51eae2011-02-25 19:05:48 +01001158 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001159 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001160 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1161 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001162
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001163 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001164
1165 if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
1166 err = hci_uuids_clear(hdev);
1167 goto unlock;
1168 }
1169
1170 found = 0;
1171
1172 list_for_each_safe(p, n, &hdev->uuids) {
1173 struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
1174
1175 if (memcmp(match->uuid, cp->uuid, 16) != 0)
1176 continue;
1177
1178 list_del(&match->list);
1179 found++;
1180 }
1181
1182 if (found == 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001183 err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1184 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001185 goto unlock;
1186 }
1187
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001188 err = update_class(hdev);
1189 if (err < 0)
1190 goto unlock;
1191
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001192 err = update_eir(hdev);
1193 if (err < 0)
1194 goto unlock;
1195
Johan Hedbergaee9b212012-02-18 15:07:59 +02001196 err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, 0, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001197
1198unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001199 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001200 hci_dev_put(hdev);
1201
1202 return err;
1203}
1204
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001205static int set_dev_class(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001206{
1207 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001208 struct mgmt_cp_set_dev_class *cp = data;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001209 int err;
1210
Szymon Janc4e51eae2011-02-25 19:05:48 +01001211 BT_DBG("request for hci%u", index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001212
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001213 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001214 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1215 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001216
Szymon Janc4e51eae2011-02-25 19:05:48 +01001217 hdev = hci_dev_get(index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001218 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001219 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1220 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001221
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001222 hci_dev_lock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001223
1224 hdev->major_class = cp->major;
1225 hdev->minor_class = cp->minor;
1226
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001227 if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
Johan Hedberg7d785252011-12-15 00:47:39 +02001228 hci_dev_unlock(hdev);
1229 cancel_delayed_work_sync(&hdev->service_cache);
1230 hci_dev_lock(hdev);
Johan Hedberg14c0b602011-12-15 00:47:37 +02001231 update_eir(hdev);
Johan Hedberg7d785252011-12-15 00:47:39 +02001232 }
Johan Hedberg14c0b602011-12-15 00:47:37 +02001233
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001234 err = update_class(hdev);
1235
1236 if (err == 0)
Johan Hedbergaee9b212012-02-18 15:07:59 +02001237 err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, 0,
1238 NULL, 0);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001239
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001240 hci_dev_unlock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001241 hci_dev_put(hdev);
1242
1243 return err;
1244}
1245
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001246static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001247{
1248 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001249 struct mgmt_cp_load_link_keys *cp = data;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001250 u16 key_count, expected_len;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001251 int i;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001252
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001253 if (len < sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001254 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1255 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001256
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001257 key_count = get_unaligned_le16(&cp->key_count);
1258
Johan Hedberg86742e12011-11-07 23:13:38 +02001259 expected_len = sizeof(*cp) + key_count *
1260 sizeof(struct mgmt_link_key_info);
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001261 if (expected_len != len) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001262 BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001263 len, expected_len);
Johan Hedbergca69b792011-11-11 18:10:00 +02001264 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1265 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001266 }
1267
Szymon Janc4e51eae2011-02-25 19:05:48 +01001268 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001269 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001270 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1271 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001272
Szymon Janc4e51eae2011-02-25 19:05:48 +01001273 BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001274 key_count);
1275
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001276 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001277
1278 hci_link_keys_clear(hdev);
1279
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001280 set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001281
1282 if (cp->debug_keys)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001283 set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001284 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001285 clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001286
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001287 for (i = 0; i < key_count; i++) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001288 struct mgmt_link_key_info *key = &cp->keys[i];
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001289
Johan Hedbergd753fdc2012-02-17 14:06:34 +02001290 hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
1291 key->type, key->pin_len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001292 }
1293
Johan Hedbergaee9b212012-02-18 15:07:59 +02001294 cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
Johan Hedberg0e5f8752011-11-11 16:18:54 +02001295
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001296 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001297 hci_dev_put(hdev);
1298
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001299 return 0;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001300}
1301
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001302static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
1303 u8 addr_type, struct sock *skip_sk)
1304{
1305 struct mgmt_ev_device_unpaired ev;
1306
1307 bacpy(&ev.addr.bdaddr, bdaddr);
1308 ev.addr.type = addr_type;
1309
1310 return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev),
1311 skip_sk);
1312}
1313
Johan Hedberg124f6e32012-02-09 13:50:12 +02001314static int unpair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001315{
1316 struct hci_dev *hdev;
Johan Hedberg124f6e32012-02-09 13:50:12 +02001317 struct mgmt_cp_unpair_device *cp = data;
1318 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001319 struct hci_cp_disconnect dc;
1320 struct pending_cmd *cmd;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001321 struct hci_conn *conn;
Johan Hedbergaee9b212012-02-18 15:07:59 +02001322 u8 status = 0;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001323 int err;
1324
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001325 if (len != sizeof(*cp))
Johan Hedberg124f6e32012-02-09 13:50:12 +02001326 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001327 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001328
Szymon Janc4e51eae2011-02-25 19:05:48 +01001329 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001330 if (!hdev)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001331 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001332 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001333
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001334 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001335
Johan Hedberga8a1d192011-11-10 15:54:38 +02001336 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02001337 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1338 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001339
Johan Hedberg124f6e32012-02-09 13:50:12 +02001340 if (cp->addr.type == MGMT_ADDR_BREDR)
1341 err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
1342 else
1343 err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
Vinicius Costa Gomesb0dbfb42012-02-02 21:08:03 -03001344
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001345 if (err < 0) {
Johan Hedbergaee9b212012-02-18 15:07:59 +02001346 status = MGMT_STATUS_NOT_PAIRED;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001347 goto unlock;
1348 }
1349
Johan Hedberga8a1d192011-11-10 15:54:38 +02001350 if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
Johan Hedbergaee9b212012-02-18 15:07:59 +02001351 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1352 &rp, sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001353 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001354 goto unlock;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001355 }
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001356
Johan Hedberg124f6e32012-02-09 13:50:12 +02001357 if (cp->addr.type == MGMT_ADDR_BREDR)
1358 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
1359 &cp->addr.bdaddr);
1360 else
1361 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
1362 &cp->addr.bdaddr);
1363
Johan Hedberga8a1d192011-11-10 15:54:38 +02001364 if (!conn) {
Johan Hedbergaee9b212012-02-18 15:07:59 +02001365 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1366 &rp, sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001367 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberga8a1d192011-11-10 15:54:38 +02001368 goto unlock;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001369 }
1370
Johan Hedberg124f6e32012-02-09 13:50:12 +02001371 cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
1372 sizeof(*cp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02001373 if (!cmd) {
1374 err = -ENOMEM;
1375 goto unlock;
1376 }
1377
1378 put_unaligned_le16(conn->handle, &dc.handle);
1379 dc.reason = 0x13; /* Remote User Terminated Connection */
1380 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1381 if (err < 0)
1382 mgmt_pending_remove(cmd);
1383
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001384unlock:
Johan Hedbergca69b792011-11-11 18:10:00 +02001385 if (err < 0)
Johan Hedbergaee9b212012-02-18 15:07:59 +02001386 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1387 &rp, sizeof(rp));
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001388 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001389 hci_dev_put(hdev);
1390
1391 return err;
1392}
1393
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001394static int disconnect(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg8962ee72011-01-20 12:40:27 +02001395{
1396 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001397 struct mgmt_cp_disconnect *cp = data;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001398 struct hci_cp_disconnect dc;
Johan Hedberg366a0332011-02-19 12:05:55 -03001399 struct pending_cmd *cmd;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001400 struct hci_conn *conn;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001401 int err;
1402
1403 BT_DBG("");
1404
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001405 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001406 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1407 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001408
Szymon Janc4e51eae2011-02-25 19:05:48 +01001409 hdev = hci_dev_get(index);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001410 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001411 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1412 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001413
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001414 hci_dev_lock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001415
1416 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001417 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1418 MGMT_STATUS_NOT_POWERED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001419 goto failed;
1420 }
1421
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001422 if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001423 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1424 MGMT_STATUS_BUSY);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001425 goto failed;
1426 }
1427
Johan Hedberg88c3df12012-02-09 14:27:38 +02001428 if (cp->addr.type == MGMT_ADDR_BREDR)
1429 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
1430 else
1431 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
Vinicius Costa Gomes365227e2011-05-06 18:41:44 -03001432
Johan Hedberg8962ee72011-01-20 12:40:27 +02001433 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001434 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1435 MGMT_STATUS_NOT_CONNECTED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001436 goto failed;
1437 }
1438
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001439 cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001440 if (!cmd) {
1441 err = -ENOMEM;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001442 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001443 }
Johan Hedberg8962ee72011-01-20 12:40:27 +02001444
1445 put_unaligned_le16(conn->handle, &dc.handle);
1446 dc.reason = 0x13; /* Remote User Terminated Connection */
1447
1448 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1449 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001450 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001451
1452failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001453 hci_dev_unlock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001454 hci_dev_put(hdev);
1455
1456 return err;
1457}
1458
Johan Hedberg48264f02011-11-09 13:58:58 +02001459static u8 link_to_mgmt(u8 link_type, u8 addr_type)
Johan Hedberg4c659c32011-11-07 23:13:39 +02001460{
1461 switch (link_type) {
1462 case LE_LINK:
Johan Hedberg48264f02011-11-09 13:58:58 +02001463 switch (addr_type) {
1464 case ADDR_LE_DEV_PUBLIC:
1465 return MGMT_ADDR_LE_PUBLIC;
1466 case ADDR_LE_DEV_RANDOM:
1467 return MGMT_ADDR_LE_RANDOM;
1468 default:
1469 return MGMT_ADDR_INVALID;
1470 }
Johan Hedberg4c659c32011-11-07 23:13:39 +02001471 case ACL_LINK:
1472 return MGMT_ADDR_BREDR;
1473 default:
1474 return MGMT_ADDR_INVALID;
1475 }
1476}
1477
Szymon Janc8ce62842011-03-01 16:55:32 +01001478static int get_connections(struct sock *sk, u16 index)
Johan Hedberg2784eb42011-01-21 13:56:35 +02001479{
Johan Hedberg2784eb42011-01-21 13:56:35 +02001480 struct mgmt_rp_get_connections *rp;
1481 struct hci_dev *hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001482 struct hci_conn *c;
Johan Hedberga38528f2011-01-22 06:46:43 +02001483 size_t rp_len;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001484 u16 count;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001485 int i, err;
1486
1487 BT_DBG("");
1488
Szymon Janc4e51eae2011-02-25 19:05:48 +01001489 hdev = hci_dev_get(index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001490 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001491 return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
1492 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001493
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001494 hci_dev_lock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001495
1496 count = 0;
Johan Hedbergb644ba32012-01-17 21:48:47 +02001497 list_for_each_entry(c, &hdev->conn_hash.list, list) {
1498 if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1499 count++;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001500 }
1501
Johan Hedberg4c659c32011-11-07 23:13:39 +02001502 rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
Johan Hedberga38528f2011-01-22 06:46:43 +02001503 rp = kmalloc(rp_len, GFP_ATOMIC);
1504 if (!rp) {
Johan Hedberg2784eb42011-01-21 13:56:35 +02001505 err = -ENOMEM;
1506 goto unlock;
1507 }
1508
Johan Hedberg2784eb42011-01-21 13:56:35 +02001509 put_unaligned_le16(count, &rp->conn_count);
1510
Johan Hedberg2784eb42011-01-21 13:56:35 +02001511 i = 0;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001512 list_for_each_entry(c, &hdev->conn_hash.list, list) {
Johan Hedbergb644ba32012-01-17 21:48:47 +02001513 if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1514 continue;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001515 bacpy(&rp->addr[i].bdaddr, &c->dst);
Johan Hedberg48264f02011-11-09 13:58:58 +02001516 rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
Johan Hedberg4c659c32011-11-07 23:13:39 +02001517 if (rp->addr[i].type == MGMT_ADDR_INVALID)
1518 continue;
1519 i++;
1520 }
1521
1522 /* Recalculate length in case of filtered SCO connections, etc */
1523 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberg2784eb42011-01-21 13:56:35 +02001524
Johan Hedbergaee9b212012-02-18 15:07:59 +02001525 err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, 0, rp, rp_len);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001526
1527unlock:
Johan Hedberga38528f2011-01-22 06:46:43 +02001528 kfree(rp);
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001529 hci_dev_unlock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001530 hci_dev_put(hdev);
1531 return err;
1532}
1533
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001534static int send_pin_code_neg_reply(struct sock *sk, u16 index,
1535 struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
1536{
1537 struct pending_cmd *cmd;
1538 int err;
1539
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001540 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001541 sizeof(*cp));
1542 if (!cmd)
1543 return -ENOMEM;
1544
Johan Hedbergd8457692012-02-17 14:24:57 +02001545 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
1546 sizeof(cp->addr.bdaddr), &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001547 if (err < 0)
1548 mgmt_pending_remove(cmd);
1549
1550 return err;
1551}
1552
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001553static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001554{
1555 struct hci_dev *hdev;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001556 struct hci_conn *conn;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001557 struct mgmt_cp_pin_code_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001558 struct hci_cp_pin_code_reply reply;
Johan Hedberg366a0332011-02-19 12:05:55 -03001559 struct pending_cmd *cmd;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001560 int err;
1561
1562 BT_DBG("");
1563
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001564 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001565 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1566 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001567
Szymon Janc4e51eae2011-02-25 19:05:48 +01001568 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001569 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001570 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1571 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001572
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001573 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001574
1575 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001576 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1577 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001578 goto failed;
1579 }
1580
Johan Hedbergd8457692012-02-17 14:24:57 +02001581 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001582 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001583 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1584 MGMT_STATUS_NOT_CONNECTED);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001585 goto failed;
1586 }
1587
1588 if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
Johan Hedbergd8457692012-02-17 14:24:57 +02001589 struct mgmt_cp_pin_code_neg_reply ncp;
1590
1591 memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr));
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001592
1593 BT_ERR("PIN code is not 16 bytes long");
1594
1595 err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
1596 if (err >= 0)
1597 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001598 MGMT_STATUS_INVALID_PARAMS);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001599
1600 goto failed;
1601 }
1602
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001603 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data,
1604 len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001605 if (!cmd) {
1606 err = -ENOMEM;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001607 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001608 }
Johan Hedberg980e1a52011-01-22 06:10:07 +02001609
Johan Hedbergd8457692012-02-17 14:24:57 +02001610 bacpy(&reply.bdaddr, &cp->addr.bdaddr);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001611 reply.pin_len = cp->pin_len;
Waldemar Rymarkiewicz24718ca2011-06-01 17:28:47 +02001612 memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
Johan Hedberg980e1a52011-01-22 06:10:07 +02001613
1614 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
1615 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001616 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001617
1618failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001619 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001620 hci_dev_put(hdev);
1621
1622 return err;
1623}
1624
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001625static int pin_code_neg_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001626{
1627 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001628 struct mgmt_cp_pin_code_neg_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001629 int err;
1630
1631 BT_DBG("");
1632
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001633 if (len != sizeof(*cp))
1634 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001635 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001636
Szymon Janc4e51eae2011-02-25 19:05:48 +01001637 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001638 if (!hdev)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001639 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001640 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001641
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001642 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001643
1644 if (!test_bit(HCI_UP, &hdev->flags)) {
Szymon Janc4e51eae2011-02-25 19:05:48 +01001645 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001646 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001647 goto failed;
1648 }
1649
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001650 err = send_pin_code_neg_reply(sk, index, hdev, cp);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001651
1652failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001653 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001654 hci_dev_put(hdev);
1655
1656 return err;
1657}
1658
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001659static int set_io_capability(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001660{
1661 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001662 struct mgmt_cp_set_io_capability *cp = data;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001663
1664 BT_DBG("");
1665
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001666 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001667 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1668 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001669
Szymon Janc4e51eae2011-02-25 19:05:48 +01001670 hdev = hci_dev_get(index);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001671 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001672 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1673 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001674
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001675 hci_dev_lock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001676
1677 hdev->io_capability = cp->io_capability;
1678
1679 BT_DBG("%s IO capability set to 0x%02x", hdev->name,
Szymon Jancb8534e02011-03-01 16:55:34 +01001680 hdev->io_capability);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001681
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001682 hci_dev_unlock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001683 hci_dev_put(hdev);
1684
Johan Hedbergaee9b212012-02-18 15:07:59 +02001685 return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, 0);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001686}
1687
Johan Hedberge9a416b2011-02-19 12:05:56 -03001688static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1689{
1690 struct hci_dev *hdev = conn->hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001691 struct pending_cmd *cmd;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001692
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001693 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
Johan Hedberge9a416b2011-02-19 12:05:56 -03001694 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1695 continue;
1696
Johan Hedberge9a416b2011-02-19 12:05:56 -03001697 if (cmd->user_data != conn)
1698 continue;
1699
1700 return cmd;
1701 }
1702
1703 return NULL;
1704}
1705
1706static void pairing_complete(struct pending_cmd *cmd, u8 status)
1707{
1708 struct mgmt_rp_pair_device rp;
1709 struct hci_conn *conn = cmd->user_data;
1710
Johan Hedbergba4e5642011-11-11 00:07:34 +02001711 bacpy(&rp.addr.bdaddr, &conn->dst);
1712 rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001713
Johan Hedbergaee9b212012-02-18 15:07:59 +02001714 cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
1715 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001716
1717 /* So we don't get further callbacks for this connection */
1718 conn->connect_cfm_cb = NULL;
1719 conn->security_cfm_cb = NULL;
1720 conn->disconn_cfm_cb = NULL;
1721
1722 hci_conn_put(conn);
1723
Johan Hedberga664b5b2011-02-19 12:06:02 -03001724 mgmt_pending_remove(cmd);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001725}
1726
1727static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1728{
1729 struct pending_cmd *cmd;
1730
1731 BT_DBG("status %u", status);
1732
Johan Hedberg56e5cb82011-11-08 20:40:16 +02001733 cmd = find_pairing(conn);
1734 if (!cmd)
1735 BT_DBG("Unable to find a pending command");
1736 else
Johan Hedberge2113262012-02-18 15:20:03 +02001737 pairing_complete(cmd, mgmt_status(status));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001738}
1739
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001740static int pair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001741{
1742 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001743 struct mgmt_cp_pair_device *cp = data;
Johan Hedberg1425acb2011-11-11 00:07:35 +02001744 struct mgmt_rp_pair_device rp;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001745 struct pending_cmd *cmd;
1746 u8 sec_level, auth_type;
1747 struct hci_conn *conn;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001748 int err;
1749
1750 BT_DBG("");
1751
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001752 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001753 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1754 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001755
Szymon Janc4e51eae2011-02-25 19:05:48 +01001756 hdev = hci_dev_get(index);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001757 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001758 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1759 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001760
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001761 hci_dev_lock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001762
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001763 sec_level = BT_SECURITY_MEDIUM;
1764 if (cp->io_cap == 0x03)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001765 auth_type = HCI_AT_DEDICATED_BONDING;
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001766 else
Johan Hedberge9a416b2011-02-19 12:05:56 -03001767 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001768
Johan Hedbergba4e5642011-11-11 00:07:34 +02001769 if (cp->addr.type == MGMT_ADDR_BREDR)
1770 conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001771 auth_type);
1772 else
Johan Hedbergba4e5642011-11-11 00:07:34 +02001773 conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001774 auth_type);
1775
Johan Hedberg1425acb2011-11-11 00:07:35 +02001776 memset(&rp, 0, sizeof(rp));
1777 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1778 rp.addr.type = cp->addr.type;
1779
Ville Tervo30e76272011-02-22 16:10:53 -03001780 if (IS_ERR(conn)) {
Johan Hedberge2113262012-02-18 15:20:03 +02001781 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1782 MGMT_STATUS_CONNECT_FAILED,
1783 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001784 goto unlock;
1785 }
1786
1787 if (conn->connect_cfm_cb) {
1788 hci_conn_put(conn);
Johan Hedberge2113262012-02-18 15:20:03 +02001789 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1790 MGMT_STATUS_BUSY, &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001791 goto unlock;
1792 }
1793
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001794 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001795 if (!cmd) {
1796 err = -ENOMEM;
1797 hci_conn_put(conn);
1798 goto unlock;
1799 }
1800
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001801 /* For LE, just connecting isn't a proof that the pairing finished */
Johan Hedbergba4e5642011-11-11 00:07:34 +02001802 if (cp->addr.type == MGMT_ADDR_BREDR)
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001803 conn->connect_cfm_cb = pairing_complete_cb;
1804
Johan Hedberge9a416b2011-02-19 12:05:56 -03001805 conn->security_cfm_cb = pairing_complete_cb;
1806 conn->disconn_cfm_cb = pairing_complete_cb;
1807 conn->io_capability = cp->io_cap;
1808 cmd->user_data = conn;
1809
1810 if (conn->state == BT_CONNECTED &&
1811 hci_conn_security(conn, sec_level, auth_type))
1812 pairing_complete(cmd, 0);
1813
1814 err = 0;
1815
1816unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001817 hci_dev_unlock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001818 hci_dev_put(hdev);
1819
1820 return err;
1821}
1822
Johan Hedberg28424702012-02-02 04:02:29 +02001823static int cancel_pair_device(struct sock *sk, u16 index,
1824 unsigned char *data, u16 len)
1825{
1826 struct mgmt_addr_info *addr = (void *) data;
1827 struct hci_dev *hdev;
1828 struct pending_cmd *cmd;
1829 struct hci_conn *conn;
1830 int err;
1831
1832 BT_DBG("");
1833
1834 if (len != sizeof(*addr))
1835 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1836 MGMT_STATUS_INVALID_PARAMS);
1837
1838 hdev = hci_dev_get(index);
1839 if (!hdev)
1840 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1841 MGMT_STATUS_INVALID_PARAMS);
1842
1843 hci_dev_lock(hdev);
1844
1845 cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
1846 if (!cmd) {
1847 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1848 MGMT_STATUS_INVALID_PARAMS);
1849 goto unlock;
1850 }
1851
1852 conn = cmd->user_data;
1853
1854 if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
1855 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1856 MGMT_STATUS_INVALID_PARAMS);
1857 goto unlock;
1858 }
1859
1860 pairing_complete(cmd, MGMT_STATUS_CANCELLED);
1861
Johan Hedbergaee9b212012-02-18 15:07:59 +02001862 err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr,
Johan Hedberg28424702012-02-02 04:02:29 +02001863 sizeof(*addr));
1864unlock:
1865 hci_dev_unlock(hdev);
1866 hci_dev_put(hdev);
1867
1868 return err;
1869}
1870
Brian Gix0df4c182011-11-16 13:53:13 -08001871static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02001872 u8 type, u16 mgmt_op, u16 hci_op,
1873 __le32 passkey)
Johan Hedberga5c29682011-02-19 12:05:57 -03001874{
Johan Hedberga5c29682011-02-19 12:05:57 -03001875 struct pending_cmd *cmd;
1876 struct hci_dev *hdev;
Brian Gix0df4c182011-11-16 13:53:13 -08001877 struct hci_conn *conn;
Johan Hedberga5c29682011-02-19 12:05:57 -03001878 int err;
1879
Szymon Janc4e51eae2011-02-25 19:05:48 +01001880 hdev = hci_dev_get(index);
Johan Hedberga5c29682011-02-19 12:05:57 -03001881 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001882 return cmd_status(sk, index, mgmt_op,
1883 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberga5c29682011-02-19 12:05:57 -03001884
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001885 hci_dev_lock(hdev);
Johan Hedberg08ba5382011-03-16 14:29:34 +02001886
Johan Hedberga5c29682011-02-19 12:05:57 -03001887 if (!test_bit(HCI_UP, &hdev->flags)) {
Brian Gix0df4c182011-11-16 13:53:13 -08001888 err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
1889 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001890 }
1891
Johan Hedberg272d90d2012-02-09 15:26:12 +02001892 if (type == MGMT_ADDR_BREDR)
1893 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
1894 else
Brian Gix47c15e22011-11-16 13:53:14 -08001895 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
Brian Gix47c15e22011-11-16 13:53:14 -08001896
Johan Hedberg272d90d2012-02-09 15:26:12 +02001897 if (!conn) {
1898 err = cmd_status(sk, index, mgmt_op,
1899 MGMT_STATUS_NOT_CONNECTED);
1900 goto done;
1901 }
1902
1903 if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
Brian Gix47c15e22011-11-16 13:53:14 -08001904 /* Continue with pairing via SMP */
Brian Gix5fe57d92011-12-21 16:12:13 -08001905 err = smp_user_confirm_reply(conn, mgmt_op, passkey);
Brian Gix47c15e22011-11-16 13:53:14 -08001906
Brian Gix5fe57d92011-12-21 16:12:13 -08001907 if (!err)
1908 err = cmd_status(sk, index, mgmt_op,
1909 MGMT_STATUS_SUCCESS);
1910 else
1911 err = cmd_status(sk, index, mgmt_op,
1912 MGMT_STATUS_FAILED);
1913
Brian Gix47c15e22011-11-16 13:53:14 -08001914 goto done;
1915 }
1916
Brian Gix0df4c182011-11-16 13:53:13 -08001917 cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
Johan Hedberga5c29682011-02-19 12:05:57 -03001918 if (!cmd) {
1919 err = -ENOMEM;
Brian Gix0df4c182011-11-16 13:53:13 -08001920 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001921 }
1922
Brian Gix0df4c182011-11-16 13:53:13 -08001923 /* Continue with pairing via HCI */
Brian Gix604086b2011-11-23 08:28:33 -08001924 if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
1925 struct hci_cp_user_passkey_reply cp;
1926
1927 bacpy(&cp.bdaddr, bdaddr);
1928 cp.passkey = passkey;
1929 err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
1930 } else
1931 err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
1932
Johan Hedberga664b5b2011-02-19 12:06:02 -03001933 if (err < 0)
1934 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03001935
Brian Gix0df4c182011-11-16 13:53:13 -08001936done:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001937 hci_dev_unlock(hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03001938 hci_dev_put(hdev);
1939
1940 return err;
1941}
1942
Brian Gix0df4c182011-11-16 13:53:13 -08001943static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
1944{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001945 struct mgmt_cp_user_confirm_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001946
1947 BT_DBG("");
1948
1949 if (len != sizeof(*cp))
1950 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
1951 MGMT_STATUS_INVALID_PARAMS);
1952
Johan Hedberg272d90d2012-02-09 15:26:12 +02001953 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1954 MGMT_OP_USER_CONFIRM_REPLY,
1955 HCI_OP_USER_CONFIRM_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001956}
1957
1958static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
1959 u16 len)
1960{
Johan Hedbergc9c26592011-12-15 00:47:41 +02001961 struct mgmt_cp_user_confirm_neg_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001962
1963 BT_DBG("");
1964
1965 if (len != sizeof(*cp))
1966 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
1967 MGMT_STATUS_INVALID_PARAMS);
1968
Johan Hedberg272d90d2012-02-09 15:26:12 +02001969 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1970 MGMT_OP_USER_CONFIRM_NEG_REPLY,
1971 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001972}
1973
Brian Gix604086b2011-11-23 08:28:33 -08001974static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
1975{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001976 struct mgmt_cp_user_passkey_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001977
1978 BT_DBG("");
1979
1980 if (len != sizeof(*cp))
1981 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
1982 EINVAL);
1983
Johan Hedberg272d90d2012-02-09 15:26:12 +02001984 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1985 MGMT_OP_USER_PASSKEY_REPLY,
1986 HCI_OP_USER_PASSKEY_REPLY,
1987 cp->passkey);
Brian Gix604086b2011-11-23 08:28:33 -08001988}
1989
1990static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
1991 u16 len)
1992{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001993 struct mgmt_cp_user_passkey_neg_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001994
1995 BT_DBG("");
1996
1997 if (len != sizeof(*cp))
1998 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
1999 EINVAL);
2000
Johan Hedberg272d90d2012-02-09 15:26:12 +02002001 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2002 MGMT_OP_USER_PASSKEY_NEG_REPLY,
2003 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
Brian Gix604086b2011-11-23 08:28:33 -08002004}
2005
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002006static int set_local_name(struct sock *sk, u16 index, void *data,
Johan Hedbergb312b1612011-03-16 14:29:37 +02002007 u16 len)
2008{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002009 struct mgmt_cp_set_local_name *mgmt_cp = data;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002010 struct hci_cp_write_local_name hci_cp;
2011 struct hci_dev *hdev;
2012 struct pending_cmd *cmd;
2013 int err;
2014
2015 BT_DBG("");
2016
2017 if (len != sizeof(*mgmt_cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02002018 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2019 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002020
2021 hdev = hci_dev_get(index);
2022 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002023 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2024 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002025
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002026 hci_dev_lock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002027
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002028 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data,
2029 len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002030 if (!cmd) {
2031 err = -ENOMEM;
2032 goto failed;
2033 }
2034
2035 memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
2036 err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
2037 &hci_cp);
2038 if (err < 0)
2039 mgmt_pending_remove(cmd);
2040
2041failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002042 hci_dev_unlock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002043 hci_dev_put(hdev);
2044
2045 return err;
2046}
2047
Szymon Jancc35938b2011-03-22 13:12:21 +01002048static int read_local_oob_data(struct sock *sk, u16 index)
2049{
2050 struct hci_dev *hdev;
2051 struct pending_cmd *cmd;
2052 int err;
2053
2054 BT_DBG("hci%u", index);
2055
2056 hdev = hci_dev_get(index);
2057 if (!hdev)
2058 return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002059 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancc35938b2011-03-22 13:12:21 +01002060
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002061 hci_dev_lock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002062
2063 if (!test_bit(HCI_UP, &hdev->flags)) {
2064 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002065 MGMT_STATUS_NOT_POWERED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002066 goto unlock;
2067 }
2068
2069 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
2070 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002071 MGMT_STATUS_NOT_SUPPORTED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002072 goto unlock;
2073 }
2074
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002075 if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002076 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
2077 MGMT_STATUS_BUSY);
Szymon Jancc35938b2011-03-22 13:12:21 +01002078 goto unlock;
2079 }
2080
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002081 cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
Szymon Jancc35938b2011-03-22 13:12:21 +01002082 if (!cmd) {
2083 err = -ENOMEM;
2084 goto unlock;
2085 }
2086
2087 err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
2088 if (err < 0)
2089 mgmt_pending_remove(cmd);
2090
2091unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002092 hci_dev_unlock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002093 hci_dev_put(hdev);
2094
2095 return err;
2096}
2097
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002098static int add_remote_oob_data(struct sock *sk, u16 index, void *data,
2099 u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002100{
2101 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002102 struct mgmt_cp_add_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002103 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002104 int err;
2105
2106 BT_DBG("hci%u ", index);
2107
2108 if (len != sizeof(*cp))
2109 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002110 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002111
2112 hdev = hci_dev_get(index);
2113 if (!hdev)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002114 return cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
2115 MGMT_STATUS_INVALID_PARAMS,
2116 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002117
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002118 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002119
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002120 err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
Szymon Janc2763eda2011-03-22 13:12:22 +01002121 cp->randomizer);
2122 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002123 status = MGMT_STATUS_FAILED;
Szymon Janc2763eda2011-03-22 13:12:22 +01002124 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002125 status = 0;
2126
2127 err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
2128 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002129
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002130 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002131 hci_dev_put(hdev);
2132
2133 return err;
2134}
2135
2136static int remove_remote_oob_data(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002137 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002138{
2139 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002140 struct mgmt_cp_remove_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002141 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002142 int err;
2143
2144 BT_DBG("hci%u ", index);
2145
2146 if (len != sizeof(*cp))
2147 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002148 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002149
2150 hdev = hci_dev_get(index);
2151 if (!hdev)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002152 return cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
2153 MGMT_STATUS_INVALID_PARAMS,
2154 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002155
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002156 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002157
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002158 err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
Szymon Janc2763eda2011-03-22 13:12:22 +01002159 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002160 status = MGMT_STATUS_INVALID_PARAMS;
Szymon Janc2763eda2011-03-22 13:12:22 +01002161 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002162 status = 0;
2163
2164 err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status,
2165 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002166
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002167 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002168 hci_dev_put(hdev);
2169
2170 return err;
2171}
2172
Andre Guedes5e0452c2012-02-17 20:39:38 -03002173static int discovery(struct hci_dev *hdev)
2174{
2175 int err;
2176
2177 if (lmp_host_le_capable(hdev)) {
2178 if (lmp_bredr_capable(hdev)) {
2179 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2180 LE_SCAN_INT, LE_SCAN_WIN,
2181 LE_SCAN_TIMEOUT_BREDR_LE);
2182 } else {
2183 hdev->discovery.type = DISCOV_TYPE_LE;
2184 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2185 LE_SCAN_INT, LE_SCAN_WIN,
2186 LE_SCAN_TIMEOUT_LE_ONLY);
2187 }
2188 } else {
2189 hdev->discovery.type = DISCOV_TYPE_BREDR;
2190 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
2191 }
2192
2193 return err;
2194}
2195
2196int mgmt_interleaved_discovery(struct hci_dev *hdev)
2197{
2198 int err;
2199
2200 BT_DBG("%s", hdev->name);
2201
2202 hci_dev_lock(hdev);
2203
2204 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
2205 if (err < 0)
2206 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2207
2208 hci_dev_unlock(hdev);
2209
2210 return err;
2211}
2212
Johan Hedberg450dfda2011-11-12 11:58:22 +02002213static int start_discovery(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002214 void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002215{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002216 struct mgmt_cp_start_discovery *cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002217 struct pending_cmd *cmd;
2218 struct hci_dev *hdev;
2219 int err;
2220
2221 BT_DBG("hci%u", index);
2222
Johan Hedberg450dfda2011-11-12 11:58:22 +02002223 if (len != sizeof(*cp))
2224 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2225 MGMT_STATUS_INVALID_PARAMS);
2226
Johan Hedberg14a53662011-04-27 10:29:56 -04002227 hdev = hci_dev_get(index);
2228 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002229 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2230 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002231
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002232 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002233
Johan Hedbergbd2d1332011-11-07 23:13:37 +02002234 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002235 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2236 MGMT_STATUS_NOT_POWERED);
Johan Hedbergbd2d1332011-11-07 23:13:37 +02002237 goto failed;
2238 }
2239
Johan Hedbergff9ef572012-01-04 14:23:45 +02002240 if (hdev->discovery.state != DISCOVERY_STOPPED) {
2241 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2242 MGMT_STATUS_BUSY);
2243 goto failed;
2244 }
2245
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002246 cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002247 if (!cmd) {
2248 err = -ENOMEM;
2249 goto failed;
2250 }
2251
Andre Guedes4aab14e2012-02-17 20:39:36 -03002252 hdev->discovery.type = cp->type;
2253
2254 switch (hdev->discovery.type) {
Andre Guedesf39799f2012-02-17 20:39:35 -03002255 case DISCOV_TYPE_BREDR:
Andre Guedes3fd24152012-02-03 17:48:01 -03002256 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
Andre Guedesf39799f2012-02-17 20:39:35 -03002257 break;
2258
2259 case DISCOV_TYPE_LE:
Andre Guedes3fd24152012-02-03 17:48:01 -03002260 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
2261 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
Andre Guedesf39799f2012-02-17 20:39:35 -03002262 break;
2263
Andre Guedes5e0452c2012-02-17 20:39:38 -03002264 case DISCOV_TYPE_INTERLEAVED:
2265 err = discovery(hdev);
2266 break;
2267
Andre Guedesf39799f2012-02-17 20:39:35 -03002268 default:
Andre Guedes3fd24152012-02-03 17:48:01 -03002269 err = -EINVAL;
Andre Guedesf39799f2012-02-17 20:39:35 -03002270 }
Andre Guedes3fd24152012-02-03 17:48:01 -03002271
Johan Hedberg14a53662011-04-27 10:29:56 -04002272 if (err < 0)
2273 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002274 else
2275 hci_discovery_set_state(hdev, DISCOVERY_STARTING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002276
2277failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002278 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002279 hci_dev_put(hdev);
2280
2281 return err;
2282}
2283
Johan Hedbergd9306502012-02-20 23:25:18 +02002284static int stop_discovery(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002285{
Johan Hedbergd9306502012-02-20 23:25:18 +02002286 struct mgmt_cp_stop_discovery *mgmt_cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002287 struct hci_dev *hdev;
2288 struct pending_cmd *cmd;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002289 struct hci_cp_remote_name_req_cancel cp;
2290 struct inquiry_entry *e;
Johan Hedberg14a53662011-04-27 10:29:56 -04002291 int err;
2292
2293 BT_DBG("hci%u", index);
2294
Johan Hedbergd9306502012-02-20 23:25:18 +02002295 if (len != sizeof(*mgmt_cp))
2296 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2297 MGMT_STATUS_INVALID_PARAMS);
2298
Johan Hedberg14a53662011-04-27 10:29:56 -04002299 hdev = hci_dev_get(index);
2300 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002301 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2302 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002303
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002304 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002305
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002306 if (!hci_discovery_active(hdev)) {
Johan Hedbergd9306502012-02-20 23:25:18 +02002307 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY,
2308 MGMT_STATUS_REJECTED,
2309 &mgmt_cp->type, sizeof(mgmt_cp->type));
2310 goto unlock;
2311 }
2312
2313 if (hdev->discovery.type != mgmt_cp->type) {
2314 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY,
2315 MGMT_STATUS_INVALID_PARAMS,
2316 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002317 goto unlock;
Johan Hedbergff9ef572012-01-04 14:23:45 +02002318 }
2319
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002320 cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002321 if (!cmd) {
2322 err = -ENOMEM;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002323 goto unlock;
Johan Hedberg14a53662011-04-27 10:29:56 -04002324 }
2325
Andre Guedes343f9352012-02-17 20:39:37 -03002326 if (hdev->discovery.state == DISCOVERY_FINDING) {
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002327 err = hci_cancel_inquiry(hdev);
2328 if (err < 0)
2329 mgmt_pending_remove(cmd);
2330 else
2331 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
2332 goto unlock;
2333 }
2334
2335 e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
2336 if (!e) {
2337 mgmt_pending_remove(cmd);
Johan Hedbergaee9b212012-02-18 15:07:59 +02002338 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, 0,
Johan Hedbergd9306502012-02-20 23:25:18 +02002339 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002340 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2341 goto unlock;
2342 }
2343
2344 bacpy(&cp.bdaddr, &e->data.bdaddr);
2345 err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
2346 sizeof(cp), &cp);
Johan Hedberg14a53662011-04-27 10:29:56 -04002347 if (err < 0)
2348 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002349 else
2350 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002351
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002352unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002353 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002354 hci_dev_put(hdev);
2355
2356 return err;
2357}
2358
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002359static int confirm_name(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg561aafb2012-01-04 13:31:59 +02002360{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002361 struct mgmt_cp_confirm_name *cp = data;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002362 struct inquiry_entry *e;
2363 struct hci_dev *hdev;
2364 int err;
2365
2366 BT_DBG("hci%u", index);
2367
2368 if (len != sizeof(*cp))
2369 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2370 MGMT_STATUS_INVALID_PARAMS);
2371
2372 hdev = hci_dev_get(index);
2373 if (!hdev)
2374 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2375 MGMT_STATUS_INVALID_PARAMS);
2376
2377 hci_dev_lock(hdev);
2378
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002379 if (!hci_discovery_active(hdev)) {
2380 err = cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2381 MGMT_STATUS_FAILED);
2382 goto failed;
2383 }
2384
Johan Hedberga198e7b2012-02-17 14:27:06 +02002385 e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002386 if (!e) {
2387 err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME,
2388 MGMT_STATUS_INVALID_PARAMS);
2389 goto failed;
2390 }
2391
2392 if (cp->name_known) {
2393 e->name_state = NAME_KNOWN;
2394 list_del(&e->list);
2395 } else {
2396 e->name_state = NAME_NEEDED;
Johan Hedberga3d4e20a2012-01-09 00:53:02 +02002397 hci_inquiry_cache_update_resolve(hdev, e);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002398 }
2399
2400 err = 0;
2401
2402failed:
2403 hci_dev_unlock(hdev);
2404
2405 return err;
2406}
2407
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002408static int block_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002409{
2410 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002411 struct mgmt_cp_block_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002412 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002413 int err;
2414
2415 BT_DBG("hci%u", index);
2416
Antti Julku7fbec222011-06-15 12:01:15 +03002417 if (len != sizeof(*cp))
2418 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002419 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002420
2421 hdev = hci_dev_get(index);
2422 if (!hdev)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002423 return cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
2424 MGMT_STATUS_INVALID_PARAMS,
2425 &cp->addr, sizeof(cp->addr));
Antti Julku7fbec222011-06-15 12:01:15 +03002426
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002427 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002428
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002429 err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002430 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002431 status = MGMT_STATUS_FAILED;
Antti Julku7fbec222011-06-15 12:01:15 +03002432 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002433 status = 0;
2434
2435 err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, status,
2436 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002437
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002438 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002439 hci_dev_put(hdev);
2440
2441 return err;
2442}
2443
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002444static int unblock_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002445{
2446 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002447 struct mgmt_cp_unblock_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002448 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002449 int err;
2450
2451 BT_DBG("hci%u", index);
2452
Antti Julku7fbec222011-06-15 12:01:15 +03002453 if (len != sizeof(*cp))
2454 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002455 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002456
2457 hdev = hci_dev_get(index);
2458 if (!hdev)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002459 return cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2460 MGMT_STATUS_INVALID_PARAMS,
2461 &cp->addr, sizeof(cp->addr));
Antti Julku7fbec222011-06-15 12:01:15 +03002462
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002463 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002464
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002465 err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002466 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002467 status = MGMT_STATUS_INVALID_PARAMS;
Antti Julku7fbec222011-06-15 12:01:15 +03002468 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002469 status = 0;
2470
2471 err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, status,
2472 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002473
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002474 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002475 hci_dev_put(hdev);
2476
2477 return err;
2478}
2479
Antti Julkuf6422ec2011-06-22 13:11:56 +03002480static int set_fast_connectable(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002481 void *data, u16 len)
Antti Julkuf6422ec2011-06-22 13:11:56 +03002482{
2483 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002484 struct mgmt_mode *cp = data;
Antti Julkuf6422ec2011-06-22 13:11:56 +03002485 struct hci_cp_write_page_scan_activity acp;
2486 u8 type;
2487 int err;
2488
2489 BT_DBG("hci%u", index);
2490
2491 if (len != sizeof(*cp))
2492 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002493 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002494
2495 hdev = hci_dev_get(index);
2496 if (!hdev)
2497 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002498 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002499
2500 hci_dev_lock(hdev);
2501
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002502 if (cp->val) {
Antti Julkuf6422ec2011-06-22 13:11:56 +03002503 type = PAGE_SCAN_TYPE_INTERLACED;
2504 acp.interval = 0x0024; /* 22.5 msec page scan interval */
2505 } else {
2506 type = PAGE_SCAN_TYPE_STANDARD; /* default */
2507 acp.interval = 0x0800; /* default 1.28 sec page scan */
2508 }
2509
2510 acp.window = 0x0012; /* default 11.25 msec page scan window */
2511
2512 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
2513 sizeof(acp), &acp);
2514 if (err < 0) {
2515 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002516 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002517 goto done;
2518 }
2519
2520 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
2521 if (err < 0) {
2522 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002523 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002524 goto done;
2525 }
2526
Johan Hedbergaee9b212012-02-18 15:07:59 +02002527 err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, 0,
2528 NULL, 0);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002529done:
2530 hci_dev_unlock(hdev);
2531 hci_dev_put(hdev);
2532
2533 return err;
2534}
2535
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002536static int load_long_term_keys(struct sock *sk, u16 index,
2537 void *cp_data, u16 len)
2538{
2539 struct hci_dev *hdev;
2540 struct mgmt_cp_load_long_term_keys *cp = cp_data;
2541 u16 key_count, expected_len;
2542 int i;
2543
2544 if (len < sizeof(*cp))
2545 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2546 EINVAL);
2547
2548 key_count = get_unaligned_le16(&cp->key_count);
2549
2550 expected_len = sizeof(*cp) + key_count *
2551 sizeof(struct mgmt_ltk_info);
2552 if (expected_len != len) {
2553 BT_ERR("load_keys: expected %u bytes, got %u bytes",
2554 len, expected_len);
2555 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2556 EINVAL);
2557 }
2558
2559 hdev = hci_dev_get(index);
2560 if (!hdev)
2561 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2562 ENODEV);
2563
2564 BT_DBG("hci%u key_count %u", index, key_count);
2565
2566 hci_dev_lock(hdev);
2567
2568 hci_smp_ltks_clear(hdev);
2569
2570 for (i = 0; i < key_count; i++) {
2571 struct mgmt_ltk_info *key = &cp->keys[i];
2572 u8 type;
2573
2574 if (key->master)
2575 type = HCI_SMP_LTK;
2576 else
2577 type = HCI_SMP_LTK_SLAVE;
2578
2579 hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
2580 type, 0, key->authenticated, key->val,
2581 key->enc_size, key->ediv, key->rand);
2582 }
2583
2584 hci_dev_unlock(hdev);
2585 hci_dev_put(hdev);
2586
2587 return 0;
2588}
2589
Johan Hedberg03811012010-12-08 00:21:06 +02002590int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
2591{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002592 void *buf;
2593 u8 *cp;
Johan Hedberg03811012010-12-08 00:21:06 +02002594 struct mgmt_hdr *hdr;
Szymon Janc4e51eae2011-02-25 19:05:48 +01002595 u16 opcode, index, len;
Johan Hedberg03811012010-12-08 00:21:06 +02002596 int err;
2597
2598 BT_DBG("got %zu bytes", msglen);
2599
2600 if (msglen < sizeof(*hdr))
2601 return -EINVAL;
2602
Gustavo F. Padovane63a15e2011-04-04 18:56:53 -03002603 buf = kmalloc(msglen, GFP_KERNEL);
Johan Hedberg03811012010-12-08 00:21:06 +02002604 if (!buf)
2605 return -ENOMEM;
2606
2607 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
2608 err = -EFAULT;
2609 goto done;
2610 }
2611
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002612 hdr = buf;
Johan Hedberg03811012010-12-08 00:21:06 +02002613 opcode = get_unaligned_le16(&hdr->opcode);
Szymon Janc4e51eae2011-02-25 19:05:48 +01002614 index = get_unaligned_le16(&hdr->index);
Johan Hedberg03811012010-12-08 00:21:06 +02002615 len = get_unaligned_le16(&hdr->len);
2616
2617 if (len != msglen - sizeof(*hdr)) {
2618 err = -EINVAL;
2619 goto done;
2620 }
2621
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002622 cp = buf + sizeof(*hdr);
2623
Johan Hedberg03811012010-12-08 00:21:06 +02002624 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +02002625 case MGMT_OP_READ_VERSION:
2626 err = read_version(sk);
2627 break;
Johan Hedberge70bb2e2012-02-13 16:59:33 +02002628 case MGMT_OP_READ_COMMANDS:
2629 err = read_commands(sk);
2630 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +02002631 case MGMT_OP_READ_INDEX_LIST:
2632 err = read_index_list(sk);
2633 break;
Johan Hedbergf7b64e692010-12-13 21:07:06 +02002634 case MGMT_OP_READ_INFO:
Szymon Janc4e51eae2011-02-25 19:05:48 +01002635 err = read_controller_info(sk, index);
Johan Hedbergf7b64e692010-12-13 21:07:06 +02002636 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002637 case MGMT_OP_SET_POWERED:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002638 err = set_powered(sk, index, cp, len);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002639 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +02002640 case MGMT_OP_SET_DISCOVERABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002641 err = set_discoverable(sk, index, cp, len);
Johan Hedberg73f22f62010-12-29 16:00:25 +02002642 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002643 case MGMT_OP_SET_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002644 err = set_connectable(sk, index, cp, len);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002645 break;
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002646 case MGMT_OP_SET_FAST_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002647 err = set_fast_connectable(sk, index, cp, len);
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002648 break;
Johan Hedbergc542a062011-01-26 13:11:03 +02002649 case MGMT_OP_SET_PAIRABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002650 err = set_pairable(sk, index, cp, len);
Johan Hedbergc542a062011-01-26 13:11:03 +02002651 break;
Johan Hedberg33ef95e2012-02-16 23:56:27 +02002652 case MGMT_OP_SET_LINK_SECURITY:
2653 err = set_link_security(sk, index, cp, len);
2654 break;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02002655 case MGMT_OP_SET_SSP:
2656 err = set_ssp(sk, index, cp, len);
2657 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002658 case MGMT_OP_ADD_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002659 err = add_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002660 break;
2661 case MGMT_OP_REMOVE_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002662 err = remove_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002663 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002664 case MGMT_OP_SET_DEV_CLASS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002665 err = set_dev_class(sk, index, cp, len);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002666 break;
Johan Hedberg86742e12011-11-07 23:13:38 +02002667 case MGMT_OP_LOAD_LINK_KEYS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002668 err = load_link_keys(sk, index, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002669 break;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002670 case MGMT_OP_DISCONNECT:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002671 err = disconnect(sk, index, cp, len);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002672 break;
Johan Hedberg2784eb42011-01-21 13:56:35 +02002673 case MGMT_OP_GET_CONNECTIONS:
Szymon Janc8ce62842011-03-01 16:55:32 +01002674 err = get_connections(sk, index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02002675 break;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002676 case MGMT_OP_PIN_CODE_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002677 err = pin_code_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002678 break;
2679 case MGMT_OP_PIN_CODE_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002680 err = pin_code_neg_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002681 break;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002682 case MGMT_OP_SET_IO_CAPABILITY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002683 err = set_io_capability(sk, index, cp, len);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002684 break;
Johan Hedberge9a416b2011-02-19 12:05:56 -03002685 case MGMT_OP_PAIR_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002686 err = pair_device(sk, index, cp, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002687 break;
Johan Hedberg28424702012-02-02 04:02:29 +02002688 case MGMT_OP_CANCEL_PAIR_DEVICE:
2689 err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len);
2690 break;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002691 case MGMT_OP_UNPAIR_DEVICE:
2692 err = unpair_device(sk, index, cp, len);
2693 break;
Johan Hedberga5c29682011-02-19 12:05:57 -03002694 case MGMT_OP_USER_CONFIRM_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002695 err = user_confirm_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002696 break;
2697 case MGMT_OP_USER_CONFIRM_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002698 err = user_confirm_neg_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002699 break;
Brian Gix604086b2011-11-23 08:28:33 -08002700 case MGMT_OP_USER_PASSKEY_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002701 err = user_passkey_reply(sk, index, cp, len);
Brian Gix604086b2011-11-23 08:28:33 -08002702 break;
2703 case MGMT_OP_USER_PASSKEY_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002704 err = user_passkey_neg_reply(sk, index, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002705 break;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002706 case MGMT_OP_SET_LOCAL_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002707 err = set_local_name(sk, index, cp, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002708 break;
Szymon Jancc35938b2011-03-22 13:12:21 +01002709 case MGMT_OP_READ_LOCAL_OOB_DATA:
2710 err = read_local_oob_data(sk, index);
2711 break;
Szymon Janc2763eda2011-03-22 13:12:22 +01002712 case MGMT_OP_ADD_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002713 err = add_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002714 break;
2715 case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002716 err = remove_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002717 break;
Johan Hedberg14a53662011-04-27 10:29:56 -04002718 case MGMT_OP_START_DISCOVERY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002719 err = start_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002720 break;
2721 case MGMT_OP_STOP_DISCOVERY:
Johan Hedbergd9306502012-02-20 23:25:18 +02002722 err = stop_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002723 break;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002724 case MGMT_OP_CONFIRM_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002725 err = confirm_name(sk, index, cp, len);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002726 break;
Antti Julku7fbec222011-06-15 12:01:15 +03002727 case MGMT_OP_BLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002728 err = block_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002729 break;
2730 case MGMT_OP_UNBLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002731 err = unblock_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002732 break;
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002733 case MGMT_OP_LOAD_LONG_TERM_KEYS:
2734 err = load_long_term_keys(sk, index, cp, len);
2735 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002736 default:
Johan Hedberg72a734e2010-12-30 00:38:22 +02002737 BT_DBG("Unknown op %u", opcode);
Johan Hedbergca69b792011-11-11 18:10:00 +02002738 err = cmd_status(sk, index, opcode,
2739 MGMT_STATUS_UNKNOWN_COMMAND);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002740 break;
2741 }
Johan Hedberg72a734e2010-12-30 00:38:22 +02002742
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002743 if (err < 0)
Johan Hedberg72a734e2010-12-30 00:38:22 +02002744 goto done;
2745
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002746 err = msglen;
2747
2748done:
2749 kfree(buf);
2750 return err;
2751}
2752
Johan Hedbergb24752f2011-11-03 14:40:33 +02002753static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
2754{
2755 u8 *status = data;
2756
2757 cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
2758 mgmt_pending_remove(cmd);
2759}
2760
Johan Hedberg744cf192011-11-08 20:40:14 +02002761int mgmt_index_added(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002762{
Johan Hedberg744cf192011-11-08 20:40:14 +02002763 return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002764}
2765
Johan Hedberg744cf192011-11-08 20:40:14 +02002766int mgmt_index_removed(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002767{
Johan Hedbergb24752f2011-11-03 14:40:33 +02002768 u8 status = ENODEV;
2769
Johan Hedberg744cf192011-11-08 20:40:14 +02002770 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002771
Johan Hedberg744cf192011-11-08 20:40:14 +02002772 return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002773}
2774
2775struct cmd_lookup {
Johan Hedberg03811012010-12-08 00:21:06 +02002776 struct sock *sk;
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002777 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +02002778};
2779
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002780static void settings_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberg03811012010-12-08 00:21:06 +02002781{
Johan Hedberg03811012010-12-08 00:21:06 +02002782 struct cmd_lookup *match = data;
2783
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002784 send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
Johan Hedberg03811012010-12-08 00:21:06 +02002785
2786 list_del(&cmd->list);
2787
2788 if (match->sk == NULL) {
2789 match->sk = cmd->sk;
2790 sock_hold(match->sk);
2791 }
2792
2793 mgmt_pending_free(cmd);
2794}
2795
Johan Hedberg744cf192011-11-08 20:40:14 +02002796int mgmt_powered(struct hci_dev *hdev, u8 powered)
Johan Hedberg03811012010-12-08 00:21:06 +02002797{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002798 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002799 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002800 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002801
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002802 mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002803
Johan Hedbergb24752f2011-11-03 14:40:33 +02002804 if (!powered) {
2805 u8 status = ENETDOWN;
Johan Hedberg744cf192011-11-08 20:40:14 +02002806 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002807 }
2808
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002809 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002810
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002811 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002812 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002813
2814 if (match.sk)
2815 sock_put(match.sk);
2816
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002817 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002818}
2819
Johan Hedberg744cf192011-11-08 20:40:14 +02002820int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
Johan Hedberg03811012010-12-08 00:21:06 +02002821{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002822 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002823 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002824 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002825
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002826 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002827
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002828 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002829
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002830 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002831 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002832 if (match.sk)
2833 sock_put(match.sk);
2834
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002835 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002836}
2837
Johan Hedberg744cf192011-11-08 20:40:14 +02002838int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
Johan Hedberg03811012010-12-08 00:21:06 +02002839{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002840 __le32 ev;
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002841 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002842 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002843
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002844 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
2845 &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002846
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002847 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002848
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002849 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002850
2851 if (match.sk)
2852 sock_put(match.sk);
2853
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002854 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002855}
2856
Johan Hedberg744cf192011-11-08 20:40:14 +02002857int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002858{
Johan Hedbergca69b792011-11-11 18:10:00 +02002859 u8 mgmt_err = mgmt_status(status);
2860
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002861 if (scan & SCAN_PAGE)
Johan Hedberg744cf192011-11-08 20:40:14 +02002862 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002863 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002864
2865 if (scan & SCAN_INQUIRY)
Johan Hedberg744cf192011-11-08 20:40:14 +02002866 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002867 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002868
2869 return 0;
2870}
2871
Johan Hedberg744cf192011-11-08 20:40:14 +02002872int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
2873 u8 persistent)
Johan Hedberg03811012010-12-08 00:21:06 +02002874{
Johan Hedberg86742e12011-11-07 23:13:38 +02002875 struct mgmt_ev_new_link_key ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002876
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002877 memset(&ev, 0, sizeof(ev));
Johan Hedberg03811012010-12-08 00:21:06 +02002878
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002879 ev.store_hint = persistent;
Johan Hedbergd753fdc2012-02-17 14:06:34 +02002880 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2881 ev.key.addr.type = MGMT_ADDR_BREDR;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002882 ev.key.type = key->type;
2883 memcpy(ev.key.val, key->val, 16);
2884 ev.key.pin_len = key->pin_len;
Johan Hedberg03811012010-12-08 00:21:06 +02002885
Johan Hedberg744cf192011-11-08 20:40:14 +02002886 return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002887}
Johan Hedbergf7520542011-01-20 12:34:39 +02002888
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002889int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
2890{
2891 struct mgmt_ev_new_long_term_key ev;
2892
2893 memset(&ev, 0, sizeof(ev));
2894
2895 ev.store_hint = persistent;
2896 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2897 ev.key.addr.type = key->bdaddr_type;
2898 ev.key.authenticated = key->authenticated;
2899 ev.key.enc_size = key->enc_size;
2900 ev.key.ediv = key->ediv;
2901
2902 if (key->type == HCI_SMP_LTK)
2903 ev.key.master = 1;
2904
2905 memcpy(ev.key.rand, key->rand, sizeof(key->rand));
2906 memcpy(ev.key.val, key->val, sizeof(key->val));
2907
2908 return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
2909 &ev, sizeof(ev), NULL);
2910}
2911
Johan Hedbergafc747a2012-01-15 18:11:07 +02002912int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedbergb644ba32012-01-17 21:48:47 +02002913 u8 addr_type, u8 *name, u8 name_len,
2914 u8 *dev_class)
Johan Hedbergf7520542011-01-20 12:34:39 +02002915{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002916 char buf[512];
2917 struct mgmt_ev_device_connected *ev = (void *) buf;
2918 u16 eir_len = 0;
Johan Hedbergf7520542011-01-20 12:34:39 +02002919
Johan Hedbergb644ba32012-01-17 21:48:47 +02002920 bacpy(&ev->addr.bdaddr, bdaddr);
2921 ev->addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002922
Johan Hedbergb644ba32012-01-17 21:48:47 +02002923 if (name_len > 0)
2924 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
2925 name, name_len);
2926
2927 if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
2928 eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
2929 EIR_CLASS_OF_DEV, dev_class, 3);
2930
2931 put_unaligned_le16(eir_len, &ev->eir_len);
2932
2933 return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
2934 sizeof(*ev) + eir_len, NULL);
Johan Hedbergf7520542011-01-20 12:34:39 +02002935}
2936
Johan Hedberg8962ee72011-01-20 12:40:27 +02002937static void disconnect_rsp(struct pending_cmd *cmd, void *data)
2938{
Szymon Jancc68fb7f2011-03-22 13:12:19 +01002939 struct mgmt_cp_disconnect *cp = cmd->param;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002940 struct sock **sk = data;
Johan Hedberga38528f2011-01-22 06:46:43 +02002941 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002942
Johan Hedberg88c3df12012-02-09 14:27:38 +02002943 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2944 rp.addr.type = cp->addr.type;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002945
Johan Hedbergaee9b212012-02-18 15:07:59 +02002946 cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
2947 sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02002948
2949 *sk = cmd->sk;
2950 sock_hold(*sk);
2951
Johan Hedberga664b5b2011-02-19 12:06:02 -03002952 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002953}
2954
Johan Hedberg124f6e32012-02-09 13:50:12 +02002955static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberga8a1d192011-11-10 15:54:38 +02002956{
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002957 struct hci_dev *hdev = data;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002958 struct mgmt_cp_unpair_device *cp = cmd->param;
2959 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002960
2961 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02002962 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2963 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002964
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002965 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
2966
Johan Hedbergaee9b212012-02-18 15:07:59 +02002967 cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02002968
2969 mgmt_pending_remove(cmd);
2970}
2971
Johan Hedbergafc747a2012-01-15 18:11:07 +02002972int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
2973 u8 link_type, u8 addr_type)
Johan Hedbergf7520542011-01-20 12:34:39 +02002974{
Johan Hedberg4c659c32011-11-07 23:13:39 +02002975 struct mgmt_addr_info ev;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002976 struct sock *sk = NULL;
2977 int err;
2978
Johan Hedberg744cf192011-11-08 20:40:14 +02002979 mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
Johan Hedbergf7520542011-01-20 12:34:39 +02002980
Johan Hedbergf7520542011-01-20 12:34:39 +02002981 bacpy(&ev.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02002982 ev.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002983
Johan Hedbergafc747a2012-01-15 18:11:07 +02002984 err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
2985 sk);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002986
2987 if (sk)
2988 sock_put(sk);
2989
Johan Hedberg124f6e32012-02-09 13:50:12 +02002990 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002991 hdev);
Johan Hedberga8a1d192011-11-10 15:54:38 +02002992
Johan Hedberg8962ee72011-01-20 12:40:27 +02002993 return err;
2994}
2995
Johan Hedberg88c3df12012-02-09 14:27:38 +02002996int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
2997 u8 link_type, u8 addr_type, u8 status)
Johan Hedberg8962ee72011-01-20 12:40:27 +02002998{
Johan Hedberg88c3df12012-02-09 14:27:38 +02002999 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003000 struct pending_cmd *cmd;
3001 int err;
3002
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003003 cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003004 if (!cmd)
3005 return -ENOENT;
3006
Johan Hedberg88c3df12012-02-09 14:27:38 +02003007 bacpy(&rp.addr.bdaddr, bdaddr);
3008 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg37d9ef72011-11-10 15:54:39 +02003009
Johan Hedberg88c3df12012-02-09 14:27:38 +02003010 err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
Johan Hedbergaee9b212012-02-18 15:07:59 +02003011 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02003012
Johan Hedberga664b5b2011-02-19 12:06:02 -03003013 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003014
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003015 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
3016 hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003017 return err;
Johan Hedbergf7520542011-01-20 12:34:39 +02003018}
Johan Hedberg17d5c042011-01-22 06:09:08 +02003019
Johan Hedberg48264f02011-11-09 13:58:58 +02003020int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3021 u8 addr_type, u8 status)
Johan Hedberg17d5c042011-01-22 06:09:08 +02003022{
3023 struct mgmt_ev_connect_failed ev;
3024
Johan Hedberg4c659c32011-11-07 23:13:39 +02003025 bacpy(&ev.addr.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02003026 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003027 ev.status = mgmt_status(status);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003028
Johan Hedberg744cf192011-11-08 20:40:14 +02003029 return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003030}
Johan Hedberg980e1a52011-01-22 06:10:07 +02003031
Johan Hedberg744cf192011-11-08 20:40:14 +02003032int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003033{
3034 struct mgmt_ev_pin_code_request ev;
3035
Johan Hedbergd8457692012-02-17 14:24:57 +02003036 bacpy(&ev.addr.bdaddr, bdaddr);
3037 ev.addr.type = MGMT_ADDR_BREDR;
Waldemar Rymarkiewicza770bb52011-04-28 12:07:59 +02003038 ev.secure = secure;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003039
Johan Hedberg744cf192011-11-08 20:40:14 +02003040 return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003041 NULL);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003042}
3043
Johan Hedberg744cf192011-11-08 20:40:14 +02003044int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3045 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003046{
3047 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003048 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003049 int err;
3050
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003051 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003052 if (!cmd)
3053 return -ENOENT;
3054
Johan Hedbergd8457692012-02-17 14:24:57 +02003055 bacpy(&rp.addr.bdaddr, bdaddr);
3056 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003057
Johan Hedbergaee9b212012-02-18 15:07:59 +02003058 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
3059 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003060
Johan Hedberga664b5b2011-02-19 12:06:02 -03003061 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003062
3063 return err;
3064}
3065
Johan Hedberg744cf192011-11-08 20:40:14 +02003066int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3067 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003068{
3069 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003070 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003071 int err;
3072
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003073 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003074 if (!cmd)
3075 return -ENOENT;
3076
Johan Hedbergd8457692012-02-17 14:24:57 +02003077 bacpy(&rp.addr.bdaddr, bdaddr);
3078 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003079
Johan Hedbergaee9b212012-02-18 15:07:59 +02003080 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
3081 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003082
Johan Hedberga664b5b2011-02-19 12:06:02 -03003083 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003084
3085 return err;
3086}
Johan Hedberga5c29682011-02-19 12:05:57 -03003087
Johan Hedberg744cf192011-11-08 20:40:14 +02003088int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003089 u8 link_type, u8 addr_type, __le32 value,
3090 u8 confirm_hint)
Johan Hedberga5c29682011-02-19 12:05:57 -03003091{
3092 struct mgmt_ev_user_confirm_request ev;
3093
Johan Hedberg744cf192011-11-08 20:40:14 +02003094 BT_DBG("%s", hdev->name);
Johan Hedberga5c29682011-02-19 12:05:57 -03003095
Johan Hedberg272d90d2012-02-09 15:26:12 +02003096 bacpy(&ev.addr.bdaddr, bdaddr);
3097 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg55bc1a32011-04-28 11:28:56 -07003098 ev.confirm_hint = confirm_hint;
Johan Hedberga5c29682011-02-19 12:05:57 -03003099 put_unaligned_le32(value, &ev.value);
3100
Johan Hedberg744cf192011-11-08 20:40:14 +02003101 return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003102 NULL);
Johan Hedberga5c29682011-02-19 12:05:57 -03003103}
3104
Johan Hedberg272d90d2012-02-09 15:26:12 +02003105int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
3106 u8 link_type, u8 addr_type)
Brian Gix604086b2011-11-23 08:28:33 -08003107{
3108 struct mgmt_ev_user_passkey_request ev;
3109
3110 BT_DBG("%s", hdev->name);
3111
Johan Hedberg272d90d2012-02-09 15:26:12 +02003112 bacpy(&ev.addr.bdaddr, bdaddr);
3113 ev.addr.type = link_to_mgmt(link_type, addr_type);
Brian Gix604086b2011-11-23 08:28:33 -08003114
3115 return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
3116 NULL);
3117}
3118
Brian Gix0df4c182011-11-16 13:53:13 -08003119static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003120 u8 link_type, u8 addr_type, u8 status,
3121 u8 opcode)
Johan Hedberga5c29682011-02-19 12:05:57 -03003122{
3123 struct pending_cmd *cmd;
3124 struct mgmt_rp_user_confirm_reply rp;
3125 int err;
3126
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003127 cmd = mgmt_pending_find(opcode, hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03003128 if (!cmd)
3129 return -ENOENT;
3130
Johan Hedberg272d90d2012-02-09 15:26:12 +02003131 bacpy(&rp.addr.bdaddr, bdaddr);
3132 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergaee9b212012-02-18 15:07:59 +02003133 err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
3134 &rp, sizeof(rp));
Johan Hedberga5c29682011-02-19 12:05:57 -03003135
Johan Hedberga664b5b2011-02-19 12:06:02 -03003136 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03003137
3138 return err;
3139}
3140
Johan Hedberg744cf192011-11-08 20:40:14 +02003141int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003142 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003143{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003144 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3145 status, MGMT_OP_USER_CONFIRM_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003146}
3147
Johan Hedberg272d90d2012-02-09 15:26:12 +02003148int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3149 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003150{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003151 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3152 status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003153}
Johan Hedberg2a611692011-02-19 12:06:00 -03003154
Brian Gix604086b2011-11-23 08:28:33 -08003155int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003156 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003157{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003158 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3159 status, MGMT_OP_USER_PASSKEY_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003160}
3161
Johan Hedberg272d90d2012-02-09 15:26:12 +02003162int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3163 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003164{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003165 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3166 status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003167}
3168
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003169int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3170 u8 addr_type, u8 status)
Johan Hedberg2a611692011-02-19 12:06:00 -03003171{
3172 struct mgmt_ev_auth_failed ev;
3173
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003174 bacpy(&ev.addr.bdaddr, bdaddr);
3175 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003176 ev.status = mgmt_status(status);
Johan Hedberg2a611692011-02-19 12:06:00 -03003177
Johan Hedberg744cf192011-11-08 20:40:14 +02003178 return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg2a611692011-02-19 12:06:00 -03003179}
Johan Hedbergb312b1612011-03-16 14:29:37 +02003180
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003181int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
3182{
3183 struct cmd_lookup match = { NULL, hdev };
3184 __le32 ev;
3185 int err;
3186
3187 if (status) {
3188 u8 mgmt_err = mgmt_status(status);
3189 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
3190 cmd_status_rsp, &mgmt_err);
3191 return 0;
3192 }
3193
3194 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
3195 &match);
3196
3197 ev = cpu_to_le32(get_current_settings(hdev));
3198 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3199
3200 if (match.sk)
3201 sock_put(match.sk);
3202
3203 return err;
3204}
3205
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003206int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status)
3207{
3208 struct cmd_lookup match = { NULL, hdev };
3209 __le32 ev;
3210 int err;
3211
3212 if (status) {
3213 u8 mgmt_err = mgmt_status(status);
3214 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev,
3215 cmd_status_rsp, &mgmt_err);
3216 return 0;
3217 }
3218
3219 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
3220
3221 ev = cpu_to_le32(get_current_settings(hdev));
3222 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3223
3224 if (match.sk)
3225 sock_put(match.sk);
3226
3227 return err;
3228}
3229
Johan Hedberg744cf192011-11-08 20:40:14 +02003230int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
Johan Hedbergb312b1612011-03-16 14:29:37 +02003231{
3232 struct pending_cmd *cmd;
3233 struct mgmt_cp_set_local_name ev;
3234 int err;
3235
3236 memset(&ev, 0, sizeof(ev));
3237 memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
3238
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003239 cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02003240 if (!cmd)
3241 goto send_event;
3242
3243 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003244 err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02003245 mgmt_status(status));
Johan Hedbergb312b1612011-03-16 14:29:37 +02003246 goto failed;
3247 }
3248
Johan Hedberg744cf192011-11-08 20:40:14 +02003249 update_eir(hdev);
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03003250
Johan Hedbergaee9b212012-02-18 15:07:59 +02003251 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
Johan Hedbergb312b1612011-03-16 14:29:37 +02003252 sizeof(ev));
3253 if (err < 0)
3254 goto failed;
3255
3256send_event:
Johan Hedberg744cf192011-11-08 20:40:14 +02003257 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
Johan Hedbergb312b1612011-03-16 14:29:37 +02003258 cmd ? cmd->sk : NULL);
3259
3260failed:
3261 if (cmd)
3262 mgmt_pending_remove(cmd);
3263 return err;
3264}
Szymon Jancc35938b2011-03-22 13:12:21 +01003265
Johan Hedberg744cf192011-11-08 20:40:14 +02003266int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
3267 u8 *randomizer, u8 status)
Szymon Jancc35938b2011-03-22 13:12:21 +01003268{
3269 struct pending_cmd *cmd;
3270 int err;
3271
Johan Hedberg744cf192011-11-08 20:40:14 +02003272 BT_DBG("%s status %u", hdev->name, status);
Szymon Jancc35938b2011-03-22 13:12:21 +01003273
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003274 cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01003275 if (!cmd)
3276 return -ENOENT;
3277
3278 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003279 err = cmd_status(cmd->sk, hdev->id,
Johan Hedbergca69b792011-11-11 18:10:00 +02003280 MGMT_OP_READ_LOCAL_OOB_DATA,
3281 mgmt_status(status));
Szymon Jancc35938b2011-03-22 13:12:21 +01003282 } else {
3283 struct mgmt_rp_read_local_oob_data rp;
3284
3285 memcpy(rp.hash, hash, sizeof(rp.hash));
3286 memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
3287
Johan Hedberg744cf192011-11-08 20:40:14 +02003288 err = cmd_complete(cmd->sk, hdev->id,
3289 MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergaee9b212012-02-18 15:07:59 +02003290 0, &rp, sizeof(rp));
Szymon Jancc35938b2011-03-22 13:12:21 +01003291 }
3292
3293 mgmt_pending_remove(cmd);
3294
3295 return err;
3296}
Johan Hedberge17acd42011-03-30 23:57:16 +03003297
Johan Hedberg48264f02011-11-09 13:58:58 +02003298int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg561aafb2012-01-04 13:31:59 +02003299 u8 addr_type, u8 *dev_class, s8 rssi,
Johan Hedberge319d2e2012-01-15 19:51:59 +02003300 u8 cfm_name, u8 *eir, u16 eir_len)
Johan Hedberge17acd42011-03-30 23:57:16 +03003301{
Johan Hedberge319d2e2012-01-15 19:51:59 +02003302 char buf[512];
3303 struct mgmt_ev_device_found *ev = (void *) buf;
Johan Hedberg1dc06092012-01-15 21:01:23 +02003304 size_t ev_size;
Johan Hedberge17acd42011-03-30 23:57:16 +03003305
Johan Hedberg1dc06092012-01-15 21:01:23 +02003306 /* Leave 5 bytes for a potential CoD field */
3307 if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
Andre Guedes7d262f82012-01-10 18:20:49 -03003308 return -EINVAL;
3309
Johan Hedberg1dc06092012-01-15 21:01:23 +02003310 memset(buf, 0, sizeof(buf));
3311
Johan Hedberge319d2e2012-01-15 19:51:59 +02003312 bacpy(&ev->addr.bdaddr, bdaddr);
3313 ev->addr.type = link_to_mgmt(link_type, addr_type);
3314 ev->rssi = rssi;
3315 ev->confirm_name = cfm_name;
Johan Hedberge17acd42011-03-30 23:57:16 +03003316
Johan Hedberg1dc06092012-01-15 21:01:23 +02003317 if (eir_len > 0)
Johan Hedberge319d2e2012-01-15 19:51:59 +02003318 memcpy(ev->eir, eir, eir_len);
Johan Hedberge17acd42011-03-30 23:57:16 +03003319
Johan Hedberg1dc06092012-01-15 21:01:23 +02003320 if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
3321 eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
3322 dev_class, 3);
3323
3324 put_unaligned_le16(eir_len, &ev->eir_len);
3325
3326 ev_size = sizeof(*ev) + eir_len;
Andre Guedesf8523592011-09-09 18:56:26 -03003327
Johan Hedberge319d2e2012-01-15 19:51:59 +02003328 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
Johan Hedberge17acd42011-03-30 23:57:16 +03003329}
Johan Hedberga88a9652011-03-30 13:18:12 +03003330
Johan Hedbergb644ba32012-01-17 21:48:47 +02003331int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3332 u8 addr_type, s8 rssi, u8 *name, u8 name_len)
Johan Hedberga88a9652011-03-30 13:18:12 +03003333{
Johan Hedbergb644ba32012-01-17 21:48:47 +02003334 struct mgmt_ev_device_found *ev;
3335 char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
3336 u16 eir_len;
Johan Hedberga88a9652011-03-30 13:18:12 +03003337
Johan Hedbergb644ba32012-01-17 21:48:47 +02003338 ev = (struct mgmt_ev_device_found *) buf;
Johan Hedberga88a9652011-03-30 13:18:12 +03003339
Johan Hedbergb644ba32012-01-17 21:48:47 +02003340 memset(buf, 0, sizeof(buf));
Johan Hedberga88a9652011-03-30 13:18:12 +03003341
Johan Hedbergb644ba32012-01-17 21:48:47 +02003342 bacpy(&ev->addr.bdaddr, bdaddr);
3343 ev->addr.type = link_to_mgmt(link_type, addr_type);
3344 ev->rssi = rssi;
3345
3346 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
3347 name_len);
3348
3349 put_unaligned_le16(eir_len, &ev->eir_len);
3350
Johan Hedberg053c7e02012-02-04 00:06:00 +02003351 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
3352 sizeof(*ev) + eir_len, NULL);
Johan Hedberga88a9652011-03-30 13:18:12 +03003353}
Johan Hedberg314b2382011-04-27 10:29:57 -04003354
Andre Guedes7a135102011-11-09 17:14:25 -03003355int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
Johan Hedberg164a6e72011-11-01 17:06:44 +02003356{
3357 struct pending_cmd *cmd;
Johan Hedbergf808e162012-02-19 12:52:07 +02003358 u8 type;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003359 int err;
3360
Andre Guedes203159d2012-02-13 15:41:01 -03003361 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
3362
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003363 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003364 if (!cmd)
3365 return -ENOENT;
3366
Johan Hedbergf808e162012-02-19 12:52:07 +02003367 type = hdev->discovery.type;
3368
3369 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3370 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003371 mgmt_pending_remove(cmd);
3372
3373 return err;
3374}
3375
Andre Guedese6d465c2011-11-09 17:14:26 -03003376int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
3377{
3378 struct pending_cmd *cmd;
3379 int err;
3380
3381 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
3382 if (!cmd)
3383 return -ENOENT;
3384
Johan Hedbergd9306502012-02-20 23:25:18 +02003385 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3386 &hdev->discovery.type,
3387 sizeof(hdev->discovery.type));
Johan Hedberg03811012010-12-08 00:21:06 +02003388 mgmt_pending_remove(cmd);
3389
3390 return err;
3391}
Johan Hedberg314b2382011-04-27 10:29:57 -04003392
Johan Hedberg744cf192011-11-08 20:40:14 +02003393int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
Johan Hedberg314b2382011-04-27 10:29:57 -04003394{
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003395 struct mgmt_ev_discovering ev;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003396 struct pending_cmd *cmd;
3397
Andre Guedes343fb142011-11-22 17:14:19 -03003398 BT_DBG("%s discovering %u", hdev->name, discovering);
3399
Johan Hedberg164a6e72011-11-01 17:06:44 +02003400 if (discovering)
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003401 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003402 else
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003403 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003404
3405 if (cmd != NULL) {
Johan Hedbergf808e162012-02-19 12:52:07 +02003406 u8 type = hdev->discovery.type;
3407
Johan Hedbergd9306502012-02-20 23:25:18 +02003408 cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0,
Johan Hedbergf808e162012-02-19 12:52:07 +02003409 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003410 mgmt_pending_remove(cmd);
3411 }
3412
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003413 memset(&ev, 0, sizeof(ev));
3414 ev.type = hdev->discovery.type;
3415 ev.discovering = discovering;
3416
3417 return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg314b2382011-04-27 10:29:57 -04003418}
Antti Julku5e762442011-08-25 16:48:02 +03003419
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003420int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003421{
3422 struct pending_cmd *cmd;
3423 struct mgmt_ev_device_blocked ev;
3424
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003425 cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003426
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003427 bacpy(&ev.addr.bdaddr, bdaddr);
3428 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003429
Johan Hedberg744cf192011-11-08 20:40:14 +02003430 return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
3431 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003432}
3433
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003434int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003435{
3436 struct pending_cmd *cmd;
3437 struct mgmt_ev_device_unblocked ev;
3438
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003439 cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003440
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003441 bacpy(&ev.addr.bdaddr, bdaddr);
3442 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003443
Johan Hedberg744cf192011-11-08 20:40:14 +02003444 return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
3445 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003446}
Marcel Holtmannd7b7e792012-02-20 21:47:49 +01003447
3448module_param(enable_hs, bool, 0644);
3449MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
3450
3451module_param(enable_le, bool, 0644);
3452MODULE_PARM_DESC(enable_le, "Enable Low Energy support");